From 812aa90acbef4930ae356ab194f3f2a8f573f6e4 Mon Sep 17 00:00:00 2001 From: Martin Schröder Date: Tue, 26 Dec 2006 23:37:34 +0000 Subject: Import pdftex 1.40-rc4, which also changes the signature of print_version_and_exit, and adds a new library (libs/obsdcompat). git-svn-id: svn://tug.org/texlive/trunk@2959 c570f23f-e606-0410-a88d-b1316a301751 --- Build/source/Makefile.in | 3 + Build/source/configure | 26 +- Build/source/configure.in | 13 +- Build/source/libs/configure | 3 + Build/source/libs/configure.in | 3 + Build/source/texk/web2c/Makefile.in | 20 +- Build/source/texk/web2c/bibtex.ch | 2 +- Build/source/texk/web2c/configure | 16 +- Build/source/texk/web2c/configure.in | 3 +- Build/source/texk/web2c/cpascal.h | 3 +- Build/source/texk/web2c/cwebboot.c | 2 +- Build/source/texk/web2c/cwebdir/comm-w2c.ch | 2 +- Build/source/texk/web2c/dvicopy.ch | 2 +- Build/source/texk/web2c/dvitomp.ch | 2 +- Build/source/texk/web2c/dvitype.ch | 2 +- Build/source/texk/web2c/gftodvi.ch | 2 +- Build/source/texk/web2c/gftopk.ch | 2 +- Build/source/texk/web2c/gftype.ch | 2 +- Build/source/texk/web2c/lib/printversion.c | 10 +- Build/source/texk/web2c/lib/texmfmp.c | 39 +- Build/source/texk/web2c/mft.ch | 4 +- Build/source/texk/web2c/omegaware/odvicopy.ch | 2 +- Build/source/texk/web2c/omegaware/odvitype.ch | 2 +- Build/source/texk/web2c/omegaware/ofm2opl.ch | 2 +- Build/source/texk/web2c/omegaware/opl2ofm.ch | 2 +- Build/source/texk/web2c/omegaware/otangle.ch | 2 +- Build/source/texk/web2c/omegaware/ovf2ovp.ch | 2 +- Build/source/texk/web2c/omegaware/ovp2ovf.ch | 2 +- Build/source/texk/web2c/patgen.ch | 3 +- Build/source/texk/web2c/pdfetexdir/ChangeLog | 69 - Build/source/texk/web2c/pdfetexdir/Makefile.in | 3 - Build/source/texk/web2c/pdfetexdir/NEWS | 1 - Build/source/texk/web2c/pdfetexdir/README | 1 - Build/source/texk/web2c/pdfetexdir/extratime.ch | 131 - Build/source/texk/web2c/pdfetexdir/pdfetex.ch1 | 82 - Build/source/texk/web2c/pdfetexdir/pdfetex.ch2 | 242 - Build/source/texk/web2c/pdfetexdir/pdfetex.defines | 105 - Build/source/texk/web2c/pdfetexdir/pdfetex.h | 50 - Build/source/texk/web2c/pdfetexdir/pdfetex.mak | 164 - Build/source/texk/web2c/pdfetexdir/pdfetex.mk | 133 - Build/source/texk/web2c/pdfetexdir/pdfetexextra.in | 41 - Build/source/texk/web2c/pdfetexdir/tex.ch0 | 42 - Build/source/texk/web2c/pdfetexdir/tex.ch1 | 45 - Build/source/texk/web2c/pdfetexdir/tex.ch2 | 21 - Build/source/texk/web2c/pdftexdir/.indent.pro | 12 + Build/source/texk/web2c/pdftexdir/ChangeLog | 97 +- Build/source/texk/web2c/pdftexdir/Makefile.in | 15 +- Build/source/texk/web2c/pdftexdir/NEWS | 332 +- Build/source/texk/web2c/pdftexdir/README | 37 +- Build/source/texk/web2c/pdftexdir/avl.c | 1135 +- Build/source/texk/web2c/pdftexdir/avl.h | 145 +- Build/source/texk/web2c/pdftexdir/avlstuff.c | 119 +- Build/source/texk/web2c/pdftexdir/change-files.txt | 109 +- Build/source/texk/web2c/pdftexdir/depend.mk | 7 +- Build/source/texk/web2c/pdftexdir/epdf.c | 82 +- Build/source/texk/web2c/pdftexdir/epdf.h | 149 +- Build/source/texk/web2c/pdftexdir/hz.ch | 1935 - Build/source/texk/web2c/pdftexdir/image.h | 22 +- Build/source/texk/web2c/pdftexdir/macnames.c | 14 +- Build/source/texk/web2c/pdftexdir/mapfile.c | 958 +- Build/source/texk/web2c/pdftexdir/misc.ch | 21 - Build/source/texk/web2c/pdftexdir/noligatures.ch | 116 - Build/source/texk/web2c/pdftexdir/pdfstrcmp.ch | 609 - Build/source/texk/web2c/pdftexdir/pdftex.ch | 7115 +--- Build/source/texk/web2c/pdftexdir/pdftex.defines | 39 +- Build/source/texk/web2c/pdftexdir/pdftex.h | 19 +- Build/source/texk/web2c/pdftexdir/pdftex.mk | 180 +- Build/source/texk/web2c/pdftexdir/pdftex.web | 39123 +++++++++++++++++++ Build/source/texk/web2c/pdftexdir/pdftex2.ch | 89 - Build/source/texk/web2c/pdftexdir/pdftexextra.in | 9 +- Build/source/texk/web2c/pdftexdir/pdftexlib.mk | 14 +- Build/source/texk/web2c/pdftexdir/pdftoepdf.cc | 854 +- Build/source/texk/web2c/pdftexdir/pdftosrc.cc | 68 +- Build/source/texk/web2c/pdftexdir/pkin.c | 580 +- Build/source/texk/web2c/pdftexdir/ptexlib.h | 270 +- Build/source/texk/web2c/pdftexdir/ptexmac.h | 260 +- Build/source/texk/web2c/pdftexdir/randoms.ch | 569 - Build/source/texk/web2c/pdftexdir/subfont.c | 229 + Build/source/texk/web2c/pdftexdir/tex.ch0 | 10 +- Build/source/texk/web2c/pdftexdir/tex.ch1 | 39 - Build/source/texk/web2c/pdftexdir/tex.pch | 355 - Build/source/texk/web2c/pdftexdir/tounicode.c | 438 + Build/source/texk/web2c/pdftexdir/ttf2afm.c | 431 +- Build/source/texk/web2c/pdftexdir/utils.c | 1333 +- Build/source/texk/web2c/pdftexdir/vadjust.ch | 273 - Build/source/texk/web2c/pdftexdir/vfpacket.c | 19 +- Build/source/texk/web2c/pdftexdir/writeenc.c | 206 +- Build/source/texk/web2c/pdftexdir/writefont.c | 761 +- Build/source/texk/web2c/pdftexdir/writeimg.c | 80 +- Build/source/texk/web2c/pdftexdir/writejbig2.c | 745 + Build/source/texk/web2c/pdftexdir/writejbig2.h | 151 + Build/source/texk/web2c/pdftexdir/writejpg.c | 283 +- Build/source/texk/web2c/pdftexdir/writepng.c | 583 +- Build/source/texk/web2c/pdftexdir/writet1.c | 1344 +- Build/source/texk/web2c/pdftexdir/writet3.c | 173 +- Build/source/texk/web2c/pdftexdir/writettf.c | 913 +- Build/source/texk/web2c/pdftexdir/writettf.h | 38 +- Build/source/texk/web2c/pdftexdir/writezip.c | 39 +- Build/source/texk/web2c/pktogf.ch | 2 +- Build/source/texk/web2c/pktype.ch | 2 +- Build/source/texk/web2c/pltotf.ch | 2 +- Build/source/texk/web2c/pooltype.ch | 3 +- Build/source/texk/web2c/tangle.ch | 4 +- Build/source/texk/web2c/tangleboot.p | 2 +- Build/source/texk/web2c/texmfmp-help.h | 62 +- Build/source/texk/web2c/texmfmp.h | 13 +- Build/source/texk/web2c/tftopl.ch | 2 +- Build/source/texk/web2c/vftovp.ch | 2 +- Build/source/texk/web2c/vptovf.ch | 2 +- Build/source/texk/web2c/weave.ch | 4 +- Build/source/texk/web2c/web2c/coerce.h | 4 - Build/source/texk/web2c/web2c/convert | 4 +- 112 files changed, 47716 insertions(+), 17246 deletions(-) delete mode 100644 Build/source/texk/web2c/pdfetexdir/ChangeLog delete mode 100644 Build/source/texk/web2c/pdfetexdir/Makefile.in delete mode 120000 Build/source/texk/web2c/pdfetexdir/NEWS delete mode 120000 Build/source/texk/web2c/pdfetexdir/README delete mode 100644 Build/source/texk/web2c/pdfetexdir/extratime.ch delete mode 100644 Build/source/texk/web2c/pdfetexdir/pdfetex.ch1 delete mode 100644 Build/source/texk/web2c/pdfetexdir/pdfetex.ch2 delete mode 100644 Build/source/texk/web2c/pdfetexdir/pdfetex.defines delete mode 100644 Build/source/texk/web2c/pdfetexdir/pdfetex.h delete mode 100644 Build/source/texk/web2c/pdfetexdir/pdfetex.mak delete mode 100644 Build/source/texk/web2c/pdfetexdir/pdfetex.mk delete mode 100644 Build/source/texk/web2c/pdfetexdir/pdfetexextra.in delete mode 100644 Build/source/texk/web2c/pdfetexdir/tex.ch0 delete mode 100644 Build/source/texk/web2c/pdfetexdir/tex.ch1 delete mode 100644 Build/source/texk/web2c/pdfetexdir/tex.ch2 create mode 100644 Build/source/texk/web2c/pdftexdir/.indent.pro delete mode 100644 Build/source/texk/web2c/pdftexdir/hz.ch delete mode 100644 Build/source/texk/web2c/pdftexdir/misc.ch delete mode 100644 Build/source/texk/web2c/pdftexdir/noligatures.ch delete mode 100644 Build/source/texk/web2c/pdftexdir/pdfstrcmp.ch create mode 100644 Build/source/texk/web2c/pdftexdir/pdftex.web delete mode 100644 Build/source/texk/web2c/pdftexdir/pdftex2.ch delete mode 100644 Build/source/texk/web2c/pdftexdir/randoms.ch create mode 100644 Build/source/texk/web2c/pdftexdir/subfont.c delete mode 100644 Build/source/texk/web2c/pdftexdir/tex.ch1 delete mode 100644 Build/source/texk/web2c/pdftexdir/tex.pch create mode 100644 Build/source/texk/web2c/pdftexdir/tounicode.c delete mode 100644 Build/source/texk/web2c/pdftexdir/vadjust.ch create mode 100644 Build/source/texk/web2c/pdftexdir/writejbig2.c create mode 100644 Build/source/texk/web2c/pdftexdir/writejbig2.h (limited to 'Build') diff --git a/Build/source/Makefile.in b/Build/source/Makefile.in index 525f105d46f..533e00d5faa 100644 --- a/Build/source/Makefile.in +++ b/Build/source/Makefile.in @@ -41,6 +41,7 @@ FREETYPEDIR=libs/freetype FREETYPE2DIR=libs/freetype2 ICUDIR=libs/icu-xetex TECKITDIR=libs/teckit +LIBOBSDCOMPATDIR=libs/obsdcompat # Subdirectories that have makefiles ESUBDIRS = @ESUBDIRS@ texk @@ -176,6 +177,8 @@ $(ICUDIR)/lib/libsicuuc.a $(ICUDIR)/lib/libsicule.a $(ICUDIR)/lib/libsicudata.a: cd $(ICUDIR); $(MAKE) $(TECKITDIR)/lib/.libs/libTECkit.a: cd $(TECKITDIR); $(MAKE) +$(LIBOBSDCOMPATDIR)/libopenbsd-compat.a: + cd $(LIBOBSDCOMPATDIR); $(MAKE) libopenbsd-compat.a # Prevent GNU make v3 from overflowing arg limit on SysV. .NOEXPORT: diff --git a/Build/source/configure b/Build/source/configure index 609f72000b7..04209966065 100755 --- a/Build/source/configure +++ b/Build/source/configure @@ -3389,6 +3389,7 @@ FREETYPEDIR=libs/freetype FREETYPE2DIR=libs/freetype2 ICUDIR=libs/icu-xetex TECKITDIR=libs/teckit +LIBOBSDCOMPATDIR=libs/obsdcompat GDDIR=libs/gd test "$no_x" = yes && { with_xdvik=no; with_oxdvik=no; } @@ -3437,6 +3438,13 @@ test "$with_xetex" != no && : ${needs_libxpdf=yes} : ${needs_libxpdf=no} export needs_libxpdf +# we need libobsdcompat for pdftex +test ! -d $srcdir/$LIBOBSDCOMPATDIR && : ${needs_obsdcompat=no} +test "$with_pdftex" != no && : ${needs_obsdcompat=yes} +test "$with_pdfetex" != no && : ${needs_obsdcompat=yes} +: ${needs_obsdcompat=no} +export needs_obsdcompat + # we need gd for dvipng test ! -d $srcdir/$GDDIR && : ${needs_gd=no} test "$with_dvipng" != no && : ${needs_gd=yes} @@ -4780,11 +4788,18 @@ else fi +if test "$needs_obsdcompat" = no; then + LIBOBSDCFLAGS= + LDLIBOBSD= + LIBOBSDDEP= +else + LIBOBSDCFLAGS='-I$(LIBOBSDDIR)' + LDLIBOBSD='$(LIBOBSDCOMPATDIR)/libopenbsd-compat.a' + LIBOBSDDEP='$(LIBOBSDCOMPATDIR)/libopenbsd-compat.a' +fi - - -LIBSDEP="$CURSESDEP $ZLIBDEP $LIBPNGDEP $LIBXPDFDEP $LIBT1DEP $GDDEP $FREETYPEDEP $FREETYPE2DEP $ICUDEP $TECKITDEP" +LIBSDEP="$CURSESDEP $ZLIBDEP $LIBPNGDEP $LIBXPDFDEP $LIBT1DEP $GDDEP $FREETYPEDEP $FREETYPE2DEP $ICUDEP $TECKITDEP $LIBOBSDDEP" LIBSDIRS= test "$needs_ncurses" = yes && test "$using_system_ncurses" != yes \ @@ -4807,6 +4822,8 @@ test "$needs_icu" = yes && test "$using_system_icu" != yes \ && LIBSDIRS="$ICUDIR $LIBSDIRS" test "$needs_teckit" = yes \ && LIBSDIRS="$TECKITDIR $LIBSDIRS" +test "$needs_obsdcompat" = yes \ + && LIBSDIRS="$LIBOBSDCOMPATDIR $LIBSDIRS" subdirs="$subdirs libs" @@ -5041,6 +5058,9 @@ s%@CURSESDEP@%$CURSESDEP%g s%@LIBXPDFCPPFLAGS@%$LIBXPDFCPPFLAGS%g s%@LDLIBXPDF@%$LDLIBXPDF%g s%@LIBXPDFDEP@%$LIBXPDFDEP%g +s%@LIBOBSDCFLAGS@%$LIBOBSDCFLAGS%g +s%@LDLIBOBSD@%$LDLIBOBSD%g +s%@LIBOBSDDEP@%$LIBOBSDDEP%g s%@LIBT1CPPFLAGS@%$LIBT1CPPFLAGS%g s%@LDLIBT1@%$LDLIBT1%g s%@LIBT1DEP@%$LIBT1DEP%g diff --git a/Build/source/configure.in b/Build/source/configure.in index 4a15068bb86..cea71276fca 100644 --- a/Build/source/configure.in +++ b/Build/source/configure.in @@ -115,6 +115,7 @@ FREETYPE2DIR=libs/freetype2 ICUDIR=libs/icu-xetex TECKITDIR=libs/teckit GDDIR=libs/gd +LIBOBSDCOMPATDIR=libs/obsdcompat test "$no_x" = yes && { with_xdvik=no; with_oxdvik=no; } export with_xdvik with_oxdvik @@ -162,6 +163,13 @@ test "$with_xetex" != no && : ${needs_libxpdf=yes} : ${needs_libxpdf=no} export needs_libxpdf +# we need libobsdcompat for pdftex +test ! -d $srcdir/$LIBOBSDCOMPATDIR && : ${needs_obsdcompat=no} +test "$with_pdftex" != no && : ${needs_obsdcompat=yes} +test "$with_pdfetex" != no && : ${needs_obsdcompat=yes} +: ${needs_obsdcompat=no} +export needs_obsdcompat + # we need gd for dvipng test ! -d $srcdir/$GDDIR && : ${needs_gd=no} test "$with_dvipng" != no && : ${needs_gd=yes} @@ -206,8 +214,9 @@ sinclude(libs/freetype/freetype.ac) sinclude(libs/freetype2/freetype2.ac) sinclude(libs/icu-xetex/icu-xetex.ac) sinclude(libs/teckit/teckit.ac) +sinclude($LIBOBSDCOMPATDIR/libobsdcompat.ac) -LIBSDEP="$CURSESDEP $ZLIBDEP $LIBPNGDEP $LIBXPDFDEP $LIBT1DEP $GDDEP $FREETYPEDEP $FREETYPE2DEP $ICUDEP $TECKITDEP" +LIBSDEP="$CURSESDEP $ZLIBDEP $LIBPNGDEP $LIBXPDFDEP $LIBT1DEP $GDDEP $FREETYPEDEP $FREETYPE2DEP $ICUDEP $TECKITDEP $LIBOBSDDEP" LIBSDIRS= test "$needs_ncurses" = yes && test "$using_system_ncurses" != yes \ @@ -230,6 +239,8 @@ test "$needs_icu" = yes && test "$using_system_icu" != yes \ && LIBSDIRS="$ICUDIR $LIBSDIRS" test "$needs_teckit" = yes \ && LIBSDIRS="$TECKITDIR $LIBSDIRS" +test "$needs_obsdcompat" = yes \ + && LIBSDIRS="$LIBOBSDCOMPATDIR $LIBSDIRS" AC_CONFIG_SUBDIRS([libs]) diff --git a/Build/source/libs/configure b/Build/source/libs/configure index c441b160fee..ca1b1e1cf56 100755 --- a/Build/source/libs/configure +++ b/Build/source/libs/configure @@ -3161,6 +3161,9 @@ if test "x$needs_zlib" != xno && test "$using_system_zlib" != yes; then ac_config_files="$ac_config_files zlib/Makefile" fi +if test "x$needs_obsdcompat" != xno; then + subdirs="$subdirs obsdcompat" +fi # create md5 directory when building outside the srcdir: if test -d $srcdir/md5; then diff --git a/Build/source/libs/configure.in b/Build/source/libs/configure.in index 0344c31a1f3..f6371675eca 100644 --- a/Build/source/libs/configure.in +++ b/Build/source/libs/configure.in @@ -66,6 +66,9 @@ fi if test "x$needs_zlib" != xno && test "$using_system_zlib" != yes; then KPSE_CONFIG_FILES([zlib/Makefile]) fi +if test "x$needs_obsdcompat" != xno; then + AC_CONFIG_SUBDIRS([obsdcompat]) +fi # create md5 directory when building outside the srcdir: if test -d $srcdir/md5; then diff --git a/Build/source/texk/web2c/Makefile.in b/Build/source/texk/web2c/Makefile.in index b8a3a33c272..49a3e985fb5 100644 --- a/Build/source/texk/web2c/Makefile.in +++ b/Build/source/texk/web2c/Makefile.in @@ -37,6 +37,8 @@ DIFFFLAGS = kpse_include ../make/common.mk kpse_include ../make/programs.mk +XCPPFLAGS = $(PDFXCPPFLAGS) + CXX = @CXX@ CXXLD = $(CXX) cxx_link_command = $(CXXLD) -o $@ $(LDFLAGS) @@ -558,7 +560,6 @@ kpse_include etexdir/etex.mk kpse_include omegadir/omega.mk kpse_include alephdir/aleph.mk kpse_include pdftexdir/pdftex.mk -kpse_include pdfetexdir/pdfetex.mk kpse_include xetexdir/xetex.mk # # Common @@ -1001,14 +1002,14 @@ manpages: @MAINT@ cd $(distdir); rm -f $(addsuffix .c, $(programs) tangleboot) @MAINT@ cd $(distdir); rm -f $(addsuffix .h, $(programs) tangleboot) @MAINT@ cd $(distdir); rm -f etex.ch omega.ch aleph.ch -@MAINT@ cd $(distdir); rm -f pdftex.ch pdfetex.ch +@MAINT@ cd $(distdir); rm -f pdftex.ch @MAINT@ cd $(distdir); bash -c "rm -f tests/x* tests/allbib.{bbl,blg,dvi,log}" @MAINT@ cd $(distdir)/tests && ln -s ../../../../tests/xampl.bib . @MAINT@ cd $(distdir); bash -c "rm -f $(omega_files) alephmem.h" @MAINT@ cd $(distdir); bash -c "rm -f cweb.c *{coerce,d}.h" @MAINT@ cd $(distdir); bash -c "rm -f omega*.c aleph*.c" @MAINT@ cd $(distdir); bash -c "rm -f tex*.c etex*.c" -@MAINT@ cd $(distdir); bash -c "rm -f pdftex*.c pdfetex*.c" +@MAINT@ cd $(distdir); bash -c "rm -f pdftex*.c" @MAINT@ cd $(distdir); bash -c "rm -f mf*.c mp*.c" @MAINT@ cd $(distdir); bash -c "rm -f macnames.c *extra.c" @MAINT@ touch $(distdir)/doc/*.info* @@ -1018,7 +1019,7 @@ manpages: @MAINT@ mkdir -p $(distdir) @MAINT@ for f in *.web; do ln $$f $(distdir); done @MAINT@ rm -f $(distdir)/etex.web $(distdir)/omega.web $(distdir)/aleph.web -@MAINT@ rm -f $(distdir)/pdftex.web $(distdir)/pdfetex.web +@MAINT@ rm -f $(distdir)/pdfetex.web @MAINT@ cd $(distdir); bash -c "rm -f $(omega_files)" @MAINT@ chmod -w $(distdir)/* @MAINT@ GZIP=-9 tar czf web-$(version).tar.gz $(top_distdir) @@ -1059,10 +1060,9 @@ manpages: @MAINT@ GZIP=-9 tar czf web2c-$(version)-aleph-`cat alephdir/aleph.version`.tar.gz $(top_distdir) @MAINT@ rm -rf $(top_distdir) @MAINT@ -@MAINT@pdftex-dist: pdftexdir/pdftex.version +@MAINT@pdfetex-dist: pdfetexdir/pdfetex.version @MAINT@ rm -rf $(top_distdir) -@MAINT@ mkdir -p $(distdir)/pdftexdir $(distdir)/pdfetexdir -@MAINT@ cp -rp pdftexdir/* $(distdir)/pdftexdir +@MAINT@ mkdir -p $(distdir)/pdfetexdir @MAINT@ cp -rp pdfetexdir/* $(distdir)/pdfetexdir @MAINT@ rm -rf $(distdir)/pdf*texdir/CVS $(distdir)/pdf*texdir/*/CVS @MAINT@ rm -f $(distdir)/pdf*texdir/Makefile @@ -1085,7 +1085,7 @@ manpages: @MAINT@ rm -f $(top_distdir)/libs/xpdf/*/Makefile @MAINT@ rm -f $(top_distdir)/libs/xpdf/*/*.[oa] @MAINT@ rm -f $(top_distdir)/libs/xpdf/*/c-auto.h -@MAINT@ GZIP=-9 tar czf web2c-$(version)-pdftex-`cat pdftexdir/pdftex.version`.tar.gz $(top_distdir) +@MAINT@ GZIP=-9 tar czf web2c-$(version)-pdftex-`cat pdfetexdir/pdfetex.version`.tar.gz $(top_distdir) @MAINT@ rm -rf $(top_distdir) @MAINT@ @MAINT@libs-dist: @@ -1100,7 +1100,7 @@ manpages: @MAINT@ rm -f $(libs_distdir)/*/config.* @MAINT@ rm -f $(libs_distdir)/*/c-auto.h @MAINT@ rm -f $(libs_distdir)/*/*.[oa] -@MAINT@ GZIP=-9 tar czf web2c-$(version)-pdftexlibs.tar.gz $(top_distdir) +@MAINT@ GZIP=-9 tar czf web2c-$(version)-pdfetexlibs.tar.gz $(top_distdir) @MAINT@ rm -rf $(top_distdir) kpse_include ../make/dist.mk @@ -1118,7 +1118,7 @@ installcheck: # # Cleaning. -all_subdirs = doc lib man $(mpware) web2c window $(omegafonts) $(otps) $(pdftexdir) $(pdfetexdir) $(mpostdir) +all_subdirs = doc lib man $(mpware) web2c window $(omegafonts) $(otps) $(pdftexdir) $(mpostdir) # Having a multiple-target rule with the subdir loop fails because of # the dependencies introduced by clean.mk. Yet, we want the diff --git a/Build/source/texk/web2c/bibtex.ch b/Build/source/texk/web2c/bibtex.ch index 1bd2f219808..972667faf81 100644 --- a/Build/source/texk/web2c/bibtex.ch +++ b/Build/source/texk/web2c/bibtex.ch @@ -1205,7 +1205,7 @@ begin usage_help (BIBTEX_HELP, nil); end else if argument_is ('version') then begin - print_version_and_exit (banner, 'Oren Patashnik', nil); + print_version_and_exit (banner, 'Oren Patashnik', nil, nil); end; {Else it was a flag; |getopt| has already done the assignment.} until getopt_return_val = -1; diff --git a/Build/source/texk/web2c/configure b/Build/source/texk/web2c/configure index 0a2e9656079..4e11231b3ca 100755 --- a/Build/source/texk/web2c/configure +++ b/Build/source/texk/web2c/configure @@ -2779,6 +2779,16 @@ else LIBXPDFDEP='$(LIBXPDFDIR)/xpdf/libxpdf.a $(LIBXPDFDIR)/goo/libGoo.a $(LIBXPDFDIR)/fofi/libfofi.a' fi +if test "$needs_obsdcompat" = no; then + LIBOBSDCFLAGS= + LDLIBOBSD= + LIBOBSDDEP= +else + LIBOBSDCFLAGS='-I$(LIBOBSDDIR)' + LDLIBOBSD='$(LIBOBSDDIR)/libopenbsd-compat.a' + LIBOBSDDEP='$(LIBOBSDDIR)/libopenbsd-compat.a' +fi + @@ -3186,8 +3196,7 @@ else fi # For pdfeTeX -if test -d $srcdir/pdfetexdir && test -d $srcdir/pdftexdir \ - && test -d $srcdir/etexdir; then +if test -d $srcdir/pdftexdir; then test "${with_pdfetex+set}" = set || with_pdfetex=yes else if test "x$with_pdfetex" = xyes; then @@ -5291,6 +5300,9 @@ s%@LIBPNGDEP@%$LIBPNGDEP%g s%@LIBXPDFCPPFLAGS@%$LIBXPDFCPPFLAGS%g s%@LDLIBXPDF@%$LDLIBXPDF%g s%@LIBXPDFDEP@%$LIBXPDFDEP%g +s%@LIBOBSDCFLAGS@%$LIBOBSDCFLAGS%g +s%@LDLIBOBSD@%$LDLIBOBSD%g +s%@LIBOBSDDEP@%$LIBOBSDDEP%g s%@FREETYPE2CPPFLAGS@%$FREETYPE2CPPFLAGS%g s%@LDFREETYPE2@%$LDFREETYPE2%g s%@FREETYPE2DEP@%$FREETYPE2DEP%g diff --git a/Build/source/texk/web2c/configure.in b/Build/source/texk/web2c/configure.in index 40ec4974d27..d13e73f9ff5 100644 --- a/Build/source/texk/web2c/configure.in +++ b/Build/source/texk/web2c/configure.in @@ -157,8 +157,7 @@ else fi # For pdfeTeX -if test -d $srcdir/pdfetexdir && test -d $srcdir/pdftexdir \ - && test -d $srcdir/etexdir; then +if test -d $srcdir/pdftexdir; then test "${with_pdfetex+set}" = set || with_pdfetex=yes else if test "x$with_pdfetex" = xyes; then diff --git a/Build/source/texk/web2c/cpascal.h b/Build/source/texk/web2c/cpascal.h index efe830359dd..c16d12f06d7 100644 --- a/Build/source/texk/web2c/cpascal.h +++ b/Build/source/texk/web2c/cpascal.h @@ -23,6 +23,7 @@ #include #include #include +#include /* Help messages. */ #include "help.h" @@ -253,7 +254,7 @@ extern boolean eoln P1H(FILE *); extern void readln P1H(FILE *); extern void fprintreal P4H(FILE *, double, int, int); extern integer inputint P1H(FILE *); -extern void printversionandexit P3H(const_string, const_string, const_string); +extern void printversionandexit P4H(const_string, const_string, const_string, char*); extern void zinput2ints P2H(integer *, integer *); extern void zinput3ints P3H(integer *, integer *, integer *); extern integer zround P1H(double); diff --git a/Build/source/texk/web2c/cwebboot.c b/Build/source/texk/web2c/cwebboot.c index 1514ba76f72..30f96a37712 100644 --- a/Build/source/texk/web2c/cwebboot.c +++ b/Build/source/texk/web2c/cwebboot.c @@ -1164,7 +1164,7 @@ if(strcmp("-version",*argv)==0||strcmp("--version",*argv)==0) #line 614 "./cwebdir/comm-w2c.ch" printversionandexit((program==ctangle?ctangle_banner:cweave_banner), -"Silvio Levy and Donald E. Knuth",NULL); +"Silvio Levy and Donald E. Knuth",NULL, NULL); /*:85*/ diff --git a/Build/source/texk/web2c/cwebdir/comm-w2c.ch b/Build/source/texk/web2c/cwebdir/comm-w2c.ch index 2a80aca53c6..40c65a814e1 100644 --- a/Build/source/texk/web2c/cwebdir/comm-w2c.ch +++ b/Build/source/texk/web2c/cwebdir/comm-w2c.ch @@ -613,7 +613,7 @@ usagehelp(program==ctangle ? CTANGLEHELP : CWEAVEHELP, NULL); @= printversionandexit((program==ctangle ? ctangle_banner : cweave_banner), - "Silvio Levy and Donald E. Knuth", NULL); + "Silvio Levy and Donald E. Knuth", NULL, NULL); @.--version@> @** Index. diff --git a/Build/source/texk/web2c/dvicopy.ch b/Build/source/texk/web2c/dvicopy.ch index 4d6a7d39143..fc99074b1ac 100644 --- a/Build/source/texk/web2c/dvicopy.ch +++ b/Build/source/texk/web2c/dvicopy.ch @@ -517,7 +517,7 @@ begin usage_help (DVICOPY_HELP, nil); end else if argument_is ('version') then begin - print_version_and_exit (banner, 'Peter Breitenlohner', nil); + print_version_and_exit (banner, 'Peter Breitenlohner', nil, nil); end else if argument_is ('magnification') then begin out_mag := atou (optarg); diff --git a/Build/source/texk/web2c/dvitomp.ch b/Build/source/texk/web2c/dvitomp.ch index 65ae6e931d7..c30adbbaeca 100644 --- a/Build/source/texk/web2c/dvitomp.ch +++ b/Build/source/texk/web2c/dvitomp.ch @@ -442,7 +442,7 @@ begin usage_help (DVITOMP_HELP, nil); end else if argument_is ('version') then begin - print_version_and_exit (term_banner, 'AT&T Bell Laboraties', 'John Hobby.'); + print_version_and_exit (term_banner, 'AT&T Bell Laboraties', 'John Hobby.', nil); end; {Else it was a flag; |getopt| has already done the assignment.} until getopt_return_val = -1; diff --git a/Build/source/texk/web2c/dvitype.ch b/Build/source/texk/web2c/dvitype.ch index 7e05f1c7c95..248fb21ce3d 100644 --- a/Build/source/texk/web2c/dvitype.ch +++ b/Build/source/texk/web2c/dvitype.ch @@ -535,7 +535,7 @@ begin usage_help (DVITYPE_HELP, nil); end else if argument_is ('version') then begin - print_version_and_exit (banner, nil, 'D.E. Knuth'); + print_version_and_exit (banner, nil, 'D.E. Knuth', nil); end else if argument_is ('output-level') then begin out_mode := optarg[0] - '0'; diff --git a/Build/source/texk/web2c/gftodvi.ch b/Build/source/texk/web2c/gftodvi.ch index 4808125681d..979c0466654 100644 --- a/Build/source/texk/web2c/gftodvi.ch +++ b/Build/source/texk/web2c/gftodvi.ch @@ -626,7 +626,7 @@ begin usage_help (GFTODVI_HELP, nil); end else if argument_is ('version') then begin - print_version_and_exit (banner, nil, 'D.E. Knuth'); + print_version_and_exit (banner, nil, 'D.E. Knuth', nil); end else if argument_is ('overflow-label-offset') then begin offset_in_points := atof (optarg); diff --git a/Build/source/texk/web2c/gftopk.ch b/Build/source/texk/web2c/gftopk.ch index a62a73c6ef7..16f8824c4b6 100644 --- a/Build/source/texk/web2c/gftopk.ch +++ b/Build/source/texk/web2c/gftopk.ch @@ -374,7 +374,7 @@ begin usage_help (GFTOPK_HELP, nil); end else if argument_is ('version') then begin - print_version_and_exit (banner, nil, 'Tomas Rokicki'); + print_version_and_exit (banner, nil, 'Tomas Rokicki', nil); end; {Else it was a flag; |getopt| has already done the assignment.} until getopt_return_val = -1; diff --git a/Build/source/texk/web2c/gftype.ch b/Build/source/texk/web2c/gftype.ch index 9c52f5099fa..9eb5ecdde85 100644 --- a/Build/source/texk/web2c/gftype.ch +++ b/Build/source/texk/web2c/gftype.ch @@ -320,7 +320,7 @@ begin usage_help (GFTYPE_HELP, nil); end else if argument_is ('version') then begin - print_version_and_exit (banner, nil, 'D.R. Fuchs'); + print_version_and_exit (banner, nil, 'D.R. Fuchs', nil); end; {Else it was a flag.} until getopt_return_val = -1; diff --git a/Build/source/texk/web2c/lib/printversion.c b/Build/source/texk/web2c/lib/printversion.c index cbcf5d56a00..a120deccb3c 100644 --- a/Build/source/texk/web2c/lib/printversion.c +++ b/Build/source/texk/web2c/lib/printversion.c @@ -16,8 +16,10 @@ This is PROGRAM, Version VERSION-NUMBER program, but tangle doesn't allow multiline string constants ... */ void -printversionandexit P3C(const_string, banner, - const_string, copyright_holder, const_string, author) +printversionandexit P4C(const_string, banner, + const_string, copyright_holder, + const_string, author, + char*, extra_info) { extern string versionstring; /* from web2c/lib/version.c */ extern KPSEDLL string kpathsea_version_string;/* from kpathsea/version.c */ @@ -54,5 +56,9 @@ printversionandexit P3C(const_string, banner, printf ("Primary author of %s: %s.\n", prog_name, author); puts ("Kpathsea written by Karl Berry, Olaf Weber, and others.\n"); + if (extra_info) { + puts (extra_info); + } + uexit (0); } diff --git a/Build/source/texk/web2c/lib/texmfmp.c b/Build/source/texk/web2c/lib/texmfmp.c index 865a991b39b..cf7fa725f93 100644 --- a/Build/source/texk/web2c/lib/texmfmp.c +++ b/Build/source/texk/web2c/lib/texmfmp.c @@ -44,8 +44,7 @@ #include #elif defined (pdfTeX) #include -#elif defined (pdfeTeX) -#include +#include #elif defined (Omega) #include #elif defined (eOmega) @@ -187,7 +186,7 @@ maininit P2C(int, ac, string *, av) /* Must be initialized before options are parsed. */ interactionoption = 4; -#if defined(pdfTeX) || defined(pdfeTeX) +#if defined(pdfTeX) ptexbanner = BANNER; #endif @@ -273,7 +272,7 @@ maininit P2C(int, ac, string *, av) } #endif #endif -#if defined(eTeX) || defined(pdfeTeX) || defined(Aleph) || defined(XeTeX) +#if defined(eTeX) || defined(Aleph) || defined(XeTeX) if (etexp) { fprintf(stderr, "-etex only works with -ini\n"); } @@ -591,7 +590,6 @@ ipcpage P1C(int, is_eof) { static boolean begun = false; unsigned len = 0; - unsigned i; string p = ""; if (!begun) { @@ -607,8 +605,9 @@ ipcpage P1C(int, is_eof) #endif name = (string)xmalloc (len + 1); #if !defined(Omega) && !defined(eOmega) && !defined(Aleph) - strncpy (name, &strpool[strstart[outputfilename]], len); + strncpy (name, (string)&strpool[strstart[outputfilename]], len); #else + unsigned i; for (i=0; i - - * pdfetex.ch2: Adapted to e-TeX version 2.2. - -2004-08-26 Olaf Weber - - * pdfetexextra.in: File was renamed from pdftexextra.h; the - current pdfTeX and e-TeX versions are substituted from - ../pdftexdir/pdftex.version and ../etexdir/etex.version. Variant - of code supplied by Peter Breitenlohner . - - * pdfetex.mk: Added rule to insert pdfTeX and e-TeX versions into - pdfetexextra.h. Variant of code supplied by Peter Breitenlohner - . - -2004-08-25 Olaf Weber - - * pdfetex.mk: vadjust.ch moved from pdfxtexdir to pdftexdir. - -2004-08-17 Olaf Weber - - * pdfetex.mk: Resurrected etex.fix, there is again a need to - include late-breaking changes. - -2004-07-29 Peter Breitenlohner - - * pdfetex.ch[12]: Somewhat rearrange handling of kern and math - nodes in order to reduce interference between eTeX and pdfTeX - change files. - - * pdfetex.ch1 (hlist_out): Keep full mixed direction typesetting - code. - -2004-07-25 Peter Breitenlohner - - * pdfetex.ch[12]: Reorganize additional command codes for convert - and last_item in order to reduce interference between eTeX and - pdfTeX change files. - -2004-07-23 Peter Breitenlohner - - * pdfetex.ch[12], tex.ch[01]: Reorganize handling of banner - string in order to reduce interference between eTeX, pdfTeX, - and web2c change files. - -2004-07-22 Peter Breitenlohner - - * pdfetex.ch[12]: Removed unused token types for pdfTeX's - additional token list parameters. - -2004-07-19 Peter Breitenlohner - - * pdfetex.ch[12], tex.ch0: Reorganize handling of additional - token list and integer parameters and e-TeX's additional - penalties in order to reduce interference between eTeX, pdfTeX, - and web2c change files. - - * pdfetex.mk (pdfetex.ch): Remove tex.ch2 from build rules. - - * tex.ch2: Removed. - -2004-07-17 Peter Breitenlohner - - * pdfetex.mk: Rewrite build rules for pdfetex.{web,ch}, - remove etex.{ch0,ch1,fix}, tex.ch2, and several non-dependencies. - - * pdfetex.ch2: Adapt to eTeX changes. - - * ChangeLog: New file. diff --git a/Build/source/texk/web2c/pdfetexdir/Makefile.in b/Build/source/texk/web2c/pdfetexdir/Makefile.in deleted file mode 100644 index 2e44137efe7..00000000000 --- a/Build/source/texk/web2c/pdfetexdir/Makefile.in +++ /dev/null @@ -1,3 +0,0 @@ -mostlyclean clean: -distclean maintainer-clean extraclean: clean - rm -f Makefile diff --git a/Build/source/texk/web2c/pdfetexdir/NEWS b/Build/source/texk/web2c/pdfetexdir/NEWS deleted file mode 120000 index c0976dad2e1..00000000000 --- a/Build/source/texk/web2c/pdfetexdir/NEWS +++ /dev/null @@ -1 +0,0 @@ -../pdftexdir/NEWS \ No newline at end of file diff --git a/Build/source/texk/web2c/pdfetexdir/README b/Build/source/texk/web2c/pdfetexdir/README deleted file mode 120000 index 4809940adaa..00000000000 --- a/Build/source/texk/web2c/pdfetexdir/README +++ /dev/null @@ -1 +0,0 @@ -../pdftexdir/README \ No newline at end of file diff --git a/Build/source/texk/web2c/pdfetexdir/extratime.ch b/Build/source/texk/web2c/pdfetexdir/extratime.ch deleted file mode 100644 index d417ab25e29..00000000000 --- a/Build/source/texk/web2c/pdfetexdir/extratime.ch +++ /dev/null @@ -1,131 +0,0 @@ -% -% extratime.ch -- benchmarking support -% -% (c) 2005 Taco Hoekwater, taco@elvenkind.com -% -% This change file implements an interface to the precise system -% time (if available) as an aid in benchmarking TeX macro code. -% -% Two new primitives are provided: -% -% \item |\elapsedtime| is a read-only integer that (initially) -% returns the amount of time passed since the start of this run. -% This amount is given in `scaled seconds': the value 65536 -% counts as one second. -% If more time has passed than 32767 seconds, |\maxint| will be -% returned. -% -% \item |\resettime| updates the internal timer, such that -% subsequent calls to |\elapsedtime| will restart from 0. -% -% Besides this web change file, -% there is also a new C function needed in |texmfmp.{c,h}|, and a -% new definition in |texmf.defines| to keep |convert| happy. - -% This change file is intended to be used as the last one in -% pdfetex's |tie| chains, a patch for |Makefile.in| is included. -% -% - -@x -@ The integer parameters should really be initialized by a macro package; -@y -@ @= -@!epochseconds: integer; -@!microseconds: integer; - -@ -@p function get_microinterval:integer; -var s,@!m:integer; {seconds and microseconds} -begin - seconds_and_micros(s,m); - if (s-epochseconds)>32767 then - get_microinterval := max_integer - else if (microseconds -@y -primitive("badness",last_item,badness_code); -@!@:badness_}{\.{\\badness} primitive@> -primitive("elapsedtime",last_item,elapsed_time_code); -@!@:elapsed_time_}{\.{\\elapsedtime} primitive@> -@z - -@x l.30672 -@d pdftex_last_extension_code == pdftex_first_extension_code + 24 -@y -@d reset_timer_code == pdftex_first_extension_code + 25 -@d pdftex_last_extension_code == pdftex_first_extension_code + 25 -@z - - -@x l.30735 -primitive("pdftrailer",extension,pdf_trailer_code);@/ -@!@:pdf_trailer_}{\.{\\pdftrailer} primitive@> -@y -primitive("pdftrailer",extension,pdf_trailer_code);@/ -@!@:pdf_trailer_}{\.{\\pdftrailer} primitive@> -primitive("resettimer",extension,reset_timer_code);@/ -@!@:reset_timer_}{\.{\\resettimer} primitive@> -@z - - -@x l.30776 - pdf_trailer_code: print_esc("pdftrailer"); -@y - pdf_trailer_code: print_esc("pdftrailer"); - reset_timer_code: print_esc("resettimer"); -@z - -@x l.30822 -pdf_trailer_code: @; -@y -pdf_trailer_code: @; -reset_timer_code: @; -@z - - -@x l.31776 -@ @= -@y -@ @= - seconds_and_micros(epochseconds,microseconds); - -@ @= -begin - seconds_and_micros(epochseconds,microseconds); -end - -@ @= -@z - -@x l.33134 -last_node_type_code: print_esc("lastnodetype"); -@y -last_node_type_code: print_esc("lastnodetype"); -elapsed_time_code: print_esc("elapsedtime"); -@z - -@x l.33124 -eTeX_version_code: cur_val:=eTeX_version; -@y -eTeX_version_code: cur_val:=eTeX_version; -elapsed_time_code: cur_val:=get_microinterval; -@z - diff --git a/Build/source/texk/web2c/pdfetexdir/pdfetex.ch1 b/Build/source/texk/web2c/pdfetexdir/pdfetex.ch1 deleted file mode 100644 index 7e9db26eeb6..00000000000 --- a/Build/source/texk/web2c/pdfetexdir/pdfetex.ch1 +++ /dev/null @@ -1,82 +0,0 @@ -% -% Copyright (c) 1996-2002 Han The Thanh, -% -% This file is part of pdfTeX. -% -% pdfTeX is free software; you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation; either version 2 of the License, or -% (at your option) any later version. -% -% pdfTeX is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with pdfTeX; if not, write to the Free Software -% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -% -% $Id: //depot/Build/source.development/TeX/texk/web2c/pdfetexdir/pdfetex.ch1#9 $ -% -@x [1.2] l.188 -@d TeX_banner=='This is TeX, Version 3.141592' {printed when \TeX\ starts} -@# -@d banner==eTeX_banner -@y -@d banner=='This is TeX, Version 3.141592' {printed when \TeX\ starts} -@z - -@x [17.230] l.4727 - first pdfTeX, then e-TeX additional token list parameters -@d tex_toks=local_base+10 {end of \TeX's token list parameters} -@y -@d toks_base=local_base+10 {table of 256 token list registers} -@z - -@x [17.236] l.4960 - first pdfTeX, then e-TeX additional integer parameters -@d tex_int_pars=55 {total number of \TeX's integer parameters} -@y -@d int_pars=55 {total number of integer parameters} -@z - -@x [26.416] l.8400- first pdfTeX, then e-TeX additional command codes -@d badness_code=input_line_no_code+1 {code for \.{\\badness}} -@# -@d eTeX_int=badness_code+1 {first of \eTeX\ codes for integers} -@y -@d badness_code=glue_val+2 {code for \.{\\badness}} -@# -@d eTeX_int=pdftex_last_item_codes+1 {first of \eTeX\ codes for integers} -@z - -@x [26.424] l.8506 -if m>=input_line_no_code then -@y -if cur_chr>glue_val then -@z - -@x [26.424] l.8507 - input_line_no_code: cur_val:=line; - badness_code: cur_val:=last_badness; -@y - begin if cur_chr=input_line_no_code then cur_val:=line - else cur_val:=last_badness; {|cur_chr=badness_code|} -@z - -@x [27.468] l.9207 - first e-TeX, then pdfTeX additional command codes -@d job_name_code=etex_convert_codes {command code for \.{\\jobname}} -@y -@d job_name_code=5 {command code for \.{\\jobname}} -@z - -@x [649] -hpack:=r; -@y -exit: hpack:=r; -@z - -@x [33.651] l.12936 - kern_node: x:=x+width(p); -@y - kern_node,math_node: x:=x+width(p); -@z diff --git a/Build/source/texk/web2c/pdfetexdir/pdfetex.ch2 b/Build/source/texk/web2c/pdfetexdir/pdfetex.ch2 deleted file mode 100644 index 67ddf06edd9..00000000000 --- a/Build/source/texk/web2c/pdfetexdir/pdfetex.ch2 +++ /dev/null @@ -1,242 +0,0 @@ -% -% Copyright (c) 1996-2002 Han The Thanh, -% -% This file is part of pdfTeX. -% -% pdfTeX is free software; you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation; either version 2 of the License, or -% (at your option) any later version. -% -% pdfTeX is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with pdfTeX; if not, write to the Free Software -% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -% -% $Id: //depot/Build/source.development/TeX/texk/web2c/pdfetexdir/pdfetex.ch2#20 $ -% -@x limbo -\def\pdfTeX{pdf\TeX} -@y -\def\pdfTeX{pdf\TeX} -\def\pdfeTeX{pdf\eTeX} -@z - -@x [2] -@d pdfTeX_banner=='This is pdfTeX, Version 3.141592',pdftex_version_string -@y -@d pdfeTeX_banner=='This is pdfeTeX, Version 3.141592',pdftex_version_string,eTeX_version_string - {printed when \pdfeTeX\ starts} -@# -@d pdfTeX_banner=='This is pdfTeX, Version 3.141592',pdftex_version_string -@z - -@x [2] -@d banner==pdfTeX_banner -@y -@d banner==pdfeTeX_banner -@z - -@x [230] -- first pdfTeX, then e-TeX additional token list parameters -@d toks_base=pdf_toks {table of 256 token list registers} -@# -@d etex_toks_base=tex_toks {base for \eTeX's token list parameters} -@y -@d etex_toks_base=pdf_toks {base for \eTeX's token list parameters} -@z - -@x [236] -- first pdfTeX, then e-TeX additional integer parameters -@d int_pars=pdf_int_pars {total number of integer parameters} -@# -@d etex_int_base=tex_int_pars {base for \eTeX's integer parameters} -@y -@d etex_int_base=pdf_int_pars {base for \eTeX's integer parameters} -@z - -@x [424] - else begin case m of - begin case m of -@y - else begin case m of -@z - -@x [424] - end; {there are no other cases} -@y -@z - -@x [468] -- first e-TeX, then pdfTeX additional command codes -@d pdftex_first_expand_code = 5 {base for \pdfTeX's command codes} -@y -@d pdftex_first_expand_code = etex_convert_codes {base for \pdfTeX's command codes} -@z - -@x [32f] -@!edge:scaled; {left edge of sub-box, or right edge of leader space} -@y -@!edge:scaled; {right edge of sub-box or leader space} -@!prev_p:pointer; {one step behind |p|} -@z - -@x [32f] -base_line:=cur_v; left_edge:=cur_h; -@y -base_line:=cur_v; -prev_p:=this_box+list_offset; -@; -left_edge:=cur_h; -@z - -@x [32f] -decr(cur_s); -@y -@; -decr(cur_s); -@z - -@x [32f] - p:=link(p); -@y - prev_p:=link(prev_p); {N.B.: not |prev_p:=p|, |p| might be |lig_trick|} - p:=link(p); -@z - -@x [32f] -kern_node,math_node:cur_h:=cur_h+width(p); -@y -kern_node:cur_h:=cur_h+width(p); -math_node: @; -@z - -@x [32f] -othercases do_nothing -@y -@/@@; -othercases do_nothing -@z - -@x [32f] -next_p:p:=link(p); -@y -next_p:prev_p:=p; p:=link(p); -@z - -@x [32f] - temp_ptr:=p; edge:=cur_h; -@y - temp_ptr:=p; edge:=cur_h+width(p); - if cur_dir=right_to_left then cur_h:=edge; -@z - -@x [32f] - cur_h:=edge+width(p); cur_v:=base_line; -@y - cur_h:=edge; cur_v:=base_line; -@z - -@x [32f] -rule_wd:=rule_wd+cur_g; -@y -rule_wd:=rule_wd+cur_g; -if eTeX_ex then @; -@z - -@x [32f] - edge:=cur_h+rule_wd; lx:=0; -@y - if cur_dir=right_to_left then cur_h:=cur_h-10; - edge:=cur_h+rule_wd; lx:=0; -@z - -@x [32f] - cur_h:=edge-10; goto next_p; -@y - if cur_dir=right_to_left then cur_h:=edge - else cur_h:=edge-10; - goto next_p; -@z - -@x [32f] -save_h:=cur_h; temp_ptr:=leader_box; -@y -save_h:=cur_h; temp_ptr:=leader_box; -if cur_dir=right_to_left then cur_h:=cur_h+leader_wd; -@z - -@x [32f] - cur_h:=left_edge+shift_amount(p); {shift the box right} -@y - if cur_dir=right_to_left then cur_h:=left_edge-shift_amount(p) - else cur_h:=left_edge+shift_amount(p); {shift the box right} -@z - -@x [32f] -if (rule_ht>0)and(rule_wd>0) then {we don't output empty rules} - pdf_set_rule(cur_h, cur_v, rule_wd, rule_ht); -@y -if (rule_ht>0)and(rule_wd>0) then {we don't output empty rules} - begin if cur_dir=right_to_left then cur_h:=cur_h-rule_wd; - pdf_set_rule(cur_h, cur_v, rule_wd, rule_ht); - cur_h:=left_edge; - end; -@z - -@x [32f] -begin cur_h:=left_edge+shift_amount(leader_box);@/ -@y -begin if cur_dir=right_to_left then - cur_h:=left_edge-shift_amount(leader_box) - else cur_h:=left_edge+shift_amount(leader_box); -@z - -@x [32f] -@<(\pdfTeX) Ship box |p| out@>; -@y -@<(\pdfTeX) Ship box |p| out@>; -if eTeX_ex then @; -@z - -@x -pdf_print("/Producer (pdfTeX-"); -@y -pdf_print("/Producer (pdfeTeX-"); -@z - -@x [649] -exit: {|hpack|} -@y -@z - -@x [651] - math_node: x:=x+width(p); -@y -@z - -@x - obj_xform_resources(k) := null; - scan_int; - if box(cur_val) = null then - pdf_error("ext1", "\pdfxform cannot be used with a void box"); - obj_xform_width(k) := width(box(cur_val)); - obj_xform_height(k) := height(box(cur_val)); - obj_xform_depth(k) := depth(box(cur_val)); - obj_xform_box(k) := box(cur_val); {save pointer to the box} - box(cur_val) := null; - pdf_last_xform := k; -@y - obj_xform_resources(k) := null; - scan_int; - fetch_box(p); - if p = null then - pdf_error("ext1", "\pdfxform cannot be used with a void box"); - obj_xform_width(k) := width(p); - obj_xform_height(k) := height(p); - obj_xform_depth(k) := depth(p); - obj_xform_box(k) := p; {save pointer to the box} - change_box(null); - pdf_last_xform := k; -@z diff --git a/Build/source/texk/web2c/pdfetexdir/pdfetex.defines b/Build/source/texk/web2c/pdfetexdir/pdfetex.defines deleted file mode 100644 index 456f0b7ac60..00000000000 --- a/Build/source/texk/web2c/pdfetexdir/pdfetex.defines +++ /dev/null @@ -1,105 +0,0 @@ -{ -Copyright (c) 1996-2005 Han The Thanh, - -This file is part of pdfTeX. - -pdfTeX is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -pdfTeX is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with pdfTeX; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -$Id: //depot/Build/source.development/TeX/texk/web2c/pdfetexdir/pdfetex.defines#13 $ -} - -{ pdftex.defines: additions for pdfTeX } - -{ macros from pdftex.h } -@define function pdfcharmarked(); -@define function getlpcode(); -@define function getrpcode(); -@define function getefcode(); -@define function texbopenin(); -@define function vfbopenin (); -@define procedure pdfmarkchar(); -@define procedure writepdf(); - -{ functions from mapfile.c } -@define function hasfmentry(); -@define function tfmoffm(); -@define procedure checkextfm(); -@define procedure pdfmapfile(); -@define procedure pdfmapline(); -@define procedure pdfinitmapfile(); - -{ functions from utils.c } -@define procedure escapehex(); -@define procedure escapename(); -@define procedure escapestring(); -@define function extxnoverd(); -@define procedure getcreationdate; -@define procedure getfiledump(); -@define procedure getfilemoddate(); -@define procedure getfilesize(); -@define procedure getmatch(); -@define procedure getmd5sum(); -@define function getresnameprefix; -@define procedure initstarttime; -@define function isquotebad; -@define function makecstring(); -@define procedure matchstrings(); -@define procedure libpdffinish; -@define procedure makepdftexbanner; -@define procedure printcreationdate; -@define procedure printID(); -@define procedure setjobid(); -@define procedure unescapehex(); -@define procedure writestreamlength(); - - -{ functions from vfpacket.c } -@define function newvfpacket(); -@define function packetbyte; -@define procedure startpacket(); -@define procedure storepacket(); - -{ functions from writefont.c } -@define procedure dopdffont(); - -{ functions from writeimg.c } -@define function checkimageb(); -@define function checkimagec(); -@define function checkimagei(); -@define function epdforigx(); -@define function epdforigy(); -@define function imageheight(); -@define function imagepages(); -@define function imagecolordepth(); -@define function imagewidth(); -@define function imagexres(); -@define function imageyres(); -@define function ispdfimage(); -@define function readimage(); -@define procedure deleteimage(); -@define procedure updateimageprocset(); -@define procedure writeimage(); - -{ functions from writet3.c } -@define function getpkcharwidth(); - -{ functions from writezip.c } -@define procedure writezip(); - -{ functions from avlstuff.c } -@define procedure avlputobj(); -@define function avlfindobj(); - -{ end of pdftex.defines } diff --git a/Build/source/texk/web2c/pdfetexdir/pdfetex.h b/Build/source/texk/web2c/pdfetexdir/pdfetex.h deleted file mode 100644 index 3503b880dc8..00000000000 --- a/Build/source/texk/web2c/pdfetexdir/pdfetex.h +++ /dev/null @@ -1,50 +0,0 @@ -/* -Copyright (c) 1996-2002 Han The Thanh, - -This file is part of pdfTeX. - -pdfTeX is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -pdfTeX is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with pdfTeX; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -$Id: //depot/Build/source.development/TeX/texk/web2c/pdfetexdir/pdfetex.h#4 $ -*/ - -/* Additions to texmfmp.h for pdfTeX */ - -/* mark a char in font */ -#define pdfmarkchar(f, c) pdfcharused[f][c/8] |= (1<<(c%8)) - -/* test whether a char in font is marked */ -#define pdfcharmarked(f, c) (boolean)(pdfcharused[f][c/8] & (1<<(c%8))) - -/* writepdf() always writes by fwrite() */ -#define writepdf(a, b) \ - (void) fwrite ((char *) &pdfbuf[a], sizeof (pdfbuf[a]), \ - (int) ((b) - (a) + 1), pdffile) - -#define getlpcode(f, c) \ - (pdffontlpbase[f] == 0 ? 0 : pdfmem[pdffontlpbase[f] + c]) - -#define getrpcode(f, c) \ - (pdffontrpbase[f] == 0 ? 0 : pdfmem[pdffontrpbase[f] + c]) - -#define getefcode(f, c) \ - (pdffontefbase[f] == 0 ? 1000 : pdfmem[pdffontefbase[f] + c]) - -#define texbopenin(f) \ - open_input (&(f), kpse_tex_format, FOPEN_RBIN_MODE) -#define vfbopenin(f) \ - open_input (&(f), kpse_vf_format, FOPEN_RBIN_MODE) - -#include diff --git a/Build/source/texk/web2c/pdfetexdir/pdfetex.mak b/Build/source/texk/web2c/pdfetexdir/pdfetex.mak deleted file mode 100644 index c30864e2df0..00000000000 --- a/Build/source/texk/web2c/pdfetexdir/pdfetex.mak +++ /dev/null @@ -1,164 +0,0 @@ -################################################################################ -# -# Makefile : pdfeTeX, web2c win32.mak makefile fragment to build pdfe-TeX -# Author : Fabrice Popineau -# Platform : Win32, Microsoft VC++ 6.0, depends upon fpTeX 0.5 sources -# Time-stamp: <04/03/20 17:02:03 popineau> -# -################################################################################ - -Makefile: $(srcdir)\pdfetexdir\pdfetex.mak - -# We build pdfetex -pdfetex = $(objdir)\pdfetex.exe -!ifdef TEX_DLL -pdfetex = $(pdfetex) $(objdir)\$(library_prefix)pdfetex.dll -!endif - -# The C sources. -pdfetex_c = pdfetex.c -pdfetex_o = $(objdir)\pdfetex.obj - -!ifdef TEX_DLL -$(objdir)\$(library_prefix)pdfetex.exp: $(objdir)\$(library_prefix)pdfetex.lib - -$(objdir)\$(library_prefix)pdfetex.lib: $(pdfetex_o) - $(archive) /DEF $(pdfetex_o) - -$(objdir)\$(library_prefix)pdfetex.dll: $(pdfetex_o) $(objdir)\$(library_prefix)pdfetex.exp $(objdir)\pdfetex.res $(pdftexlibs) $(kpathsealib) $(proglib) - $(link_dll) $(**) $(socketlibs) $(conlibs) - -$(objdir)\pdfetex.exe: $(objdir)\win32main.obj $(objdir)\$(library_prefix)pdfetex.lib $(proglib) - $(link) $(**) $(conlibs) -!else -$(objdir)\pdfetex.exe: $(pdfetex_o) $(objdir)\win32main.obj $(objdir)\pdfetex.res $(pdftexlibs) $(kpathsealib) $(proglib) - $(link) $(**) $(socketlibs) $(conlibs) -!endif - -pdfetex: $(pdfetex) - -# C file dependencies. -$(pdfetex_c) pdfetexcoerce.h pdfetexd.h: pdfetex.p $(web2c_texmf) - $(web2c) pdfetex - -# Tangling -pdfetex.p pdfetex.pool: $(objdir)\tangle.exe pdfetex.web pdfetex.ch - .\$(objdir)\tangle pdfetex.web pdfetex.ch - -pdfetex_files = \ - etexdir\etex.ch0 \ - etexdir\etex.ch \ - etexdir\etex.fix \ - etexdir\etex.ch1 \ - pdfetexdir\pdfetex.ch1 \ - pdftexdir\pdftex.ch \ - pdftexdir/hz.ch \ - pdftexdir/misc.ch \ - pdfetexdir\pdfetex.ch2 - -pdfetex_changefiles = \ - pdfetexdir\tex.ch0 \ - tex.ch \ - tex-supp-w32.ch \ - etexdir\tex.ch1 \ - etexdir\tex.ech \ - etexdir\tex.ch2 \ - pdfetexdir\tex.ch1 \ - pdftexdir\tex.pch \ - pdfetexdir\tex.ch2 \ -# pdfetexdir\tex.ch3 - -# Generation of the web and ch files. -pdfetex.web: $(objdir)\tie.exe tex.web $(pdfetex_files) \ - pdfetexdir\pdfetex.h pdfetexdir\pdfetex.defines # pdfetexdir\pdfetex.mak - .\$(objdir)\tie -m pdfetex.web tex.web $(pdfetex_files) - -pdfetex.ch: $(objdir)\tie.exe pdfetex.web $(pdfetex_changefiles) # pdfetexdir/pdfetex.mak - .\$(objdir)\tie -c pdfetex.ch pdfetex.web $(pdfetex_changefiles) - -pdfetexdir\pdfetex.h: pdftexdir\pdftex.h - -@$(del) $(@) $(redir_stderr) - $(copy) $(**) $(@) - -pdfetexdir\pdfetex.defines: pdftexdir\pdftex.defines - -@$(del) $(@) $(redir_stderr) - $(copy) $(**) $(@) - -check: pdfetex-check -pdfetex-check: pdfetex pdfetex.efmt - -clean:: pdfetex-clean -pdfetex-clean: -# $(LIBTOOL) --mode=clean $(RM) pdfetex - -@echo $(verbose) & ( \ - for %%i in ($(pdfetex_o) $(pdfetex_c) pdfetexextra.c pdfetexcoerce.h \ - pdfetexd.h pdfetex.p pdfetex.pool pdfetex.web pdfetex.ch \ - pdfetex.efmt pdfetex.log) do $(del) %%i $(redir_stderr) \ - ) - -# Dumps -# all_pdfefmts = pdfetex.efmt $(pdfefmts) -# pdfefmts: $(all_pdfefmts) - -# pdfetex.efmt: $(pdfetex) -# $(dumpenv) $(make) progname=pdfetex files="etex.src plain.tex cmr10.tfm" prereq-check -# $(dumpenv) ./pdfetex --progname=pdfetex --jobname=pdfetex --ini "*\pdfoutput=1\input etex.src \dump" $@ -pdfetexdir/pdfetexextra.h: pdfetexdir/pdfetexextra.in pdftexdir/pdftex.version etexdir/etex.version - sed -e s/PDFTEX-VERSION/`cat pdftexdir/pdftex.version`/ \ - -e s/ETEX-VERSION/`cat etexdir/etex.version`/ \ - $(srcdir)/pdfetexdir/pdfetexextra.in >$@ - -# Tangling -pdfetex.p pdfetex.pool: tangle pdfetex.web pdfetex.ch - $(TANGLE) pdfetex.web pdfetex.ch - -# Generation of the web and ch file. -# Sources for pdfetex.web: -pdfetex_web_srcs = $(srcdir)/tex.web \ - $(srcdir)/etexdir/etex.ch \ - $(srcdir)/etexdir/etex.fix \ - $(srcdir)/pdfetexdir/pdfetex.ch1 \ - $(srcdir)/pdftexdir/pdftex.ch \ - $(srcdir)/pdftexdir/hz.ch \ - $(srcdir)/pdftexdir/misc.ch \ - $(srcdir)/pdftexdir/vadjust.ch \ - $(srcdir)/pdftexdir/pdftex2.ch \ - $(srcdir)/pdfetexdir/pdfetex.ch2 -# Sources for pdfetex.ch: -pdfetex_ch_srcs = pdfetex.web \ - $(srcdir)/pdfetexdir/tex.ch0 \ - $(srcdir)/tex.ch \ - $(srcdir)/etexdir/tex.ch1 \ - $(srcdir)/etexdir/tex.ech \ - $(srcdir)/pdfetexdir/tex.ch1 \ - $(srcdir)/pdftexdir/tex.pch \ - $(srcdir)/pdftexdir/noligatures.ch \ - $(srcdir)/pdftexdir/pdfstrcmp.ch \ - $(srcdir)/pdftexdir/randoms.ch -# Rules: -pdfetex.web: tie pdfetexdir/pdfetex.mk $(pdfetex_web_srcs) - $(TIE) -m pdfetex.web $(pdfetex_web_srcs) -pdfetex.ch: $(pdfetex_ch_srcs) - $(TIE) -c pdfetex.ch $(pdfetex_ch_srcs) - -# for developing only -pdfetex-org.web: $(pdfetex_ch_srcs_org) - $(TIE) -m $@ $(pdfetex_ch_srcs_org) -pdfetex-all.web: pdfetex.web pdfetex.ch - $(TIE) -m $@ pdfetex.web pdfetex.ch - -$(srcdir)/pdfetexdir/pdfetex.h: $(srcdir)/pdftexdir/pdftex.h - cp -f $(srcdir)/pdftexdir/pdftex.h $@ - -$(srcdir)/pdfetexdir/pdfetex.defines: $(srcdir)/pdftexdir/pdftex.defines - cp -f $(srcdir)/pdftexdir/pdftex.defines $@ - -check: @PETEX@ pdfetex-check -pdfetex-check: pdfetex pdfetex.fmt - -clean:: pdfetex-clean -pdfetex-clean: - $(LIBTOOL) --mode=clean $(RM) pdfetex - rm -f $(pdfetex_o) $(pdfetex_c) pdfetexextra.c pdfetexcoerce.h - rm -f pdfetexdir/pdfetexextra.h - rm -f pdfetexd.h pdfetex.p pdfetex.pool pdfetex.web pdfetex.ch - rm -f pdfetex.fmt pdfetex.log - -# Dumps -all_pdfefmts = @FMU@ pdfetex.fmt $(pdfefmts) - -dumps: @PETEX@ pdfefmts -pdfefmts: $(all_pdfefmts) - -pdfefmtdir = $(web2cdir)/pdfetex -$(pdfefmtdir):: - $(SHELL) $(top_srcdir)/../mkinstalldirs $(pdfefmtdir) - -pdfetex.fmt: pdfetex - $(dumpenv) $(MAKE) progname=pdfetex files="etex.src plain.tex cmr10.tfm" prereq-check - $(dumpenv) ./pdfetex --progname=pdfetex --jobname=pdfetex --ini \*\\pdfoutput=1\\input etex.src \\dump - -This file is part of pdfTeX. - -pdfTeX is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -pdfTeX is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with pdfTeX; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -$Id: pdfetexextra.in,v 1.4 2004/08/26 18:19:57 olaf Exp $ -*/ - -/* pdfetexextra.in: banner etc. for pdfeTeX. - - This is included by pdfeTeX, from ../pdfetexextra.c - (generated from ../lib/texmfmp.c). -*/ - -#define BANNER "This is pdfeTeX, Version 3.141592-PDFTEX-VERSION-ETEX-VERSION" -#define COPYRIGHT_HOLDER "Peter Breitenlohner (eTeX)/Han The Thanh (pdfTeX)" -#define AUTHOR NULL -#define PROGRAM_HELP PDFETEXHELP -#define BUG_ADDRESS "pdftex@tug.org" -#define DUMP_VAR TEXformatdefault -#define DUMP_LENGTH_VAR formatdefaultlength -#define DUMP_OPTION "fmt" -#define DUMP_EXT ".fmt" -#define INPUT_FORMAT kpse_tex_format -#define INI_PROGRAM "pdfeinitex" -#define VIR_PROGRAM "pdfevirtex" - diff --git a/Build/source/texk/web2c/pdfetexdir/tex.ch0 b/Build/source/texk/web2c/pdfetexdir/tex.ch0 deleted file mode 100644 index 2dcb3746d6f..00000000000 --- a/Build/source/texk/web2c/pdfetexdir/tex.ch0 +++ /dev/null @@ -1,42 +0,0 @@ -% -% Copyright (c) 1996-2002 Han The Thanh, -% -% This file is part of pdfTeX. -% -% pdfTeX is free software; you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation; either version 2 of the License, or -% (at your option) any later version. -% -% pdfTeX is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with pdfTeX; if not, write to the Free Software -% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -% -% $Id: //depot/Build/source.development/TeX/texk/web2c/pdfetexdir/tex.ch0#10 $ -% -% Change file to assist in creating the web2c-specific change file. -% This one comes first, resolves inconsistencies between the generated -% pdfetex.web and tex.ch - -@x [1.2] l.188 -@d TeX_banner=='This is TeX, Version 3.141592' {printed when \TeX\ starts} -@# -@d banner==pdfeTeX_banner -@y -@d banner=='This is TeX, Version 3.141592' {printed when \TeX\ starts} -@z - -@x [17.236] l.4960 - first web2c, then pdfTeX, then e-TeX additional integer parameters -@d tex_int_pars=55 {total number of \TeX's integer parameters} -@# -@d pdftex_first_integer_code = tex_int_pars {base for \pdfTeX's integer parameters} -@y -@d int_pars=55 {total number of integer parameters} -@# -@d etex_int_base=tex_int_pars {base for \eTeX's integer parameters} -@z diff --git a/Build/source/texk/web2c/pdfetexdir/tex.ch1 b/Build/source/texk/web2c/pdfetexdir/tex.ch1 deleted file mode 100644 index 48f85ef8941..00000000000 --- a/Build/source/texk/web2c/pdfetexdir/tex.ch1 +++ /dev/null @@ -1,45 +0,0 @@ -% -% Copyright (c) 1996-2002 Han The Thanh, -% -% This file is part of pdfTeX. -% -% pdfTeX is free software; you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation; either version 2 of the License, or -% (at your option) any later version. -% -% pdfTeX is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with pdfTeX; if not, write to the Free Software -% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -% -% $Id: //depot/Build/source.development/TeX/texk/web2c/pdfetexdir/tex.ch1#12 $ -% -% Change file to assist in creating the web2c-specific change file. -% This one resolves inconsistencies between the etex changes for -% web2c and pdftex changes for web2c. - -@x [1] m.2 l.188 - Web2C: extensions imply we're not pdfeTeX -@d pdfeTeX_banner=='This is pdfeTeX, Version 3.141592',pdftex_version_string,eTeX_version_string -@y -@d pdfeTeX_banner_k=='This is pdfeTeXk, Version 3.141592',pdftex_version_string,eTeX_version_string -@d pdfeTeX_banner=='This is pdfeTeX, Version 3.141592',pdftex_version_string,eTeX_version_string -@z - -@x [1] m.2 l.188 - banner -@d banner==eTeX_banner -@d banner_k==eTeX_banner_k -@y -@d banner==pdfeTeX_banner -@d banner_k==pdfeTeX_banner_k -@z - -@x [17.236] l.4960 - first web2c, then pdfTeX, then e-TeX additional integer parameters -@d etex_int_base=web2c_int_pars {base for \eTeX's integer parameters} -@y -@d pdftex_first_integer_code = web2c_int_pars {base for \pdfTeX's integer parameters} -@z diff --git a/Build/source/texk/web2c/pdfetexdir/tex.ch2 b/Build/source/texk/web2c/pdfetexdir/tex.ch2 deleted file mode 100644 index 4f647d8c242..00000000000 --- a/Build/source/texk/web2c/pdfetexdir/tex.ch2 +++ /dev/null @@ -1,21 +0,0 @@ -% -% Copyright (c) 1996-2002 Han The Thanh, -% -% This file is part of pdfTeX. -% -% pdfTeX is free software; you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation; either version 2 of the License, or -% (at your option) any later version. -% -% pdfTeX is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with pdfTeX; if not, write to the Free Software -% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -% -% $Id: //depot/Build/source.development/TeX/texk/web2c/pdfetexdir/tex.ch2#8 $ -% diff --git a/Build/source/texk/web2c/pdftexdir/.indent.pro b/Build/source/texk/web2c/pdftexdir/.indent.pro new file mode 100644 index 00000000000..e9e690f1ba8 --- /dev/null +++ b/Build/source/texk/web2c/pdftexdir/.indent.pro @@ -0,0 +1,12 @@ +/* $Id$ */ +--k-and-r-style +--blank-lines-after-procedures +--line-length 80 +--procnames-start-lines +--no-space-after-function-call-names +--dont-break-procedure-type +--space-after-cast +/* this does not work in indent 2.2.6 :-( */ +--no-tabs +/* this is new in indent 2.2.9 */ +--preprocessor-indentation 2 diff --git a/Build/source/texk/web2c/pdftexdir/ChangeLog b/Build/source/texk/web2c/pdftexdir/ChangeLog index a0a770ed962..d6004e43700 100644 --- a/Build/source/texk/web2c/pdftexdir/ChangeLog +++ b/Build/source/texk/web2c/pdftexdir/ChangeLog @@ -1,104 +1,69 @@ -2004-10-29 Jan Nieuwenhuizen +2004-08-21 Peter Breitenlohner - * pdftex.mk (pdftex): Build fix: pdftexextra.c depends on - pdftexextra.h. - -2004-09-07 The pdfTeX team: - - * README: pdftex version 1.20a. - - * NEWS: pdftex version 1.20a. - - * pdftex.ch: pdftex version 1.20a. + * pdfetex.ch2: Adapted to e-TeX version 2.2. 2004-08-26 Olaf Weber - * pdftexlib.mk (pdftexdir/libpdf.a): Add pdftexdir/pdftexextra.h - to the dependencies, so it gets built before we go and try to - build the library. - - * pdftex.version: Is from now on a generated file. + * pdfetexextra.in: File was renamed from pdftexextra.h; the + current pdfTeX and e-TeX versions are substituted from + ../pdftexdir/pdftex.version and ../etexdir/etex.version. Variant + of code supplied by Peter Breitenlohner . - * pdftexextra.in: Renamed from pdftexextra.h; the current pdfTeX - version is substituted from pdftex.version. Variant of code - supplied by Peter Breitenlohner . - - * pdftex.mk: Added rules to extract current pdfTeX version from - pdftex.ch and insert into pdftex.version and pdftexextra.h. - Variant of code supplied by Peter Breitenlohner + * pdfetex.mk: Added rule to insert pdfTeX and e-TeX versions into + pdfetexextra.h. Variant of code supplied by Peter Breitenlohner . 2004-08-25 Olaf Weber - * vadjust.ch: Moved here from pdfxtexdir. - - * pdftex.mk: vadjust.ch moved to this directory from pdfxtexdir. - -2004-08-23 Olaf Weber - - * pdftex.mk: Scrap rules for pdftexinfo. These are obsolete. + * pdfetex.mk: vadjust.ch moved from pdfxtexdir to pdftexdir. - * pdftex.mak: Scrap rules for pdftexinfo. +2004-08-17 Olaf Weber -2004-08-06 Olaf Weber - - * Makefile.in: The .cc.o suffix rule should use ALL_CXXFLAGS (as - is done for ALL_CFLAGS). From Jan Nieuwenhuizen - . + * pdfetex.mk: Resurrected etex.fix, there is again a need to + include late-breaking changes. 2004-07-29 Peter Breitenlohner - * pdftex.ch, hz.ch: Somewhat rearrange handling of kern and - math nodes in order to reduce interference between eTeX and - pdfTeX change files. Somewhat disentangle margin kerns such - that pdftex.web and pdfetex.web can be built with or without - hz.ch (not true for pdf*tex.ch). + * pdfetex.ch[12]: Somewhat rearrange handling of kern and math + nodes in order to reduce interference between eTeX and pdfTeX + change files. + + * pdfetex.ch1 (hlist_out): Keep full mixed direction typesetting + code. 2004-07-25 Peter Breitenlohner - * pdftex.ch: Reorganize additional command codes for convert + * pdfetex.ch[12]: Reorganize additional command codes for convert and last_item in order to reduce interference between eTeX and pdfTeX change files. - * change-files.txt: Adjust to current status. - 2004-07-23 Peter Breitenlohner - * pdftex.ch, tex.ch[01], tex.pch: Reorganize handling of banner + * pdfetex.ch[12], tex.ch[01]: Reorganize handling of banner string in order to reduce interference between eTeX, pdfTeX, and web2c change files. 2004-07-22 Peter Breitenlohner - * pdftex.ch: Removed unused token types for pdfTeX's additional - token list parameters. + * pdfetex.ch[12]: Removed unused token types for pdfTeX's + additional token list parameters. 2004-07-19 Peter Breitenlohner - * hz.ch, pdftex.ch, tex.ch[01], tex.pch: Reorganize handling of - additional token list and integer parameters in order to reduce - interference between eTeX, pdfTeX, and web2c change files. - - * pdftex.ch: Add a note that this file not only defines pdftex.web - in terms of changes to be applies to tex.web, but in addition - is used to define pdfetex.web. - - * tex.pch: Add a note that this file is used to construct the - web2c change files for both pdfTeX and pfdeTeX. + * pdfetex.ch[12], tex.ch0: Reorganize handling of additional + token list and integer parameters and e-TeX's additional + penalties in order to reduce interference between eTeX, pdfTeX, + and web2c change files. - * pdftex.mk (pdftex.ch): Remove tex.ch2 from build rules. + * pdfetex.mk (pdfetex.ch): Remove tex.ch2 from build rules. - * tex.ch2: Removed + * tex.ch2: Removed. 2004-07-17 Peter Breitenlohner - * pdftex.mk: Rewrite build rules for pdftex.{web,ch}. - - * pdftex.ch: Adapt glue and leader box rounding to TeX 3.141592. - Remove a few modules that were identical copies of those in TeX. - -2004-07-16 Peter Breitenlohner + * pdfetex.mk: Rewrite build rules for pdfetex.{web,ch}, + remove etex.{ch0,ch1,fix}, tex.ch2, and several non-dependencies. - * ttf2afm.c: Add various const's, to avoid compiler warnings. + * pdfetex.ch2: Adapt to eTeX changes. * ChangeLog: New file. diff --git a/Build/source/texk/web2c/pdftexdir/Makefile.in b/Build/source/texk/web2c/pdftexdir/Makefile.in index 631e10c9861..0f77f973d62 100644 --- a/Build/source/texk/web2c/pdftexdir/Makefile.in +++ b/Build/source/texk/web2c/pdftexdir/Makefile.in @@ -8,14 +8,14 @@ kpse_include ../make/library.mk kpathsea_srcdir_parent = $(srcdir)/../.. kpathsea_dir_parent = ../.. -ALL_CXXFLAGS = @CXXFLAGS@ @DEFS@ $(XXCFLAGS) -I. -I$(srcdir) -I$(kpathsea_dir_parent) -I$(kpathsea_srcdir_parent) @LIBXPDFCPPFLAGS@ +ALL_CXXFLAGS = @CXXFLAGS@ @DEFS@ $(XXCFLAGS) -I. -I$(srcdir) -I$(kpathsea_dir_parent) -I$(kpathsea_srcdir_parent) @LIBXPDFCPPFLAGS@ -I$(LIBOBSDCOMPATDIR) -I$(LIBOBSDCOMPATDIR)/.. -I$(LIBOBSDCOMPATFSRCDIR) -I$(LIBOBSDCOMPATFSRCDIR)/.. CXX = @CXX@ .SUFFIXES: .cc .o .cc.o: $(CXX) $(ALL_CXXFLAGS) -c $< -# XCFLAGS=-Wstrict-prototypes -Wmissing-prototypes +# XCFLAGS=-Wstrict-prototypes -Wmissing-prototypes LIBPNGDIR=../../../libs/libpng LIBPNGSRCDIR=$(srcdir)/$(LIBPNGDIR) @@ -29,13 +29,18 @@ LIBXPDFSRCDIR=$(srcdir)/$(LIBXPDFDIR) LIBMD5DIR=../../../libs/md5 LIBMD5SRCDIR=$(srcdir)/$(LIBMD5DIR) -XCPPFLAGS=-I.. -I$(srcdir)/.. -I../.. -I$(srcdir)/../.. -I$(LIBMD5DIR) -I$(LIBMD5SRCDIR)\ -@LIBPNGCPPFLAGS@ @ZLIBCPPFLAGS@ -DpdfTeX +LIBOBSDCOMPATDIR=../../../libs/obsdcompat +LIBOBSDCOMPATFSRCDIR=$(srcdir)/$(LIBOBSDCOMPATDIR) +XCPPFLAGS=-I.. -I$(srcdir)/.. -I../.. -I$(srcdir)/../.. -I$(LIBMD5DIR) \ + -I$(LIBMD5SRCDIR) @LIBPNGCPPFLAGS@ @ZLIBCPPFLAGS@ \ + -I$(LIBXPDFDIR) -I$(LIBXPDFDIR)/.. -I$(LIBXPDFSRCDIR) -I$(LIBXPDFSRCDIR)/.. \ + -I$(LIBOBSDCOMPATDIR) -I$(LIBOBSDCOMPATDIR)/.. -I$(LIBOBSDCOMPATFSRCDIR) \ + -I$(LIBOBSDCOMPATFSRCDIR)/.. -DpdfTeX OBJS = epdf.o mapfile.o utils.o vfpacket.o pkin.o \ writefont.o writet1.o writet3.o writezip.o writeenc.o writettf.o \ -writejpg.o writepng.o writeimg.o pdftoepdf.o avl.o avlstuff.o +writejpg.o writejbig2.o writepng.o writeimg.o pdftoepdf.o avl.o avlstuff.o subfont.o tounicode.o all: libpdf.a diff --git a/Build/source/texk/web2c/pdftexdir/NEWS b/Build/source/texk/web2c/pdftexdir/NEWS index 98e30db8b7f..7e334eba775 100644 --- a/Build/source/texk/web2c/pdftexdir/NEWS +++ b/Build/source/texk/web2c/pdftexdir/NEWS @@ -1,3 +1,301 @@ +----------------------------------------------------- +pdfTeX 3.141592-1.40.0-rc4 was released on 2006-12-22 +----------------------------------------------------- +- pdfetex (as a separate binary/program) is gone; all extensions are now in + pdftex. The make files still know about the target pdfetex, but it's just a + copy of pdftex +- The sources of TeX, eTeX and pdfTeX have been merged into one file pdftex.web + and one change file pdftex.ch. The sources of TeX and eTeX distributed are + just there for reference +- New features: + - pdfTeX can now generate PDF 1.5 object streams, which leads to smaller + PDFs. This is controlled by setting \pdfobjcompresslevel to a value >0 (3 + compress everything, 2 don't compress /Info, 1 also don't compress embedded + PDFs) and works only with PDF >=1.5 + - pdfTeX now supports another image file format: JBIG2 files (.jbig2) + - the memory allocated for objects and destination names now grows + dynamically + - pdfTeX now generates smaller PDFs since for char positioning the width of + chars (/Widths) is stored with more precision and so the chars must rarely + be positioned separately + - the PK resolution is now taken from the "pk_dpi" parameter in texmf.cnf if + it has not been set in the format file or by the user + - pdfTeX now removes the generated pdf in case of a fatal error + - pdfTeX now sets /ModDate and /Trapped in the InfoDict by default (the + values can be overridden with \pdfinfo) + - the format of warnings and errors issued by pdfTeX has been changed and + unified + - the output of -version has been extended and now includes information about + the libraries actually used, which can be handy when using shared libraries + - rules and their positioning on the page are now inside a qQ group +- New primitives: + - TODO: pdfTeX can now handle Colorstacks + - \pdfprimitive\TeX-primitive executes the original \TeX-primitive even if + the command has been redefined since. E.g. + \let\relax\undefined\pdfprimitive\relax + expands to \relax. + \ifpdfprimitive\TeX-primitive is true if \TeX-primitive has its original + meaning + - \ifpdfabsnum and \ifpdfabsdim are like \ifnum and \ifdim, but don't care + about negative numbers + - shell escape: if the first character of a filename for \openin, \openout + \input is a pipe symbol (|), the filename is assumed to be a request for a + pipe to a command line that is given in the rest of the filename + - draftmode: With \pdfdraftmode=1 or the commandline switch -draftmode pdfTeX + doesn't write the output pdf and doesn't actually read any images, thus + speeding up compilations when you know you need an extra run but don't care + about the output, e.g. just to get the BibTeX references right + - \pdfpxdimen (introduced in 1.30) is now a real dimension parameter. It + allows one base dimensions on pixels. 1px defaults to 1bp (or 72dpi), but + can be changed with the \pdfpxdimen primitive: + \pdfpxdimen=1in % 1 dpi + \divide\pdfpxdimen by 96 % 96 dpi + \hsize=1200px + Now \hsize is (1in/96)*1200 + - \pdflastlink returns the object number of the last \pdfstartlink + - \pdfsavepos now works in DVI mode +- Fonts: + - support for subfonts: This creates all needed map entries for subfonts + automatically and handles Unicode mappings + - ToUnicode entries for Type1 fonts (patch 580) +- HZ: + - added patch 462: HZ font expansion in autoexpand mode now produces smaller + pdf files, as it works by modififying the text matrix for single fonts + instead of embedding many expanded font versions. Therefore HZ now can be + used also for TrueType fonts and even for non-embedded fonts (e.g. + Times-Roman) +- Bugfixes: + - the PDF statistics in the log are now correct + - added JFIF detection patch to writejpg.c (bug 548) + - bugfix: invalid pdf created if font name contains space (509) + - bugfix: truetype OS/2 v3 fonts (594) + - bugfix: Type1C (CFF) fonts of embedded pdfs didn't get replaced +- pdfTeX uses libpng 1.2.14 +- changes from rc3: + various small fixes + +----------------------------------------------------- +pdfTeX 3.141592-1.40.0-rc3 was released on 2006-12-20 +----------------------------------------------------- +- changes from rc2: + - map file handling: + - in usual cases the behaviour is the same like in previous version. So if a + font file is given with '<' or '<<', things should work without problems. + - changes in odd cases: + - no font file --> no font descriptor + - font file given but not included --> will be treated like font file + missing + a warning + - minimize the distinction between standard fonts and other type1 fonts: the + *only* place this makes a difference is that if a font file is missing + (and the font is used), a warning will be generated for normal fonts but + not for standard fonts + +----------------------------------------------------- +pdfTeX 3.141592-1.40.0-rc2 was released on 2006-12-13 +----------------------------------------------------- +- changes from rc1: + - fix \letterspacefont (bug 712) + - fix problems with unused objects + +----------------------------------------------------- +pdfTeX 3.141592-1.40.0-rc1 was released on 2006-12-11 +----------------------------------------------------- +- changes from last beta: + - ToUnicode was broken in the latest beta (bug 711) + - pdftosrc now handles compressed xref tables (PDF 1.5) + - new primitive \letterspacefont to implement letter-spacing on-the-fly + +--------------------------------------------------------------- +pdfTeX 3.141592-1.40.0-beta-20061206 was released on 2006-12-06 +--------------------------------------------------------------- +- bugfix: crash with autokern (663) +- memory for virtual fonts can now grow dynamically (patch 705) +- rework the font mechanism (patches 597 and 709) +- updated libpng to 1.2.14 + +--------------------------------------------------------------- +pdfTeX 3.141592-1.40.0-beta-20061108 was released on 2006-11-08 +--------------------------------------------------------------- +- bugfix: extra kerning+expansion+ligatures (481) +- bugfix: extra kerning does not work with ligatures (482) +- bugfix: extra kerning inhibits extra spacing (483) +- bugfix: extra kerning inhibits hyphenation (484) +- bugfix: ttf2afm fails to include characters specified with uniXXXX (565) + +--------------------------------------------------------------- +pdfTeX 3.141592-1.40.0-beta-20061017 was released on 2006-10-17 +--------------------------------------------------------------- +- updated patch 580 (ToUnicode entries for Type1 fonts) +- bugfix: nested links (479) +- bugfix: conv_toks had some problems (609) +- bugfix: problems with TrueType fonts (611) + +--------------------------------------------------------------- +pdfTeX 3.141592-1.40.0-beta-20060928 was released on 2006-09-28 +--------------------------------------------------------------- +- added patch 603 (Colorstack) +- added patch 462 (HZ font expansion by text matrix manipulation) +- updated obsdcompat from openssh 4.4pl1 +- bugfix: \pdfescapestring + \pdfescapename had been broken (605) + +--------------------------------------------------------------- +pdfTeX 3.141592-1.40.0-beta-20060906 was released on 2006-09-06 +--------------------------------------------------------------- +- bugfix: \pdfdraftmode could be tricked (600) +- bugfix: Type1C (CFF) fonts of embedded pdfs didn't get replaced + +--------------------------------------------------------------- +pdfTeX 3.141592-1.40.0-beta-20060822 was released on 2006-08-22 +--------------------------------------------------------------- +- new feature: with \pdfdraftmode=1 (or -draftmode) pdfTeX doesn't write an + output pdf file and doesn't read the content of any image file, thus speeding + up the run. This is usefull when you know you need another run of pdfTeX e.g. + just to get all the references right, but don't care for the output of this + run +- changed the format of pdfTeX warnings and unified the four(!) functions + pdfTeX uses for writing out errors and warnings +- expanded the output of -version to include information about the libraries + used +- fixed a bug where -20060811 would loop forever +- fixed some compilation problems with obsdcompat + +--------------------------------------------------------------- +pdfTeX 3.141592-1.40.0-beta-20060811 was released on 2006-08-11 +--------------------------------------------------------------- +- renamed primitives: + \primitive -> \pdfprimitive + \ifprimitive -> \ifpdfprimitive + \ifabsdim -> \ifpdfabsdim + \ifabsnum -> \ifpdfabsnum +- new version of \primitive/\ifprimitive (but see above) +- new dimen: \pdfpxdimen +- support for jbig2 files +- new library: obsdcompat, which has improved string functions and allows the + use of __attribute__ +- update libpng to 1.2.12 +- added JFIF detection patch to writejpg.c (was bug 548) +- bugfix: truetype OS/2 v3 fonts (594) +- bugfix: file with only \immediate\pdfobj stream results in garbage (505) +- bugfix: invalid pdf created if font name contains space (509) + +--------------------------------------------------------------- +pdfTeX 3.141592-1.40.0-beta-20060725 was released on 2006-07-25 +--------------------------------------------------------------- +- added patches by Christian Schenk forwarded by Hartmut +- added patch 421 (popen) from Taco +- added lots of (void) to writettf* and ttf2afm to silence the compiler +- removed some unused variables from tounicode.c +- add new code by Thanh (patch 580) +- added security patch for xpdf :-( +- added JFIF detection patch to writejpg.c (bug 548) + +--------------------------------------------------------------- +pdfTeX 3.141592-1.40.0-beta-20060213 was released on 2006-02-13 +--------------------------------------------------------------- +- pdftex.web has been improved a bit +- new patch: + - 473 \ifabsnum and \ifabsdim +- the manual has been updated a bit and is again included + +--------------------------------------------------------------- +pdfTeX 3.141592-1.40.0-beta-20060125 was released on 2006-01-25 +--------------------------------------------------------------- +- pdfTeX now includes pdfeTeX, i.e. there is no way to make a binary without + eTeX support. Also the sources of TeX, eTeX and pdfTeX have been merged into + one file pdftex.web +- most pdfTeX change files are gone; only one file (pdftex.ch) remains. +- improved patches: + - 393 Ensure newline before endstream + +--------------------------------------------------------------- +pdfTeX 3.141592-1.40.0-beta-20060117 was released on 2006-01-17 +--------------------------------------------------------------- +- pdfTeX is now pdfeTeX, i.e. there is no way to make a binary without eTeX + support. Also the sources of TeX, eTeX and pdfTeX have been merged into one + file pdfetex.web +- strip trailing zeros in fractions of reals written +- removed patch 467 \ifindef +- improved logging of objstreams + +---------------------------------------------------------------- +pdfTeX 3.141592-1.40.0-alpha-20060111 was released on 2006-01-11 +---------------------------------------------------------------- +- new patches: + - 466 \primitive and \ifprimitive + - 467 \ifinedef +- improved patches: + - 393 Ensure newline before endstream +- pdfTeX now sets /ModDate and /Trapped by default (the values can be + overridden with \pdfinfo) +- more fixes to remove warnings from -Wall + +---------------------------------------------------------------- +pdfTeX 3.141592-1.40.0-alpha-20060106 was released on 2006-01-06 +---------------------------------------------------------------- +- many small fixes to remove warnings from -Wall +- removed some unused variables +- really fix bug 444 +- add some GCC extensions and use *nprintf instead of *printf where possible +- fixed a unicode bug in ttf2afm + +---------------------------------------------------------------- +pdfTeX 3.141592-1.40.0-alpha-20051226 was released on 2005-12-26 +---------------------------------------------------------------- +- improved patches: + - 453 Object Streams for PDF-1.5 + - take PK resolution from "pk_dpi" parameter in texmf.cnf, if it has not + been set by the format file or by the user + - 432 Rules in a PDF qQ group +- some fixes for Thanhs patch +- some improvements from -Wall +- .indent.pro for indent + +---------------------------------------------------------------- +pdfTeX 3.141592-1.40.0-alpha-20051221 was released on 2005-12-21 +---------------------------------------------------------------- +- has a large patch from Thanh +- pdflastlink has been fixed +- remove generated pdf in case of fatal error + +---------------------------------------------------------------- +pdfTeX 3.141592-1.40.0-alpha-20051208 was released on 2005-12-08 +---------------------------------------------------------------- +- improved patches: + - 386 grow obj_tab and dest_names arrays dynamically + - 453 Object Streams for PDF-1.5 + - take PK resolution from "pk_dpi" parameter in texmf.cnf, if it has not + been set by the format file or by the user +- write out PDF statistics later to make them correct +- makefile target pdfetex-all.pdf for a huge file + +---------------------------------------------------------------- +pdfTeX 3.141592-1.40.0-alpha-20051205 was released on 2005-12-05 +---------------------------------------------------------------- +- has these patches: + - 386 grow obj_tab and dest_names arrays dynamically + - 416 new primitive pdflastlink + - 427 Character positioning patch + - 432 Rules in a PDF qQ group + - 434 Remove queued map item handling + - 438 \pdfsavepos in DVI mode + - 443 512 zeros in Type1 fonts not copied anymore + - 447 Don't write /Encoding for non-reencoded fonts + - 453 Object Streams for PDF-1.5 + - take PK resolution from "pk_dpi" parameter in texmf.cnf, if it has not + been set by the format file or by the user + +------------------------------------------------- +pdfTeX 3.141592-1.30.6 was released on 2006-02-16 +------------------------------------------------- +- bugfix: updated xpdf to 3.01pl2 + +------------------------------------------------- +pdfTeX 3.141592-1.30.5 was released on 2005-12-04 +------------------------------------------------- +- bugfix: \pdfximage prints the file name twice (bug 441) +- bugfix: buffer overflow with \pdfmatch (bug 444) +- bugfix: updated xpdf to 3.01pl1 + ------------------------------------------------- pdfTeX 3.141592-1.30.4 was released on 2005-10-17 ------------------------------------------------- @@ -33,7 +331,7 @@ pdfTeX 3.141592-1.30.0 was released on 2005-08-01 - pdftex knows two new units: - new Didot (1nd=0.375mm) - new Cicero (1nc=12nd) -- New primitives: +- New primitives: - Inclusion of pngs has been greatly improved: It now does - alpha channel and transparency (only if %!PDF >=1.4) - 16-bit colour (only if %!PDF >=1.5); this must be enabled by setting @@ -44,7 +342,7 @@ pdfTeX 3.141592-1.30.0 was released on 2005-08-01 - fast direct embedding of png streams, if their structure allows this; the automatic fall-back is to decompress and re-compress as before. - pdfTeX now has an interface to the precise system time (if available) as an - aid in benchmarking TeX macro code. + aid in benchmarking TeX macro code. - \pdfelapsedtime is a read-only integer that (initially) returns the amount of time passed since the start of this run. This amount is given in `scaled seconds': the value 65536 counts as one second. If more time @@ -55,7 +353,7 @@ pdfTeX 3.141592-1.30.0 was released on 2005-08-01 - \pdfuniformdeviate generates a uniformly distributed random integer value between 0 (inclusive) and (exclusive). - \pdfnormaldeviate expands to a random integer value with a mean of 0 and - a unit of 65536. + a unit of 65536. - \pdfrandomseed allows access to the current seed value. - \pdfsetrandomseed sets the random seed to a specific value. - \pdfnoligatures\f disables ligatures for the loaded font \f. @@ -102,7 +400,7 @@ pdfTeX 3.141592-1.30.0 was released on 2005-08-01 - New experimental primitives: - \pdfmatch [icase] [subcount }] {}{} Implements pattern matching using the POSIX regex. - It returns the same values as \pdfstrcmp, but with the following semantics: + It returns the same values as \pdfstrcmp, but with the following semantics: -1: error case (invalid pattern, ...) 0: no match 1: match found @@ -236,7 +534,7 @@ pdfTeX 3.141592-1.20a was released on 2004-09-06 - The PTEX.FullBanner key is now inserted in the document info dict instead of the catalog. Newer versions of Acrobat display it. - Now includes support for encTeX. -- Extended \pdfannot to use reserveobjnum/useobjnum +- Extended \pdfannot to use reserveobjnum/useobjnum See http://www.ntg.nl/pipermail/ntg-pdftex/2004-March/000424.html - Bugfix: pdfTeX would read an .xpdfrc when including pdfs. See http://tug.org/pipermail/pdftex/2004-March/004835.html @@ -250,7 +548,7 @@ pdfTeX 3.141592-1.20a was released on 2004-09-06 - pdfTeX uses zlib 1.2.1 - pdfTeX uses xpdf 3.00 - pdfTeX uses libpng 1.2.7 - + ------------------------------------------------ pdfTeX 3.141592-1.11b was released on 2003-10-06 ------------------------------------------------ @@ -265,7 +563,7 @@ pdfTeX 3.141592-1.11a was released on 2003-08-02 - The behaviour when an included pdf has a newer version than the one specified with \pdfoptionpdfminorversion can be controlled by the new internal integer \pdfoptionpdfinclusionerrorlevel: If it's 0, pdfTeX gives only a warning; if - it's 1, it raises an error. + it's 1, it raises an error. This can also be set in pdftex.cfg with pdf_inclusion_errorlevel - The syntax for outlines has been extended to allow attributes: --> [count ] @@ -277,7 +575,7 @@ pdfTeX 3.141592-1.11a was released on 2003-08-02 --> | When the optional keyword `reserveobjnum' and `useobjnum' is not given, the - behaviour is unchanged. + behaviour is unchanged. See also http://tug.org/mailman/htdig/pdftex/2003-March/003814.html - Bugfix: /CreationDate didn't care for the timezone and could be fooled. See http://www.ntg.nl/pipermail/ntg-pdftex/2003-July/000223.html @@ -289,7 +587,7 @@ pdfTeX 3.141592-1.11a was released on 2003-08-02 See http://tug.org/pipermail/pdftex/2003-July/004262.html - Bugfix: \pdfstartlink would produce spurious /NewWindow entries. Also the syntax of and has been cleaned up. - See http://www.ntg.nl/pipermail/ntg-pdftex/2003-June/000167.html + See http://www.ntg.nl/pipermail/ntg-pdftex/2003-June/000167.html - Bugfix: \pdffontattr was not used for T3 fonts. See http://tug.org/pipermail/pdftex/2003-June/004127.html - Bugfix: The cropbox of an included pdf would sometimes not be used. @@ -305,7 +603,7 @@ pdfTeX 3.141592-1.10b was released on 2003-02-26 - Bugfix: Numbers in pdfs would sometimes be just a wee bit of (changed implementation of pdftoepdf.convertNumToPDF). - Bugfix: When setting the /Producer, /Creator or /CreationDate keys with - /pdfinfo pdfTeX would also insert default values which caused duplicate keys + \pdfinfo pdfTeX would also insert default values which caused duplicate keys in the info dict (see http://tug.org/pipermail/pdftex/2003-February/003524.html). - The reading of mapfiles is much faster (see @@ -316,7 +614,7 @@ pdfTeX 3.141592-1.10b was released on 2003-02-26 http://tug.org/pipermail/pdftex/2002-March/002367.html). - Bugfix: decimal_digits defaulted to 0, which caused problems with included images when including pdfs (see - http://tug.org/pipermail/pdftex/2003-February/003518.html). + http://tug.org/pipermail/pdftex/2003-February/003518.html). It now defaults to 4. - Bugfix: the type of included images was decided based only on the extensions; now pdfTeX looks for magic bytes at the start of files (see @@ -330,16 +628,16 @@ pdfTeX 3.14159-1.10a was released on 2003-01-16 - Additional attributes like MetaData are copied from included pdfs. - Currently the Page Group attribute (see sections 7.3.6 and 7.5.5 of the pdf 1.4 reference) of included pdfs is NOT copied (as we don't know how to - handle this when imposing pdfs). + handle this when imposing pdfs). This means including pdfs with transparency features will work as long as the included pdf doesn't use Page Groups; if it does, the result might not be what is expected (One application known to use Page Groups is Adobe Illustrator v9++). The distributed pdfTeX binaries will give a warning about any occurance of a Page Group in an included pdf and ignore the Page Group (thus producing syntactically correct pdfs but probably not the - intended output). + intended output). Acrobat 5++ and newer versions of Jaws display files without Page Groups - correctly, so this might not really be a problem. + correctly, so this might not really be a problem. See pdftoepdf.cc for preprocessor flags which allow this behaviour to be changed (copying the Page Group (most likely producing incorrect pdf) or treating Page Groups as errors). @@ -347,7 +645,7 @@ pdfTeX 3.14159-1.10a was released on 2003-01-16 generated file and the latest allowed pdf version of included pdfs: \pdfoptionpdfminorversion=3 tells pdfTeX to set the pdf version to 1.3 and allows only included pdfs with - versions <= 1.3 . + versions <= 1.3 . The default for \pdfoptionpdfminorversion is 4 for pdf 1.4 This must appear before any data is written to the generated pdf file, so you should put it at the very start of your files. @@ -371,7 +669,7 @@ pdfTeX 3.14159-1.10a was released on 2003-01-16 (thanks to Hartmut Henkel). - The support for the inclusion of tif images has been removed as it was of limited use (it worked only for gray-scale and rgb images) and produced wrong - results (all other images types where treated as rgb images). + results (all other images types where treated as rgb images). Tif images can be converted on *nix on-the-fly to pngs with the help of ImageMagick, epstopdf.sty or pdftex.def v0.4 or newer, and this rule: \DeclareGraphicsRule{.tif}{png}{.png}{`convert #1 `basename #1 .tif`.png} @@ -396,5 +694,5 @@ pdfTeX 3.14159-1.00a-pretest-20010806 was released on 2001-08-06 pdfTeX 3.14159-1.00a-pretest-20010804 was released on 2001-08-04 ---------------------------------------------------------------- -$Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/NEWS#88 $ +$Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/NEWS#86 $ // vim: tw=79:ts=2:expandtab:autoindent diff --git a/Build/source/texk/web2c/pdftexdir/README b/Build/source/texk/web2c/pdftexdir/README index c56f359cbbe..e11225a53a1 100644 --- a/Build/source/texk/web2c/pdftexdir/README +++ b/Build/source/texk/web2c/pdftexdir/README @@ -1,31 +1,28 @@ pdfTeX ====== -README for version 3.14159-1.30.4 of $Date: 2005/10/17 $ -======================================================== - -This directory contains the version 1.30.4 of pdfTeX, an extended version of -TeX that can create PDF directly from TeX source files and enhance the -result of TeX typesetting with the help of PDF. When PDF output is not -selected, pdfTeX produces normal DVI output, otherwise it produces PDF -output that looks identical to the DVI output. An important aspect of this -project is to investigate alternative justification algorithms, optionally -making use of multiple master fonts. - -pdfeTeX is an even more extended version of TeX than pdfTeX because it is -not based on TeX but on eTeX, an extended version of TeX. - -pdfTeX is based on the original TeX sources and Web2c, and has been +README for version 3.14159-1.40.0-rc4 +===================================== + +This directory contains the version 1.40.0-rc4 of pdfTeX, an extended +version of eTeX that can create PDF directly from TeX source files and +enhance the result of TeX typesetting with the help of PDF. When PDF output +is not selected, pdfTeX produces normal DVI output, otherwise it produces +PDF output that looks identical to the DVI output. An important aspect of +this project is to investigate alternative justification algorithms, +optionally making use of multiple master fonts. + +pdfTeX is based on the original eTeX sources and Web2c, and has been successfully compiled on Unix, Win32 and DOS systems. It is still under development and features may change; it produces reasonable PDF code. See the file NEWS for changes to the program. -Documentation about pdfTeX can be found at http://www.pdftex.org +Documentation about pdfTeX can be found at http://www.pdftex.org There is also a mailing list on pdfTeX; go to http://tug.org/mailman/listinfo/pdftex for more information about it. Its -searchable archive can be found at http://tug.org/pipermail/pdftex/ +searchable archive can be found at http://tug.org/pipermail/pdftex/ Please read http://www.catb.org/~esr/faqs/smart-questions.html before asking any questions on the pdfTeX mailing list. @@ -36,7 +33,7 @@ http://www.ntg.nl/mailman/listinfo/ntg-pdftex LEGAL ISSUES ============ -pdfTeX is copyright (c) 1996-2005 Han The Thanh, +pdfTeX is copyright (c) 1996-2006 Han The Thanh, pdfTeX is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -75,7 +72,7 @@ Peter Breitenlohner, Ricardo Sanchez Carmenes, Otfried Cheong, Thomas Esser, Hans Hagen, Hartmut Henkel, Taco Hoekwater, Pawel Jackowski, Tom Kacvinsky, Akira Kakuto, Reinhard Kotucha, Derek B. Noonburg, Heiko Oberdiek, Jiri Osoba, Fabrice Popineau, Sebastian Rahtz, Tomas Rokicki, -Martin Schröder, Petr Sojka, Olaf Weber, Jiri Zlatuska. +Martin Schröder, Petr Sojka, Ralf Utermann, Olaf Weber, Jiri Zlatuska. pdfTeX is built on top of TeX, written by Donald Knuth cum suis (Liang and so), and eTeX, written by Peter Breitenlohner cum suis; part of the @@ -86,5 +83,5 @@ paragraph optimization is inspired by the work of Hermann Zapf. Please send bug reports, offers of help, etc. to martin@pdftex.org -$Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/README#33 $ +$Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/README#30 $ // vim: tw=75 diff --git a/Build/source/texk/web2c/pdftexdir/avl.c b/Build/source/texk/web2c/pdftexdir/avl.c index 7b6bc17eac0..8d53954a7bc 100644 --- a/Build/source/texk/web2c/pdftexdir/avl.c +++ b/Build/source/texk/web2c/pdftexdir/avl.c @@ -33,455 +33,406 @@ with comparison function |compare| using parameter |param| and memory allocator |allocator|. Returns |NULL| if memory allocation failed. */ -struct avl_table * -avl_create (avl_comparison_func *compare, void *param, - struct libavl_allocator *allocator) +struct avl_table *avl_create(avl_comparison_func * compare, void *param, + struct libavl_allocator *allocator) { - struct avl_table *tree; + struct avl_table *tree; - assert (compare != NULL); + assert(compare != NULL); - if (allocator == NULL) - allocator = &avl_allocator_default; + if (allocator == NULL) + allocator = &avl_allocator_default; - tree = allocator->libavl_malloc (allocator, sizeof *tree); - if (tree == NULL) - return NULL; + tree = allocator->libavl_malloc(allocator, sizeof *tree); + if (tree == NULL) + return NULL; - tree->avl_root = NULL; - tree->avl_compare = compare; - tree->avl_param = param; - tree->avl_alloc = allocator; - tree->avl_count = 0; - tree->avl_generation = 0; + tree->avl_root = NULL; + tree->avl_compare = compare; + tree->avl_param = param; + tree->avl_alloc = allocator; + tree->avl_count = 0; + tree->avl_generation = 0; - return tree; + return tree; } /* Search |tree| for an item matching |item|, and return it if found. Otherwise return |NULL|. */ -void * -avl_find (const struct avl_table *tree, const void *item) +void *avl_find(const struct avl_table *tree, const void *item) { - const struct avl_node *p; - - assert (tree != NULL && item != NULL); - for (p = tree->avl_root; p != NULL; ) - { - int cmp = tree->avl_compare (item, p->avl_data, tree->avl_param); - - if (cmp < 0) - p = p->avl_link[0]; - else if (cmp > 0) - p = p->avl_link[1]; - else /* |cmp == 0| */ - return p->avl_data; + const struct avl_node *p; + + assert(tree != NULL && item != NULL); + for (p = tree->avl_root; p != NULL;) { + int cmp = tree->avl_compare(item, p->avl_data, tree->avl_param); + + if (cmp < 0) + p = p->avl_link[0]; + else if (cmp > 0) + p = p->avl_link[1]; + else /* |cmp == 0| */ + return p->avl_data; } - return NULL; + return NULL; } /* Inserts |item| into |tree| and returns a pointer to |item|'s address. If a duplicate item is found in the tree, returns a pointer to the duplicate without inserting |item|. Returns |NULL| in case of memory allocation failure. */ -void ** -avl_probe (struct avl_table *tree, void *item) +void **avl_probe(struct avl_table *tree, void *item) { - struct avl_node *y, *z; /* Top node to update balance factor, and parent. */ - struct avl_node *p, *q; /* Iterator, and parent. */ - struct avl_node *n; /* Newly inserted node. */ - struct avl_node *w; /* New root of rebalanced subtree. */ - int dir; /* Direction to descend. */ - - unsigned char da[AVL_MAX_HEIGHT]; /* Cached comparison results. */ - int k = 0; /* Number of cached results. */ - - assert (tree != NULL && item != NULL); - - z = (struct avl_node *) &tree->avl_root; - y = tree->avl_root; - dir = 0; - for (q = z, p = y; p != NULL; q = p, p = p->avl_link[dir]) - { - int cmp = tree->avl_compare (item, p->avl_data, tree->avl_param); - if (cmp == 0) - return &p->avl_data; - - if (p->avl_balance != 0) - z = q, y = p, k = 0; - da[k++] = dir = cmp > 0; + struct avl_node *y, *z; /* Top node to update balance factor, and parent. */ + struct avl_node *p, *q; /* Iterator, and parent. */ + struct avl_node *n; /* Newly inserted node. */ + struct avl_node *w; /* New root of rebalanced subtree. */ + int dir; /* Direction to descend. */ + + unsigned char da[AVL_MAX_HEIGHT]; /* Cached comparison results. */ + int k = 0; /* Number of cached results. */ + + assert(tree != NULL && item != NULL); + + z = (struct avl_node *) &tree->avl_root; + y = tree->avl_root; + dir = 0; + for (q = z, p = y; p != NULL; q = p, p = p->avl_link[dir]) { + int cmp = tree->avl_compare(item, p->avl_data, tree->avl_param); + if (cmp == 0) + return &p->avl_data; + + if (p->avl_balance != 0) + z = q, y = p, k = 0; + da[k++] = dir = cmp > 0; } - n = q->avl_link[dir] = - tree->avl_alloc->libavl_malloc (tree->avl_alloc, sizeof *n); - if (n == NULL) - return NULL; - - tree->avl_count++; - n->avl_data = item; - n->avl_link[0] = n->avl_link[1] = NULL; - n->avl_balance = 0; - if (y == NULL) - return &n->avl_data; + n = q->avl_link[dir] = + tree->avl_alloc->libavl_malloc(tree->avl_alloc, sizeof *n); + if (n == NULL) + return NULL; - for (p = y, k = 0; p != n; p = p->avl_link[da[k]], k++) - if (da[k] == 0) - p->avl_balance--; - else - p->avl_balance++; - - if (y->avl_balance == -2) - { - struct avl_node *x = y->avl_link[0]; - if (x->avl_balance == -1) - { - w = x; - y->avl_link[0] = x->avl_link[1]; - x->avl_link[1] = y; - x->avl_balance = y->avl_balance = 0; - } - else - { - assert (x->avl_balance == +1); - w = x->avl_link[1]; - x->avl_link[1] = w->avl_link[0]; - w->avl_link[0] = x; - y->avl_link[0] = w->avl_link[1]; - w->avl_link[1] = y; - if (w->avl_balance == -1) - x->avl_balance = 0, y->avl_balance = +1; - else if (w->avl_balance == 0) + tree->avl_count++; + n->avl_data = item; + n->avl_link[0] = n->avl_link[1] = NULL; + n->avl_balance = 0; + if (y == NULL) + return &n->avl_data; + + for (p = y, k = 0; p != n; p = p->avl_link[da[k]], k++) + if (da[k] == 0) + p->avl_balance--; + else + p->avl_balance++; + + if (y->avl_balance == -2) { + struct avl_node *x = y->avl_link[0]; + if (x->avl_balance == -1) { + w = x; + y->avl_link[0] = x->avl_link[1]; + x->avl_link[1] = y; x->avl_balance = y->avl_balance = 0; - else /* |w->avl_balance == +1| */ - x->avl_balance = -1, y->avl_balance = 0; - w->avl_balance = 0; + } else { + assert(x->avl_balance == +1); + w = x->avl_link[1]; + x->avl_link[1] = w->avl_link[0]; + w->avl_link[0] = x; + y->avl_link[0] = w->avl_link[1]; + w->avl_link[1] = y; + if (w->avl_balance == -1) + x->avl_balance = 0, y->avl_balance = +1; + else if (w->avl_balance == 0) + x->avl_balance = y->avl_balance = 0; + else /* |w->avl_balance == +1| */ + x->avl_balance = -1, y->avl_balance = 0; + w->avl_balance = 0; } - } - else if (y->avl_balance == +2) - { - struct avl_node *x = y->avl_link[1]; - if (x->avl_balance == +1) - { - w = x; - y->avl_link[1] = x->avl_link[0]; - x->avl_link[0] = y; - x->avl_balance = y->avl_balance = 0; - } - else - { - assert (x->avl_balance == -1); - w = x->avl_link[0]; - x->avl_link[0] = w->avl_link[1]; - w->avl_link[1] = x; - y->avl_link[1] = w->avl_link[0]; - w->avl_link[0] = y; - if (w->avl_balance == +1) - x->avl_balance = 0, y->avl_balance = -1; - else if (w->avl_balance == 0) + } else if (y->avl_balance == +2) { + struct avl_node *x = y->avl_link[1]; + if (x->avl_balance == +1) { + w = x; + y->avl_link[1] = x->avl_link[0]; + x->avl_link[0] = y; x->avl_balance = y->avl_balance = 0; - else /* |w->avl_balance == -1| */ - x->avl_balance = +1, y->avl_balance = 0; - w->avl_balance = 0; + } else { + assert(x->avl_balance == -1); + w = x->avl_link[0]; + x->avl_link[0] = w->avl_link[1]; + w->avl_link[1] = x; + y->avl_link[1] = w->avl_link[0]; + w->avl_link[0] = y; + if (w->avl_balance == +1) + x->avl_balance = 0, y->avl_balance = -1; + else if (w->avl_balance == 0) + x->avl_balance = y->avl_balance = 0; + else /* |w->avl_balance == -1| */ + x->avl_balance = +1, y->avl_balance = 0; + w->avl_balance = 0; } - } - else - return &n->avl_data; - z->avl_link[y != z->avl_link[0]] = w; + } else + return &n->avl_data; + z->avl_link[y != z->avl_link[0]] = w; - tree->avl_generation++; - return &n->avl_data; + tree->avl_generation++; + return &n->avl_data; } /* Inserts |item| into |table|. Returns |NULL| if |item| was successfully inserted or if a memory allocation error occurred. Otherwise, returns the duplicate item. */ -void * -avl_insert (struct avl_table *table, void *item) +void *avl_insert(struct avl_table *table, void *item) { - void **p = avl_probe (table, item); - return p == NULL || *p == item ? NULL : *p; + void **p = avl_probe(table, item); + return p == NULL || *p == item ? NULL : *p; } /* Inserts |item| into |table|, replacing any duplicate item. Returns |NULL| if |item| was inserted without replacing a duplicate, or if a memory allocation error occurred. Otherwise, returns the item that was replaced. */ -void * -avl_replace (struct avl_table *table, void *item) +void *avl_replace(struct avl_table *table, void *item) { - void **p = avl_probe (table, item); - if (p == NULL || *p == item) - return NULL; - else - { - void *r = *p; - *p = item; - return r; + void **p = avl_probe(table, item); + if (p == NULL || *p == item) + return NULL; + else { + void *r = *p; + *p = item; + return r; } } /* Deletes from |tree| and returns an item matching |item|. Returns a null pointer if no matching item found. */ -void * -avl_delete (struct avl_table *tree, const void *item) +void *avl_delete(struct avl_table *tree, const void *item) { - /* Stack of nodes. */ - struct avl_node *pa[AVL_MAX_HEIGHT]; /* Nodes. */ - unsigned char da[AVL_MAX_HEIGHT]; /* |avl_link[]| indexes. */ - int k; /* Stack pointer. */ + /* Stack of nodes. */ + struct avl_node *pa[AVL_MAX_HEIGHT]; /* Nodes. */ + unsigned char da[AVL_MAX_HEIGHT]; /* |avl_link[]| indexes. */ + int k; /* Stack pointer. */ - struct avl_node *p; /* Traverses tree to find node to delete. */ - int cmp; /* Result of comparison between |item| and |p|. */ + struct avl_node *p; /* Traverses tree to find node to delete. */ + int cmp; /* Result of comparison between |item| and |p|. */ - assert (tree != NULL && item != NULL); + assert(tree != NULL && item != NULL); - k = 0; - p = (struct avl_node *) &tree->avl_root; - for (cmp = -1; cmp != 0; - cmp = tree->avl_compare (item, p->avl_data, tree->avl_param)) - { - int dir = cmp > 0; + k = 0; + p = (struct avl_node *) &tree->avl_root; + for (cmp = -1; cmp != 0; + cmp = tree->avl_compare(item, p->avl_data, tree->avl_param)) { + int dir = cmp > 0; - pa[k] = p; - da[k++] = dir; + pa[k] = p; + da[k++] = dir; - p = p->avl_link[dir]; - if (p == NULL) - return NULL; + p = p->avl_link[dir]; + if (p == NULL) + return NULL; } - item = p->avl_data; - - if (p->avl_link[1] == NULL) - pa[k - 1]->avl_link[da[k - 1]] = p->avl_link[0]; - else - { - struct avl_node *r = p->avl_link[1]; - if (r->avl_link[0] == NULL) - { - r->avl_link[0] = p->avl_link[0]; - r->avl_balance = p->avl_balance; - pa[k - 1]->avl_link[da[k - 1]] = r; - da[k] = 1; - pa[k++] = r; - } - else - { - struct avl_node *s; - int j = k++; - - for (;;) - { - da[k] = 0; - pa[k++] = r; - s = r->avl_link[0]; - if (s->avl_link[0] == NULL) - break; - - r = s; + item = p->avl_data; + + if (p->avl_link[1] == NULL) + pa[k - 1]->avl_link[da[k - 1]] = p->avl_link[0]; + else { + struct avl_node *r = p->avl_link[1]; + if (r->avl_link[0] == NULL) { + r->avl_link[0] = p->avl_link[0]; + r->avl_balance = p->avl_balance; + pa[k - 1]->avl_link[da[k - 1]] = r; + da[k] = 1; + pa[k++] = r; + } else { + struct avl_node *s; + int j = k++; + + for (;;) { + da[k] = 0; + pa[k++] = r; + s = r->avl_link[0]; + if (s->avl_link[0] == NULL) + break; + + r = s; } - s->avl_link[0] = p->avl_link[0]; - r->avl_link[0] = s->avl_link[1]; - s->avl_link[1] = p->avl_link[1]; - s->avl_balance = p->avl_balance; + s->avl_link[0] = p->avl_link[0]; + r->avl_link[0] = s->avl_link[1]; + s->avl_link[1] = p->avl_link[1]; + s->avl_balance = p->avl_balance; - pa[j - 1]->avl_link[da[j - 1]] = s; - da[j] = 1; - pa[j] = s; + pa[j - 1]->avl_link[da[j - 1]] = s; + da[j] = 1; + pa[j] = s; } } - tree->avl_alloc->libavl_free (tree->avl_alloc, p); - - assert (k > 0); - while (--k > 0) - { - struct avl_node *y = pa[k]; - - if (da[k] == 0) - { - y->avl_balance++; - if (y->avl_balance == +1) - break; - else if (y->avl_balance == +2) - { - struct avl_node *x = y->avl_link[1]; - if (x->avl_balance == -1) - { - struct avl_node *w; - assert (x->avl_balance == -1); - w = x->avl_link[0]; - x->avl_link[0] = w->avl_link[1]; - w->avl_link[1] = x; - y->avl_link[1] = w->avl_link[0]; - w->avl_link[0] = y; - if (w->avl_balance == +1) - x->avl_balance = 0, y->avl_balance = -1; - else if (w->avl_balance == 0) - x->avl_balance = y->avl_balance = 0; - else /* |w->avl_balance == -1| */ - x->avl_balance = +1, y->avl_balance = 0; - w->avl_balance = 0; - pa[k - 1]->avl_link[da[k - 1]] = w; - } - else - { - y->avl_link[1] = x->avl_link[0]; - x->avl_link[0] = y; - pa[k - 1]->avl_link[da[k - 1]] = x; - if (x->avl_balance == 0) - { - x->avl_balance = -1; - y->avl_balance = +1; - break; - } - else - x->avl_balance = y->avl_balance = 0; + tree->avl_alloc->libavl_free(tree->avl_alloc, p); + + assert(k > 0); + while (--k > 0) { + struct avl_node *y = pa[k]; + + if (da[k] == 0) { + y->avl_balance++; + if (y->avl_balance == +1) + break; + else if (y->avl_balance == +2) { + struct avl_node *x = y->avl_link[1]; + if (x->avl_balance == -1) { + struct avl_node *w; + assert(x->avl_balance == -1); + w = x->avl_link[0]; + x->avl_link[0] = w->avl_link[1]; + w->avl_link[1] = x; + y->avl_link[1] = w->avl_link[0]; + w->avl_link[0] = y; + if (w->avl_balance == +1) + x->avl_balance = 0, y->avl_balance = -1; + else if (w->avl_balance == 0) + x->avl_balance = y->avl_balance = 0; + else /* |w->avl_balance == -1| */ + x->avl_balance = +1, y->avl_balance = 0; + w->avl_balance = 0; + pa[k - 1]->avl_link[da[k - 1]] = w; + } else { + y->avl_link[1] = x->avl_link[0]; + x->avl_link[0] = y; + pa[k - 1]->avl_link[da[k - 1]] = x; + if (x->avl_balance == 0) { + x->avl_balance = -1; + y->avl_balance = +1; + break; + } else + x->avl_balance = y->avl_balance = 0; } } - } - else - { - y->avl_balance--; - if (y->avl_balance == -1) - break; - else if (y->avl_balance == -2) - { - struct avl_node *x = y->avl_link[0]; - if (x->avl_balance == +1) - { - struct avl_node *w; - assert (x->avl_balance == +1); - w = x->avl_link[1]; - x->avl_link[1] = w->avl_link[0]; - w->avl_link[0] = x; - y->avl_link[0] = w->avl_link[1]; - w->avl_link[1] = y; - if (w->avl_balance == -1) - x->avl_balance = 0, y->avl_balance = +1; - else if (w->avl_balance == 0) - x->avl_balance = y->avl_balance = 0; - else /* |w->avl_balance == +1| */ - x->avl_balance = -1, y->avl_balance = 0; - w->avl_balance = 0; - pa[k - 1]->avl_link[da[k - 1]] = w; - } - else - { - y->avl_link[0] = x->avl_link[1]; - x->avl_link[1] = y; - pa[k - 1]->avl_link[da[k - 1]] = x; - if (x->avl_balance == 0) - { - x->avl_balance = +1; - y->avl_balance = -1; - break; - } - else - x->avl_balance = y->avl_balance = 0; + } else { + y->avl_balance--; + if (y->avl_balance == -1) + break; + else if (y->avl_balance == -2) { + struct avl_node *x = y->avl_link[0]; + if (x->avl_balance == +1) { + struct avl_node *w; + assert(x->avl_balance == +1); + w = x->avl_link[1]; + x->avl_link[1] = w->avl_link[0]; + w->avl_link[0] = x; + y->avl_link[0] = w->avl_link[1]; + w->avl_link[1] = y; + if (w->avl_balance == -1) + x->avl_balance = 0, y->avl_balance = +1; + else if (w->avl_balance == 0) + x->avl_balance = y->avl_balance = 0; + else /* |w->avl_balance == +1| */ + x->avl_balance = -1, y->avl_balance = 0; + w->avl_balance = 0; + pa[k - 1]->avl_link[da[k - 1]] = w; + } else { + y->avl_link[0] = x->avl_link[1]; + x->avl_link[1] = y; + pa[k - 1]->avl_link[da[k - 1]] = x; + if (x->avl_balance == 0) { + x->avl_balance = +1; + y->avl_balance = -1; + break; + } else + x->avl_balance = y->avl_balance = 0; } } } } - tree->avl_count--; - tree->avl_generation++; - return (void *) item; + tree->avl_count--; + tree->avl_generation++; + return (void *) item; } /* Refreshes the stack of parent pointers in |trav| and updates its generation number. */ -static void -trav_refresh (struct avl_traverser *trav) +static void trav_refresh(struct avl_traverser *trav) { - assert (trav != NULL); + assert(trav != NULL); - trav->avl_generation = trav->avl_table->avl_generation; + trav->avl_generation = trav->avl_table->avl_generation; - if (trav->avl_node != NULL) - { - avl_comparison_func *cmp = trav->avl_table->avl_compare; - void *param = trav->avl_table->avl_param; - struct avl_node *node = trav->avl_node; - struct avl_node *i; + if (trav->avl_node != NULL) { + avl_comparison_func *cmp = trav->avl_table->avl_compare; + void *param = trav->avl_table->avl_param; + struct avl_node *node = trav->avl_node; + struct avl_node *i; - trav->avl_height = 0; - for (i = trav->avl_table->avl_root; i != node; ) - { - assert (trav->avl_height < AVL_MAX_HEIGHT); - assert (i != NULL); + trav->avl_height = 0; + for (i = trav->avl_table->avl_root; i != node;) { + assert(trav->avl_height < AVL_MAX_HEIGHT); + assert(i != NULL); - trav->avl_stack[trav->avl_height++] = i; - i = i->avl_link[cmp (node->avl_data, i->avl_data, param) > 0]; + trav->avl_stack[trav->avl_height++] = i; + i = i->avl_link[cmp(node->avl_data, i->avl_data, param) > 0]; } } } /* Initializes |trav| for use with |tree| and selects the null node. */ -void -avl_t_init (struct avl_traverser *trav, struct avl_table *tree) +void avl_t_init(struct avl_traverser *trav, struct avl_table *tree) { - trav->avl_table = tree; - trav->avl_node = NULL; - trav->avl_height = 0; - trav->avl_generation = tree->avl_generation; + trav->avl_table = tree; + trav->avl_node = NULL; + trav->avl_height = 0; + trav->avl_generation = tree->avl_generation; } /* Initializes |trav| for |tree| and selects and returns a pointer to its least-valued item. Returns |NULL| if |tree| contains no nodes. */ -void * -avl_t_first (struct avl_traverser *trav, struct avl_table *tree) +void *avl_t_first(struct avl_traverser *trav, struct avl_table *tree) { - struct avl_node *x; + struct avl_node *x; - assert (tree != NULL && trav != NULL); + assert(tree != NULL && trav != NULL); - trav->avl_table = tree; - trav->avl_height = 0; - trav->avl_generation = tree->avl_generation; + trav->avl_table = tree; + trav->avl_height = 0; + trav->avl_generation = tree->avl_generation; - x = tree->avl_root; - if (x != NULL) - while (x->avl_link[0] != NULL) - { - assert (trav->avl_height < AVL_MAX_HEIGHT); - trav->avl_stack[trav->avl_height++] = x; - x = x->avl_link[0]; - } - trav->avl_node = x; + x = tree->avl_root; + if (x != NULL) + while (x->avl_link[0] != NULL) { + assert(trav->avl_height < AVL_MAX_HEIGHT); + trav->avl_stack[trav->avl_height++] = x; + x = x->avl_link[0]; + } + trav->avl_node = x; - return x != NULL ? x->avl_data : NULL; + return x != NULL ? x->avl_data : NULL; } /* Initializes |trav| for |tree| and selects and returns a pointer to its greatest-valued item. Returns |NULL| if |tree| contains no nodes. */ -void * -avl_t_last (struct avl_traverser *trav, struct avl_table *tree) +void *avl_t_last(struct avl_traverser *trav, struct avl_table *tree) { - struct avl_node *x; + struct avl_node *x; - assert (tree != NULL && trav != NULL); + assert(tree != NULL && trav != NULL); - trav->avl_table = tree; - trav->avl_height = 0; - trav->avl_generation = tree->avl_generation; + trav->avl_table = tree; + trav->avl_height = 0; + trav->avl_generation = tree->avl_generation; - x = tree->avl_root; - if (x != NULL) - while (x->avl_link[1] != NULL) - { - assert (trav->avl_height < AVL_MAX_HEIGHT); - trav->avl_stack[trav->avl_height++] = x; - x = x->avl_link[1]; - } - trav->avl_node = x; + x = tree->avl_root; + if (x != NULL) + while (x->avl_link[1] != NULL) { + assert(trav->avl_height < AVL_MAX_HEIGHT); + trav->avl_stack[trav->avl_height++] = x; + x = x->avl_link[1]; + } + trav->avl_node = x; - return x != NULL ? x->avl_data : NULL; + return x != NULL ? x->avl_data : NULL; } /* Searches for |item| in |tree|. @@ -489,36 +440,34 @@ avl_t_last (struct avl_traverser *trav, struct avl_table *tree) as well. If there is no matching item, initializes |trav| to the null item and returns |NULL|. */ -void * -avl_t_find (struct avl_traverser *trav, struct avl_table *tree, void *item) +void *avl_t_find(struct avl_traverser *trav, struct avl_table *tree, void *item) { - struct avl_node *p, *q; - - assert (trav != NULL && tree != NULL && item != NULL); - trav->avl_table = tree; - trav->avl_height = 0; - trav->avl_generation = tree->avl_generation; - for (p = tree->avl_root; p != NULL; p = q) - { - int cmp = tree->avl_compare (item, p->avl_data, tree->avl_param); - - if (cmp < 0) - q = p->avl_link[0]; - else if (cmp > 0) - q = p->avl_link[1]; - else /* |cmp == 0| */ - { - trav->avl_node = p; - return p->avl_data; + struct avl_node *p, *q; + + assert(trav != NULL && tree != NULL && item != NULL); + trav->avl_table = tree; + trav->avl_height = 0; + trav->avl_generation = tree->avl_generation; + for (p = tree->avl_root; p != NULL; p = q) { + int cmp = tree->avl_compare(item, p->avl_data, tree->avl_param); + + if (cmp < 0) + q = p->avl_link[0]; + else if (cmp > 0) + q = p->avl_link[1]; + else { /* |cmp == 0| */ + + trav->avl_node = p; + return p->avl_data; } - assert (trav->avl_height < AVL_MAX_HEIGHT); - trav->avl_stack[trav->avl_height++] = p; + assert(trav->avl_height < AVL_MAX_HEIGHT); + trav->avl_stack[trav->avl_height++] = p; } - trav->avl_height = 0; - trav->avl_node = NULL; - return NULL; + trav->avl_height = 0; + trav->avl_node = NULL; + return NULL; } /* Attempts to insert |item| into |tree|. @@ -528,190 +477,163 @@ avl_t_find (struct avl_traverser *trav, struct avl_table *tree, void *item) its location. No replacement of the item occurs. If a memory allocation failure occurs, |NULL| is returned and |trav| is initialized to the null item. */ -void * -avl_t_insert (struct avl_traverser *trav, struct avl_table *tree, void *item) +void *avl_t_insert(struct avl_traverser *trav, struct avl_table *tree, + void *item) { - void **p; - - assert (trav != NULL && tree != NULL && item != NULL); - - p = avl_probe (tree, item); - if (p != NULL) - { - trav->avl_table = tree; - trav->avl_node = - ((struct avl_node *) - ((char *) p - offsetof (struct avl_node, avl_data))); - trav->avl_generation = tree->avl_generation - 1; - return *p; - } - else - { - avl_t_init (trav, tree); - return NULL; + void **p; + + assert(trav != NULL && tree != NULL && item != NULL); + + p = avl_probe(tree, item); + if (p != NULL) { + trav->avl_table = tree; + trav->avl_node = ((struct avl_node *) + ((char *) p - offsetof(struct avl_node, avl_data))); + trav->avl_generation = tree->avl_generation - 1; + return *p; + } else { + avl_t_init(trav, tree); + return NULL; } } /* Initializes |trav| to have the same current node as |src|. */ -void * -avl_t_copy (struct avl_traverser *trav, const struct avl_traverser *src) +void *avl_t_copy(struct avl_traverser *trav, const struct avl_traverser *src) { - assert (trav != NULL && src != NULL); - - if (trav != src) - { - trav->avl_table = src->avl_table; - trav->avl_node = src->avl_node; - trav->avl_generation = src->avl_generation; - if (trav->avl_generation == trav->avl_table->avl_generation) - { - trav->avl_height = src->avl_height; - memcpy (trav->avl_stack, (const void *) src->avl_stack, - sizeof *trav->avl_stack * trav->avl_height); + assert(trav != NULL && src != NULL); + + if (trav != src) { + trav->avl_table = src->avl_table; + trav->avl_node = src->avl_node; + trav->avl_generation = src->avl_generation; + if (trav->avl_generation == trav->avl_table->avl_generation) { + trav->avl_height = src->avl_height; + memcpy(trav->avl_stack, (const void *) src->avl_stack, + sizeof *trav->avl_stack * trav->avl_height); } } - return trav->avl_node != NULL ? trav->avl_node->avl_data : NULL; + return trav->avl_node != NULL ? trav->avl_node->avl_data : NULL; } /* Returns the next data item in inorder within the tree being traversed with |trav|, or if there are no more data items returns |NULL|. */ -void * -avl_t_next (struct avl_traverser *trav) +void *avl_t_next(struct avl_traverser *trav) { - struct avl_node *x; + struct avl_node *x; - assert (trav != NULL); + assert(trav != NULL); - if (trav->avl_generation != trav->avl_table->avl_generation) - trav_refresh (trav); + if (trav->avl_generation != trav->avl_table->avl_generation) + trav_refresh(trav); - x = trav->avl_node; - if (x == NULL) - { - return avl_t_first (trav, trav->avl_table); - } - else if (x->avl_link[1] != NULL) - { - assert (trav->avl_height < AVL_MAX_HEIGHT); - trav->avl_stack[trav->avl_height++] = x; - x = x->avl_link[1]; - - while (x->avl_link[0] != NULL) - { - assert (trav->avl_height < AVL_MAX_HEIGHT); - trav->avl_stack[trav->avl_height++] = x; - x = x->avl_link[0]; + x = trav->avl_node; + if (x == NULL) { + return avl_t_first(trav, trav->avl_table); + } else if (x->avl_link[1] != NULL) { + assert(trav->avl_height < AVL_MAX_HEIGHT); + trav->avl_stack[trav->avl_height++] = x; + x = x->avl_link[1]; + + while (x->avl_link[0] != NULL) { + assert(trav->avl_height < AVL_MAX_HEIGHT); + trav->avl_stack[trav->avl_height++] = x; + x = x->avl_link[0]; } - } - else - { - struct avl_node *y; - - do - { - if (trav->avl_height == 0) - { - trav->avl_node = NULL; - return NULL; + } else { + struct avl_node *y; + + do { + if (trav->avl_height == 0) { + trav->avl_node = NULL; + return NULL; } - y = x; - x = trav->avl_stack[--trav->avl_height]; + y = x; + x = trav->avl_stack[--trav->avl_height]; } - while (y == x->avl_link[1]); + while (y == x->avl_link[1]); } - trav->avl_node = x; + trav->avl_node = x; - return x->avl_data; + return x->avl_data; } /* Returns the previous data item in inorder within the tree being traversed with |trav|, or if there are no more data items returns |NULL|. */ -void * -avl_t_prev (struct avl_traverser *trav) +void *avl_t_prev(struct avl_traverser *trav) { - struct avl_node *x; + struct avl_node *x; - assert (trav != NULL); + assert(trav != NULL); - if (trav->avl_generation != trav->avl_table->avl_generation) - trav_refresh (trav); + if (trav->avl_generation != trav->avl_table->avl_generation) + trav_refresh(trav); - x = trav->avl_node; - if (x == NULL) - { - return avl_t_last (trav, trav->avl_table); - } - else if (x->avl_link[0] != NULL) - { - assert (trav->avl_height < AVL_MAX_HEIGHT); - trav->avl_stack[trav->avl_height++] = x; - x = x->avl_link[0]; - - while (x->avl_link[1] != NULL) - { - assert (trav->avl_height < AVL_MAX_HEIGHT); - trav->avl_stack[trav->avl_height++] = x; - x = x->avl_link[1]; + x = trav->avl_node; + if (x == NULL) { + return avl_t_last(trav, trav->avl_table); + } else if (x->avl_link[0] != NULL) { + assert(trav->avl_height < AVL_MAX_HEIGHT); + trav->avl_stack[trav->avl_height++] = x; + x = x->avl_link[0]; + + while (x->avl_link[1] != NULL) { + assert(trav->avl_height < AVL_MAX_HEIGHT); + trav->avl_stack[trav->avl_height++] = x; + x = x->avl_link[1]; } - } - else - { - struct avl_node *y; - - do - { - if (trav->avl_height == 0) - { - trav->avl_node = NULL; - return NULL; + } else { + struct avl_node *y; + + do { + if (trav->avl_height == 0) { + trav->avl_node = NULL; + return NULL; } - y = x; - x = trav->avl_stack[--trav->avl_height]; + y = x; + x = trav->avl_stack[--trav->avl_height]; } - while (y == x->avl_link[0]); + while (y == x->avl_link[0]); } - trav->avl_node = x; + trav->avl_node = x; - return x->avl_data; + return x->avl_data; } /* Returns |trav|'s current item. */ -void * -avl_t_cur (struct avl_traverser *trav) +void *avl_t_cur(struct avl_traverser *trav) { - assert (trav != NULL); + assert(trav != NULL); - return trav->avl_node != NULL ? trav->avl_node->avl_data : NULL; + return trav->avl_node != NULL ? trav->avl_node->avl_data : NULL; } /* Replaces the current item in |trav| by |new| and returns the item replaced. |trav| must not have the null item selected. The new item must not upset the ordering of the tree. */ -void * -avl_t_replace (struct avl_traverser *trav, void *new) +void *avl_t_replace(struct avl_traverser *trav, void *new) { - void *old; + void *old; - assert (trav != NULL && trav->avl_node != NULL && new != NULL); - old = trav->avl_node->avl_data; - trav->avl_node->avl_data = new; - return old; + assert(trav != NULL && trav->avl_node != NULL && new != NULL); + old = trav->avl_node->avl_data; + trav->avl_node->avl_data = new; + return old; } static void -copy_error_recovery (struct avl_node **stack, int height, - struct avl_table *new, avl_item_func *destroy) +copy_error_recovery(struct avl_node **stack, int height, + struct avl_table *new, avl_item_func * destroy) { - assert (stack != NULL && height >= 0 && new != NULL); + assert(stack != NULL && height >= 0 && new != NULL); - for (; height > 2; height -= 2) - stack[height - 1]->avl_link[1] = NULL; - avl_destroy (new, destroy); + for (; height > 2; height -= 2) + stack[height - 1]->avl_link[1] = NULL; + avl_destroy(new, destroy); } /* Copies |org| to a newly created tree, which is returned. @@ -723,168 +645,147 @@ copy_error_recovery (struct avl_node **stack, int height, and returns |NULL|. If |allocator != NULL|, it is used for allocation in the new tree. Otherwise, the same allocator used for |org| is used. */ -struct avl_table * -avl_copy (const struct avl_table *org, avl_copy_func *copy, - avl_item_func *destroy, struct libavl_allocator *allocator) +struct avl_table *avl_copy(const struct avl_table *org, avl_copy_func * copy, + avl_item_func * destroy, + struct libavl_allocator *allocator) { - struct avl_node *stack[2 * (AVL_MAX_HEIGHT + 1)]; - int height = 0; + struct avl_node *stack[2 * (AVL_MAX_HEIGHT + 1)]; + int height = 0; - struct avl_table *new; - const struct avl_node *x; - struct avl_node *y; + struct avl_table *new; + const struct avl_node *x; + struct avl_node *y; - assert (org != NULL); - new = avl_create (org->avl_compare, org->avl_param, - allocator != NULL ? allocator : org->avl_alloc); - if (new == NULL) - return NULL; - new->avl_count = org->avl_count; - if (new->avl_count == 0) - return new; - - x = (const struct avl_node *) &org->avl_root; - y = (struct avl_node *) &new->avl_root; - for (;;) - { - while (x->avl_link[0] != NULL) - { - assert (height < 2 * (AVL_MAX_HEIGHT + 1)); - - y->avl_link[0] = - new->avl_alloc->libavl_malloc (new->avl_alloc, - sizeof *y->avl_link[0]); - if (y->avl_link[0] == NULL) - { - if (y != (struct avl_node *) &new->avl_root) - { - y->avl_data = NULL; - y->avl_link[1] = NULL; + assert(org != NULL); + new = avl_create(org->avl_compare, org->avl_param, + allocator != NULL ? allocator : org->avl_alloc); + if (new == NULL) + return NULL; + new->avl_count = org->avl_count; + if (new->avl_count == 0) + return new; + + x = (const struct avl_node *) &org->avl_root; + y = (struct avl_node *) &new->avl_root; + for (;;) { + while (x->avl_link[0] != NULL) { + assert(height < 2 * (AVL_MAX_HEIGHT + 1)); + + y->avl_link[0] = + new->avl_alloc->libavl_malloc(new->avl_alloc, + sizeof *y->avl_link[0]); + if (y->avl_link[0] == NULL) { + if (y != (struct avl_node *) &new->avl_root) { + y->avl_data = NULL; + y->avl_link[1] = NULL; } - copy_error_recovery (stack, height, new, destroy); - return NULL; + copy_error_recovery(stack, height, new, destroy); + return NULL; } - stack[height++] = (struct avl_node *) x; - stack[height++] = y; - x = x->avl_link[0]; - y = y->avl_link[0]; + stack[height++] = (struct avl_node *) x; + stack[height++] = y; + x = x->avl_link[0]; + y = y->avl_link[0]; } - y->avl_link[0] = NULL; - - for (;;) - { - y->avl_balance = x->avl_balance; - if (copy == NULL) - y->avl_data = x->avl_data; - else - { - y->avl_data = copy (x->avl_data, org->avl_param); - if (y->avl_data == NULL) - { - y->avl_link[1] = NULL; - copy_error_recovery (stack, height, new, destroy); - return NULL; + y->avl_link[0] = NULL; + + for (;;) { + y->avl_balance = x->avl_balance; + if (copy == NULL) + y->avl_data = x->avl_data; + else { + y->avl_data = copy(x->avl_data, org->avl_param); + if (y->avl_data == NULL) { + y->avl_link[1] = NULL; + copy_error_recovery(stack, height, new, destroy); + return NULL; } } - if (x->avl_link[1] != NULL) - { - y->avl_link[1] = - new->avl_alloc->libavl_malloc (new->avl_alloc, - sizeof *y->avl_link[1]); - if (y->avl_link[1] == NULL) - { - copy_error_recovery (stack, height, new, destroy); - return NULL; + if (x->avl_link[1] != NULL) { + y->avl_link[1] = + new->avl_alloc->libavl_malloc(new->avl_alloc, + sizeof *y->avl_link[1]); + if (y->avl_link[1] == NULL) { + copy_error_recovery(stack, height, new, destroy); + return NULL; } - x = x->avl_link[1]; - y = y->avl_link[1]; - break; - } - else - y->avl_link[1] = NULL; + x = x->avl_link[1]; + y = y->avl_link[1]; + break; + } else + y->avl_link[1] = NULL; - if (height <= 2) - return new; + if (height <= 2) + return new; - y = stack[--height]; - x = stack[--height]; + y = stack[--height]; + x = stack[--height]; } } } /* Frees storage allocated for |tree|. If |destroy != NULL|, applies it to each data item in inorder. */ -void -avl_destroy (struct avl_table *tree, avl_item_func *destroy) +void avl_destroy(struct avl_table *tree, avl_item_func * destroy) { - struct avl_node *p, *q; - - assert (tree != NULL); - - for (p = tree->avl_root; p != NULL; p = q) - if (p->avl_link[0] == NULL) - { - q = p->avl_link[1]; - if (destroy != NULL && p->avl_data != NULL) - destroy (p->avl_data, tree->avl_param); - tree->avl_alloc->libavl_free (tree->avl_alloc, p); - } - else - { - q = p->avl_link[0]; - p->avl_link[0] = q->avl_link[1]; - q->avl_link[1] = p; - } - - tree->avl_alloc->libavl_free (tree->avl_alloc, tree); + struct avl_node *p, *q; + + assert(tree != NULL); + + for (p = tree->avl_root; p != NULL; p = q) + if (p->avl_link[0] == NULL) { + q = p->avl_link[1]; + if (destroy != NULL && p->avl_data != NULL) + destroy(p->avl_data, tree->avl_param); + tree->avl_alloc->libavl_free(tree->avl_alloc, p); + } else { + q = p->avl_link[0]; + p->avl_link[0] = q->avl_link[1]; + q->avl_link[1] = p; + } + + tree->avl_alloc->libavl_free(tree->avl_alloc, tree); } /* Allocates |size| bytes of space using |malloc()|. Returns a null pointer if allocation fails. */ -void * -avl_malloc (struct libavl_allocator *allocator, size_t size) +void *avl_malloc(struct libavl_allocator *allocator, size_t size) { - assert (allocator != NULL && size > 0); - return malloc (size); + assert(allocator != NULL && size > 0); + return malloc(size); } /* Frees |block|. */ -void -avl_free (struct libavl_allocator *allocator, void *block) +void avl_free(struct libavl_allocator *allocator, void *block) { - assert (allocator != NULL && block != NULL); - free (block); + assert(allocator != NULL && block != NULL); + free(block); } /* Default memory allocator that uses |malloc()| and |free()|. */ -struct libavl_allocator avl_allocator_default = - { +struct libavl_allocator avl_allocator_default = { avl_malloc, avl_free - }; +}; #undef NDEBUG #include /* Asserts that |avl_insert()| succeeds at inserting |item| into |table|. */ void -(avl_assert_insert) (struct avl_table *table, void *item) -{ - void **p = avl_probe (table, item); - assert (p != NULL && *p == item); + (avl_assert_insert) (struct avl_table * table, void *item) { + void **p = avl_probe(table, item); + assert(p != NULL && *p == item); } /* Asserts that |avl_delete()| really removes |item| from |table|, and returns the removed item. */ -void * -(avl_assert_delete) (struct avl_table *table, void *item) -{ - void *p = avl_delete (table, item); - assert (p != NULL); - return p; +void *(avl_assert_delete) (struct avl_table * table, void *item) { + void *p = avl_delete(table, item); + assert(p != NULL); + return p; } - diff --git a/Build/source/texk/web2c/pdftexdir/avl.h b/Build/source/texk/web2c/pdftexdir/avl.h index 446a11a0ba9..c993ee01f26 100644 --- a/Build/source/texk/web2c/pdftexdir/avl.h +++ b/Build/source/texk/web2c/pdftexdir/avl.h @@ -24,100 +24,95 @@ */ #ifndef AVL_H -#define AVL_H 1 +# define AVL_H 1 -#include -#if defined(__cplusplus) +# include +# if defined(__cplusplus) extern "C" { -#endif +# endif /* Function types. */ -typedef int avl_comparison_func (const void *avl_a, const void *avl_b, - void *avl_param); -typedef void avl_item_func (void *avl_item, void *avl_param); -typedef void *avl_copy_func (void *avl_item, void *avl_param); + typedef int avl_comparison_func(const void *avl_a, const void *avl_b, + void *avl_param); + typedef void avl_item_func(void *avl_item, void *avl_param); + typedef void *avl_copy_func(void *avl_item, void *avl_param); -#ifndef LIBAVL_ALLOCATOR -#define LIBAVL_ALLOCATOR +# ifndef LIBAVL_ALLOCATOR +# define LIBAVL_ALLOCATOR /* Memory allocator. */ -struct libavl_allocator - { - void *(*libavl_malloc) (struct libavl_allocator *, size_t libavl_size); - void (*libavl_free) (struct libavl_allocator *, void *libavl_block); - }; -#endif + struct libavl_allocator { + void *(*libavl_malloc) (struct libavl_allocator *, size_t libavl_size); + void (*libavl_free) (struct libavl_allocator *, void *libavl_block); + }; +# endif /* Default memory allocator. */ -extern struct libavl_allocator avl_allocator_default; -void *avl_malloc (struct libavl_allocator *, size_t); -void avl_free (struct libavl_allocator *, void *); + extern struct libavl_allocator avl_allocator_default; + void *avl_malloc(struct libavl_allocator *, size_t); + void avl_free(struct libavl_allocator *, void *); /* Maximum AVL height. */ -#ifndef AVL_MAX_HEIGHT -#define AVL_MAX_HEIGHT 32 -#endif +# ifndef AVL_MAX_HEIGHT +# define AVL_MAX_HEIGHT 32 +# endif /* Tree data structure. */ -struct avl_table - { - struct avl_node *avl_root; /* Tree's root. */ - avl_comparison_func *avl_compare; /* Comparison function. */ - void *avl_param; /* Extra argument to |avl_compare|. */ - struct libavl_allocator *avl_alloc; /* Memory allocator. */ - size_t avl_count; /* Number of items in tree. */ - unsigned long avl_generation; /* Generation number. */ - }; + struct avl_table { + struct avl_node *avl_root; /* Tree's root. */ + avl_comparison_func *avl_compare; /* Comparison function. */ + void *avl_param; /* Extra argument to |avl_compare|. */ + struct libavl_allocator *avl_alloc; /* Memory allocator. */ + size_t avl_count; /* Number of items in tree. */ + unsigned long avl_generation; /* Generation number. */ + }; /* An AVL tree node. */ -struct avl_node - { - struct avl_node *avl_link[2]; /* Subtrees. */ - void *avl_data; /* Pointer to data. */ - signed char avl_balance; /* Balance factor. */ - }; + struct avl_node { + struct avl_node *avl_link[2]; /* Subtrees. */ + void *avl_data; /* Pointer to data. */ + signed char avl_balance; /* Balance factor. */ + }; /* AVL traverser structure. */ -struct avl_traverser - { - struct avl_table *avl_table; /* Tree being traversed. */ - struct avl_node *avl_node; /* Current node in tree. */ - struct avl_node *avl_stack[AVL_MAX_HEIGHT]; - /* All the nodes above |avl_node|. */ - size_t avl_height; /* Number of nodes in |avl_parent|. */ - unsigned long avl_generation; /* Generation number. */ - }; + struct avl_traverser { + struct avl_table *avl_table; /* Tree being traversed. */ + struct avl_node *avl_node; /* Current node in tree. */ + struct avl_node *avl_stack[AVL_MAX_HEIGHT]; + /* All the nodes above |avl_node|. */ + size_t avl_height; /* Number of nodes in |avl_parent|. */ + unsigned long avl_generation; /* Generation number. */ + }; /* Table functions. */ -struct avl_table *avl_create (avl_comparison_func *, void *, - struct libavl_allocator *); -struct avl_table *avl_copy (const struct avl_table *, avl_copy_func *, - avl_item_func *, struct libavl_allocator *); -void avl_destroy (struct avl_table *, avl_item_func *); -void **avl_probe (struct avl_table *, void *); -void *avl_insert (struct avl_table *, void *); -void *avl_replace (struct avl_table *, void *); -void *avl_delete (struct avl_table *, const void *); -void *avl_find (const struct avl_table *, const void *); -void avl_assert_insert (struct avl_table *, void *); -void *avl_assert_delete (struct avl_table *, void *); - -#define avl_count(table) ((size_t) (table)->avl_count) + struct avl_table *avl_create(avl_comparison_func *, void *, + struct libavl_allocator *); + struct avl_table *avl_copy(const struct avl_table *, avl_copy_func *, + avl_item_func *, struct libavl_allocator *); + void avl_destroy(struct avl_table *, avl_item_func *); + void **avl_probe(struct avl_table *, void *); + void *avl_insert(struct avl_table *, void *); + void *avl_replace(struct avl_table *, void *); + void *avl_delete(struct avl_table *, const void *); + void *avl_find(const struct avl_table *, const void *); + void avl_assert_insert(struct avl_table *, void *); + void *avl_assert_delete(struct avl_table *, void *); + +# define avl_count(table) ((size_t) (table)->avl_count) /* Table traverser functions. */ -void avl_t_init (struct avl_traverser *, struct avl_table *); -void *avl_t_first (struct avl_traverser *, struct avl_table *); -void *avl_t_last (struct avl_traverser *, struct avl_table *); -void *avl_t_find (struct avl_traverser *, struct avl_table *, void *); -void *avl_t_insert (struct avl_traverser *, struct avl_table *, void *); -void *avl_t_copy (struct avl_traverser *, const struct avl_traverser *); -void *avl_t_next (struct avl_traverser *); -void *avl_t_prev (struct avl_traverser *); -void *avl_t_cur (struct avl_traverser *); -void *avl_t_replace (struct avl_traverser *, void *); - -#if defined(__cplusplus) + void avl_t_init(struct avl_traverser *, struct avl_table *); + void *avl_t_first(struct avl_traverser *, struct avl_table *); + void *avl_t_last(struct avl_traverser *, struct avl_table *); + void *avl_t_find(struct avl_traverser *, struct avl_table *, void *); + void *avl_t_insert(struct avl_traverser *, struct avl_table *, void *); + void *avl_t_copy(struct avl_traverser *, const struct avl_traverser *); + void *avl_t_next(struct avl_traverser *); + void *avl_t_prev(struct avl_traverser *); + void *avl_t_cur(struct avl_traverser *); + void *avl_t_replace(struct avl_traverser *, void *); + +# if defined(__cplusplus) } -#endif - -#endif /* avl.h */ +# endif +#endif /* avl.h */ diff --git a/Build/source/texk/web2c/pdftexdir/avlstuff.c b/Build/source/texk/web2c/pdftexdir/avlstuff.c index 1fab93223f1..1a05c762eb1 100644 --- a/Build/source/texk/web2c/pdftexdir/avlstuff.c +++ b/Build/source/texk/web2c/pdftexdir/avlstuff.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2004 Han The Thanh, +Copyright (c) 2004-2005 Han The Thanh, This file is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by Free @@ -15,9 +15,8 @@ You should have received a copy of the GNU General Public License along with this file; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -$Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/avlstuff.c#5 $ +$Id: avlstuff.c,v 1.12 2005/07/11 20:27:39 hahe Exp hahe $ -formatted by indent -kr */ #include "ptexlib.h" @@ -25,16 +24,11 @@ formatted by indent -kr #include #include "avl.h" -static const char perforce_id[] = - "$Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/avlstuff.c#5 $"; - -/* One AVL tree for each obj_type 0...pdfobjtypemax */ - static struct avl_table *PdfObjTree[pdfobjtypemax + 1] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; - -/* memory management functions for avl */ +/**********************************************************************/ +/* memory management functions for AVL */ void *avl_xmalloc(struct libavl_allocator *allocator, size_t size) { @@ -53,67 +47,89 @@ struct libavl_allocator avl_xallocator = { avl_xfree }; +/**********************************************************************/ +/* general AVL comparison functions */ -/* AVL sort objentry into avl_table[] */ +int comp_int_entry(const void *pa, const void *pb, void *p) +{ + cmp_return(*(const int *) pa, *(const int *) pb); + return 0; +} + +int comp_string_entry(const void *pa, const void *pb, void *p) +{ + return strcmp((const char *) pa, (const char *) pb); +} + +/**********************************************************************/ +/* One AVL tree for each obj_type 0...pdfobjtypemax */ + + +typedef struct oentry_ { + integer int0; + integer objptr; +} oentry; + +/* AVL sort oentry into avl_table[] */ int compare_info(const void *pa, const void *pb, void *param) { integer a, b; int as, ae, bs, be, al, bl; - a = ((const objentry *) pa)->int0; - b = ((const objentry *) pb)->int0; - if (a < 0 && b < 0) { /* string comparison */ + a = ((const oentry *) pa)->int0; + b = ((const oentry *) pb)->int0; + if (a < 0 && b < 0) { /* string comparison */ as = strstart[-a]; - ae = strstart[-a + 1]; /* start of next string in pool */ + ae = strstart[-a + 1]; /* start of next string in pool */ bs = strstart[-b]; be = strstart[-b + 1]; al = ae - as; bl = be - bs; - if (al < bl) /* compare first by string length */ + if (al < bl) /* compare first by string length */ return -1; - else if (al > bl) + if (al > bl) return 1; - else - for (; as < ae; as++, bs++) { - if (strpool[as] < strpool[bs]) - return -1; - if (strpool[as] > strpool[bs]) - return 1; - } - return 0; - } else { /* integer comparison */ + for (; as < ae; as++, bs++) { + if (strpool[as] < strpool[bs]) + return -1; + if (strpool[as] > strpool[bs]) + return 1; + } + } else { /* integer comparison */ if (a < b) return -1; - else if (a > b) + if (a > b) return 1; - else - return 0; } + return 0; } - void avlputobj(integer objptr, integer t) { static void **pp; + static oentry *oe; if (PdfObjTree[t] == NULL) { PdfObjTree[t] = avl_create(compare_info, NULL, &avl_xallocator); if (PdfObjTree[t] == NULL) pdftex_fail("avlstuff.c: avl_create() PdfObjTree failed"); } - pp = avl_probe(PdfObjTree[t], &(objtab[objptr])); + oe = xtalloc(1, oentry); + oe->int0 = objtab[objptr].int0; + oe->objptr = objptr; /* allows to relocate objtab */ + pp = avl_probe(PdfObjTree[t], oe); if (pp == NULL) pdftex_fail("avlstuff.c: avl_probe() out of memory in insertion"); } -/* replacement for linear search pascal function "find_obj" */ +/* replacement for linear search pascal function "find_obj()" */ integer avlfindobj(integer t, integer i, integer byname) { - static objentry *p; - static objentry tmp; + static oentry *p; + static oentry tmp; if (byname > 0) tmp.int0 = -i; @@ -121,11 +137,38 @@ integer avlfindobj(integer t, integer i, integer byname) tmp.int0 = i; if (PdfObjTree[t] == NULL) return 0; - p = (objentry *) avl_find(PdfObjTree[t], &tmp); + p = (oentry *) avl_find(PdfObjTree[t], &tmp); if (p == NULL) return 0; - else - return (int) (p - (objentry *) objtab); + return p->objptr; +} + +/**********************************************************************/ + +struct avl_table *mf_tree = NULL; + +typedef struct { + char *tfm_name; + internalfontnumber fontnum; +} mf_entry; + +/**********************************************************************/ +/* cleaning up... */ + +static void destroy_oentry(void *pa, void *pb) +{ + oentry *p = (oentry *) pa; + xfree(p); +} + +void PdfObjTree_free() +{ + int i; + + for (i = 0; i <= pdfobjtypemax; i++) { + if (PdfObjTree[i] != NULL) + avl_destroy(PdfObjTree[i], destroy_oentry); + } } -/* end of file avlstuff.c */ +/**********************************************************************/ diff --git a/Build/source/texk/web2c/pdftexdir/change-files.txt b/Build/source/texk/web2c/pdftexdir/change-files.txt index 496c1e313ed..c6a3387f41d 100644 --- a/Build/source/texk/web2c/pdftexdir/change-files.txt +++ b/Build/source/texk/web2c/pdftexdir/change-files.txt @@ -1,30 +1,6 @@ How change files are applied for pdftex/pdfetex? -============================= pdftex ============================= - -1) pdftex.web is generated from - ./tie -m pdftex.web $(srcdir)/tex.web \ - $(srcdir)/pdftexdir/pdftex.ch \ - $(srcdir)/pdftexdir/hz.ch \ - $(srcdir)/pdftexdir/misc.ch - -Input: - tex.web: - the original tex sources - - pdftexdir/pdftex.ch: - PDF-specific changes - - pdftexdir/hz.ch: - HZ extension - - pdftexdir/misc.ch: - miscellany extensions - - -Output: - pdftex.web: - pdftex without system-dependent changes +============================= pdftex ============================= 2) pdftex.ch is generated from ./tie -c pdftex.ch pdftex.web \ @@ -34,8 +10,8 @@ Output: $(srcdir)/pdftexdir/tex.pch Input: - pdftex.web: - from step 1), ie pdftex without system-dependent changes + pdftex.web: + pdftex without system-dependent changes pdftexdir/tex.ch0: % Change file to assist in creating the web2c-specific change file. @@ -44,89 +20,16 @@ Input: change the pdftex banner to tex banner, so tex.ch can be applied - tex.ch: + tex.ch: the main web2c-specific (or other system-dependent) change file - pdftexdir/tex.ch1: + pdftexdir/pdftex.ch: + % Change file to assist in creating the web2c-specific change file. % This one resolves inconsistencies between tex.ch and tex.pch. change the tex banner to pdftex banner (reverse tex.ch0) - pdftexdir/tex.pch: - % Change file containing web2c-specific or system-dependent changes to - % pdftex - Output: pdftex.ch (not pdftexdir/pdftex.ch): additional & system-dependent changes - - -============================= pdfetex ============================= - -1) pdfetex.web is generated from - ./tie -m pdfetex.web $(srcdir)/tex.web \ - $(srcdir)/etexdir/etex.ch \ - $(srcdir)/pdfetexdir/pdfetex.ch1 \ - $(srcdir)/pdftexdir/pdftex.ch \ - $(srcdir)/pdftexdir/hz.ch \ - $(srcdir)/pdftexdir/misc.ch \ - $(srcdir)/pdfetexdir/pdfetex.ch2 - - pdfetex.web should contain system-independent changes only; - -Input: - etexdir/etex.ch: - system-independent changes by etex - - pdfetexdir/pdfetex.ch1: - reverse some etex changes so system-independent pdftex change files can - be applied - - pdftexdir/{pdftex,hz,misc}.ch: - system-independent changes by pdftex - - pdfetexdir/pdfetex.ch2: - merge changes by etexdir/etex.ch & pdftexdir/{pdftex,hz,...}.ch - -Output: - pdfetex.web: - pdfetex without system-dependent changes - - -2) pdfetex.ch is generated from - ./tie -c pdfetex.ch pdfetex.web \ - $(srcdir)/pdfetexdir/tex.ch0 \ - $(srcdir)/tex.ch \ - $(srcdir)/etexdir/tex.ch1 \ - $(srcdir)/etexdir/tex.ech \ - $(srcdir)/pdfetexdir/tex.ch1 \ - $(srcdir)/pdftexdir/tex.pch - -Input: - pdfetex.web: - from step 1), ie pdfetex without system-dependent changes - - pdfetexdir/tex.ch0: - % Change file to assist in creating the web2c-specific change file. - % This one comes first, resolves inconsistencies between the generated - % pdfetex.web and tex.ch - - change the pdfetex banner to tex banner, so tex.ch can be applied - - tex.ch: - the main web2c-specific (or other system-dependent) change file - - the etex changes files: - system-dependent changes by etex - - pdfetexdir/tex.ch1: - reverses some system-dependent changes by etex (the banner) so - pdftexdir/tex.pch can be applied - - pdftexdir/tex.pch: - system-dependent changes by pdftex - -Output: - pdfetex.ch: - additional & system-dependent changes diff --git a/Build/source/texk/web2c/pdftexdir/depend.mk b/Build/source/texk/web2c/pdftexdir/depend.mk index 3f83c3d34d9..fa9f9dac2c4 100644 --- a/Build/source/texk/web2c/pdftexdir/depend.mk +++ b/Build/source/texk/web2c/pdftexdir/depend.mk @@ -8,7 +8,8 @@ papersiz.o: papersiz.c ptexlib.h ../pdftexd.h ../texmfmp.h \ utils.o: utils.c ptexlib.h ../pdftexd.h ../texmfmp.h ../cpascal.h \ ../config.h ../help.h ../texmfmem.h ../pdftexcoerce.h ptexmac.h \ ../../../libs/zlib/zlib.h ../../../libs/zlib/zconf.h \ - ../../../libs/md5/md5.h pdftexextra.h + ../../../libs/md5/md5.h pdftexextra.h \ + ../../../libs/obsdcompat/openbsd-compat.h vfpacket.o: vfpacket.c ptexlib.h ../pdftexd.h ../texmfmp.h \ ../cpascal.h ../config.h ../help.h ../texmfmem.h ../pdftexcoerce.h \ ptexmac.h @@ -27,7 +28,7 @@ writezip.o: writezip.c ptexlib.h ../pdftexd.h ../texmfmp.h \ writeenc.o: writeenc.c ptexlib.h ../pdftexd.h ../texmfmp.h \ ../cpascal.h ../config.h ../help.h ../texmfmem.h ../pdftexcoerce.h \ ptexmac.h -writettf.o: writettf.c ptexlib.h ../pdftexd.h ../texmfmp.h \ +writettf.o: writettf.c ptexlib.h writettf.h ../pdftexd.h ../texmfmp.h \ ../cpascal.h ../config.h ../help.h ../texmfmem.h ../pdftexcoerce.h \ ptexmac.h macnames.c writejpg.o: writejpg.c ptexlib.h ../pdftexd.h ../texmfmp.h \ @@ -45,7 +46,7 @@ pdftoepdf.o: pdftoepdf.cc ../../../libs/xpdf/xpdf/Object.h \ ../../../libs/xpdf/xpdf/XRef.h ../../../libs/xpdf/xpdf/Catalog.h \ ../../../libs/xpdf/xpdf/Page.h ../../../libs/xpdf/xpdf/GfxFont.h \ ../../../libs/xpdf/xpdf/Error.h epdf.h -ttf2afm.o: ttf2afm.c macnames.c +ttf2afm.o: ttf2afm.c writettf.h macnames.c pdftosrc.o: pdftosrc.cc ../../../libs/xpdf/xpdf/Object.h \ ../../../libs/xpdf/xpdf/Array.h ../../../libs/xpdf/xpdf/Dict.h \ ../../../libs/xpdf/xpdf/Stream.h ../../../libs/xpdf/xpdf/XRef.h \ diff --git a/Build/source/texk/web2c/pdftexdir/epdf.c b/Build/source/texk/web2c/pdftexdir/epdf.c index 95000755b0d..1b13c2b0d8c 100644 --- a/Build/source/texk/web2c/pdftexdir/epdf.c +++ b/Build/source/texk/web2c/pdftexdir/epdf.c @@ -17,56 +17,86 @@ You should have received a copy of the GNU General Public License along with pdfTeX; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -$Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/epdf.c#10 $ +$Id: epdf.c,v 1.45 2006/08/20 10:56:04 hahe Exp hahe $ */ #include "ptexlib.h" #include #include - -integer pdfbufmax = pdfbufsize; +#include extern void epdf_check_mem(void); +extern void register_fd_entry(fd_entry *); + -int is_subsetable(fm_entry *fm) +int is_subsetable(fm_entry * fm) { - return is_included(fm) && is_subsetted(fm); + assert(is_included(fm)); + return is_subsetted(fm); } -int is_type1(fm_entry *fm) +fd_entry *epdf_create_fontdescriptor(fm_entry * fm) { - return is_t1fontfile(fm); + fd_entry *fd; + if ((fd = lookup_fd_entry(fm->ff_name, fm->slant, fm->extend)) == NULL) { + fm->in_use = true; + fd = new_fd_entry(); + fd->fm = fm; + register_fd_entry(fd); + fd->fd_objnum = pdfnewobjnum(); + assert(fm->ps_name != NULL); + fd->fontname = xstrdup(fm->ps_name); /* just fallback */ + /* preset_fontmetrics (fo->fd, f); */ + fd->gl_tree = avl_create(comp_string_entry, NULL, &avl_xallocator); + assert(fd->gl_tree != NULL); + } + return fd; } -void mark_glyphs(fm_entry *fm, char *charset) +integer get_fd_objnum(fd_entry * fd) { - char *new_charset = fm->charset; - if (charset == NULL) - return; - if (new_charset == NULL) - new_charset = xstrdup(charset); - else { - xretalloc(new_charset, strlen(new_charset)+strlen(charset)+1, char); - strcat(new_charset, charset); - } - fm->charset = new_charset; + assert(fd->fd_objnum != 0); + return fd->fd_objnum; } -void embed_whole_font(fm_entry *fm) +integer get_fn_objnum(fd_entry * fd) { - fm->all_glyphs = true; + if (fd->fn_objnum == 0) + fd->fn_objnum = pdfnewobjnum(); + return fd->fn_objnum; } -integer get_fontfile(fm_entry *fm) +/*********************************************************************** + * Mark glyphs used by embedded PDF file: + * 1. build fontdescriptor, if not yet existing + * 2. mark glyphs directly there + * + * Input charset from xpdf is a string of glyph names including + * leading slashes, but without blanks between them, like: /a/b/c +***********************************************************************/ + +void epdf_mark_glyphs(fd_entry * fd, char *charset) { - return fm->ff_objnum; + char *p, *q, *s; + char *glyph; + void **aa; + if (charset == NULL) + return; + assert(fd != NULL); + for (s = charset + 1, q = charset + strlen(charset); s < q; s = p + 1) { + for (p = s; *p != '\0' && *p != '/'; p++); + *p = '\0'; + if ((char *) avl_find(fd->gl_tree, s) == NULL) { + glyph = xstrdup(s); + aa = avl_probe(fd->gl_tree, glyph); + assert(aa != NULL); + } + } } -integer get_fontname(fm_entry *fm) +void embed_whole_font(fd_entry * fd) { - if (fm->fn_objnum == 0) - fm->fn_objnum = pdfnewobjnum(); - return fm->fn_objnum; + fd->all_glyphs = true; } void epdf_free(void) diff --git a/Build/source/texk/web2c/pdftexdir/epdf.h b/Build/source/texk/web2c/pdftexdir/epdf.h index 27ce221061d..0a13d59df34 100644 --- a/Build/source/texk/web2c/pdftexdir/epdf.h +++ b/Build/source/texk/web2c/pdftexdir/epdf.h @@ -1,5 +1,5 @@ /* -Copyright (c) 1996-2004 Han The Thanh, +Copyright (c) 1996-2006 Han The Thanh, This file is part of pdfTeX. @@ -17,87 +17,112 @@ You should have received a copy of the GNU General Public License along with pdfTeX; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -$Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/epdf.h#17 $ +$Id: epdf.h,v 1.6 2005/11/28 23:29:32 hahe Exp $ */ extern "C" { #include + extern char *xstrdup(const char *); + /* the following code is extremly ugly but needed for including web2c/config.h */ -#include /* define P?H macros */ +#include /* define P?H macros */ -typedef const char *const_string; /* including kpathsea/types.h doesn't work on some systems */ + typedef const char *const_string; /* including kpathsea/types.h doesn't work on some systems */ -#define KPATHSEA_CONFIG_H /* avoid including other kpathsea header files */ - /* from web2c/config.h */ +#define KPATHSEA_CONFIG_H /* avoid including other kpathsea header files */ + /* from web2c/config.h */ -#ifdef CONFIG_H /* CONFIG_H has been defined by some xpdf */ -#undef CONFIG_H /* header file */ +#ifdef CONFIG_H /* CONFIG_H has been defined by some xpdf */ +# undef CONFIG_H /* header file */ #endif -#include /* define SIZEOF_LONG */ -#include /* define type integer */ +#include /* define SIZEOF_LONG */ +#include /* define type integer */ #include -#define pdfbufsize pdfbufmax - -extern float epdf_width; -extern float epdf_height; -extern float epdf_orig_x; -extern float epdf_orig_y; -extern integer epdf_selected_page; -extern integer epdf_num_pages; -extern integer epdf_page_box; -extern integer epdf_always_use_pdf_pagebox; -extern void *epdf_doc; -extern void *epdf_xref; - -extern integer pdfstreamlength; -extern integer pdfptr; -typedef unsigned char eightbits ; -extern eightbits pdfbuf[]; -extern integer pdfbufmax; - -extern char notdef[]; - -extern int is_subsetable(struct fm_entry *); -extern int is_type1(struct fm_entry *); -extern struct fm_entry * lookup_fontmap(char *); -extern integer get_fontfile(struct fm_entry *); -extern integer get_fontname(struct fm_entry *); -extern integer pdfnewobjnum(void); -extern integer read_pdf_info(char*, char*, integer, integer, integer, integer); -extern void embed_whole_font(struct fm_entry *); -extern void epdf_check_mem(void); -extern void epdf_delete(void); -extern void epdf_free(void); -extern void mark_glyphs(struct fm_entry *, char *); -extern void pdf_printf(const char *fmt,...); -extern void pdf_puts(const char *); -extern void pdfbeginstream(void); -extern void pdfendobj(void); -extern void pdfendstream(void); -extern void pdfflush(void); -extern void pdftex_fail(const char *fmt,...); -extern void pdftex_warn(const char *fmt,...); -extern void tex_printf(const char *, ...); -extern void write_enc(char **, struct enc_entry *, integer); -extern void write_epdf(void); -extern void zpdfbegindict(integer); -extern void zpdfbeginobj(integer); -extern void zpdfcreateobj(integer, integer); -extern void zpdfnewdict(integer, integer); +#include "openbsd-compat.h" + +/* #-define pdfbufsize pdfbufmax */ + + extern float epdf_width; + extern float epdf_height; + extern float epdf_orig_x; + extern float epdf_orig_y; + extern integer epdf_selected_page; + extern integer epdf_num_pages; + extern integer epdf_page_box; + extern void *epdf_doc; + extern void *epdf_xref; + + extern integer pdfboxspecmedia; + extern integer pdfboxspeccrop; + extern integer pdfboxspecbleed; + extern integer pdfboxspectrim; + extern integer pdfboxspecart; + + extern integer pdfstreamlength; + extern integer pdfptr; + extern integer poolptr; + typedef unsigned char eightbits; + extern eightbits *pdfbuf; + extern integer pdfbufsize; + extern integer pdfosmode; + extern eightbits pdflastbyte; + + extern char notdef[]; + + extern int is_subsetable(struct fm_entry *); + extern struct fm_entry *lookup_fontmap(char *); + extern integer get_fontfile(struct fm_entry *); + extern integer get_fontname(struct fm_entry *); + extern integer pdfnewobjnum(void); + extern integer read_pdf_info(char *, char *, integer, integer, integer, + integer); + extern void embed_whole_font(struct fd_entry *); + extern void epdf_check_mem(void); + extern void epdf_delete(void); + extern void epdf_free(void); + __attribute__ ((format(printf, 1, 2))) + extern void pdf_printf(const char *fmt, ...); + extern void pdf_puts(const char *); + extern void pdfbeginstream(void); + extern void pdfendobj(void); + extern void pdfendstream(void); + extern void pdfflush(void); + __attribute__ ((noreturn, format(printf, 1, 2))) + extern void pdftex_fail(const char *fmt, ...); + __attribute__ ((format(printf, 1, 2))) + extern void pdftex_warn(const char *fmt, ...); + __attribute__ ((format(printf, 1, 2))) + extern void tex_printf(const char *, ...); + extern void write_epdf(void); + extern void zpdfbegindict(integer, bool); + extern void zpdfbeginobj(integer, bool); + extern void zpdfcreateobj(integer, integer); + extern void zpdfnewdict(integer, integer, bool); + extern void zpdfosgetosbuf(integer); + +/* epdf.c */ + extern void epdf_mark_glyphs(struct fd_entry *, char *); + extern struct fd_entry *epdf_create_fontdescriptor(struct fm_entry *); + extern int get_fd_objnum(struct fd_entry *); + extern int get_fn_objnum(struct fd_entry *); + +/* write_enc.c */ + extern void epdf_write_enc(char **, integer); /* utils.c */ -extern char *convertStringToPDFString(char *in, int len); + extern char *convertStringToPDFString(char *in, int len); + extern char *stripzeros(char *a); /* config.c */ -extern integer cfgpar(integer); + extern integer cfgpar(integer); /* avlstuff.c */ -extern void avl_put_obj (integer, integer); -extern integer avl_find_obj (integer, integer, integer); + extern void avl_put_obj(integer, integer); + extern integer avl_find_obj(integer, integer, integer); } diff --git a/Build/source/texk/web2c/pdftexdir/hz.ch b/Build/source/texk/web2c/pdftexdir/hz.ch deleted file mode 100644 index d8ec58fb5e7..00000000000 --- a/Build/source/texk/web2c/pdftexdir/hz.ch +++ /dev/null @@ -1,1935 +0,0 @@ -% WEB change file containing HZ extensions for pdfTeX -% -% Copyright (c) 1996-2002 Han Th\^e\llap{\raise 0.5ex\hbox{\'{}}} Th\`anh, -% -% This file is part of pdfTeX. -% -% pdfTeX is free software; you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation; either version 2 of the License, or -% (at your option) any later version. -% -% pdfTeX is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with pdfTeX; if not, write to the Free Software -% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -% -% $Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/hz.ch#15 $ - -@x [155] - margin kerning -@d acc_kern=2 {|subtype| of kern nodes from accents} -@y -@d acc_kern=2 {|subtype| of kern nodes from accents} - -@# {memory structure for marginal kerns} -@d margin_kern_node = 40 -@d margin_kern_node_size = 3 -@d margin_char(#) == info(# + 2) - -@# {|subtype| of marginal kerns} -@d left_side == 0 -@d right_side == 1 - -@# {base for lp/rp/ef codes starts from 2: - 0 for |hyphen_char|, - 1 for |skew_char|} -@d lp_code_base == 2 -@d rp_code_base == 3 -@d ef_code_base == 4 - -@d max_hlist_stack = 512 {maximum fill level for |hlist_stack|} -{maybe good if larger than |2 * max_quarterword|, so that box nesting level would overflow first} -@z - -@x [183] - margin kerning - kern_node: @; -@y - margin_kern_node: begin - print_esc("kern"); - print_scaled(width(p)); - if subtype(p) = left_side then - print(" (left margin)") - else - print(" (right margin)"); - end; - kern_node: @; -@z - -@x [202] - margin kerning - kern_node,math_node,penalty_node: do_nothing; -@y - kern_node,math_node,penalty_node: do_nothing; - margin_kern_node: begin - free_avail(margin_char(p)); - free_node(p, margin_kern_node_size); - goto done; - end; -@z - -@x [206] - margin kerning -kern_node,math_node,penalty_node: begin r:=get_node(small_node_size); - words:=small_node_size; - end; -@y -kern_node,math_node,penalty_node: begin r:=get_node(small_node_size); - words:=small_node_size; - end; -margin_kern_node: begin - r := get_node(margin_kern_node_size); - fast_get_avail(margin_char(r)); - font(margin_char(r)) := font(margin_char(p)); - character(margin_char(r)) := character(margin_char(p)); - words := small_node_size; - end; -@z - -@x [236] -@d pdf_int_pars=pdftex_first_integer_code + 17 {total number of \pdfTeX's integer parameters} -@y -@d pdf_adjust_spacing_code = pdftex_first_integer_code + 17 {level of spacing adjusting} -@d pdf_protrude_chars_code = pdftex_first_integer_code + 18 {protrude chars at left/right edge of paragraphs} -@d pdf_tracing_fonts_code = pdftex_first_integer_code + 19 {level of font detail in log} -@d pdf_int_pars=pdftex_first_integer_code + 20 {total number of \pdfTeX's integer parameters} -@z - -@x [236] -@d error_context_lines==int_par(error_context_lines_code) -@y -@d error_context_lines==int_par(error_context_lines_code) -@# -@d pdf_adjust_spacing == int_par(pdf_adjust_spacing_code) -@d pdf_protrude_chars == int_par(pdf_protrude_chars_code) -@d pdf_tracing_fonts == int_par(pdf_tracing_fonts_code) -@z - -@x [237] -error_context_lines_code:print_esc("errorcontextlines"); -@y -error_context_lines_code:print_esc("errorcontextlines"); -@# -pdf_adjust_spacing_code: print_esc("pdfadjustspacing"); -pdf_protrude_chars_code: print_esc("pdfprotrudechars"); -pdf_tracing_fonts_code: print_esc("pdftracingfonts"); -@z - -@x [238] -primitive("errorcontextlines",assign_int,int_base+error_context_lines_code);@/ -@!@:error_context_lines_}{\.{\\errorcontextlines} primitive@> -@y -primitive("errorcontextlines",assign_int,int_base+error_context_lines_code);@/ -@!@:error_context_lines_}{\.{\\errorcontextlines} primitive@> -primitive("pdfadjustspacing",assign_int,int_base+pdf_adjust_spacing_code);@/ -@!@:pdf_adjust_spacing_}{\.{\\pdfadjustspacing} primitive@> -primitive("pdfprotrudechars",assign_int,int_base+pdf_protrude_chars_code);@/ -@!@:pdf_protrude_chars_}{\.{\\pdfprotrudechars} primitive@> -primitive("pdftracingfonts",assign_int,int_base+pdf_tracing_fonts_code);@/ -@!@:pdf_tracing_fonts_}{\.{\\pdftracingfonts} primitive@> -@z - -@x [267] - displaying fonts -@= -print_esc(font_id_text(font(p))) -@y -@= -begin - print_esc(font_id_text(font(p))); - if pdf_tracing_fonts > 0 then begin - print(" ("); - print(font_name[font(p)]); - if font_size[font(p)] <> font_dsize[font(p)] then begin - print("@@"); - print_scaled(font_size[font(p)]); - print("pt"); - end; - print(")"); - end else - if pdf_font_expand_ratio[font(p)] <> 0 then begin - print(" ("); - if pdf_font_expand_ratio[font(p)] > 0 then - print("+"); - print_int(pdf_font_expand_ratio[font(p)]); - print(")"); - end; -end -@z - -@x [413] - font expansion -var m:halfword; {|chr_code| part of the operand token} -@y -var m:halfword; {|chr_code| part of the operand token} - n, k: integer; {accumulators} -@z - -@x [426] - font expansion, margin kerning -begin scan_font_ident; -if m=0 then scanned_result(hyphen_char[cur_val])(int_val) -else scanned_result(skew_char[cur_val])(int_val); -@y -begin scan_font_ident; -if m=0 then scanned_result(hyphen_char[cur_val])(int_val) -else if m=1 then scanned_result(skew_char[cur_val])(int_val) -else begin - n := cur_val; - scan_char_num; - k := cur_val; - case m of - lp_code_base: scanned_result(get_lp_code(n, k))(int_val); - rp_code_base: scanned_result(get_rp_code(n, k))(int_val); - ef_code_base: scanned_result(get_ef_code(n, k))(int_val); - end; -end; -@z - -@x [468] -@d pdftex_convert_codes = pdftex_first_expand_code + 9 {end of \pdfTeX's command codes} -@y -@d left_margin_kern_code = pdftex_first_expand_code + 9 {command code for \.{\\leftmarginkern}} -@d right_margin_kern_code = pdftex_first_expand_code + 10 {command code for \.{\\rightmarginkern}} -@d pdftex_convert_codes = pdftex_first_expand_code + 11 {end of \pdfTeX's command codes} -@z - -@x [468] -primitive("pdfpageref",convert,pdf_page_ref_code);@/ -@!@:pdf_page_ref_}{\.{\\pdfpageref} primitive@> -@y -primitive("pdfpageref",convert,pdf_page_ref_code);@/ -@!@:pdf_page_ref_}{\.{\\pdfpageref} primitive@> -primitive("leftmarginkern",convert,left_margin_kern_code);@/ -@!@:left_margin_kern_}{\.{\\leftmarginkern} primitive@> -primitive("rightmarginkern",convert,right_margin_kern_code);@/ -@!@:right_margin_kern_}{\.{\\rightmarginkern} primitive@> -@z - -@x [469] - pdf_page_ref_code: print_esc("pdfpageref"); -@y - pdf_page_ref_code: print_esc("pdfpageref"); - left_margin_kern_code: print_esc("leftmarginkern"); - right_margin_kern_code: print_esc("rightmarginkern"); -@z - -@x [470] -var old_setting:0..max_selector; {holds |selector| setting} -@y -var old_setting:0..max_selector; {holds |selector| setting} -p, q: pointer; -@z - -@x [471] -pdf_page_ref_code: begin - scan_int; - if cur_val <= 0 then - pdf_error("pageref", "invalid page number"); -end; -@y -pdf_page_ref_code: begin - scan_int; - if cur_val <= 0 then - pdf_error("pageref", "invalid page number"); -end; -left_margin_kern_code, right_margin_kern_code: begin - scan_int; - if (box(cur_val) = null) or (type(box(cur_val)) <> hlist_node) then - pdf_error("marginkern", "a non-empty hbox expected") -end; -@z - -@x [472] -pdf_page_ref_code: print_int(get_obj(obj_type_page, cur_val, false)); -@y -pdf_page_ref_code: print_int(get_obj(obj_type_page, cur_val, false)); -left_margin_kern_code: begin - p := list_ptr(box(cur_val)); - if (p <> null) and (not is_char_node(p)) and - (type(p) = glue_node) and (subtype(p) = left_skip_code + 1) then - p := link(p); - if (p <> null) and (not is_char_node(p)) and - (type(p) = margin_kern_node) and (subtype(p) = left_side) then - print_scaled(width(p)) - else - print("0"); - print("pt"); -end; -right_margin_kern_code: begin - q := list_ptr(box(cur_val)); - p := null; - if q <> null then begin - p := prev_rightmost(q, null); - if (p <> null) and (not is_char_node(p)) and - (type(p) = glue_node) and (subtype(p) = right_skip_code + 1) then - p := prev_rightmost(q, p); - end; - if (p <> null) and (not is_char_node(p)) and - (type(p) = margin_kern_node) and (subtype(p) = right_side) then - print_scaled(width(p)) - else - print("0"); - print("pt"); -end; -@z - -@x [622] - margin kerning -glue_node: @; -@y -glue_node: @; -margin_kern_node, -@z - - -@x [???] - font expansion -@!tmp_f: internal_font_number; {for use with |pdf_init_font|} - -@y -@!tmp_f: internal_font_number; {for use with |pdf_init_font|} -@ Here come some subroutines to deal with expanded fonts for HZ-algorithm. - -@p -function init_font_base(v: integer): integer; -var i, j: integer; -begin - i := pdf_get_mem(256); - for j := 0 to 255 do - pdf_mem[i + j] := v; - init_font_base := i; -end; - -procedure set_lp_code(f: internal_font_number; c: eight_bits; i: integer); -begin - if pdf_font_lp_base[f] = 0 then - pdf_font_lp_base[f] := init_font_base(0); - pdf_mem[pdf_font_lp_base[f] + c] := fix_int(i, -1000, 1000); -end; - -procedure set_rp_code(f: internal_font_number; c: eight_bits; i: integer); -begin - if pdf_font_rp_base[f] = 0 then - pdf_font_rp_base[f] := init_font_base(0); - pdf_mem[pdf_font_rp_base[f] + c] := fix_int(i, -1000, 1000); -end; - -procedure set_ef_code(f: internal_font_number; c: eight_bits; i: integer); -begin - if pdf_font_ef_base[f] = 0 then - pdf_font_ef_base[f] := init_font_base(1000); - pdf_mem[pdf_font_ef_base[f] + c] := fix_int(i, 0, 1000); -end; - -function expand_font_name(f: internal_font_number; e: integer): str_number; -var old_setting:0..max_selector; {holds |selector| setting} -begin - old_setting:=selector; selector:=new_string; - print(font_name[f]); - if e > 0 then - print("+"); {minus sign will be printed by |print_int|} - print_int(e); - selector:=old_setting; - expand_font_name := make_string; -end; - -function auto_expand_font(f: internal_font_number; e: integer): internal_font_number; -{creates an expanded font from the base font; doesn't load expanded tfm at all} -var k: internal_font_number; - nw, nk, ni, i, j: integer; -begin - k := font_ptr + 1; - incr(font_ptr); - if (font_ptr >= font_max) then - overflow("maximum internal font number (font_max)", font_max); - font_name[k] := expand_font_name(f, e); - font_area[k] := font_area[f]; - font_id_text(k) := font_id_text(f); - hyphen_char[k] := hyphen_char[f]; - skew_char[k] := skew_char[f]; - font_bchar[k] := font_bchar[f]; - font_false_bchar[k] := font_false_bchar[f]; - font_bc[k] := font_bc[f]; - font_ec[k] := font_ec[f]; - font_size[k] := font_size[f]; - font_dsize[k] := font_dsize[f]; - font_params[k] := font_params[f]; - font_glue[k] := font_glue[f]; - bchar_label[k] := bchar_label[f]; - - char_base[k] := char_base[f]; - height_base[k] := height_base[f]; - depth_base[k] := depth_base[f]; - lig_kern_base[k] := lig_kern_base[f]; - exten_base[k] := exten_base[f]; - param_base[k] := param_base[f]; - - nw := height_base[f] - width_base[f]; - ni := lig_kern_base[f] - italic_base[f]; - nk := exten_base[f] - (kern_base[f] + kern_base_offset); - if (fmem_ptr + nw + ni + nk >= font_mem_size) then - overflow("number of words of font memory (font_mem_size)", font_mem_size); - width_base[k] := fmem_ptr; - italic_base[k] := width_base[k] + nw; - kern_base[k] := italic_base[k] + ni - kern_base_offset; - fmem_ptr := fmem_ptr + nw + ni + nk; - - for i := 0 to nw - 1 do - font_info[width_base[k] + i].sc := - round_xn_over_d(font_info[width_base[f] + i].sc, 1000 + e, 1000); - for i := 0 to ni - 1 do - font_info[italic_base[k] + i].sc := - round_xn_over_d(font_info[italic_base[f] + i].sc, 1000 + e, 1000); - for i := 0 to nk - 1 do - font_info[kern_base[k] + kern_base_offset + i].sc := - round_xn_over_d(font_info[kern_base[f] + kern_base_offset + i].sc, 1000 + e, 1000); - - auto_expand_font := k; -end; - -procedure set_expand_param(k, f: internal_font_number; e: integer); -var i, j: integer; -begin - if pdf_font_rp_base[f] = 0 then - pdf_font_rp_base[f] := init_font_base(0); - if pdf_font_lp_base[f] = 0 then - pdf_font_lp_base[f] := init_font_base(0); - if pdf_font_ef_base[f] = 0 then - pdf_font_ef_base[f] := init_font_base(1000); - pdf_font_expand_ratio[k] := e; - pdf_font_step[k] := pdf_font_step[f]; - pdf_font_auto_expand[k] := pdf_font_auto_expand[f]; - pdf_font_blink[k] := f; {???} - pdf_font_lp_base[k] := pdf_font_lp_base[f]; - pdf_font_rp_base[k] := pdf_font_rp_base[f]; - pdf_font_ef_base[k] := pdf_font_ef_base[f]; -end; - -function tfm_lookup(s: str_number; fs: scaled): internal_font_number; -{looks up for a TFM with name |s| loaded at |fs| size; if found then flushes |s|} -var k: internal_font_number; -begin - if fs <> 0 then begin - for k := font_base + 1 to font_ptr do - if str_eq_str(font_name[k], s) and (font_size[k] = fs) then begin - flush_str(s); - tfm_lookup := k; - return; - end; - end - else begin - for k := font_base + 1 to font_ptr do - if str_eq_str(font_name[k], s) then begin - flush_str(s); - tfm_lookup := k; - return; - end; - end; - tfm_lookup := null_font; -end; - -function load_expand_font(f: internal_font_number; e: integer): internal_font_number; -{loads font |f| expanded by |e| thousandths into font memory; |e| is nonzero -and is a multiple of |pdf_font_step[f]|} -label found; -var s: str_number; {font name} - k: internal_font_number; -begin - s := expand_font_name(f, e); - k := tfm_lookup(s, font_size[f]); - if k = null_font then begin - if pdf_font_auto_expand[f] then - k := auto_expand_font(f, e) - else - k := read_font_info(null_cs, s, "", font_size[f]); - end; - set_expand_param(k, f, e); - load_expand_font := k; -end; - -function auto_expand_vf(f: internal_font_number): boolean; -{check for a virtual auto-expanded font} -var save_f, bf, lf, k: internal_font_number; - e: integer; -begin - auto_expand_vf := false; - if (not pdf_font_auto_expand[f]) or (pdf_font_blink[f] = null_font) then - return; {not an auto-expanded font} - bf := pdf_font_blink[f]; - if pdf_font_type[bf] = new_font_type then {we must process the base font first} - begin - save_f := tmp_f; - tmp_f := bf; - do_vf; - tmp_f := save_f; - end; - - if pdf_font_type[bf] <> virtual_font_type then - return; {not a virtual font} - - e := pdf_font_expand_ratio[f]; - for k := 0 to vf_local_font_num[bf] - 1 do begin - lf := vf_default_font[bf] + k; - vf_e_fnts[vf_nf] := vf_e_fnts[lf]; - vf_i_fnts[vf_nf] := auto_expand_font(vf_i_fnts[lf], e); - set_expand_param(vf_i_fnts[vf_nf], vf_i_fnts[lf], e); - incr(vf_nf); - end; - vf_packet_base[f] := vf_packet_base[bf]; - vf_local_font_num[f] := vf_local_font_num[bf]; - vf_default_font[f] := vf_nf - vf_local_font_num[f]; - - pdf_font_type[f] := virtual_font_type; - auto_expand_vf := true; -end; - -function fix_expand_value(f: internal_font_number; e: integer): integer; -{return the multiple of |pdf_font_step[f]| that is nearest to |e|} -var step: integer; - max_expand: integer; - neg: boolean; -begin - fix_expand_value := 0; - if e = 0 then - return; - if e < 0 then begin - e := -e; - neg := true; - max_expand := -pdf_font_expand_ratio[pdf_font_shrink[f]]; - end - else begin - neg := false; - max_expand := pdf_font_expand_ratio[pdf_font_stretch[f]]; - end; - if e > max_expand then - e := max_expand - else begin - step := pdf_font_step[f]; - if e mod step > 0 then - e := step*round_xn_over_d(e, 1, step); - end; - if neg then - e := -e; - fix_expand_value := e; -end; - -function get_expand_font(f: internal_font_number; e: integer): internal_font_number; -{look up and create if not found an expanded version of |f|; |f| is an -expandable font; |e| is nonzero and is a multiple of |pdf_font_step[f]|} -var k: internal_font_number; -begin - k := pdf_font_elink[f]; - while k <> null_font do begin - if pdf_font_expand_ratio[k] = e then begin - get_expand_font := k; - return; - end; - k := pdf_font_elink[k]; - end; - k := load_expand_font(f, e); - pdf_font_elink[k] := pdf_font_elink[f]; - pdf_font_elink[f] := k; - get_expand_font := k; -end; - -function expand_font(f: internal_font_number; e: integer): internal_font_number; -{looks up for font |f| expanded by |e| thousandths, |e| is an arbitrary value -between max stretch and max shrink of |f|; if not found then creates it} -var max_expand: integer; -begin - expand_font := f; - if e = 0 then - return; - e := fix_expand_value(f, e); - if e = 0 then - return; - if pdf_font_elink[f] = null_font then - pdf_error("font expansion", "uninitialized pdf_font_elink"); - expand_font := get_expand_font(f, e); -end; - -procedure read_expand_font; {read font expansion spec and load expanded font} -var font_shrink, font_stretch, font_step: integer; - f: internal_font_number; - auto_expand: boolean; -begin - {read font expansion parameters} - scan_font_ident; - f := cur_val; - if f = null_font then - pdf_error("font expansion", "invalid font identifier"); - scan_optional_equals; - scan_int; - font_stretch := fix_int(cur_val, 0, 1000); - scan_int; - font_shrink := fix_int(cur_val, 0, 1000); - scan_int; - font_step := fix_int(cur_val, 0, 1000); - if font_step = 0 then - pdf_error("font expansion", "invalid step"); - font_stretch := font_stretch - font_stretch mod font_step; - if font_stretch < 0 then - font_stretch := 0; - font_shrink := font_shrink - font_shrink mod font_step; - if font_shrink < 0 then - font_shrink := 0; - if font_shrink = 1000 then - font_shrink := font_shrink - font_step; {don't allow zero-width font} - if (font_stretch = 0) and (font_shrink = 0) then - pdf_error("font expansion", "invalid limit"); - auto_expand := false; - if scan_keyword("autoexpand") then begin - auto_expand := true; - @; end; - - {check if the font can be expanded} - if (pdf_font_expand_ratio[f] <> 0) then - pdf_error("font expansion", "this font has been expanded by another font so it cannot be used now"); - if (pdf_font_step[f] <> 0) then - {this font has been expanded, ensure the expansion parameters are identical} - begin - if (pdf_font_step[f] <> font_step) or - ((pdf_font_stretch[f] = null_font) and (font_stretch <> 0)) or - ((pdf_font_stretch[f] <> null_font) and - (pdf_font_expand_ratio[pdf_font_stretch[f]] <> font_stretch)) or - ((pdf_font_shrink[f] = null_font) and (font_shrink <> 0)) or - ((pdf_font_shrink[f] <> null_font) and - (pdf_font_expand_ratio[pdf_font_shrink[f]] <> -font_shrink)) or - (pdf_font_auto_expand[f] <> auto_expand) - then - pdf_error("font expansion", "font has been expanded with different parameters"); - end - else begin - pdf_font_step[f] := font_step; - pdf_font_auto_expand[f] := auto_expand; - if font_stretch > 0 then - pdf_font_stretch[f] := get_expand_font(f, font_stretch); - if font_shrink > 0 then - pdf_font_shrink[f] := get_expand_font(f, -font_shrink); - end; -end; -@z - -@x [32f] - margin kerning -glue_node: @<(\pdfTeX) Move right or output leaders@>; -@y -glue_node: @<(\pdfTeX) Move right or output leaders@>; -margin_kern_node, -@z - -@x [649] - font expansion -@ Here now is |hpack|, which contains few if any surprises. - -@p function hpack(@!p:pointer;@!w:scaled;@!m:small_number):pointer; -@y -@ @= -@!pdf_font_blink: ^internal_font_number; {link to base font (used for expanded fonts only)} -@!pdf_font_elink: ^internal_font_number; {link to expanded fonts (used for base fonts only)} -@!pdf_font_stretch: ^integer; {limit of stretching} -@!pdf_font_shrink: ^integer; {limit of shrinking} -@!pdf_font_step: ^integer; {amount of one step of expansion} -@!pdf_font_expand_ratio: ^integer; {expansion ratio of a particular font} -@!pdf_font_auto_expand: ^boolean; {this font is auto-expanded?} -@!pdf_font_lp_base: ^integer; {base of left-protruding factor} -@!pdf_font_rp_base: ^integer; {base of right-protruding factor} -@!pdf_font_ef_base: ^integer; {base of font expansion factor} -@!font_expand_ratio: integer; {current expansion ratio} -@!last_leftmost_char: pointer; -@!last_rightmost_char: pointer; -@!hlist_stack:array[0..max_hlist_stack] of pointer; {stack for |find_protchar_left()| and |find_protchar_right()|} -@!hlist_stack_level:0..max_hlist_stack; {fill level for |hlist_stack|} - -@ @d cal_margin_kern_var(#) == -begin - character(cp) := character(#); - font(cp) := font(#); - do_subst_font(cp, 1000); - if font(cp) <> font(#) then - margin_kern_stretch := margin_kern_stretch + left_pw(#) - left_pw(cp); - font(cp) := font(#); - do_subst_font(cp, -1000); - if font(cp) <> font(#) then - margin_kern_shrink := margin_kern_shrink + left_pw(cp) - left_pw(#); -end - -@= -begin - lp := last_leftmost_char; - rp := last_rightmost_char; - fast_get_avail(cp); - if lp <> null then - cal_margin_kern_var(lp); - if rp <> null then - cal_margin_kern_var(rp); - free_avail(cp); -end - -@ Here is |hpack|, which is place where we do font substituting when -font expansion is being used. We define some constants used when calling -|hpack| to deal with font expansion. - -@d cal_expand_ratio == 2 {calculate amount for font expansion after breaking - paragraph into lines} -@d subst_ex_font == 3 {substitute fonts} - -@d substituted = 3 {|subtype| of kern nodes that should be substituted} - -@d left_pw(#) == char_pw(#, left_side) -@d right_pw(#) == char_pw(#, right_side) - -@p -function check_expand_pars(f: internal_font_number): boolean; -var k: internal_font_number; -begin - check_expand_pars := false; - if (pdf_font_step[f] = 0) or ((pdf_font_stretch[f] = null_font) and - (pdf_font_shrink[f] = null_font)) then - return; - if cur_font_step < 0 then - cur_font_step := pdf_font_step[f] - else if cur_font_step <> pdf_font_step[f] then - pdf_error("font expansion", "using fonts with different step of expansion in one paragraph is not allowed"); - k := pdf_font_stretch[f]; - if k <> null_font then begin - if max_stretch_ratio < 0 then - max_stretch_ratio := pdf_font_expand_ratio[k] - else if max_stretch_ratio <> pdf_font_expand_ratio[k] then - pdf_error("font expansion", "using fonts with different limit of expansion in one paragraph is not allowed"); - end; - k := pdf_font_shrink[f]; - if k <> null_font then begin - if max_shrink_ratio < 0 then - max_shrink_ratio := pdf_font_expand_ratio[k] - else if max_shrink_ratio <> pdf_font_expand_ratio[k] then - pdf_error("font expansion", "using fonts with different limit of expansion in one paragraph is not allowed"); - end; - check_expand_pars := true; -end; - -function char_stretch(f: internal_font_number; c: eight_bits): scaled; -var k: internal_font_number; - dw: scaled; - ef: integer; -begin - char_stretch := 0; - k := pdf_font_stretch[f]; - ef := get_ef_code(f, c); - if (k <> null_font) and (ef > 0) then begin - dw := char_width(k)(char_info(k)(c)) - char_width(f)(char_info(f)(c)); - if dw > 0 then - char_stretch := round_xn_over_d(dw, ef, 1000); - end; -end; - -function char_shrink(f: internal_font_number; c: eight_bits): scaled; -var k: internal_font_number; - dw: scaled; - ef: integer; -begin - char_shrink := 0; - k := pdf_font_shrink[f]; - ef := get_ef_code(f, c); - if (k <> null_font) and (ef > 0) then begin - dw := char_width(f)(char_info(f)(c)) - char_width(k)(char_info(k)(c)); - if dw > 0 then - char_shrink := round_xn_over_d(dw, ef, 1000); - end; -end; - -function get_kern(f: internal_font_number; lc, rc: eight_bits): scaled; -label continue; -var i: four_quarters; - j: four_quarters; - k: font_index; - p: pointer; - s: integer; -begin - get_kern := 0; - i := char_info(f)(lc); - if char_tag(i) <> lig_tag then - return; - k := lig_kern_start(f)(i); - j := font_info[k].qqqq; - if skip_byte(j) <= stop_flag then - goto continue + 1; - k := lig_kern_restart(f)(j); -continue: - j := font_info[k].qqqq; -continue + 1: - if (next_char(j) = rc) and (skip_byte(j) <= stop_flag) and - (op_byte(j) >= kern_flag) - then begin - get_kern := char_kern(f)(j); - return; - end; - if skip_byte(j) = qi(0) then - incr(k) - else begin - if skip_byte(j) >= stop_flag then - return; - k := k + qo(skip_byte(j)) + 1; - end; - goto continue; -end; - -function kern_stretch(p: pointer): scaled; -var l, r: pointer; - d: scaled; -begin - kern_stretch := 0; - if (prev_char_p = null) or (link(prev_char_p) <> p) or (link(p) = null) - then - return; - l := prev_char_p; - r := link(p); - if type(l) = ligature_node then - l := lig_char(l); - if type(r) = ligature_node then - r := lig_char(r); - if not (is_char_node(l) and is_char_node(r) and - (font(l) = font(r)) and - (pdf_font_stretch[font(l)] <> null_font)) - then - return; - d := get_kern(pdf_font_stretch[font(l)], character(l), character(r)); - kern_stretch := round_xn_over_d(d - width(p), - get_ef_code(font(l), character(l)), 1000); -end; - -function kern_shrink(p: pointer): scaled; -var l, r: pointer; - d: scaled; -begin - kern_shrink := 0; - if (prev_char_p = null) or (link(prev_char_p) <> p) or (link(p) = null) - then - return; - l := prev_char_p; - r := link(p); - if type(l) = ligature_node then - l := lig_char(l); - if type(r) = ligature_node then - r := lig_char(r); - if not (is_char_node(l) and is_char_node(r) and - (font(l) = font(r)) and - (pdf_font_shrink[font(l)] <> null_font)) - then - return; - d := get_kern(pdf_font_shrink[font(l)], character(l), character(r)); - kern_shrink := round_xn_over_d(width(p) - d, - get_ef_code(font(l), character(l)), 1000); -end; - -procedure do_subst_font(p: pointer; ex_ratio: integer); -var f, k: internal_font_number; - r: pointer; - ef: integer; -begin - if not is_char_node(p) and (type(p) = disc_node) then begin - r := pre_break(p); - while r <> null do begin - if is_char_node(r) or (type(r) = ligature_node) then - do_subst_font(r, ex_ratio); - r := link(r); - end; - r := post_break(p); - while r <> null do begin - if is_char_node(r) or (type(r) = ligature_node) then - do_subst_font(r, ex_ratio); - r := link(r); - end; - return; - end; - if is_char_node(p) then - r := p - else if type(p) = ligature_node then - r := lig_char(p) - else begin - {|short_display_n(p, 5);|} - pdf_error("font expansion", "invalid node type"); - end; - f := font(r); - ef := get_ef_code(f, character(r)); - if ef = 0 then - return; - if (pdf_font_stretch[f] <> null_font) and (ex_ratio > 0) then - k := expand_font(f, divide_scaled(ex_ratio* - pdf_font_expand_ratio[pdf_font_stretch[f]]*ef, - 1000000, 0)) - else if (pdf_font_shrink[f] <> null_font) and (ex_ratio < 0) then - k := expand_font(f, -divide_scaled(ex_ratio* - pdf_font_expand_ratio[pdf_font_shrink[f]]*ef, - 1000000, 0)) - else - k := f; - if k <> f then begin - font(r) := k; - if not is_char_node(p) then begin - r := lig_ptr(p); - while r <> null do begin - font(r) := k; - r := link(r); - end; - end; - end; -end; - -function char_pw(p: pointer; side: small_number): scaled; -var f: internal_font_number; - c: integer; -begin - char_pw := 0; - if side = left_side then - last_leftmost_char := null - else - last_rightmost_char := null; - if p = null then - return; - if not is_char_node(p) then begin - if type(p) = ligature_node then - p := lig_char(p) - else - return; - end; - f := font(p); - if side = left_side then begin - c := get_lp_code(f, character(p)); - last_leftmost_char := p; - end - else begin - c := get_rp_code(f, character(p)); - last_rightmost_char := p; - end; - if c = 0 then - return; - char_pw := - round_xn_over_d(quad(f), c, 1000); -end; - -function new_margin_kern(w: scaled; p: pointer; side: small_number): pointer; -var k: pointer; -begin - k := get_node(margin_kern_node_size); - type(k) := margin_kern_node; - subtype(k) := side; - width(k) := w; - if p = null then - pdf_error("margin kerning", "invalid pointer to marginal char node"); - fast_get_avail(margin_char(k)); - character(margin_char(k)) := character(p); - font(margin_char(k)) := font(p); - new_margin_kern := k; -end; - -function hpack(@!p:pointer;@!w:scaled;@!m:small_number):pointer; -@z - -@x [649] - font expansion -begin last_badness:=0; r:=get_node(box_node_size); type(r):=hlist_node; -subtype(r):=min_quarterword; shift_amount(r):=0; -q:=r+list_offset; link(q):=p;@/ -@y -font_stretch: scaled; -font_shrink: scaled; -k: scaled; -begin last_badness:=0; r:=get_node(box_node_size); type(r):=hlist_node; -subtype(r):=min_quarterword; shift_amount(r):=0; -q:=r+list_offset; link(q):=p;@/ -if m = cal_expand_ratio then begin - prev_char_p := null; - font_stretch := 0; - font_shrink := 0; - font_expand_ratio := 0; -end; -@z - -@x [649] - font expansion -hpack:=r; -@y -if (m = cal_expand_ratio) and (font_expand_ratio <> 0) then begin - font_expand_ratio := fix_int(font_expand_ratio, -1000, 1000); - q := list_ptr(r); - free_node(r, box_node_size); - r := hpack(q, w, subst_ex_font); -end; -hpack:=r; -@z - -@x [651] - font expansion - kern_node: x:=x+width(p); -@y - margin_kern_node: begin - if m = cal_expand_ratio then begin - f := font(margin_char(p)); - do_subst_font(margin_char(p), 1000); - if f <> font(margin_char(p)) then - font_stretch := font_stretch - width(p) - - char_pw(margin_char(p), subtype(p)); - font(margin_char(p)) := f; - do_subst_font(margin_char(p), -1000); - if f <> font(margin_char(p)) then - font_shrink := font_shrink - width(p) - - char_pw(margin_char(p), subtype(p)); - font(margin_char(p)) := f; - end - else if m = subst_ex_font then begin - do_subst_font(margin_char(p), font_expand_ratio); - width(p) := -char_pw(margin_char(p), subtype(p)); - end; - x := x + width(p); - end; - kern_node: begin - if (m = cal_expand_ratio) and (subtype(p) = normal) then begin - k := kern_stretch(p); - if k <> 0 then begin - subtype(p) := substituted; - font_stretch := font_stretch + k; - end; - k := kern_shrink(p); - if k <> 0 then begin - subtype(p) := substituted; - font_shrink := font_shrink + k; - end; - end - else if (m = subst_ex_font) and (subtype(p) = substituted) then begin - if type(link(p)) = ligature_node then - width(p) := get_kern(font(prev_char_p), - character(prev_char_p), - character(lig_char(link(p)))) - else - width(p) := get_kern(font(prev_char_p), - character(prev_char_p), - character(link(p))) - end; - x := x + width(p); - end; -@z - -@x [651] - font expansion - ligature_node: @; -@y - ligature_node: begin - if m = subst_ex_font then - do_subst_font(p, font_expand_ratio); - @; - end; - disc_node: - if m = subst_ex_font then - do_subst_font(p, font_expand_ratio); -@z - -@x [654] - font expansion -begin f:=font(p); i:=char_info(f)(character(p)); hd:=height_depth(i); -@y -begin -if m >= cal_expand_ratio then begin - prev_char_p := p; - case m of - cal_expand_ratio: begin - f := font(p); - add_char_stretch(font_stretch)(character(p)); - add_char_shrink(font_shrink)(character(p)); - end; - subst_ex_font: - do_subst_font(p, font_expand_ratio); - endcases; -end; -f:=font(p); i:=char_info(f)(character(p)); hd:=height_depth(i); -@z - -@x [658] - font expansion -@ @= -begin @; -@y -@ If |hpack| is called with |m=cal_expand_ratio| we calculate -|font_expand_ratio| and return without checking for overfull or underfull box. - -@= -begin @; -if (m = cal_expand_ratio) and (o = normal) and (font_stretch > 0) then begin - font_expand_ratio := divide_scaled(x, font_stretch, 3); - return; -end; -@z - -@x [664] - font expansion -@ @= -begin @; -@y -@ @= -begin @; -if (m = cal_expand_ratio) and (o = normal) and (font_shrink > 0) then begin - font_expand_ratio := divide_scaled(x, font_shrink, 3); - return; -end; -@z - -@x [822] - font expansion -@d delta_node_size=7 {number of words in a delta node} -@y -@d delta_node_size=9 {number of words in a delta node} -@z - -@x [823] - font expansion, margin kerning, avoiding overfull boxes -@= -@!active_width:array[1..6] of scaled; - {distance from first active node to~|cur_p|} -@!cur_active_width:array[1..6] of scaled; {distance from current active node} -@!background:array[1..6] of scaled; {length of an ``empty'' line} -@!break_width:array[1..6] of scaled; {length being computed after current break} -@y -@d do_seven_eight(#) == if pdf_adjust_spacing > 1 then begin #(7);#(8); end -@d do_all_eight(#) == do_all_six(#); do_seven_eight(#) -@d do_one_seven_eight(#) == #(1); do_seven_eight(#) - -@d total_font_stretch == cur_active_width[7] -@d total_font_shrink == cur_active_width[8] - -@d save_active_width(#) == prev_active_width[#] := active_width[#] -@d restore_active_width(#) == active_width[#] := prev_active_width[#] - -@= -@!active_width:array[1..8] of scaled; - {distance from first active node to~|cur_p|} -@!cur_active_width:array[1..8] of scaled; {distance from current active node} -@!background:array[1..8] of scaled; {length of an ``empty'' line} -@!break_width:array[1..8] of scaled; {length being computed after current break} -@# -@!auto_breaking: boolean; {make |auto_breaking| accessible out of |line_break|} -@!prev_p: pointer; {make |prev_p| accessible out of |line_break|} -@!first_p: pointer; {to access the first node of the paragraph} -@!prev_char_p: pointer; {pointer to the previous char of an implicit kern} -@!next_char_p: pointer; {pointer to the next char of an implicit kern} -@# -@!try_prev_break: boolean; {force break at the previous legal breakpoint?} -@!prev_legal: pointer; {the previous legal breakpoint} -@!prev_prev_legal: pointer; {to save |prev_p| corresponding to |prev_legal|} -@!prev_auto_breaking: boolean; {to save |auto_breaking| corresponding to |prev_legal|} -@!prev_active_width: array[1..8] of scaled; {to save |active_width| corresponding to |prev_legal|} -@!rejected_cur_p: pointer; {the last |cur_p| that has been rejected} -@!before_rejected_cur_p: boolean; {|cur_p| is still before |rejected_cur_p|?} -@# -@!max_stretch_ratio: integer; {maximal stretch ratio of expanded fonts} -@!max_shrink_ratio: integer; {maximal shrink ratio of expanded fonts} -@!cur_font_step: integer; {the current step of expanded fonts} -@z - -@x [827] - font expansion -background[6]:=shrink(q)+shrink(r); -@y -background[6]:=shrink(q)+shrink(r); -if pdf_adjust_spacing > 1 then begin - background[7] := 0; - background[8] := 0; - max_stretch_ratio := -1; - max_shrink_ratio := -1; - cur_font_step := -1; - prev_char_p := null; -end; -@z - -@x [829] - margin kerning -@= -procedure try_break(@!pi:integer;@!break_type:small_number); -@y -@d cp_skipable(#) == {skipable nodes at the margins during character protrusion} -( - not is_char_node(#) and - ( - (type(#) = ins_node) - or (type(#) = mark_node) - or (type(#) = adjust_node) - or (type(#) = penalty_node) - or ((type(#) = whatsit_node) and - (subtype(#) <> pdf_refximage_node) and - (subtype(#) <> pdf_refxform_node)) {reference to an image or XObject form} - or ((type(#) = disc_node) and - (pre_break(#) = null) and - (post_break(#) = null) and - (replace_count(#) = 0)) {an empty |disc_node|} - or ((type(#) = math_node) and (width(#) = 0)) - or ((type(#) = kern_node) and - ((width(#) = 0) or (subtype(#) = normal))) - or ((type(#) = glue_node) and (glue_ptr(#) = zero_glue)) - or ((type(#) = hlist_node) and (width(#) = 0) and (height(#) = 0) and - (depth(#) = 0) and (list_ptr(#) = null)) - ) -) - - -@= -procedure push_node(p: pointer); -begin - if hlist_stack_level > max_hlist_stack then - pdf_error("push_node", "stack overflow"); - hlist_stack[hlist_stack_level] := p; - hlist_stack_level := hlist_stack_level + 1; -end; - -function pop_node: pointer; -begin - hlist_stack_level := hlist_stack_level - 1; - if hlist_stack_level < 0 then {would point to some bug} - pdf_error("pop_node", "stack underflow (internal error)"); - pop_node := hlist_stack[hlist_stack_level]; -end; - -function find_protchar_left(l: pointer; d: boolean): pointer; -{searches left to right from list head |l|, returns 1st non-skipable item} -var t: pointer; - run: boolean; -begin - if (link(l) <> null) and (type(l) = hlist_node) and (width(l) = 0) - and (height(l) = 0) and (depth(l) = 0) and (list_ptr(l) = null) then - l := link(l) {for paragraph start with \.{\\parindent = 0pt}} - else if d then - while (link(l) <> null) and (not (is_char_node(l) or non_discardable(l))) do - l := link(l); {std.\ discardables at line break, \TeX book, p 95} - hlist_stack_level := 0; - run := true; - repeat - t := l; - while run and (type(l) = hlist_node) and (list_ptr(l) <> null) do begin - push_node(l); - l := list_ptr(l); - end; - while run and cp_skipable(l) do begin - while (link(l) = null) and (hlist_stack_level > 0) do begin - l := pop_node; {don't visit this node again} - end; - if link(l) <> null then - l := link(l) - else if hlist_stack_level = 0 then run := false - end; - until t = l; - find_protchar_left := l; -end; - -function find_protchar_right(l, r: pointer): pointer; -{searches right to left from list tail |r| to head |l|, returns 1st non-skipable item} -var t: pointer; - run: boolean; -begin - find_protchar_right := null; - if r = null then return; - hlist_stack_level := 0; - run := true; - repeat - t := r; - while run and (type(r) = hlist_node) and (list_ptr(r) <> null) do begin - push_node(l); - push_node(r); - l := list_ptr(r); - r := l; - while link(r) <> null do - r := link(r); - end; - while run and cp_skipable(r) do begin - while (r = l) and (hlist_stack_level > 0) do begin - r := pop_node; {don't visit this node again} - l := pop_node; - end; - if (r <> l) and (r <> null) then - r := prev_rightmost(l, r) - else if (r = l) and (hlist_stack_level = 0) then run := false - end; - until t = r; - find_protchar_right := r; -end; - -function total_pw(q, p: pointer): scaled; -{returns the total width of character protrusion of a line; -|cur_break(break_node(q))| and |p| is the leftmost resp. rightmost node in the -horizontal list representing the actual line} -var l, r, s: pointer; - n: integer; -begin - if break_node(q) = null then - l := first_p - else - l := cur_break(break_node(q)); - r := prev_rightmost(prev_p, p); {get |link(r)=p|} - {let's look at the right margin first} - {| - short_display_n(r, 2); - print("&"); - short_display_n(p, 2); - print_ln; - |} - if (p <> null) and (type(p) = disc_node) and (pre_break(p) <> null) then - {a |disc_node| with non-empty |pre_break|, protrude the last char of |pre_break|} - begin - r := pre_break(p); - while link(r) <> null do - r := link(r); - end else r := find_protchar_right(l, r); - {now the left margin} - {| - short_display_n(l, 2); - print_ln; - breadth_max := 10; - depth_threshold := 2; - show_node_list(l); - print_ln; - |} - if (l <> null) and (type(l) = disc_node) then begin - if post_break(l) <> null then begin - l := post_break(l); {protrude the first char} - goto done; - end else {discard |replace_count(l)| nodes} - begin - n := replace_count(l); - l := link(l); - while n > 0 do begin - if link(l) <> null then - l := link(l); - decr(n); - end; - end; - end; - l := find_protchar_left(l, true); -done: - total_pw := left_pw(l) + right_pw(r); -end; - -procedure try_break(@!pi:integer;@!break_type:small_number); -@z - -@x -var r:pointer; {runs through the active list} -@y -var r:pointer; {runs through the active list} -@!margin_kern_stretch: scaled; -@!margin_kern_shrink: scaled; -@!lp, rp, cp: pointer; -@z - - -@x [829] - font expansion -do_all_six(copy_to_cur_active); -@y -do_all_eight(copy_to_cur_active); -@z - -@x [832] - font expansion - begin do_all_six(update_width); -@y - begin do_all_eight(update_width); -@z - -@x [837] - font expansion -begin no_break_yet:=false; do_all_six(set_break_width_to_background); -@y -begin no_break_yet:=false; do_all_eight(set_break_width_to_background); -@z - -@x [839] - font expansion -@= -@!disc_width:scaled; {the length of discretionary material preceding a break} -@y -@d reset_disc_width(#) == disc_width[#] := 0 - -@d add_disc_width_to_break_width(#) == - break_width[#] := break_width[#] + disc_width[#] - -@d add_disc_width_to_active_width(#) == - active_width[#] := active_width[#] + disc_width[#] - -@d sub_disc_width_from_active_width(#) == - active_width[#] := active_width[#] - disc_width[#] - -@d add_char_stretch_end(#) == char_stretch(f, #) -@d add_char_stretch(#) == # := # + add_char_stretch_end - -@d add_char_shrink_end(#) == char_shrink(f, #) -@d add_char_shrink(#) == # := # + add_char_shrink_end - -@d sub_char_stretch_end(#) == char_stretch(f, #) -@d sub_char_stretch(#) == # := # - sub_char_stretch_end - -@d sub_char_shrink_end(#) == char_shrink(f, #) -@d sub_char_shrink(#) == # := # - sub_char_shrink_end - -@d add_kern_stretch_end(#) == kern_stretch(#) -@d add_kern_stretch(#) == # := # + add_kern_stretch_end - -@d add_kern_shrink_end(#) == kern_shrink(#) -@d add_kern_shrink(#) == # := # + add_kern_shrink_end - -@d sub_kern_stretch_end(#) == kern_stretch(#) -@d sub_kern_stretch(#) == # := # - sub_kern_stretch_end - -@d sub_kern_shrink_end(#) == kern_shrink(#) -@d sub_kern_shrink(#) == # := # - sub_kern_shrink_end - -@= -@!disc_width: array[1..8] of scaled; {the length of discretionary material preceding a break} -@z - -@x [840] - font expansion -break_width[1]:=break_width[1]+disc_width; -@y -do_one_seven_eight(add_disc_width_to_break_width); -@z - -@x [841] - font expansion - break_width[1]:=break_width[1]-char_width(f)(char_info(f)(character(v))); -@y - break_width[1]:=break_width[1]-char_width(f)(char_info(f)(character(v))); - if (pdf_adjust_spacing > 1) and check_expand_pars(f) then begin - prev_char_p := v; - sub_char_stretch(break_width[7])(character(v)); - sub_char_shrink(break_width[8])(character(v)); - end; -@z - -@x [841] - font expansion - break_width[1]:=@|break_width[1]- - char_width(f)(char_info(f)(character(lig_char(v)))); -@y - break_width[1]:=@|break_width[1]- - char_width(f)(char_info(f)(character(lig_char(v)))); - if (pdf_adjust_spacing > 1) and check_expand_pars(f) then begin - prev_char_p := v; - sub_char_stretch(break_width[7])(character(lig_char(v))); - sub_char_shrink(break_width[8])(character(lig_char(v))); - end; -@z - -@x [841] - font expansion - hlist_node,vlist_node,rule_node,kern_node: - break_width[1]:=break_width[1]-width(v); -@y - hlist_node,vlist_node,rule_node,kern_node: begin - break_width[1]:=break_width[1]-width(v); - if (type(v) = kern_node) and - (pdf_adjust_spacing > 1) and (subtype(v) = normal) - then begin - sub_kern_stretch(break_width[7])(v); - sub_kern_shrink(break_width[8])(v); - end; - end; -@z - -@x [842] - font expansion - break_width[1]:=@|break_width[1]+char_width(f)(char_info(f)(character(s))); -@y - break_width[1]:=@|break_width[1]+char_width(f)(char_info(f)(character(s))); - if (pdf_adjust_spacing > 1) and check_expand_pars(f) then begin - prev_char_p := s; - add_char_stretch(break_width[7])(character(s)); - add_char_shrink(break_width[8])(character(s)); - end; -@z - -@x [842] - font expansion - break_width[1]:=break_width[1]+ - char_width(f)(char_info(f)(character(lig_char(s)))); -@y - break_width[1]:=break_width[1]+ - char_width(f)(char_info(f)(character(lig_char(s)))); - if (pdf_adjust_spacing > 1) and check_expand_pars(f) then begin - prev_char_p := s; - add_char_stretch(break_width[7])(character(lig_char(s))); - add_char_shrink(break_width[8])(character(lig_char(s))); - end; -@z - -@x [842] - font expansion - hlist_node,vlist_node,rule_node,kern_node: - break_width[1]:=break_width[1]+width(s); -@y - hlist_node,vlist_node,rule_node,kern_node: begin - break_width[1]:=break_width[1]+width(s); - if (type(s) = kern_node) and - (pdf_adjust_spacing > 1) and (subtype(s) = normal) - then begin - add_kern_stretch(break_width[7])(s); - add_kern_shrink(break_width[8])(s); - end; - end; -@z - -@x [843] - font expansion - begin do_all_six(convert_to_break_width); -@y - begin do_all_eight(convert_to_break_width); -@z - -@x [843] - font expansion - begin do_all_six(store_break_width); -@y - begin do_all_eight(store_break_width); -@z - -@x [843] - font expansion - do_all_six(new_delta_to_break_width); -@y - do_all_eight(new_delta_to_break_width); -@z - -@x [844] - font expansion - do_all_six(new_delta_from_break_width); -@y - do_all_eight(new_delta_from_break_width); -@z - -@x [851] - font expansion, margin kerning -shortfall:=line_width-cur_active_width[1]; {we're this much too short} -@y -shortfall:=line_width-cur_active_width[1]; {we're this much too short} - -{| -if pdf_output > 2 then begin -print_ln; -if (r <> null) and (break_node(r) <> null) then - short_display_n(cur_break(break_node(r)), 5); -print_ln; -short_display_n(cur_p, 5); -print_ln; -end; -|} - -if pdf_protrude_chars > 1 then - shortfall := shortfall + total_pw(r, cur_p); -if (pdf_adjust_spacing > 1) and (shortfall <> 0) then begin - margin_kern_stretch := 0; - margin_kern_shrink := 0; - if pdf_protrude_chars > 1 then - @; - if (shortfall > 0) and ((total_font_stretch + margin_kern_stretch) > 0) - then begin - if (total_font_stretch + margin_kern_stretch) > shortfall then - shortfall := ((total_font_stretch + margin_kern_stretch) div - (max_stretch_ratio div cur_font_step)) div 2 - else - shortfall := shortfall - (total_font_stretch + margin_kern_stretch); - end - else if (shortfall < 0) and ((total_font_shrink + margin_kern_shrink) > 0) - then begin - if (total_font_shrink + margin_kern_shrink) > -shortfall then - shortfall := -((total_font_shrink + margin_kern_shrink) div - (max_shrink_ratio div cur_font_step)) div 2 - else - shortfall := shortfall + (total_font_shrink + margin_kern_shrink); - end; -end; -@z - -@x [860] - font expansion - begin do_all_six(downdate_width); -@y - begin do_all_eight(downdate_width); -@z - -@x [860] - font expansion - begin do_all_six(update_width); - do_all_six(combine_two_deltas); -@y - begin do_all_eight(update_width); - do_all_eight(combine_two_deltas); -@z - -@x [861] - font expansion - begin do_all_six(update_active); - do_all_six(copy_to_cur_active); -@y - begin do_all_eight(update_active); - do_all_eight(copy_to_cur_active); -@z - -@x [862] - margin kerning, avoiding overfull boxes -@!auto_breaking:boolean; {is node |cur_p| outside a formula?} -@!prev_p:pointer; {helps to determine when glue nodes are breakpoints} -@y -@z - -@x [863] - margin kerning, avoiding overfull boxes - while (cur_p<>null)and(link(active)<>last_active) do -@y - prev_char_p := null; - prev_legal := null; - rejected_cur_p := null; - try_prev_break := false; - before_rejected_cur_p := false; - first_p := cur_p; {to access the first node of paragraph as the first active - node has |break_node=null|} - while (cur_p<>null)and(link(active)<>last_active) do -@z - -@x [864] - font expansion -do_all_six(store_background);@/ -@y -do_all_eight(store_background);@/ -@z - -@x [666] - font expansion - else act_width:=act_width+width(cur_p); -@y - else begin - act_width:=act_width+width(cur_p); - if (pdf_adjust_spacing > 1) and (subtype(cur_p) = normal) then begin - add_kern_stretch(active_width[7])(cur_p); - add_kern_shrink(active_width[8])(cur_p); - end; - end; -@z - -@x [866] - font expansion - act_width:=act_width+char_width(f)(char_info(f)(character(lig_char(cur_p)))); -@y - act_width:=act_width+char_width(f)(char_info(f)(character(lig_char(cur_p)))); - if (pdf_adjust_spacing > 1) and check_expand_pars(f) then begin - prev_char_p := cur_p; - add_char_stretch(active_width[7])(character(lig_char(cur_p))); - add_char_shrink(active_width[8])(character(lig_char(cur_p))); - end; -@z - -@x [867] - font expansion -act_width:=act_width+char_width(f)(char_info(f)(character(cur_p))); -@y -act_width:=act_width+char_width(f)(char_info(f)(character(cur_p))); -if (pdf_adjust_spacing > 1) and check_expand_pars(f) then begin - prev_char_p := cur_p; - add_char_stretch(active_width[7])(character(cur_p)); - add_char_shrink(active_width[8])(character(cur_p)); -end; -@z - -@x [869] - font expansion -begin s:=pre_break(cur_p); disc_width:=0; -@y -begin s:=pre_break(cur_p); -do_one_seven_eight(reset_disc_width); -@z - -@x [869] - font expansion - act_width:=act_width+disc_width; - try_break(hyphen_penalty,hyphenated); - act_width:=act_width-disc_width; -@y - do_one_seven_eight(add_disc_width_to_active_width); - try_break(hyphen_penalty,hyphenated); - do_one_seven_eight(sub_disc_width_from_active_width); -@z - -@x [870] - font expansion - disc_width:=disc_width+char_width(f)(char_info(f)(character(s))); -@y - disc_width[1]:=disc_width[1]+char_width(f)(char_info(f)(character(s))); - if (pdf_adjust_spacing > 1) and check_expand_pars(f) then begin - prev_char_p := s; - add_char_stretch(disc_width[7])(character(s)); - add_char_shrink(disc_width[8])(character(s)); - end; -@z - -@x [870] - font expansion - disc_width:=disc_width+ - char_width(f)(char_info(f)(character(lig_char(s)))); -@y - disc_width[1]:=disc_width[1]+ - char_width(f)(char_info(f)(character(lig_char(s)))); - if (pdf_adjust_spacing > 1) and check_expand_pars(f) then begin - prev_char_p := s; - add_char_stretch(disc_width[7])(character(lig_char(s))); - add_char_shrink(disc_width[8])(character(lig_char(s))); - end; -@z - -@x [870] - font expansion - hlist_node,vlist_node,rule_node,kern_node: - disc_width:=disc_width+width(s); -@y - hlist_node,vlist_node,rule_node,kern_node: begin - disc_width[1]:=disc_width[1]+width(s); - if (type(s) = kern_node) and - (pdf_adjust_spacing > 1) and (subtype(s) = normal) - then begin - add_kern_stretch(disc_width[7])(s); - add_kern_shrink(disc_width[8])(s); - end; - end; -@z - -@x [871] - font expansion - act_width:=act_width+char_width(f)(char_info(f)(character(s))); -@y - act_width:=act_width+char_width(f)(char_info(f)(character(s))); - if (pdf_adjust_spacing > 1) and check_expand_pars(f) then begin - prev_char_p := s; - add_char_stretch(active_width[7])(character(s)); - add_char_shrink(active_width[8])(character(s)); - end; -@z - -@x [871] - font expansion - act_width:=act_width+ - char_width(f)(char_info(f)(character(lig_char(s)))); -@y - act_width:=act_width+ - char_width(f)(char_info(f)(character(lig_char(s)))); - if (pdf_adjust_spacing > 1) and check_expand_pars(f) then begin - prev_char_p := s; - add_char_stretch(active_width[7])(character(lig_char(s))); - add_char_shrink(active_width[8])(character(lig_char(s))); - end; -@z - -@x [871] - font expansion - hlist_node,vlist_node,rule_node,kern_node: - act_width:=act_width+width(s); -@y - hlist_node,vlist_node,rule_node,kern_node: begin - act_width:=act_width+width(s); - if (type(s) = kern_node) and - (pdf_adjust_spacing > 1) and (subtype(s) = normal) - then begin - add_kern_stretch(active_width[7])(s); - add_kern_shrink(active_width[8])(s); - end; - end; -@z - -@x [877] - margin kerning -var q,@!r,@!s:pointer; {temporary registers for list manipulation} -@y -var q,@!r,@!s:pointer; {temporary registers for list manipulation} - p, k: pointer; - w: scaled; - glue_break: boolean; {was a break at glue?} - ptmp: pointer; -@z - -@x [881] - margin kerning -q:=cur_break(cur_p); disc_break:=false; post_disc_break:=false; -@y -q:=cur_break(cur_p); disc_break:=false; post_disc_break:=false; -glue_break := false; -@z - -@x [881] - margin kerning - subtype(q):=right_skip_code+1; add_glue_ref(right_skip); - goto done; -@y - subtype(q):=right_skip_code+1; add_glue_ref(right_skip); - glue_break := true; - goto done; -@z - -@x [881] - margin kerning -@; -done: -@y -done: - -{at this point |q| is the rightmost breakpoint; the only exception is the case -of a discretionary break with non-empty |pre_break|, then |q| has been changed -to the last node of the |pre_break| list} - -if pdf_protrude_chars > 0 then begin - if disc_break and (is_char_node(q) or (type(q) <> disc_node)) - {|q| has been reset to the last node of |pre_break|} - then begin - p := q; - ptmp := p; - end else begin - p := prev_rightmost(temp_head, q); {get |link(p) = q|} - ptmp := p; - p := find_protchar_right(temp_head, p); - end; - {| - short_display_n(p, 1); - print_ln; - |} - w := right_pw(p); - if w <> 0 then {we have found a marginal kern, append it after |ptmp|} - begin - k := new_margin_kern(-w, last_rightmost_char, right_side); - link(k) := link(ptmp); - link(ptmp) := k; - if (ptmp = q) then - q := link(q); - end; -end; - -{if |q| was not a breakpoint at glue and has been reset to |rightskip| then - we append |rightskip| after |q| now} -if not glue_break then begin - @; -end; -@z - -@x [887] - margin kerning -if left_skip<>zero_glue then -@y -{at this point |q| is the leftmost node; all discardable nodes have been discarded} -if pdf_protrude_chars > 0 then begin - p := q; - p := find_protchar_left(p, false); {no more discardables} - w := left_pw(p); - if w <> 0 then begin - k := new_margin_kern(-w, last_leftmost_char, left_side); - link(k) := q; - q := k; - end; -end; -if left_skip<>zero_glue then -@z - -@x [889] - font expansion, pre vadjust -just_box:=hpack(q,cur_width,exactly); -@y -if pdf_adjust_spacing > 0 then - just_box := hpack(q, cur_width, cal_expand_ratio) -else - just_box := hpack(q, cur_width, exactly); -@z - -@x [1110] - margin kerning -var p:pointer; {the box} -@y -var p:pointer; {the box} - r: pointer; {to remove marginal kern nodes} -@z - -@x [1110] - margin kerning -while link(tail)<>null do tail:=link(tail); -@y -while link(tail) <> null do begin - r := link(tail); - if not is_char_node(r) and (type(r) = margin_kern_node) then begin - link(tail) := link(r); - free_avail(margin_char(r)); - free_node(r, margin_kern_node_size); - end; - tail:=link(tail); -end; -@z - -@x [1147] - margin kerning -ligature_node:@; -@y -ligature_node:@; -margin_kern_node: d:=width(p); -@z - -@x [1253] - font expansion -assign_font_int: begin n:=cur_chr; scan_font_ident; f:=cur_val; - scan_optional_equals; scan_int; - if n=0 then hyphen_char[f]:=cur_val@+else skew_char[f]:=cur_val; - end; -@y -assign_font_int: begin n:=cur_chr; scan_font_ident; f:=cur_val; - if n < lp_code_base then begin - scan_optional_equals; scan_int; - if n=0 then hyphen_char[f]:=cur_val@+else skew_char[f]:=cur_val; - end - else begin - scan_char_num; p := cur_val; - scan_optional_equals; scan_int; - case n of - lp_code_base: set_lp_code(f, p, cur_val); - rp_code_base: set_rp_code(f, p, cur_val); - ef_code_base: set_ef_code(f, p, cur_val); - end; - end; -end; -@z - -@x [1254] - font expansion -primitive("skewchar",assign_font_int,1); -@!@:skew_char_}{\.{\\skewchar} primitive@> -@y -primitive("skewchar",assign_font_int,1); -@!@:skew_char_}{\.{\\skewchar} primitive@> -primitive("lpcode",assign_font_int,lp_code_base); -@!@:lp_code_}{\.{\\lpcode} primitive@> -primitive("rpcode",assign_font_int,rp_code_base); -@!@:rp_code_}{\.{\\rpcode} primitive@> -primitive("efcode",assign_font_int,ef_code_base); -@!@:ef_code_}{\.{\\efcode} primitive@> -@z - -@x [1255] - font expansion -assign_font_int: if chr_code=0 then print_esc("hyphenchar") - else print_esc("skewchar"); -@y -assign_font_int: case chr_code of -0: print_esc("hyphenchar"); -1: print_esc("skewchar"); -lp_code_base: print_esc("lpcode"); -rp_code_base: print_esc("rpcode"); -ef_code_base: print_esc("efcode"); -endcases; -@z - -@x [1344] -@d pdftex_last_extension_code == pdftex_first_extension_code + 27 -@y -@d pdf_font_expand_code == pdftex_first_extension_code + 28 -@d pdftex_last_extension_code == pdftex_first_extension_code + 28 -@z - -@x [1344] -primitive("setlanguage",extension,set_language_code);@/ -@!@:set_language_}{\.{\\setlanguage} primitive@> -@y -primitive("setlanguage",extension,set_language_code);@/ -@!@:set_language_}{\.{\\setlanguage} primitive@> -primitive("pdffontexpand",extension,pdf_font_expand_code);@/ -@!@:pdf_font_expand_}{\.{\\pdffontexpand} primitive@> -@z - -@x [1346] - immediate_code:print_esc("immediate"); - set_language_code:print_esc("setlanguage"); -@y - immediate_code:print_esc("immediate"); - set_language_code:print_esc("setlanguage"); - pdf_font_expand_code: print_esc("pdffontexpand"); -@z - -@x [1348] -set_language_code:@; -@y -set_language_code:@; -pdf_font_expand_code: @; -@z - -@x [1354] -@= -begin new_whatsit(special_node,write_node_size); write_stream(tail):=null; -p:=scan_toks(false,true); write_tokens(tail):=def_ref; -end -@y -@= -begin new_whatsit(special_node,write_node_size); write_stream(tail):=null; -p:=scan_toks(false,true); write_tokens(tail):=def_ref; -end - -@ @= - read_expand_font -@z - -@x [15??] -@= -@y -@= -function prev_rightmost(s, e: pointer): pointer; -{finds the node preceding the rightmost node |e|; |s| is some node -before |e|} -var p: pointer; -begin - prev_rightmost := null; - p := s; - if p = null then - return; - while link(p) <> e do begin - p := link(p); - if p = null then - return; - end; - prev_rightmost := p; -end; -@z - diff --git a/Build/source/texk/web2c/pdftexdir/image.h b/Build/source/texk/web2c/pdftexdir/image.h index 2f9dcc94171..8907e50f603 100644 --- a/Build/source/texk/web2c/pdftexdir/image.h +++ b/Build/source/texk/web2c/pdftexdir/image.h @@ -29,10 +29,10 @@ $Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/image.h#10 $ #define JPG_UINT8 unsigned char typedef struct { - int color_space; /* used color space. See JPG_ constants */ - JPG_UINT8 bits_per_component; /* bits per component */ - JPG_UINT32 length; /* length of file/data */ - FILE *file; /* jpg file */ + int color_space; /* used color space. See JPG_ constants */ + JPG_UINT8 bits_per_component; /* bits per component */ + JPG_UINT32 length; /* length of file/data */ + FILE *file; /* jpg file */ } JPG_IMAGE_INFO; typedef struct { @@ -45,10 +45,14 @@ typedef struct { integer orig_y; integer selected_page; integer page_box; - integer always_use_pdfpagebox; void *doc; } pdf_image_struct; +typedef struct { + integer selected_page; + FILE *file; +} JBIG2_IMAGE_INFO; + typedef struct { char *image_name; int image_type; @@ -63,6 +67,7 @@ typedef struct { pdf_image_struct *pdf; png_image_struct png; JPG_IMAGE_INFO *jpg; + JBIG2_IMAGE_INFO *jbig2; } image_struct; } image_entry; @@ -74,6 +79,7 @@ extern integer image_max; #define IMAGE_TYPE_PNG 2 #define IMAGE_TYPE_JPG 3 #define IMAGE_TYPE_TIF 4 +#define IMAGE_TYPE_JBIG2 5 #define IMAGE_COLOR_B 1 #define IMAGE_COLOR_C 2 @@ -93,12 +99,16 @@ extern integer image_max; #define png_info(N) (img_ptr(N)->image_struct.png.info_ptr) #define pdf_ptr(N) (img_ptr(N)->image_struct.pdf) #define jpg_ptr(N) (img_ptr(N)->image_struct.jpg) +#define jbig2_ptr(N) (img_ptr(N)->image_struct.jbig2) #define tif_ptr(N) (img_ptr(N)->image_struct.tif) -extern integer read_pdf_info(char*, char*, integer, integer, integer, integer); +extern integer read_pdf_info(char *, char *, integer, integer, integer, + integer); extern void write_epdf(void); extern void epdf_delete(void); extern void read_png_info(integer); extern void write_png(integer); extern void read_jpg_info(integer); extern void write_jpg(integer); +extern void read_jbig2_info(integer); +extern void write_jbig2(integer); diff --git a/Build/source/texk/web2c/pdftexdir/macnames.c b/Build/source/texk/web2c/pdftexdir/macnames.c index 7f2005e8e20..fe9fbf8151d 100644 --- a/Build/source/texk/web2c/pdftexdir/macnames.c +++ b/Build/source/texk/web2c/pdftexdir/macnames.c @@ -301,12 +301,12 @@ const char *mac_glyph_names[] = { }; const char *ambiguous_names[] = { - "Delta", /* increment */ - "Omega", /* Ohm */ - "Pi", /* product */ - "Sigma", /* summation */ - "dmacron", /* dslash */ - "macron", /* overscore */ - "periodcentered", /* middot */ + "Delta", /* increment */ + "Omega", /* Ohm */ + "Pi", /* product */ + "Sigma", /* summation */ + "dmacron", /* dslash */ + "macron", /* overscore */ + "periodcentered", /* middot */ NULL }; diff --git a/Build/source/texk/web2c/pdftexdir/mapfile.c b/Build/source/texk/web2c/pdftexdir/mapfile.c index 758290127ca..296eb57e92a 100644 --- a/Build/source/texk/web2c/pdftexdir/mapfile.c +++ b/Build/source/texk/web2c/pdftexdir/mapfile.c @@ -1,5 +1,5 @@ /* -Copyright (c) 1996-2005 Han The Thanh, +Copyright (c) 1996-2006 Han The Thanh, This file is part of pdfTeX. @@ -17,19 +17,15 @@ You should have received a copy of the GNU General Public License along with pdfTeX; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -$Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/mapfile.c#30 $ +$Id: mapfile.c,v 1.1 2006/12/14 01:21:33 hahe Exp hahe $ -source code indentation by "indent -kr -nut" */ #include #include "ptexlib.h" #include #include -#include "avlstuff.h" - -static const char perforce_id[] = - "$Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/mapfile.c#30 $"; +#include #define FM_BUF_SIZE 1024 @@ -41,70 +37,61 @@ static FILE *fm_file; #define fm_getchar() xgetc(fm_file) #define fm_eof() feof(fm_file) -enum _mode { FM_DUPIGNORE, FM_REPLACE, FM_DELETE }; -enum _ltype { MAPFILE, MAPLINE }; -enum _tfmavail { TFM_UNCHECKED, TFM_FOUND, TFM_NOTFOUND }; +typedef enum { FM_DUPIGNORE, FM_REPLACE, FM_DELETE } updatemode; +typedef enum { MAPFILE, MAPLINE } maptype; typedef struct mitem { - int mode; /* FM_DUPIGNORE or FM_REPLACE or FM_DELETE */ - int type; /* map file or map line */ + updatemode mode; /* FM_DUPIGNORE or FM_REPLACE or FM_DELETE */ + maptype type; /* map file or map line */ char *line; /* pointer to map file name or map line */ int lineno; /* line number in map file */ - struct mitem *next; /* pointer to next item, or NULL */ } mapitem; -mapitem *miptr, *mapitems = NULL; +mapitem *mitem = NULL; -fm_entry *fm_cur; static const char nontfm[] = ""; -static fm_entry *loaded_tfm_found; -static fm_entry *avail_tfm_found; -static fm_entry *non_tfm_found; -static fm_entry *not_avail_tfm_found; - -#define read_field(r, q, buf) do { \ - for (q = buf; *r != ' ' && *r != 10; *q++ = *r++); \ - *q = 0; \ - skip (r, ' '); \ + +#define read_field(r, q, buf) do { \ + q = buf; \ + while (*r != ' ' && *r != '\0') \ + *q++ = *r++; \ + *q = '\0'; \ + skip (r, ' '); \ } while (0) -#define set_field(F) do { \ - if (q > buf) \ - fm->F = xstrdup(buf); \ - if (*r == 10) \ - goto done; \ +#define set_field(F) do { \ + if (q > buf) \ + fm->F = xstrdup(buf); \ + if (*r == '\0') \ + goto done; \ } while (0) -static fm_entry *new_fm_entry(void) +fm_entry *new_fm_entry(void) { fm_entry *fm; fm = xtalloc(1, fm_entry); fm->tfm_name = NULL; + fm->sfd_name = NULL; fm->ps_name = NULL; - fm->flags = 0; + fm->fd_flags = 4; fm->ff_name = NULL; - fm->subset_tag = NULL; - fm->encoding = NULL; - fm->tfm_num = getnullfont(); - fm->tfm_avail = TFM_UNCHECKED; + fm->encname = NULL; fm->type = 0; fm->slant = 0; fm->extend = 0; - fm->ff_objnum = 0; - fm->fn_objnum = 0; - fm->fd_objnum = 0; - fm->charset = NULL; - fm->all_glyphs = false; fm->links = 0; + fm->pid = -1; + fm->eid = -1; + fm->subfont = NULL; + fm->in_use = false; return fm; } -static void delete_fm_entry(fm_entry * fm) +void delete_fm_entry(fm_entry * fm) { xfree(fm->tfm_name); + xfree(fm->sfd_name); xfree(fm->ps_name); xfree(fm->ff_name); - xfree(fm->subset_tag); - xfree(fm->charset); xfree(fm); } @@ -130,36 +117,13 @@ static fm_entry *dummy_fm_entry() return (fm_entry *) & const_fm_entry; } -char *mk_base_tfm(char *tfmname, int *i) -{ - static char buf[SMALL_BUF_SIZE]; - char *p = tfmname, *r = strend(p) - 1, *q = r; - while (q > p && isdigit(*q)) - --q; - if (!(q > p) || q == r || (*q != '+' && *q != '-')) - return NULL; - check_buf(q - p + 1, SMALL_BUF_SIZE); - strncpy(buf, p, (unsigned) (q - p)); - buf[q - p] = 0; - *i = atoi(q); - return buf; -} - -static fmentryptr fmlookup(internalfontnumber); - -boolean hasfmentry(internalfontnumber f) -{ - if (pdffontmap[f] == NULL) - pdffontmap[f] = fmlookup(f); - assert(pdffontmap[f] != NULL); - return pdffontmap[f] != (fmentryptr) dummy_fm_entry(); -} /**********************************************************************/ struct avl_table *tfm_tree = NULL; struct avl_table *ps_tree = NULL; struct avl_table *ff_tree = NULL; +struct avl_table *encname_tree = NULL; /* AVL sort fm_entry into tfm_tree by tfm_name */ @@ -169,27 +133,17 @@ static int comp_fm_entry_tfm(const void *pa, const void *pb, void *p) ((const fm_entry *) pb)->tfm_name); } -#define cmp_return(a, b) \ - if (a > b) \ - return 1; \ - if (a < b) \ - return -1 - /* AVL sort fm_entry into ps_tree by ps_name, slant, and extend */ static int comp_fm_entry_ps(const void *pa, const void *pb, void *p) { - const fm_entry *p1 = (const fm_entry *) pa, *p2 = - (const fm_entry *) pb; int i; + const fm_entry *p1 = (const fm_entry *) pa, *p2 = (const fm_entry *) pb; assert(p1->ps_name != NULL && p2->ps_name != NULL); if ((i = strcmp(p1->ps_name, p2->ps_name)) != 0) return i; cmp_return(p1->slant, p2->slant); cmp_return(p1->extend, p2->extend); - if (p1->tfm_name != NULL && p2->tfm_name != NULL && - (i = strcmp(p1->tfm_name, p2->tfm_name)) != 0) - return i; return 0; } @@ -203,20 +157,21 @@ static int comp_ff_entry(const void *pa, const void *pb, void *p) static void create_avl_trees() { - if (tfm_tree == NULL) { - tfm_tree = avl_create(comp_fm_entry_tfm, NULL, &avl_xallocator); - assert(tfm_tree != NULL); - } - if (ps_tree == NULL) { - ps_tree = avl_create(comp_fm_entry_ps, NULL, &avl_xallocator); - assert(ps_tree != NULL); - } - if (ff_tree == NULL) { - ff_tree = avl_create(comp_ff_entry, NULL, &avl_xallocator); - assert(ff_tree != NULL); - } + assert(tfm_tree == NULL); + tfm_tree = avl_create(comp_fm_entry_tfm, NULL, &avl_xallocator); + assert(tfm_tree != NULL); + assert(ps_tree == NULL); + ps_tree = avl_create(comp_fm_entry_ps, NULL, &avl_xallocator); + assert(ps_tree != NULL); + assert(ff_tree == NULL); + ff_tree = avl_create(comp_ff_entry, NULL, &avl_xallocator); + assert(ff_tree != NULL); + assert(encname_tree == NULL); + encname_tree = avl_create(comp_string_entry, NULL, &avl_xallocator); + assert(encname_tree != NULL); } +/**********************************************************************/ /* The function avl_do_entry() is not completely symmetrical with regards to tfm_name and ps_name handling, e. g. a duplicate tfm_name gives a @@ -224,28 +179,30 @@ to tfm_name and ps_name handling, e. g. a duplicate tfm_name gives a with the original version. */ -static int avl_do_entry(fm_entry * fp, int mode) +int avl_do_entry(fm_entry * fm, int mode) { - fm_entry *p, *p2; + fm_entry *p; void *a; void **aa; - struct avl_traverser trav; /* handle tfm_name link */ - if (strcmp(fp->tfm_name, nontfm) != 0) { - p = (fm_entry *) avl_find(tfm_tree, fp); + if (strcmp(fm->tfm_name, nontfm) != 0) { + p = (fm_entry *) avl_find(tfm_tree, fm); if (p != NULL) { - if (mode == FM_DUPIGNORE) { + switch (mode) { + case FM_DUPIGNORE: pdftex_warn ("fontmap entry for `%s' already exists, duplicates ignored", - fp->tfm_name); + fm->tfm_name); goto exit; - } else { /* mode == FM_REPLACE / FM_DELETE */ - if (fontused[p->tfm_num]) { + break; + case FM_REPLACE: + case FM_DELETE: + if (fm->in_use) { pdftex_warn ("fontmap entry for `%s' has been used, replace/delete not allowed", - fp->tfm_name); + fm->tfm_name); goto exit; } a = avl_delete(tfm_tree, p); @@ -253,54 +210,70 @@ static int avl_do_entry(fm_entry * fp, int mode) unset_tfmlink(p); if (!has_pslink(p)) delete_fm_entry(p); + break; + default: + assert(0); } } if (mode != FM_DELETE) { - aa = avl_probe(tfm_tree, fp); + aa = avl_probe(tfm_tree, fm); assert(aa != NULL); - set_tfmlink(fp); + set_tfmlink(fm); } } /* handle ps_name link */ - if (fp->ps_name != NULL) { - assert(fp->tfm_name != NULL); - p = (fm_entry *) avl_find(ps_tree, fp); + if (fm->ps_name != NULL) { + p = (fm_entry *) avl_find(ps_tree, fm); if (p != NULL) { - if (mode == FM_DUPIGNORE) { - pdftex_warn - ("ps_name entry for `%s' already exists, duplicates ignored", - fp->ps_name); + switch (mode) { + case FM_DUPIGNORE: goto exit; - } else { /* mode == FM_REPLACE / FM_DELETE */ - if (fontused[p->tfm_num]) { - /* REPLACE/DELETE not allowed */ - pdftex_warn - ("fontmap entry for `%s' has been used, replace/delete not allowed", - p->tfm_name); + break; + case FM_REPLACE: + case FM_DELETE: + if (fm->in_use) goto exit; - } a = avl_delete(ps_tree, p); assert(a != NULL); unset_pslink(p); if (!has_tfmlink(p)) delete_fm_entry(p); + break; + default: + assert(0); } } - if (mode != FM_DELETE) { - aa = avl_probe(ps_tree, fp); + if (mode != FM_DELETE && is_t1fontfile(fm) && is_included(fm)) { + aa = avl_probe(ps_tree, fm); assert(aa != NULL); - set_pslink(fp); + set_pslink(fm); } } exit: - if (!has_tfmlink(fp) && !has_pslink(fp)) /* e. g. after FM_DELETE */ + if (!has_tfmlink(fm) && !has_pslink(fm)) /* e. g. after FM_DELETE */ return 1; /* deallocation of fm_entry structure required */ else return 0; } +/* add the encoding name to an AVL tree. this has nothing to do with writeenc.c */ + +char *add_encname(char *s) +{ + char *p; + void **aa; + assert(s != NULL); + assert(encname_tree != NULL); + if ((p = (char *) avl_find(encname_tree, s)) == NULL) { /* encoding name not yet registered */ + p = xstrdup(s); + aa = avl_probe(encname_tree, p); + assert(aa != NULL); + } + return p; +} + /**********************************************************************/ /* consistency check for map entry, with warn flag */ @@ -308,64 +281,79 @@ int check_fm_entry(fm_entry * fm, boolean warn) { int a = 0; assert(fm != NULL); - if (fm->ps_name != NULL) { - if (is_basefont(fm)) { - if (is_fontfile(fm) && !is_included(fm)) { - if (warn) - pdftex_warn - ("invalid entry for `%s': font file must be included or omitted for base fonts", - fm->tfm_name); - a += 1; - } - } else { /* not a base font */ - /* if no font file given, drop this entry */ - if (!is_fontfile(fm)) { - if (warn) - pdftex_warn - ("invalid entry for `%s': font file missing", - fm->tfm_name); - a += 2; - } - } + + if (is_fontfile(fm) && !is_included(fm)) { + if (warn) + pdftex_warn + ("ambiguous entry for `%s': font file present but not included, " + "will be treated as font file not present", fm->tfm_name); + xfree(fm->ff_name); + /* do not set variable |a| as this entry will be still accepted */ } + + /* if both ps_name and font file are missing, drop this entry */ + if (fm->ps_name == NULL && !is_fontfile(fm)) { + if (warn) + pdftex_warn + ("invalid entry for `%s': both ps_name and font file missing", + fm->tfm_name); + a += 1; + } + + /* TrueType fonts cannot be reencoded without subsetting */ if (is_truetype(fm) && is_reencoded(fm) && !is_subsetted(fm)) { if (warn) pdftex_warn ("invalid entry for `%s': only subsetted TrueType font can be reencoded", fm->tfm_name); - a += 4; + a += 2; } - if ((fm->slant != 0 || fm->extend != 0) && - (!is_included(fm) || is_truetype(fm))) { + + /* SlantFont and ExtendFont can be used only with Type1 fonts */ + if ((fm->slant != 0 || fm->extend != 0) + && !(is_t1fontfile(fm) && is_included(fm))) { if (warn) pdftex_warn - ("invalid entry for `%s': SlantFont/ExtendFont can be used only with embedded T1 fonts", + ("invalid entry for `%s': SlantFont/ExtendFont can be used only with embedded Type1 fonts", fm->tfm_name); - a += 8; + a += 4; } + + /* the value of SlantFont and ExtendFont must be reasonable */ if (abs(fm->slant) > 1000) { if (warn) pdftex_warn ("invalid entry for `%s': too big value of SlantFont (%g)", fm->tfm_name, fm->slant / 1000.0); - a += 16; + a += 8; } if (abs(fm->extend) > 2000) { if (warn) pdftex_warn ("invalid entry for `%s': too big value of ExtendFont (%g)", fm->tfm_name, fm->extend / 1000.0); + a += 16; + } + + /* subfonts must be used with subsetted non-reencoded TrueType fonts */ + if (fm->pid != -1 && + !(is_truetype(fm) && is_subsetted(fm) && !is_reencoded(fm))) { + if (warn) + pdftex_warn + ("invalid entry for `%s': PidEid can be used only with subsetted non-reencoded TrueType fonts", + fm->tfm_name); a += 32; } + return a; } /**********************************************************************/ /* returns true if s is one of the 14 std. font names; speed-trimmed. */ -boolean check_basefont(char *s) +boolean check_std_t1font(char *s) { - static const char *basefont_names[] = { + static const char *std_t1font_names[] = { "Courier", /* 0:7 */ "Courier-Bold", /* 1:12 */ "Courier-Oblique", /* 2:15 */ @@ -382,10 +370,11 @@ boolean check_basefont(char *s) "ZapfDingbats" /* 13:12 */ }; static const int index[] = - { -1, -1, -1, -1, -1, -1, 8, 0, -1, 4, 10, 9, -1, -1, 5, 2, 12, 6, - -1, 3, -1, 7 + { -1, -1, -1, -1, -1, -1, 8, 0, -1, 4, 10, 9, -1, -1, 5, 2, 12, 6, -1, + 3, -1, 7 }; - int n = strlen(s), k = -1; + const size_t n = strlen(s); + int k = -1; if (n > 21) return false; if (n == 12) { /* three names have length 12 */ @@ -404,43 +393,42 @@ boolean check_basefont(char *s) } } else k = index[n]; - if (k > -1 && strcmp(basefont_names[k], s) == 0) + if (k > -1 && strcmp(std_t1font_names[k], s) == 0) return true; return false; }; + /**********************************************************************/ -static void fm_scan_line(mapitem * mitem) +static void fm_scan_line() { int a, b, c, j, u = 0, v = 0; float d; - char fm_line[FM_BUF_SIZE], buf[FM_BUF_SIZE]; fm_entry *fm; + char fm_line[FM_BUF_SIZE], buf[FM_BUF_SIZE]; char *p, *q, *r, *s; - p = fm_line; switch (mitem->type) { case MAPFILE: + p = fm_line; do { c = fm_getchar(); append_char_to_buf(c, p, fm_line, FM_BUF_SIZE); } while (c != 10); + *(--p) = '\0'; + r = fm_line; break; case MAPLINE: - s = mitem->line; - while ((c = *s++) != 0) - append_char_to_buf(c, p, fm_line, FM_BUF_SIZE); + r = mitem->line; /* work on string from makecstring() */ break; default: assert(0); } - append_eol(p, fm_line, FM_BUF_SIZE); - if (is_cfg_comment(*fm_line)) + if (*r == '\0' || is_cfg_comment(*r)) return; - r = fm_line; - read_field(r, q, buf); fm = new_fm_entry(); + read_field(r, q, buf); set_field(tfm_name); p = r; read_field(r, q, buf); @@ -448,18 +436,17 @@ static void fm_scan_line(mapitem * mitem) set_field(ps_name); else r = p; /* unget the field */ - if (isdigit(*r)) { /* font flags given */ - fm->flags = atoi(r); + if (isdigit(*r)) { /* font descriptor /Flags given */ + fm->fd_flags = atoi(r); while (isdigit(*r)) r++; - } else - fm->flags = 4; /* treat as Symbol font */ + } while (1) { /* loop through "specials", encoding, font file */ skip(r, ' '); switch (*r) { - case 10: + case '\0': goto done; - case '"': /* opening quote */ + case '"': /* opening quote */ r++; u = v = 0; do { @@ -468,20 +455,18 @@ static void fm_scan_line(mapitem * mitem) s = r + j; /* jump behind number, eat also blanks, if any */ if (*(s - 1) == 'E' || *(s - 1) == 'e') s--; /* e. g. 0.5ExtendFont: %f = 0.5E */ - if (strncmp(s, "SlantFont", strlen("SlantFont")) == 0) { + if (str_prefix(s, "SlantFont")) { d *= 1000.0; /* correct rounding also for neg. numbers */ fm->slant = (integer) (d > 0 ? d + 0.5 : d - 0.5); r = s + strlen("SlantFont"); - } else - if (strncmp(s, "ExtendFont", strlen("ExtendFont")) - == 0) { + } else if (str_prefix(s, "ExtendFont")) { d *= 1000.0; fm->extend = (integer) (d > 0 ? d + 0.5 : d - 0.5); if (fm->extend == 1000) fm->extend = 0; r = s + strlen("ExtendFont"); } else { /* unknown name */ - for (r = s; *r != ' ' && *r != '"' && *r != 10; r++); /* jump over name */ + for (r = s; *r != ' ' && *r != '"' && *r != '\0'; r++); /* jump over name */ c = *r; /* remember char for temporary end of string */ *r = '\0'; pdftex_warn @@ -490,7 +475,7 @@ static void fm_scan_line(mapitem * mitem) *r = c; } } else - for (; *r != ' ' && *r != '"' && *r != 10; r++); + for (; *r != ' ' && *r != '"' && *r != '\0'; r++); } while (*r == ' '); if (*r == '"') /* closing quote */ @@ -502,7 +487,14 @@ static void fm_scan_line(mapitem * mitem) goto bad_line; } break; - default: /* encoding or font file specification */ + case 'P': /* handle cases for subfonts like 'PidEid=3,1' */ + if (sscanf(r, "PidEid=%i, %i %n", &a, &b, &c) >= 2) { + fm->pid = a; + fm->eid = b; + r += c; + break; + } + default: /* encoding or font file specification */ a = b = 0; if (*r == '<') { a = *r++; @@ -511,9 +503,8 @@ static void fm_scan_line(mapitem * mitem) } read_field(r, q, buf); /* encoding, formats: '8r.enc' or '<8r.enc' or '<[8r.enc' */ - if (strlen(buf) > 4 - && strcasecmp(strend(buf) - 4, ".enc") == 0) { - fm->encoding = add_enc(buf); + if (strlen(buf) > 4 && strcasecmp(strend(buf) - 4, ".enc") == 0) { + fm->encname = add_encname(buf); u = v = 0; /* u, v used if intervening blank: "<< foo" */ } else if (strlen(buf) > 0) { /* file name given */ /* font file, formats: @@ -536,11 +527,17 @@ static void fm_scan_line(mapitem * mitem) } } done: - if (fm->ps_name != NULL && check_basefont(fm->ps_name)) - set_basefont(fm); - if (is_fontfile(fm) - && strcasecmp(strend(fm_fontfile(fm)) - 4, ".ttf") == 0) - set_truetype(fm); + if (fm->ps_name != NULL && check_std_t1font(fm->ps_name)) + set_std_t1font(fm); + if (is_fontfile(fm)) { + if (strcasecmp(strend(fm_fontfile(fm)) - 4, ".ttf") == 0) + set_truetype(fm); + else if (strcasecmp(strend(fm_fontfile(fm)) - 4, ".otf") == 0) + set_opentype(fm); + else + set_type1(fm); + } else + set_type1(fm); /* assume a builtin font is Type1 */ if (check_fm_entry(fm, true) != 0) goto bad_line; /* @@ -548,6 +545,8 @@ static void fm_scan_line(mapitem * mitem) fm points to a valid, freshly filled-out fm_entry structure. Now follows the actual work of registering/deleting. */ + if (handle_subfont_fm(fm, mitem->mode)) /* is this a subfont? */ + return; if (avl_do_entry(fm, mitem->mode) == 0) /* if success */ return; bad_line: @@ -558,111 +557,49 @@ static void fm_scan_line(mapitem * mitem) void fm_read_info() { - mapitem *tmp; - create_avl_trees(); - while (mapitems != NULL) { - assert(mapitems->line != NULL); - mapitems->lineno = 1; - switch (mapitems->type) { - case MAPFILE: - set_cur_file_name(mapitems->line); - if (!fm_open()) { - pdftex_warn("cannot open font map file"); - } else { - cur_file_name = (char *) nameoffile + 1; - tex_printf("{%s", cur_file_name); - while (!fm_eof()) { - fm_scan_line(mapitems); - mapitems->lineno++; - } - fm_close(); - tex_printf("}"); - fm_file = NULL; + if (tfm_tree == NULL) + create_avl_trees(); + if (mitem->line == NULL) /* nothing to do */ + return; + mitem->lineno = 1; + switch (mitem->type) { + case MAPFILE: + set_cur_file_name(mitem->line); + if (!fm_open()) { + pdftex_warn("cannot open font map file"); + } else { + cur_file_name = (char *) nameoffile + 1; + tex_printf("{%s", cur_file_name); + while (!fm_eof()) { + fm_scan_line(); + mitem->lineno++; } - break; - case MAPLINE: - cur_file_name = NULL; /* makes pdftex_warn() shorter */ - fm_scan_line(mapitems); - break; - default: - assert(0); + fm_close(); + tex_printf("}"); + fm_file = NULL; } - tmp = mapitems; - mapitems = mapitems->next; - xfree(tmp->line); - xfree(tmp); + break; + case MAPLINE: + cur_file_name = NULL; /* makes pdftex_warn() shorter */ + fm_scan_line(); + break; + default: + assert(0); } + mitem->line = NULL; /* done with this line */ cur_file_name = NULL; return; } /**********************************************************************/ -/* -char *mk_exname(char *basename, int e) -{ - static char buf[SMALL_BUF_SIZE]; - char *p = basename, *r; - if ((r = strrchr(p, '.')) == NULL) - r = strend(p); - check_buf(r - p + strlen(r) + 10, SMALL_BUF_SIZE); - strncpy(buf, p, (unsigned) (r - p)); - sprintf(buf + (r - p), "%+i", e); - strcat(buf, r); - return buf; -} -*/ - -internalfontnumber tfmoffm(fmentryptr fm_pt) -{ - return ((fm_entry *) fm_pt)->tfm_num; -} - -static fm_entry *mk_ex_fm(internalfontnumber f, fm_entry * basefm, int ex) -{ - fm_entry *fm; - integer e = basefm->extend; - if (e == 0) - e = 1000; - fm = new_fm_entry(); - fm->flags = basefm->flags; - fm->encoding = basefm->encoding; - fm->type = basefm->type; - fm->slant = basefm->slant; - fm->extend = roundxnoverd(e, 1000 + ex, 1000); /* modify ExtentFont to simulate expansion */ - if (fm->extend == 1000) - fm->extend = 0; - fm->tfm_name = xstrdup(makecstring(fontname[f])); - if (basefm->ps_name != NULL) - fm->ps_name = xstrdup(basefm->ps_name); - fm->ff_name = xstrdup(basefm->ff_name); - fm->ff_objnum = pdfnewobjnum(); - fm->tfm_num = f; - fm->tfm_avail = TFM_FOUND; - assert(strcmp(fm->tfm_name, nontfm) != 0); - return fm; -} - -static void init_fm(fm_entry * fm, internalfontnumber f) -{ - if (fm->fd_objnum == 0 && !no_font_desc(fm)) - fm->fd_objnum = pdfnewobjnum(); - if (fm->ff_objnum == 0 && is_included(fm)) - fm->ff_objnum = pdfnewobjnum(); - if (fm->tfm_num == getnullfont()) { - fm->tfm_num = f; - fm->tfm_avail = TFM_FOUND; - } -} - static fmentryptr fmlookup(internalfontnumber f) { - char *tfm, *p; - fm_entry *fm, *exfm; + char *tfm; + fm_entry *fm; fm_entry tmp; - int ai, e; - if (tfm_tree == NULL || mapitems != NULL) - fm_read_info(); + if (tfm_tree == NULL) + fm_read_info(); /* only to read default map file */ tfm = makecstring(fontname[f]); assert(strcmp(tfm, nontfm) != 0); @@ -670,251 +607,70 @@ static fmentryptr fmlookup(internalfontnumber f) tmp.tfm_name = tfm; fm = (fm_entry *) avl_find(tfm_tree, &tmp); if (fm != NULL) { - init_fm(fm, f); + fm->in_use = true; return (fmentryptr) fm; } - tfm = mk_base_tfm(makecstring(fontname[f]), &e); - if (tfm == NULL) /* not an expanded font, nothing to do */ - return (fmentryptr) dummy_fm_entry(); - - tmp.tfm_name = tfm; - fm = (fm_entry *) avl_find(tfm_tree, &tmp); - if (fm != NULL) { /* found an entry with the base tfm name, e.g. cmr10 */ - if (!is_t1fontfile(fm) || !is_included(fm)) { - pdftex_warn - ("font %s cannot be expanded (not an included Type1 font)", - tfm); - return (fmentryptr) dummy_fm_entry(); - } - exfm = mk_ex_fm(f, fm, e); /* copies all fields from fm except tfm name */ - init_fm(exfm, f); - ai = avl_do_entry(exfm, FM_DUPIGNORE); - assert(ai == 0); - return (fmentryptr) exfm; - } return (fmentryptr) dummy_fm_entry(); } -/* - Early check whether a font file exists. Used e. g. for replacing fonts - of embedded PDF files: Without font file, the font within the embedded - PDF-file is used. Search tree ff_tree is used in 1st instance, as it - may be faster than the kpse_find_file(), and kpse_find_file() is called - only once per font file name + expansion parameter. This might help - keeping speed, if many PDF pages with same fonts are to be embedded. - - The ff_tree contains only font files, which are actually needed, - so this tree typically is much smaller than the tfm_tree or ps_tree. -*/ - -ff_entry *check_ff_exist(fm_entry * fm) +boolean hasfmentry(internalfontnumber f) { - ff_entry *ff; - ff_entry tmp; - char *ex_ffname; - void **aa; - - assert(fm->ff_name != NULL); - tmp.ff_name = fm->ff_name; - ff = (ff_entry *) avl_find(ff_tree, &tmp); - if (ff == NULL) { /* not yet in database */ - ff = new_ff_entry(); - ff->ff_name = xstrdup(fm->ff_name); - if (is_truetype(fm)) - ff->ff_path = - kpse_find_file(fm->ff_name, kpse_truetype_format, 0); - else - ff->ff_path = - kpse_find_file(fm->ff_name, kpse_type1_format, 0); - aa = avl_probe(ff_tree, ff); - assert(aa != NULL); - } - return ff; + if (pdffontmap[f] == NULL) + pdffontmap[f] = fmlookup(f); + assert(pdffontmap[f] != NULL); + return pdffontmap[f] != (fmentryptr) dummy_fm_entry(); } -/**********************************************************************/ -static boolean used_tfm(fm_entry * p) +/* check whether a map entry is valid for font replacement */ +static boolean fm_valid_for_font_replacement(fm_entry * fm) { - internalfontnumber f; - strnumber s; ff_entry *ff; - /* check if the font file is not a TrueType font */ - /* font replacement makes sense only for included Type1 files */ - if (is_truetype(p) || !is_included(p)) - return false; + assert(fm != NULL); + assert(is_fontfile(fm)); /* ps_tree should contain only entries with font file */ + assert(is_type1(fm)); /* ps_tree should contain only Type1 entries */ - /* check if the font file is available */ - ff = check_ff_exist(p); - if (ff->ff_path == NULL) + ff = check_ff_exist(fm->ff_name, false); + assert(ff != NULL); + if (ff->ff_path == NULL) /* ...there is no font file available */ return false; - - /* check whether this font has been used */ - if (fontused[p->tfm_num]) - return true; - assert(p->tfm_name != NULL); - - /* check whether we didn't find a loaded font yet, - * and this font has been loaded */ - if (loaded_tfm_found == NULL && strcmp(p->tfm_name, nontfm) != 0) { - s = maketexstring(p->tfm_name); - if ((f = tfmlookup(s, 0)) != getnullfont()) { - loaded_tfm_found = p; - if (pdffontmap[f] == NULL) - pdffontmap[f] = (fmentryptr) p; - if (p->tfm_num == getnullfont()) - p->tfm_num = f; - assert(p->tfm_num == f); - /* don't call flushstr() here as it has been called by tfmlookup() */ - } else - flushstr(s); - } - - /* check whether we didn't find either a loaded or a loadable font yet, - * and this font is loadable */ - if (avail_tfm_found == NULL && loaded_tfm_found == NULL && - strcmp(p->tfm_name, nontfm) != 0) { - if (p->tfm_avail == TFM_UNCHECKED) { - if (kpse_find_file(p->tfm_name, kpse_tfm_format, 0) != NULL) { - avail_tfm_found = p; - p->tfm_avail = TFM_FOUND; - } else { - p->tfm_avail = TFM_NOTFOUND; - if (not_avail_tfm_found == NULL) - not_avail_tfm_found = p; - } - } - } - - /* check whether the current entry is a entry */ - if (non_tfm_found == NULL && strcmp(p->tfm_name, nontfm) == 0) - non_tfm_found = p; - - return false; + return true; /* all tests passed */ } -/* lookup_ps_name looks for an entry with a given ps name + slant + extend; - * return NULL if not found. - * - * As there may exist several such entries, we need to select the `right' - * one. We do so by checking all such entries and return the first one that - * fulfils the following criteria (in descending priority): - * - * - the tfm has been used (some char from this font has been typeset) - * - the tfm has been loaded (but not used yet) - * - the tfm can be loaded (but not loaded yet) - * - the tfm is present in map files, but cannot be loaded. In this case a - * dummy tfm can be loaded instead, and a warning should be written out +/**********************************************************************/ +/* + * lookup fontmap by ps_name; + * used for Type1 font replacement when embedding of PDF files */ -static fm_entry *lookup_ps_name(fm_entry * fm) +fm_entry *lookup_fontmap(char *ps_name) { - fm_entry *p, *p2; - struct avl_traverser t, t2; - strnumber s; - int a; - - loaded_tfm_found = NULL; - avail_tfm_found = NULL; - non_tfm_found = NULL; - not_avail_tfm_found = NULL; - - assert(fm->tfm_name == NULL); - p = (fm_entry *) avl_t_find(&t, ps_tree, fm); - if (p == NULL) - return NULL; - t2 = t; - p2 = avl_t_prev(&t2); - - /* search forward */ - do { - if (used_tfm(p)) - return p; - p = avl_t_next(&t); - } - while (p != NULL && comp_fm_entry_ps(fm, p, NULL) == 0); - - /* search backward */ - while (p2 != NULL && comp_fm_entry_ps(fm, p2, NULL) == 0) { - if (used_tfm(p2)) - return p2; - p2 = avl_t_prev(&t2); - } - - if (loaded_tfm_found != NULL) - p = loaded_tfm_found; - else if (avail_tfm_found != NULL) { - p = avail_tfm_found; - p->tfm_num = readfontinfo(getnullcs(), maketexstring(p->tfm_name), - getnullstr(), -1000); - p->tfm_avail = TFM_FOUND; - } else if (non_tfm_found != NULL) { - p = non_tfm_found; - p->tfm_num = newdummyfont(); - p->tfm_avail = TFM_FOUND; - } else if (not_avail_tfm_found != NULL) { - p = not_avail_tfm_found; - pdftex_warn("`%s' not loadable, use a dummy tfm instead", - p->tfm_name); - p2 = new_fm_entry(); - p2->flags = p->flags; - p2->encoding = p->encoding; - p2->type = p->type; - p2->slant = p->slant; - p2->extend = p->extend; - p2->tfm_name = xstrdup(nontfm); - p2->ps_name = xstrdup(p->ps_name); - if (p->ff_name != NULL) - p2->ff_name = xstrdup(p->ff_name); - p2->tfm_num = newdummyfont(); - p2->tfm_avail = TFM_FOUND; - a = avl_do_entry(p2, FM_DUPIGNORE); - assert(a == 0); - p = p2; - } else - return NULL; - assert(p->tfm_num != getnullfont()); - return p; -} - -/* Lookup fontmap for /BaseFont entries of embedded PDF-files; - * return dummy_fm_entry() if not found */ - -fm_entry *lookup_fontmap(char *bname) -{ - fm_entry *fm, *fmx; - fm_entry tmp, tmpx; - ff_entry *ff; - char buf[SMALL_BUF_SIZE]; + fm_entry *fm, *fm2, tmp; char *a, *b, *c, *d, *e, *s; - strnumber str; - int i, sl, ex, ai; - if (tfm_tree == NULL || mapitems != NULL) - fm_read_info(); - if (bname == NULL) - return NULL; - if (strlen(bname) >= SMALL_BUF_SIZE) - pdftex_fail("Font name too long: `%s'", bname); - strcpy(buf, bname); /* keep bname untouched for later */ - s = buf; - if (strlen(buf) > 7) { /* check for subsetted name tag */ + int i, sl, ex; + struct avl_traverser t, t2; + if (tfm_tree == NULL) + fm_read_info(); /* only to read default map file */ + assert(ps_name != NULL); + s = ps_name; + if (strlen(ps_name) > 7) { /* check for subsetted name tag */ for (i = 0; i < 6; i++, s++) if (*s < 'A' || *s > 'Z') break; if (i == 6 && *s == '+') s++; /* if name tag found, skip behind it */ else - s = buf; + s = ps_name; } /* - Scan -Slant_ and -Extend_ font name extensions; - three valid formats: - -Slant_ - -Slant_-Extend_ - -Extend_ - Slant entry must come _before_ Extend entry + * Scan -Slant_ and -Extend_ font name extensions; + * three valid formats: + * -Slant_ + * -Slant_-Extend_ + * -Extend_ + * Slant entry must come _before_ Extend entry */ tmp.slant = 0; @@ -924,7 +680,7 @@ fm_entry *lookup_fontmap(char *bname) sl = (int) strtol(b, &e, 10); if ((e != b) && (e == strend(b))) { tmp.slant = sl; - *a = 0; /* bname string ends before "-Slant_" */ + *a = '\0'; /* ps_name string ends before "-Slant_" */ } else { if (e != b) { /* only if is valid number */ if ((c = strstr(e, "-Extend_")) != NULL) { @@ -933,7 +689,7 @@ fm_entry *lookup_fontmap(char *bname) if ((e != d) && (e == strend(d))) { tmp.slant = sl; tmp.extend = ex; - *a = 0; /* bname string ends before "-Slant_" */ + *a = '\0'; /* ps_name string ends before "-Slant_" */ } } } @@ -944,166 +700,148 @@ fm_entry *lookup_fontmap(char *bname) ex = (int) strtol(b, &e, 10); if ((e != b) && (e == strend(b))) { tmp.extend = ex; - *a = 0; /* bname string ends before "-Extend_" */ + *a = '\0'; /* ps_name string ends before "-Extend_" */ } } } tmp.ps_name = s; - tmp.tfm_name = NULL; - fm = lookup_ps_name(&tmp); - if (fm != NULL) { - if (!(is_type1(fm) && is_included(fm))) { - pdftex_warn - ("PS name '%s' is found but ignored (not an included Type1 font)", - tmp.ps_name); - return NULL; - } - i = fm->tfm_num; - assert(i != getnullfont()); - if (pdffontmap[i] == NULL) - pdffontmap[i] = (fmentryptr) fm; - if (fm->ff_objnum == 0 && is_included(fm)) - fm->ff_objnum = pdfnewobjnum(); - if (!fontused[i]) - pdfinitfont(i); - return fm; - } -/* - The following code snipplet handles fonts with "Slant" and "Extend" - name extensions in embedded PDF files, which don't yet have an - fm_entry. If such a font is found (e. g. CMR10-Extend_1020), and no - fm_entry for this is found in the ps_tree (e. g. ps_name = "CMR10", - extend = 1020), and if an unextended base font (e. g. CMR10) is found, - a new fm_entry is created and put into the ps_tree. Then - the lookup_fontmap() function is (recursively) called again, which - then should find the new fm_entry. The same can be done manually by - a map entry e. g.: - - \pdfmapline{+ CMR10 "1.02 ExtendFont" ff_path == NULL) - return NULL; - fmx = new_fm_entry(); - fmx->flags = fm->flags; - fmx->encoding = fm->encoding; - fmx->type = fm->type; - fmx->slant = tmp.slant; - fmx->extend = tmp.extend; - fmx->tfm_name = xstrdup(nontfm); - fmx->ps_name = xstrdup(s); - fmx->ff_name = xstrdup(fm->ff_name); - ai = avl_do_entry(fmx, FM_DUPIGNORE); - assert(ai == 0); - fm = lookup_fontmap(bname); /* new try */ - assert(fm != NULL); - return fm; + fm = (fm_entry *) avl_t_find(&t, ps_tree, &tmp); + if (fm == NULL) + return NULL; /* no entry found */ + + /* at this point we know there is at least one fm_entry with given ps_name; + * we test all such entries and return the first one that is valid for font + * replacement */ + + t2 = t; + fm2 = (fm_entry *) avl_t_prev(&t2); + + /* search forward */ + do { + if (fm_valid_for_font_replacement(fm)) + return fm; + fm = (fm_entry *) avl_t_next(&t); + } while (fm != NULL && comp_fm_entry_ps(fm, &tmp, NULL) == 0); + + /* search backward */ + while (fm2 != NULL && comp_fm_entry_ps(fm2, &tmp, NULL) == 0) { + if (fm_valid_for_font_replacement(fm2)) + return fm2; + fm2 = (fm_entry *) avl_t_prev(&t2); } + return NULL; } /**********************************************************************/ /* -Add map file name or map line contents to the linked list "mapitems". -Items not beginning with [+-=] flush list with pending items. Leading -blanks and blanks immediately following [+-=] are ignored. -*/ + * Process map file given by its name or map line contents. Items not + * beginning with [+-=] flush default map file, if it has not yet been + * read. Leading blanks and blanks immediately following [+-=] are + * ignored. + */ -char *add_map_item(char *s, int type) +void process_map_item(char *s, int type) { char *p; - int l; /* length of map item (without [+-=]) */ - mapitem *tmp; int mode; - for (; *s == ' '; s++); /* ignore leading blanks */ + if (*s == ' ') + s++; /* ignore leading blank */ switch (*s) { - case '+': /* +mapfile.map, +mapline */ + case '+': /* +mapfile.map, +mapline */ mode = FM_DUPIGNORE; /* insert entry, if it is not duplicate */ s++; break; - case '=': /* =mapfile.map, =mapline */ + case '=': /* =mapfile.map, =mapline */ mode = FM_REPLACE; /* try to replace earlier entry */ s++; break; - case '-': /* -mapfile.map, -mapline */ + case '-': /* -mapfile.map, -mapline */ mode = FM_DELETE; /* try to delete entry */ s++; break; default: - mode = FM_DUPIGNORE; /* also flush pending list */ - while (mapitems != NULL) { - tmp = mapitems; - mapitems = mapitems->next; - xfree(tmp->line); - xfree(tmp); - } + mode = FM_DUPIGNORE; /* like +, but also: */ + mitem->line = NULL; /* flush default map file name */ } - for (; *s == ' '; s++); /* ignore blanks after [+-=] */ + if (*s == ' ') + s++; /* ignore blank after [+-=] */ p = s; /* map item starts here */ - switch (type) { /* find end of map item */ - case MAPFILE: - for (; *p != 0 && *p != ' ' && *p != 10; p++); + switch (type) { + case MAPFILE: /* remove blank at end */ + while (*p != '\0' && *p != ' ') + p++; + *p = '\0'; break; - case MAPLINE: /* blanks allowed */ - for (; *p != 0 && *p != 10; p++); + case MAPLINE: /* blank at end allowed */ break; default: assert(0); } - l = p - s; - if (l > 0) { /* only if real item to add */ - tmp = xtalloc(1, mapitem); - if (mapitems == NULL) - mapitems = tmp; /* start new list */ - else - miptr->next = tmp; - miptr = tmp; - miptr->mode = mode; - miptr->type = type; - miptr->line = xtalloc(l + 1, char); - *(miptr->line) = 0; - strncat(miptr->line, s, l); - miptr->next = NULL; + if (mitem->line != NULL) /* read default map file first */ + fm_read_info(); + if (*s != '\0') { /* only if real item to process */ + mitem->mode = mode; + mitem->type = type; + mitem->line = s; + fm_read_info(); } - return p; } void pdfmapfile(integer t) { - add_map_item(makecstring(tokenstostring(t)), MAPFILE); + process_map_item(makecstring(tokenstostring(t)), MAPFILE); flushstr(lasttokensstring); } void pdfmapline(integer t) { - add_map_item(makecstring(tokenstostring(t)), MAPLINE); + process_map_item(makecstring(tokenstostring(t)), MAPLINE); flushstr(lasttokensstring); } void pdfinitmapfile(string map_name) { - if (mapitems == NULL) { - mapitems = xtalloc(1, mapitem); - miptr = mapitems; - miptr->mode = FM_DUPIGNORE; - miptr->type = MAPFILE; - miptr->line = xstrdup(map_name); - miptr->next = NULL; + assert(mitem == NULL); + mitem = xtalloc(1, mapitem); + mitem->mode = FM_DUPIGNORE; + mitem->type = MAPFILE; + mitem->line = xstrdup(map_name); +} + +/**********************************************************************/ +/* + * Early check whether a font file exists. Search tree ff_tree is used + * in 1st instance, as it may be faster than the kpse_find_file(), and + * kpse_find_file() is called only once per font file name + expansion + * parameter. This might help keeping speed, if many PDF pages with + * same fonts are to be embedded. + * + * The ff_tree contains only font files, which are actually needed, + * so this tree typically is much smaller than the tfm_tree or ps_tree. + */ + +ff_entry *check_ff_exist(char *ff_name, boolean is_tt) +{ + ff_entry *ff; + ff_entry tmp; + void **aa; + + assert(ff_name != NULL); + tmp.ff_name = ff_name; + ff = (ff_entry *) avl_find(ff_tree, &tmp); + if (ff == NULL) { /* not yet in database */ + ff = new_ff_entry(); + ff->ff_name = xstrdup(ff_name); + if (is_tt) + ff->ff_path = kpse_find_file(ff_name, kpse_truetype_format, 0); + else + ff->ff_path = kpse_find_file(ff_name, kpse_type1_format, 0); + aa = avl_probe(ff_tree, ff); + assert(aa != NULL); } + return ff; } /**********************************************************************/ @@ -1138,12 +876,18 @@ static void destroy_ff_entry(void *pa, void *pb) void fm_free(void) { - if (tfm_tree != NULL) + if (tfm_tree != NULL) { avl_destroy(tfm_tree, destroy_fm_entry_tfm); - if (ps_tree != NULL) + tfm_tree = NULL; + } + if (ps_tree != NULL) { avl_destroy(ps_tree, destroy_fm_entry_ps); - if (ff_tree != NULL) + ps_tree = NULL; + } + if (ff_tree != NULL) { avl_destroy(ff_tree, destroy_ff_entry); + ff_tree = NULL; + } } /**********************************************************************/ diff --git a/Build/source/texk/web2c/pdftexdir/misc.ch b/Build/source/texk/web2c/pdftexdir/misc.ch deleted file mode 100644 index 0d69f25597c..00000000000 --- a/Build/source/texk/web2c/pdftexdir/misc.ch +++ /dev/null @@ -1,21 +0,0 @@ -% WEB change file containing miscellany extensions for pdfTeX -% -% Copyright (c) 1996-2002 Han Th\^e\llap{\raise 0.5ex\hbox{\'{}}} Th\`anh, -% -% This file is part of pdfTeX. -% -% pdfTeX is free software; you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation; either version 2 of the License, or -% (at your option) any later version. -% -% pdfTeX is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with pdfTeX; if not, write to the Free Software -% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -% -% $Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/misc.ch#5 $ diff --git a/Build/source/texk/web2c/pdftexdir/noligatures.ch b/Build/source/texk/web2c/pdftexdir/noligatures.ch deleted file mode 100644 index 2a8ef91f4a1..00000000000 --- a/Build/source/texk/web2c/pdftexdir/noligatures.ch +++ /dev/null @@ -1,116 +0,0 @@ -% Copyright (c) 2005 Han Th\^e\llap{\raise 0.5ex\hbox{\'{}}} Th\`anh, -% -% This file is part of pdfTeX. -% -% pdfTeX is free software; you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation; either version 2 of the License, or -% (at your option) any later version. -% -% pdfTeX is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with pdfTeX; if not, write to the Free Software -% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -% -% $Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/pdftex.ch#163 $ -@ This change file creates an extra primitive named \.{\\pdfnoligatures}, allowing -you to destroy all ligatures of a loaded font. The command code is -|assign_font_int|, with the new modifier value |no_lig_code|. - -Nothing other than the |char_tag| value is changed, and nothing is saved. -The primitive works directly on |font_info|, so all changes are global, -the same as for the other font assignment primitives. Usage is quite -straightforward: \.{\\pdfnoligatures \\f } disables ligatures for font \.{\\f}. -The expansion of \.{\\pdfnoligatures \\f } is either |1|, when the font has -no ligatures, or |0|, when the font does have ligatures. - -Thanh: this patch uses |orig_char_info| because it is to be inserted -after tex.pch. If you merge this into pdftex.ch, you may want to change -back to |char_info|. It would not compile without using the -|orig| trick because |effective_char| is not yet known at the spot -where |test_no_ligatures| is used. sigh. - -@x [230] -@d ef_code_base == 4 -@y -@d ef_code_base == 4 -@d no_lig_code == 6 {|tag_code| is 5 } -@z - -@x [230] -begin scan_font_ident; -if m=0 then scanned_result(hyphen_char[cur_val])(int_val) -else if m=1 then scanned_result(skew_char[cur_val])(int_val) -@y -begin scan_font_ident; -if m=0 then scanned_result(hyphen_char[cur_val])(int_val) -else if m=1 then scanned_result(skew_char[cur_val])(int_val) -else if m=no_lig_code then scanned_result(test_no_ligatures(cur_val))(int_val) -@z - -@x [577] -@= -function get_tag_code(f: internal_font_number; c: eight_bits): integer; -@y -@= -function test_no_ligatures(f: internal_font_number): integer; -label exit; -var c:integer; -begin - test_no_ligatures:= 1; - for c := font_bc[f] to font_ec[f] do - if char_exists(orig_char_info(f)(c)) then - if odd(char_tag(orig_char_info(f)(c))) then begin - test_no_ligatures:= 0; - return; - end; - exit: -end; - -function get_tag_code(f: internal_font_number; c: eight_bits): integer; -@z - -@x [???] -function init_font_base(v: integer): integer; -@y -procedure set_no_ligatures(f: internal_font_number); -var c:integer; -begin - for c := font_bc[f] to font_ec[f] do - if char_exists(orig_char_info(f)(c)) then - if char_tag(orig_char_info(f)(c))=lig_tag then - op_byte(orig_char_info(f)(c)) := (op_byte(orig_char_info(f)(c))) - lig_tag; -end; - -function init_font_base(v: integer): integer; -@z - -@x [1253] -assign_font_int: begin n:=cur_chr; scan_font_ident; f:=cur_val; - if n < lp_code_base then begin -@y -assign_font_int: begin n:=cur_chr; scan_font_ident; f:=cur_val; - if n = no_lig_code then set_no_ligatures(f) - else if n < lp_code_base then begin -@z - -@x [1254] -primitive("efcode",assign_font_int,ef_code_base); -@!@:ef_code_}{\.{\\efcode} primitive@> -@y -primitive("efcode",assign_font_int,ef_code_base); -@!@:ef_code_}{\.{\\efcode} primitive@> -primitive("pdfnoligatures",assign_font_int,no_lig_code); -@!@:no_lig_code_}{\.{\\pdfnoligatures} primitive@> -@z - -@x [1255] -ef_code_base: print_esc("efcode"); -@y -ef_code_base: print_esc("efcode"); -no_lig_code: print_esc("pdfnoligatures"); -@z diff --git a/Build/source/texk/web2c/pdftexdir/pdfstrcmp.ch b/Build/source/texk/web2c/pdftexdir/pdfstrcmp.ch deleted file mode 100644 index 071ba74f281..00000000000 --- a/Build/source/texk/web2c/pdftexdir/pdfstrcmp.ch +++ /dev/null @@ -1,609 +0,0 @@ -% Copyright (c) 2005 Han Th\^e\llap{\raise 0.5ex\hbox{\'{}}} Th\`anh, -% -% This file is part of pdfTeX. -% -% pdfTeX is free software; you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation; either version 2 of the License, or -% (at your option) any later version. -% -% pdfTeX is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with pdfTeX; if not, write to the Free Software -% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -% -% $Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/pdftex.ch#163 $ -% -% experimental patch to make \pdfstrcmp expandable -% -% put it after tex.pch -% -% x line numbers of file pdfetex-org.web before patch -% -%*********************************************************************** - -@x 9388 -@d pdftex_last_item_codes = pdftex_first_rint_code + 10 {end of \pdfTeX's command codes} -@y -@d pdf_shell_escape_code = pdftex_first_rint_code + 11 {code for \.{\\pdfshellescape}} -@d pdftex_last_item_codes = pdftex_first_rint_code + 11 {end of \pdfTeX's command codes} -@z - -@x 9442 -primitive("pdfelapsedtime",last_item,elapsed_time_code); -@!@:elapsed_time_}{\.{\\pdfelapsedtime} primitive@> -@y -primitive("pdfelapsedtime",last_item,elapsed_time_code); -@!@:elapsed_time_}{\.{\\pdfelapsedtime} primitive@> -primitive("pdfshellescape",last_item,pdf_shell_escape_code); -@!@:elapsed_time_}{\.{\\pdfshellescape} primitive@> -@z - -@x 9470 - elapsed_time_code: print_esc("pdfelapsedtime"); -@y - elapsed_time_code: print_esc("pdfelapsedtime"); - pdf_shell_escape_code: print_esc("pdfshellescape"); -@z - -@x 9561 - elapsed_time_code: cur_val := get_microinterval; -@y - elapsed_time_code: cur_val := get_microinterval; - pdf_shell_escape_code: - begin - if shell_enabled_p then cur_val := 1 - else cur_val := 0; - end; -@z - -@x 10332 -@d pdf_last_escaped_string_code = pdftex_first_expand_code + 7 {command code for \.{\\pdflastescapedstring}} -@d pdf_last_escaped_name_code = pdftex_first_expand_code + 8 {command code for \.{\\pdflastescapename}} -@y -@d pdf_escape_string_code = pdftex_first_expand_code + 7 {command code for \.{\\pdfescapestring}} -@d pdf_escape_name_code = pdftex_first_expand_code + 8 {command code for \.{\\pdfescapename}} -@z - -@x 10336 -@d pdftex_convert_codes = pdftex_first_expand_code + 11 {end of \pdfTeX's command codes} -@y -@d pdf_strcmp_code = pdftex_first_expand_code + 11 {command code for \.{\\pdfstrcmp}} -@d pdf_escape_hex_code = pdftex_first_expand_code + 12 {command code for \.{\\pdfescapehex}} -@d pdf_unescape_hex_code = pdftex_first_expand_code + 13 {command code for \.{\\pdfunescapehex}} -@d pdf_creation_date_code = pdftex_first_expand_code + 14 {command code for \.{\\pdfcreationdate}} -@d pdf_file_mod_date_code = pdftex_first_expand_code + 15 {command code for \.{\\pdffilemoddate}} -@d pdf_file_size_code = pdftex_first_expand_code + 16 {command code for \.{\\pdffilesize}} -@d pdf_mdfive_sum_code = pdftex_first_expand_code + 17 {command code for \.{\\pdfmdfivesum}} -@d pdf_file_dump_code = pdftex_first_expand_code + 18 {command code for \.{\\pdffiledump}} -@d pdf_match_code = pdftex_first_expand_code + 19 {command code for \.{\\pdfmatch}} -@d pdf_last_match_code = pdftex_first_expand_code + 20 {command code for \.{\\pdflastmatch}} -@d pdftex_convert_codes = pdftex_first_expand_code + 21 {end of \pdfTeX's command codes} -@z - -%*********************************************************************** - -@x 10369 -primitive("pdflastescapedstring",convert,pdf_last_escaped_string_code);@/ -@!@:pdf_last_escaped_string_}{\.{\\pdflastescapedstring} primitive@> -primitive("pdflastescapedname",convert,pdf_last_escaped_name_code);@/ -@!@:pdf_last_escaped_name_}{\.{\\pdflastescapedname} primitive@> -@y -primitive("pdfescapestring",convert,pdf_escape_string_code);@/ -@!@:pdf_escape_string_}{\.{\\pdfescapestring} primitive@> -primitive("pdfescapename",convert,pdf_escape_name_code);@/ -@!@:pdf_escape_name_}{\.{\\pdfescapename} primitive@> -primitive("pdfescapehex",convert,pdf_escape_hex_code);@/ -@!@:pdf_escape_hex_}{\.{\\pdfescapehex} primitive@> -primitive("pdfunescapehex",convert,pdf_unescape_hex_code);@/ -@!@:pdf_unescape_hex_}{\.{\\pdfunescapehex} primitive@> -primitive("pdfcreationdate",convert,pdf_creation_date_code);@/ -@!@:pdf_creation_date_}{\.{\\pdfcreationdate} primitive@> -primitive("pdffilemoddate",convert,pdf_file_mod_date_code);@/ -@!@:pdf_file_mod_date_}{\.{\\pdffilemoddate} primitive@> -primitive("pdffilesize",convert,pdf_file_size_code);@/ -@!@:pdf_file_size_}{\.{\\pdffilesize} primitive@> -primitive("pdfmdfivesum",convert,pdf_mdfive_sum_code);@/ -@!@:pdf_mdfive_sum_}{\.{\\pdfmdfivesum} primitive@> -primitive("pdffiledump",convert,pdf_file_dump_code);@/ -@!@:pdf_file_dump_}{\.{\\pdffiledump} primitive@> -primitive("pdfmatch",convert,pdf_match_code);@/ -@!@:pdf_match_}{\.{\\pdfmatch} primitive@> -primitive("pdflastmatch",convert,pdf_last_match_code);@/ -@!@:pdf_last_match_}{\.{\\pdflastmatch} primitive@> -primitive("pdfstrcmp",convert,pdf_strcmp_code);@/ -@!@:pdf_strcmp_}{\.{\\pdfstrcmp} primitive@> -@z - -%*********************************************************************** - -@x 10394 - pdf_last_escaped_string_code: print_esc("pdflastescapedstring"); - pdf_last_escaped_name_code: print_esc("pdflastescapedname"); -@y - pdf_escape_string_code: print_esc("pdfescapestring"); - pdf_escape_name_code: print_esc("pdfescapename"); - pdf_escape_hex_code: print_esc("pdfescapehex"); - pdf_unescape_hex_code: print_esc("pdfunescapehex"); - pdf_creation_date_code: print_esc("pdfcreationdate"); - pdf_file_mod_date_code: print_esc("pdffilemoddate"); - pdf_file_size_code: print_esc("pdffilesize"); - pdf_mdfive_sum_code: print_esc("pdfmdfivesum"); - pdf_file_dump_code: print_esc("pdffiledump"); - pdf_match_code: print_esc("pdfmatch"); - pdf_last_match_code: print_esc("pdflastmatch"); - pdf_strcmp_code: print_esc("pdfstrcmp"); -@z - -%*********************************************************************** - -@x 10403 -@p procedure conv_toks; -var old_setting:0..max_selector; {holds |selector| setting} -p, q: pointer; -@!c:number_code..job_name_code; {desired type of conversion} -@!save_scanner_status:small_number; {|scanner_status| upon entry} -@!b:pool_pointer; {base of temporary string} -@!i, l: integer; {index to access escaped string or name} -begin c:=cur_chr; @; -old_setting:=selector; selector:=new_string; b:=pool_ptr; -@; -selector:=old_setting; link(garbage):=str_toks(b); ins_list(link(temp_head)); -end; -@y -@p procedure conv_toks; -label exit; -var old_setting:0..max_selector; {holds |selector| setting} -p, q: pointer; -@!c:number_code..job_name_code; {desired type of conversion} -@!save_scanner_status:small_number; {|scanner_status| upon entry} -@!save_def_ref: pointer; {|def_ref| upon entry, important if inside `\.{\\message}'} -@!save_warning_index: pointer; -@!bool: boolean; {temp boolean} -@!i: integer; {first temp integer} -@!j: integer; {second temp integer} -@!b:pool_pointer; {base of temporary string} -@!s: str_number; {first temp string} -@!t: str_number; {second temp string} -begin c:=cur_chr; @; -old_setting:=selector; selector:=new_string; b:=pool_ptr; -@; -selector:=old_setting; link(garbage):=str_toks(b); ins_list(link(temp_head)); -exit:end; -@z - -@x 10450 -pdf_last_escaped_string_code: do_nothing; -pdf_last_escaped_name_code: do_nothing; -job_name_code: if job_name=0 then open_log_file; -@y -pdf_escape_string_code: - begin - save_scanner_status := scanner_status; - save_warning_index := warning_index; - save_def_ref := def_ref; - scan_pdf_ext_toks; - s := tokens_to_string(def_ref); - delete_token_ref(def_ref); - def_ref := save_def_ref; - warning_index := save_warning_index; - scanner_status := save_scanner_status; - b := pool_ptr; - escapestring(str_start[s]); - link(garbage) := str_toks(b); - flush_str(s); - ins_list(link(temp_head)); - return; - end; -pdf_escape_name_code: - begin - save_scanner_status := scanner_status; - save_warning_index := warning_index; - save_def_ref := def_ref; - scan_pdf_ext_toks; - s := tokens_to_string(def_ref); - delete_token_ref(def_ref); - def_ref := save_def_ref; - warning_index := save_warning_index; - scanner_status := save_scanner_status; - b := pool_ptr; - escapename(str_start[s]); - link(garbage) := str_toks(b); - flush_str(s); - ins_list(link(temp_head)); - return; - end; -pdf_escape_hex_code: - begin - save_scanner_status := scanner_status; - save_warning_index := warning_index; - save_def_ref := def_ref; - scan_pdf_ext_toks; - s := tokens_to_string(def_ref); - delete_token_ref(def_ref); - def_ref := save_def_ref; - warning_index := save_warning_index; - scanner_status := save_scanner_status; - b := pool_ptr; - escapehex(str_start[s]); - link(garbage) := str_toks(b); - flush_str(s); - ins_list(link(temp_head)); - return; - end; -pdf_unescape_hex_code: - begin - save_scanner_status := scanner_status; - save_warning_index := warning_index; - save_def_ref := def_ref; - scan_pdf_ext_toks; - s := tokens_to_string(def_ref); - delete_token_ref(def_ref); - def_ref := save_def_ref; - warning_index := save_warning_index; - scanner_status := save_scanner_status; - b := pool_ptr; - unescapehex(str_start[s]); - link(garbage) := str_toks(b); - flush_str(s); - ins_list(link(temp_head)); - return; - end; -pdf_creation_date_code: - begin - b := pool_ptr; - getcreationdate; - link(garbage) := str_toks(b); - ins_list(link(temp_head)); - return; - end; -pdf_file_mod_date_code: - begin - save_scanner_status := scanner_status; - save_warning_index := warning_index; - save_def_ref := def_ref; - scan_pdf_ext_toks; - s := tokens_to_string(def_ref); - delete_token_ref(def_ref); - def_ref := save_def_ref; - warning_index := save_warning_index; - scanner_status := save_scanner_status; - b := pool_ptr; - getfilemoddate(s); - link(garbage) := str_toks(b); - flush_str(s); - ins_list(link(temp_head)); - return; - end; -pdf_file_size_code: - begin - save_scanner_status := scanner_status; - save_warning_index := warning_index; - save_def_ref := def_ref; - scan_pdf_ext_toks; - s := tokens_to_string(def_ref); - delete_token_ref(def_ref); - def_ref := save_def_ref; - warning_index := save_warning_index; - scanner_status := save_scanner_status; - b := pool_ptr; - getfilesize(s); - link(garbage) := str_toks(b); - flush_str(s); - ins_list(link(temp_head)); - return; - end; -pdf_mdfive_sum_code: - begin - save_scanner_status := scanner_status; - save_warning_index := warning_index; - save_def_ref := def_ref; - bool := scan_keyword("file"); - scan_pdf_ext_toks; - s := tokens_to_string(def_ref); - delete_token_ref(def_ref); - def_ref := save_def_ref; - warning_index := save_warning_index; - scanner_status := save_scanner_status; - b := pool_ptr; - getmd5sum(s, bool); - link(garbage) := str_toks(b); - flush_str(s); - ins_list(link(temp_head)); - return; - end; -pdf_file_dump_code: - begin - save_scanner_status := scanner_status; - save_warning_index := warning_index; - save_def_ref := def_ref; - {scan offset} - cur_val := 0; - if (scan_keyword("offset")) then begin - scan_int; - if (cur_val < 0) then begin - print_err("Bad file offset"); -@.Bad file offset@> - help2("A file offset must be between 0 and 2^{31}-1,")@/ - ("I changed this one to zero."); - int_error(cur_val); - cur_val := 0; - end; - end; - i := cur_val; - {scan length} - cur_val := 0; - if (scan_keyword("length")) then begin - scan_int; - if (cur_val < 0) then begin - print_err("Bad dump length"); -@.Bad dump length@> - help2("A dump length must be between 0 and 2^{31}-1,")@/ - ("I changed this one to zero."); - int_error(cur_val); - cur_val := 0; - end; - end; - j := cur_val; - {scan file name} - scan_pdf_ext_toks; - s := tokens_to_string(def_ref); - delete_token_ref(def_ref); - def_ref := save_def_ref; - warning_index := save_warning_index; - scanner_status := save_scanner_status; - b := pool_ptr; - getfiledump(s, i, j); - link(garbage) := str_toks(b); - flush_str(s); - ins_list(link(temp_head)); - return; - end; -pdf_match_code: - begin - save_scanner_status := scanner_status; - save_warning_index := warning_index; - save_def_ref := def_ref; - {scan for icase} - bool := scan_keyword("icase"); - {scan for subcount} - i := -1; {default for subcount} - if scan_keyword("subcount") then begin - scan_int; - i := cur_val; - end; - scan_pdf_ext_toks; - s := tokens_to_string(def_ref); - delete_token_ref(def_ref); - scan_pdf_ext_toks; - t := tokens_to_string(def_ref); - delete_token_ref(def_ref); - def_ref := save_def_ref; - warning_index := save_warning_index; - scanner_status := save_scanner_status; - b := pool_ptr; - matchstrings(s, t, i, bool); - link(garbage) := str_toks(b); - flush_str(t); - flush_str(s); - ins_list(link(temp_head)); - return; - end; -pdf_last_match_code: - begin - scan_int; - if cur_val < 0 then begin - print_err("Bad match number"); -@.Bad match number@> - help2("Since I expected zero or a positive number,")@/ - ("I changed this one to zero."); - int_error(cur_val); - cur_val := 0; - end; - b := pool_ptr; - getmatch(cur_val); - link(garbage) := str_toks(b); - ins_list(link(temp_head)); - return; - end; -pdf_strcmp_code: - begin - save_scanner_status := scanner_status; - save_warning_index := warning_index; - save_def_ref := def_ref; - compare_strings; - def_ref := save_def_ref; - warning_index := save_warning_index; - scanner_status := save_scanner_status; - end; -job_name_code: if job_name=0 then open_log_file; -@z - -%*********************************************************************** - -@x 10512 -pdf_last_escaped_string_code: begin - l := escapedstrlen; - for i := 0 to l - 1 do - print_char(getescapedstrchar(i)); -end; -pdf_last_escaped_name_code: begin - l := escapednamelen; - for i := 0 to l - 1 do - print_char(getescapednamechar(i)); -end; -job_name_code: print(job_name); -@y -pdf_strcmp_code: print_int(cur_val); -job_name_code: print(job_name); -@z - -%*********************************************************************** - -@x 32168 -@d pdf_strcmp_code == pdftex_first_extension_code + 24 -@d pdf_escape_string_code == pdftex_first_extension_code + 25 -@d pdf_escape_name_code == pdftex_first_extension_code + 26 -@d reset_timer_code == pdftex_first_extension_code + 27 -@d pdf_font_expand_code == pdftex_first_extension_code + 28 -@d pdftex_last_extension_code == pdftex_first_extension_code + 28 -@y -@d reset_timer_code == pdftex_first_extension_code + 24 -@d pdf_font_expand_code == pdftex_first_extension_code + 25 -@d pdftex_last_extension_code == pdftex_first_extension_code + 25 -@z - -%*********************************************************************** - -@x 32239 -primitive("pdfstrcmp",extension,pdf_strcmp_code);@/ -@!@:pdf_strcmp_}{\.{\\pdfstrcmp} primitive@> -primitive("pdfescapestring",extension,pdf_escape_string_code);@/ -@!@:pdf_escape_string_}{\.{\\pdfescapestring} primitive@> -primitive("pdfescapename",extension,pdf_escape_name_code);@/ -@!@:pdf_escape_name_}{\.{\\pdfescapename} primitive@> -@y -@z - -%*********************************************************************** - -@x 32287 - pdf_strcmp_code: print_esc("pdfstrcmp"); - pdf_escape_string_code: print_esc("pdfescapestring"); - pdf_escape_name_code: print_esc("pdfescapename"); -@y -@z - -%*********************************************************************** - -@x 32337 -pdf_strcmp_code: @; -pdf_escape_string_code: @; -pdf_escape_name_code: @; -@y -@z - -%*********************************************************************** - -@x 32463 -procedure compare_strings; {to implement \.{\\pdfstrcmp}} -label done; -var s1, s2: str_number; - i1, i2, j1, j2: pool_pointer; - result: integer; -begin - call_func(scan_toks(false, true)); - s1 := tokens_to_string(def_ref); - delete_token_ref(def_ref); - call_func(scan_toks(false, true)); - s2 := tokens_to_string(def_ref); - delete_token_ref(def_ref); - i1 := str_start[s1]; - j1 := str_start[s1 + 1]; - i2 := str_start[s2]; - j2 := str_start[s2 + 1]; - while (i1 < j1) and (i2 < j2) do begin - if str_pool[i1] < str_pool[i2] then begin - result := -1; - goto done; - end; - if str_pool[i1] > str_pool[i2] then begin - result := 1; - goto done; - end; - incr(i1); - incr(i2); - end; - if (i1 = j1) and (i2 = j2) then - result := 0 - else if i1 < j1 then - result := 1 - else - result := -1; -done: - pdf_retval := result; - flush_str(s2); - flush_str(s1); -end; -@y -procedure compare_strings; {to implement \.{\\pdfstrcmp}} -label done; -var s1, s2: str_number; - i1, i2, j1, j2: pool_pointer; -begin - call_func(scan_toks(false, true)); - s1 := tokens_to_string(def_ref); - delete_token_ref(def_ref); - call_func(scan_toks(false, true)); - s2 := tokens_to_string(def_ref); - delete_token_ref(def_ref); - i1 := str_start[s1]; - j1 := str_start[s1 + 1]; - i2 := str_start[s2]; - j2 := str_start[s2 + 1]; - while (i1 < j1) and (i2 < j2) do begin - if str_pool[i1] < str_pool[i2] then begin - cur_val := -1; - goto done; - end; - if str_pool[i1] > str_pool[i2] then begin - cur_val := 1; - goto done; - end; - incr(i1); - incr(i2); - end; - if (i1 = j1) and (i2 = j2) then - cur_val := 0 - else if i1 < j1 then - cur_val := 1 - else - cur_val := -1; -done: - flush_str(s2); - flush_str(s1); - cur_val_level := int_val; -end; -@z - -%*********************************************************************** - -@x 33387 -@ @= -begin - scan_pdf_ext_toks; - escapestr(tokens_to_string(def_ref)); - flush_str(last_tokens_string); - delete_token_ref(def_ref); -end - -@ @= -begin - scan_pdf_ext_toks; - escapename(tokens_to_string(def_ref)); - flush_str(last_tokens_string); - delete_token_ref(def_ref); -end -@y -@z - -@x 33406 -@ @= -compare_strings -@y -@z - -@x 33409 -@ @= - seconds_and_micros(epochseconds,microseconds); -@y -@ @= - seconds_and_micros(epochseconds,microseconds); - init_start_time; -@z - -%*********************************************************************** diff --git a/Build/source/texk/web2c/pdftexdir/pdftex.ch b/Build/source/texk/web2c/pdftexdir/pdftex.ch index 6ae23335465..31211b2477f 100644 --- a/Build/source/texk/web2c/pdftexdir/pdftex.ch +++ b/Build/source/texk/web2c/pdftexdir/pdftex.ch @@ -1,32 +1,5 @@ -% WEB change file containing code for pdfTeX features extending TeX; % to be applied to tex.web (Version 3.141592) in order to define the -% pdfTeX program. % -% Note: This file, pdftex.ch, defines pdftex.web in terms of changes to be -% applied to tex.web; in terms of a program such as TIE (or equivalent), -% -% tex.web ) -% + ) => tie -m ... => etex.web -% pdftexdir/pdftex.ch ) -% -% In addition, this file is used to define pdfetex.web, a combination -% of e-TeX and pdfTeX as follows: -% -% tex.web ) -% + ) -% etexdir/etex.ch ) -% + ) -% pdfetexdir/pdfetex.ch1 ) => tie -m ... => pdfetex.web -% + ) -% pdftexdir/pdftex.ch ) -% + ) -% pdfetexdir/pdfetex.ch2 ) -% -% where the two (small) files pdfetexdir/pdfetex.ch[12] take care of -% interferences between e-Tex changes (etexdir/etex.ch) and pdfTeX changes -% (pdftexdir/pdftex.ch). Consequently, changes in these files have to be -% coordinated. -% -% Copyright (c) 1996-2005 Han Th\^e\llap{\raise 0.5ex\hbox{\'{}}} Th\`anh, +% Copyright (c) 1996-2006 Han The Thanh, % % This file is part of pdfTeX. % @@ -44,708 +17,83 @@ % along with pdfTeX; if not, write to the Free Software % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA % -% $Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/pdftex.ch#171 $ -% -% The TeX program is copyright (C) 1982 by D. E. Knuth. -% TeX is a trademark of the American Mathematical Society. +% $Id: //depot/Build/source.development/TeX/texk/web2c/pdfetexdir/pdfetex.ch2#20 $ % -% all pdfTeX new primitives have the prefix "\pdf", apart from: -% o extension of \vadjust -% o extension of \font -% o \efcode -% o \lpcode, \rpcode -% o \leftmarginkern, \rightmarginkern - -@x limbo -\def\PASCAL{Pascal} -@y -\def\PASCAL{Pascal} -\def\pdfTeX{pdf\TeX} -\def\PDF{PDF} -@z - -@x [2] - This change is made for TeX 3.141592 -@d banner=='This is TeX, Version 3.141592' {printed when \TeX\ starts} -@y -@d pdftex_version==130 { \.{\\pdftexversion} } -@d pdftex_revision=="4" { \.{\\pdftexrevision} } -@d pdftex_version_string=='-1.30.4' {current \pdfTeX\ version} -@# -@d pdfTeX_banner=='This is pdfTeX, Version 3.141592',pdftex_version_string - {printed when \pdfTeX\ starts} -@# -@d TeX_banner=='This is TeX, Version 3.141592' {printed when \TeX\ starts} -@# -@d banner==pdfTeX_banner -@z - -% Some procedures that need to be declared forward -@x [173] -@* \[12] Displaying boxes. -@y -@@; - -@* \[12] Displaying boxes. -@z - -% Define pdftex token list parameters -@x [230] -@d toks_base=local_base+10 {table of 256 token list registers} -@y -@d tex_toks=local_base+10 {end of \TeX's token list parameters} -@# -@d pdftex_first_loc = tex_toks {base for \pdfTeX's token list parameters} -@d pdf_pages_attr_loc = pdftex_first_loc + 0 {points to token list for \.{\\pdfpagesattr}} -@d pdf_page_attr_loc = pdftex_first_loc + 1 {points to token list for \.{\\pdfpageattr}} -@d pdf_page_resources_loc = pdftex_first_loc + 2 {points to token list for \.{\\pdfpageresources}} -@d pdf_pk_mode_loc = pdftex_first_loc + 3 {points to token list for \.{\\pdfpkmode}} -@d pdf_toks=pdftex_first_loc+4 {end of \pdfTeX's token list parameters} -@# -@d toks_base=pdf_toks {table of 256 token list registers} -@z - -@x [230] -@d err_help==equiv(err_help_loc) -@y -@d err_help==equiv(err_help_loc) -@d pdf_pages_attr==equiv(pdf_pages_attr_loc) -@d pdf_page_attr==equiv(pdf_page_attr_loc) -@d pdf_page_resources==equiv(pdf_page_resources_loc) -@d pdf_pk_mode==equiv(pdf_pk_mode_loc) -@z - -@x [230] -primitive("errhelp",assign_toks,err_help_loc); -@!@:err_help_}{\.{\\errhelp} primitive@> -@y -primitive("errhelp",assign_toks,err_help_loc); -@!@:err_help_}{\.{\\errhelp} primitive@> -primitive("pdfpagesattr",assign_toks,pdf_pages_attr_loc); -@!@:pdf_pages_attr_}{\.{\\pdfpagesattr} primitive@> -primitive("pdfpageattr",assign_toks,pdf_page_attr_loc); -@!@:pdf_page_attr_}{\.{\\pdfpageattr} primitive@> -primitive("pdfpageresources",assign_toks,pdf_page_resources_loc); -@!@:pdf_page_resources_}{\.{\\pdfpageresources} primitive@> -primitive("pdfpkmode",assign_toks,pdf_pk_mode_loc); -@!@:pdf_pk_mode_}{\.{\\pdfpkmode} primitive@> -@z - -@x [231] - othercases print_esc("errhelp") -@y - pdf_pages_attr_loc: print_esc("pdfpagesattr"); - pdf_page_attr_loc: print_esc("pdfpageattr"); - pdf_page_resources_loc: print_esc("pdfpageresources"); - pdf_pk_mode_loc: print_esc("pdfpkmode"); - othercases print_esc("errhelp") +@x [0.0] l.83 - WEAVE: we want the whole file + \def\?##1]{\hbox{Changes to \hbox to 1em{\hfil##1}.\ }} + } +\let\maybe=\iffalse +@y 83 + \def\?##1]{\hbox{Changes to \hbox to 1em{\hfil##1}.\ }} + } +\let\maybe=\iftrue @z -% Define pdftex integer parameters -@x [236] -@d int_pars=55 {total number of integer parameters} -@y -@d tex_int_pars=55 {total number of \TeX's integer parameters} -@# -@d pdftex_first_integer_code = tex_int_pars {base for \pdfTeX's integer parameters} -@d pdf_output_code = pdftex_first_integer_code + 0 {switch on PDF output if positive} -@d pdf_compress_level_code = pdftex_first_integer_code + 1 {compress level of streams} -@d pdf_decimal_digits_code = pdftex_first_integer_code + 2 {digits after the decimal point of numbers} -@d pdf_move_chars_code = pdftex_first_integer_code + 3 {move chars 0..31 to higher area if possible} -@d pdf_image_resolution_code = pdftex_first_integer_code + 4 {default image resolution} -@d pdf_pk_resolution_code = pdftex_first_integer_code + 5 {default resolution of PK font} -@d pdf_unique_resname_code = pdftex_first_integer_code + 6 {generate unique names for resouces} -@d pdf_option_always_use_pdfpagebox_code = pdftex_first_integer_code + 7 {if the pdf inclusion should always use a specific pdf page box} -@d pdf_option_pdf_inclusion_errorlevel_code = pdftex_first_integer_code + 8 {if the pdf inclusion should treat pdfs newer than |pdf_minor_version| as an error} -@d pdf_minor_version_code = pdftex_first_integer_code + 9 {fractional part of the pdf version produced} -@d pdf_force_pagebox_code = pdftex_first_integer_code + 10 {if the pdf inclusion should always use a specific pdf page box} -@d pdf_inclusion_errorlevel_code = pdftex_first_integer_code + 11 {if the pdf inclusion should treat pdfs newer than |pdf_minor_version| as an error} -@d pdf_gamma_code = pdftex_first_integer_code + 12 -@d pdf_image_gamma_code = pdftex_first_integer_code + 13 -@d pdf_image_hicolor_code = pdftex_first_integer_code + 14 -@d pdf_image_apply_gamma_code = pdftex_first_integer_code + 15 -@d pdf_px_dimen_code = pdftex_first_integer_code + 16 {the code for the pixel dimen value} -@d pdf_int_pars=pdftex_first_integer_code + 17 {total number of \pdfTeX's integer parameters} -@# -@d int_pars=pdf_int_pars {total number of integer parameters} -@z - -@x [236] -@d error_context_lines==int_par(error_context_lines_code) -@y -@d error_context_lines==int_par(error_context_lines_code) -@# -@d pdf_output == int_par(pdf_output_code) -@d pdf_compress_level == int_par(pdf_compress_level_code) -@d pdf_decimal_digits == int_par(pdf_decimal_digits_code) -@d pdf_move_chars == int_par(pdf_move_chars_code) -@d pdf_image_resolution == int_par(pdf_image_resolution_code) -@d pdf_pk_resolution == int_par(pdf_pk_resolution_code) -@d pdf_unique_resname == int_par(pdf_unique_resname_code) -@d pdf_option_always_use_pdfpagebox == int_par(pdf_option_always_use_pdfpagebox_code) -@d pdf_option_pdf_inclusion_errorlevel == int_par(pdf_option_pdf_inclusion_errorlevel_code) -@d pdf_minor_version == int_par(pdf_minor_version_code) -@d pdf_force_pagebox == int_par(pdf_force_pagebox_code) -@d pdf_inclusion_errorlevel == int_par(pdf_inclusion_errorlevel_code) -@d pdf_gamma == int_par(pdf_gamma_code) -@d pdf_image_gamma == int_par(pdf_image_gamma_code) -@d pdf_image_hicolor == int_par(pdf_image_hicolor_code) -@d pdf_image_apply_gamma == int_par(pdf_image_apply_gamma_code) -@d pdf_px_dimen == int_par(pdf_px_dimen_code) -@z +% Change file to assist in creating the web2c-specific change file. +% This one resolves inconsistencies between tex.ch and tex.ech. +% Public domain. -@x [237] -error_context_lines_code:print_esc("errorcontextlines"); +@x [1] m.2 l.188 - Web2C: extensions imply we're not e-TeX +@d eTeX_banner=='This is e-TeX, Version 3.141592',eTeX_version_string @y -error_context_lines_code:print_esc("errorcontextlines"); -@# -pdf_output_code: print_esc("pdfoutput"); -pdf_compress_level_code: print_esc("pdfcompresslevel"); -pdf_decimal_digits_code: print_esc("pdfdecimaldigits"); -pdf_move_chars_code: print_esc("pdfmovechars"); -pdf_image_resolution_code: print_esc("pdfimageresolution"); -pdf_pk_resolution_code: print_esc("pdfpkresolution"); -pdf_unique_resname_code: print_esc("pdfuniqueresname"); -pdf_option_always_use_pdfpagebox_code: print_esc("pdfoptionalwaysusepdfpagebox"); -pdf_option_pdf_inclusion_errorlevel_code: print_esc("pdfoptionpdfinclusionerrorlevel"); -pdf_minor_version_code: print_esc("pdfminorversion"); -pdf_force_pagebox_code: print_esc("pdfforcepagebox"); -pdf_inclusion_errorlevel_code: print_esc("pdfinclusionerrorlevel"); -pdf_gamma_code: print_esc("pdfgamma"); -pdf_image_gamma_code: print_esc("pdfimagegamma"); -pdf_image_hicolor_code: print_esc("pdfimagehicolor"); -pdf_image_apply_gamma_code: print_esc("pdfimageapplygamma"); -pdf_px_dimen_code: print_esc("pdfpxdimen"); +@d eTeX_banner_k=='This is e-TeXk, Version 3.141592',eTeX_version_string +@d eTeX_banner=='This is e-TeX, Version 3.141592',eTeX_version_string @z -@x [238] -primitive("errorcontextlines",assign_int,int_base+error_context_lines_code);@/ -@!@:error_context_lines_}{\.{\\errorcontextlines} primitive@> +@x [1] m.2 l.188 - Web2C: extensions imply we're not pdfeTeX +@d pdfeTeX_banner=='This is pdfeTeX, Version 3.141592',pdftex_version_string,eTeX_version_string @y -primitive("errorcontextlines",assign_int,int_base+error_context_lines_code);@/ -@!@:error_context_lines_}{\.{\\errorcontextlines} primitive@> -primitive("pdfoutput",assign_int,int_base+pdf_output_code);@/ -@!@:pdf_output_}{\.{\\pdfoutput} primitive@> -primitive("pdfcompresslevel",assign_int,int_base+pdf_compress_level_code);@/ -@!@:pdf_compress_level_}{\.{\\pdfcompresslevel} primitive@> -primitive("pdfdecimaldigits",assign_int,int_base+pdf_decimal_digits_code);@/ -@!@:pdf_decimal_digits_}{\.{\\pdfdecimaldigits} primitive@> -primitive("pdfmovechars",assign_int,int_base+pdf_move_chars_code);@/ -@!@:pdf_move_chars_}{\.{\\pdfmovechars} primitive@> -primitive("pdfimageresolution",assign_int,int_base+pdf_image_resolution_code);@/ -@!@:pdf_image_resolution_}{\.{\\pdfimageresolution} primitive@> -primitive("pdfpkresolution",assign_int,int_base+pdf_pk_resolution_code);@/ -@!@:pdf_pk_resolution_}{\.{\\pdfpkresolution} primitive@> -primitive("pdfuniqueresname",assign_int,int_base+pdf_unique_resname_code);@/ -@!@:pdf_unique_resname_}{\.{\\pdfuniqueresname} primitive@> -primitive("pdfoptionpdfminorversion",assign_int,int_base+pdf_minor_version_code);@/ -@!@:pdf_minor_version_}{\.{\\pdfoptionpdfminorversion} primitive@> -primitive("pdfoptionalwaysusepdfpagebox",assign_int,int_base+pdf_option_always_use_pdfpagebox_code);@/ -@!@:pdf_option_always_use_pdfpagebox_}{\.{\\pdfoptionalwaysusepdfpagebox} primitive@> -primitive("pdfoptionpdfinclusionerrorlevel",assign_int,int_base+pdf_option_pdf_inclusion_errorlevel_code);@/ -@!@:pdf_option_pdf_inclusion_errorlevel_}{\.{\\pdfoptionpdfinclusionerrorlevel} primitive@> -primitive("pdfminorversion",assign_int,int_base+pdf_minor_version_code);@/ -@!@:pdf_minor_version_}{\.{\\pdfminorversion} primitive@> -primitive("pdfforcepagebox",assign_int,int_base+pdf_force_pagebox_code);@/ -@!@:pdf_force_pagebox_}{\.{\\pdfforcepagebox} primitive@> -primitive("pdfinclusionerrorlevel",assign_int,int_base+pdf_inclusion_errorlevel_code);@/ -@!@:pdf_inclusion_errorlevel_}{\.{\\pdfinclusionerrorlevel} primitive@> -primitive("pdfgamma",assign_int,int_base+pdf_gamma_code);@/ -@!@:pdf_gamma_}{\.{\\pdfgamma} primitive@> -primitive("pdfimagegamma",assign_int,int_base+pdf_image_gamma_code);@/ -@!@:pdf_image_gamma_}{\.{\\pdfimagegamma} primitive@> -primitive("pdfimagehicolor",assign_int,int_base+pdf_image_hicolor_code);@/ -@!@:pdf_image_hicolor_}{\.{\\pdfimagehicolor} primitive@> -primitive("pdfimageapplygamma",assign_int,int_base+pdf_image_apply_gamma_code);@/ -@!@:pdf_image_apply_gamma_}{\.{\\pdfimageapplygamma} primitive@> -primitive("pdfpxdimen",assign_int,int_base+pdf_px_dimen_code);@/ -@!@:pdf_px_dimen_}{\.{\\pdfpxdimen} primitive@> +@d pdfeTeX_banner_k=='This is pdfeTeXk, Version 3.141592',pdftex_version_string,eTeX_version_string +@d pdfeTeX_banner=='This is pdfeTeX, Version 3.141592',pdftex_version_string,eTeX_version_string @z -@x [240] - pxdimen -for k:=int_base to del_code_base-1 do eqtb[k].int:=0; +@x [1] m.2 l.188 - Web2C: extensions imply we're not pdfTeX +@d pdfTeX_banner=='This is pdfTeX, Version 3.141592',pdftex_version_string @y -for k:=int_base to del_code_base-1 do eqtb[k].int:=0; -pdf_px_dimen := unity; +@d pdfTeX_banner_k=='This is pdfTeXk, Version 3.141592',pdftex_version_string +@d pdfTeX_banner=='This is pdfTeX, Version 3.141592',pdftex_version_string @z -% Define pdftex dimension parameters -@x [247] -@d emergency_stretch_code=20 {reduces badnesses on final pass of line-breaking} +@x [1] m.2 l.188 - banner +@d banner==TeX_banner +@d banner_k==TeX_banner_k @y -@d emergency_stretch_code=20 {reduces badnesses on final pass of line-breaking} -@d pdftex_first_dimen_code = 21 {first number defined in this section} -@d pdf_h_origin_code = pdftex_first_dimen_code + 0 {horigin of the PDF output} -@d pdf_v_origin_code = pdftex_first_dimen_code + 1 {vorigin of the PDF output} -@d pdf_page_width_code = pdftex_first_dimen_code + 2 {page width of the PDF output} -@d pdf_page_height_code = pdftex_first_dimen_code + 3 {page height of the PDF output} -@d pdf_link_margin_code = pdftex_first_dimen_code + 4 {link margin in the PDF output} -@d pdf_dest_margin_code = pdftex_first_dimen_code + 5 {dest margin in the PDF output} -@d pdf_thread_margin_code = pdftex_first_dimen_code + 6 {thread margin in the PDF output} -@d pdf_first_line_height_code = pdftex_first_dimen_code + 7 -@d pdf_last_line_depth_code = pdftex_first_dimen_code + 8 -@d pdf_each_line_height_code = pdftex_first_dimen_code + 9 -@d pdf_each_line_depth_code = pdftex_first_dimen_code + 10 -@d pdftex_last_dimen_code = pdftex_first_dimen_code + 10 {last number defined in this section} +@d banner==pdfTeX_banner +@d banner_k==pdfTeX_banner_k @z @x -@d dimen_pars=21 {total number of dimension parameters} -@y -@d dimen_pars = pdftex_last_dimen_code + 1 {total number of dimension parameters} -@z - -@x [247] -@d emergency_stretch==dimen_par(emergency_stretch_code) -@y -@d emergency_stretch==dimen_par(emergency_stretch_code) -@d pdf_h_origin == dimen_par(pdf_h_origin_code) -@d pdf_v_origin == dimen_par(pdf_v_origin_code) -@d pdf_page_width == dimen_par(pdf_page_width_code) -@d pdf_page_height == dimen_par(pdf_page_height_code) -@d pdf_link_margin == dimen_par(pdf_link_margin_code) -@d pdf_dest_margin == dimen_par(pdf_dest_margin_code) -@d pdf_thread_margin == dimen_par(pdf_thread_margin_code) -@d pdf_first_line_height == dimen_par(pdf_first_line_height_code) -@d pdf_last_line_depth == dimen_par(pdf_last_line_depth_code) -@d pdf_each_line_height == dimen_par(pdf_each_line_height_code) -@d pdf_each_line_depth == dimen_par(pdf_each_line_depth_code) -@z - -@x [247] -emergency_stretch_code:print_esc("emergencystretch"); -@y -emergency_stretch_code:print_esc("emergencystretch"); -pdf_h_origin_code: print_esc("pdfhorigin"); -pdf_v_origin_code: print_esc("pdfvorigin"); -pdf_page_width_code: print_esc("pdfpagewidth"); -pdf_page_height_code: print_esc("pdfpageheight"); -pdf_link_margin_code: print_esc("pdflinkmargin"); -pdf_dest_margin_code: print_esc("pdfdestmargin"); -pdf_thread_margin_code: print_esc("pdfthreadmargin"); -pdf_first_line_height_code: print_esc("pdffirstlineheight"); -pdf_last_line_depth_code: print_esc("pdflastlinedepth"); -pdf_each_line_height_code: print_esc("pdfeachlineheight"); -pdf_each_line_depth_code: print_esc("pdfeachlinedepth"); -@z - -@x [248] -primitive("emergencystretch",assign_dimen,dimen_base+emergency_stretch_code);@/ -@!@:emergency_stretch_}{\.{\\emergencystretch} primitive@> -@y -primitive("emergencystretch",assign_dimen,dimen_base+emergency_stretch_code);@/ -@!@:emergency_stretch_}{\.{\\emergencystretch} primitive@> -primitive("pdfhorigin",assign_dimen,dimen_base+pdf_h_origin_code);@/ -@!@:pdf_h_origin_}{\.{\\pdfhorigin} primitive@> -primitive("pdfvorigin",assign_dimen,dimen_base+pdf_v_origin_code);@/ -@!@:pdf_v_origin_}{\.{\\pdfvorigin} primitive@> -primitive("pdfpagewidth",assign_dimen,dimen_base+pdf_page_width_code);@/ -@!@:pdf_page_width_}{\.{\\pdfpagewidth} primitive@> -primitive("pdfpageheight",assign_dimen,dimen_base+pdf_page_height_code);@/ -@!@:pdf_page_height_}{\.{\\pdfpageheight} primitive@> -primitive("pdflinkmargin",assign_dimen,dimen_base+pdf_link_margin_code);@/ -@!@:pdf_link_margin_}{\.{\\pdflinkmargin} primitive@> -primitive("pdfdestmargin",assign_dimen,dimen_base+pdf_dest_margin_code);@/ -@!@:pdf_dest_margin_}{\.{\\pdfdestmargin} primitive@> -primitive("pdfthreadmargin",assign_dimen,dimen_base+pdf_thread_margin_code);@/ -@!@:pdf_thread_margin_}{\.{\\pdfthreadmargin} primitive@> -primitive("pdffirstlineheight",assign_dimen,dimen_base+pdf_first_line_height_code);@/ -@!@:pdf_first_line_height_}{\.{\\pdffirstlineheight} primitive@> -primitive("pdflastlinedepth",assign_dimen,dimen_base+pdf_last_line_depth_code);@/ -@!@:pdf_last_line_depth_}{\.{\\pdflastlinedepth} primitive@> -primitive("pdfeachlineheight",assign_dimen,dimen_base+pdf_each_line_height_code);@/ -@!@:pdf_each_line_height_}{\.{\\pdfeachlineheight} primitive@> -primitive("pdfeachlinedepth",assign_dimen,dimen_base+pdf_each_line_depth_code);@/ -@!@:pdf_each_line_depth_}{\.{\\pdfeachlinedepth} primitive@> -@z - -% Define some read-only pdfTeX primitives -@x [416] -|glue_val|, |input_line_no_code|, or |badness_code|. -@y -|glue_val|, |input_line_no_code|, or |badness_code|. -\pdfTeX\ adds the codes for its extensions: |pdftex_version_code|, \dots\ . -@z - -@x [416] -@d badness_code=glue_val+2 {code for \.{\\badness}} -@y -@d badness_code=input_line_no_code+1 {code for \.{\\badness}} -@# -@d pdftex_first_rint_code = badness_code + 1 {base for \pdfTeX's command codes} -@d pdftex_version_code = pdftex_first_rint_code + 0 {code for \.{\\pdftexversion}} -@d pdf_last_obj_code = pdftex_first_rint_code + 1 {code for \.{\\pdflastobj}} -@d pdf_last_xform_code = pdftex_first_rint_code + 2 {code for \.{\\pdflastxform}} -@d pdf_last_ximage_code = pdftex_first_rint_code + 3 {code for \.{\\pdflastximage}} -@d pdf_last_ximage_pages_code = pdftex_first_rint_code + 4 {code for \.{\\pdflastximagepages}} -@d pdf_last_annot_code = pdftex_first_rint_code + 5 {code for \.{\\pdflastannot}} -@d pdf_last_x_pos_code = pdftex_first_rint_code + 6 {code for \.{\\pdflastxpos}} -@d pdf_last_y_pos_code = pdftex_first_rint_code + 7 {code for \.{\\pdflastypos}} -@d pdf_retval_code = pdftex_first_rint_code + 8 {global multi-purpose return value} -@d pdf_last_ximage_colordepth_code = pdftex_first_rint_code + 9 {code for \.{\\pdflastximagecolordepth}} -@d elapsed_time_code = pdftex_first_rint_code + 10 {code for \.{\\pdfelapsedtime}} -@d pdftex_last_item_codes = pdftex_first_rint_code + 10 {end of \pdfTeX's command codes} -@z - -@x [416] -primitive("badness",last_item,badness_code); -@!@:badness_}{\.{\\badness} primitive@> -@y -primitive("badness",last_item,badness_code); -@!@:badness_}{\.{\\badness} primitive@> -primitive("pdftexversion",last_item,pdftex_version_code);@/ -@!@:pdftex_version_}{\.{\\pdftexversion} primitive@> -primitive("pdflastobj",last_item,pdf_last_obj_code);@/ -@!@:pdf_last_obj_}{\.{\\pdflastobj} primitive@> -primitive("pdflastxform",last_item,pdf_last_xform_code);@/ -@!@:pdf_last_xform_}{\.{\\pdflastxform} primitive@> -primitive("pdflastximage",last_item,pdf_last_ximage_code);@/ -@!@:pdf_last_ximage_}{\.{\\pdflastximage} primitive@> -primitive("pdflastximagepages",last_item,pdf_last_ximage_pages_code);@/ -@!@:pdf_last_ximage_pages_}{\.{\\pdflastximagepages} primitive@> -primitive("pdflastannot",last_item,pdf_last_annot_code);@/ -@!@:pdf_last_annot_}{\.{\\pdflastannot} primitive@> -primitive("pdflastxpos",last_item,pdf_last_x_pos_code);@/ -@!@:pdf_last_x_pos_}{\.{\\pdflastxpos} primitive@> -primitive("pdflastypos",last_item,pdf_last_y_pos_code);@/ -@!@:pdf_last_y_pos_}{\.{\\pdflastypos} primitive@> -primitive("pdfretval",last_item,pdf_retval_code);@/ -@!@:pdf_retval_}{\.{\\pdfretval} primitive@> -primitive("pdflastximagecolordepth",last_item,pdf_last_ximage_colordepth_code);@/ -@!@:pdf_last_ximage_colordepth_}{\.{\\pdflastximagecolordepth} primitive@> -primitive("pdfelapsedtime",last_item,elapsed_time_code); -@!@:elapsed_time_}{\.{\\pdfelapsedtime} primitive@> -@z - -@x [417] - othercases print_esc("badness") -@y - pdftex_version_code: print_esc("pdftexversion"); - pdf_last_obj_code: print_esc("pdflastobj"); - pdf_last_xform_code: print_esc("pdflastxform"); - pdf_last_ximage_code: print_esc("pdflastximage"); - pdf_last_ximage_pages_code: print_esc("pdflastximagepages"); - pdf_last_annot_code: print_esc("pdflastannot"); - pdf_last_x_pos_code: print_esc("pdflastxpos"); - pdf_last_y_pos_code: print_esc("pdflastypos"); - pdf_retval_code: print_esc("pdfretval"); - pdf_last_ximage_colordepth_code: print_esc("pdflastximagecolordepth"); - elapsed_time_code: print_esc("pdfelapsedtime"); - othercases print_esc("badness") -@z - -@x [424] -if cur_chr>glue_val then -@y -if m>=input_line_no_code then -@z - -@x [424] - begin if cur_chr=input_line_no_code then cur_val:=line - else cur_val:=last_badness; {|cur_chr=badness_code|} -@y - begin case m of - input_line_no_code: cur_val:=line; - badness_code: cur_val:=last_badness; - pdftex_version_code: cur_val := pdftex_version; - pdf_last_obj_code: cur_val := pdf_last_obj; - pdf_last_xform_code: cur_val := pdf_last_xform; - pdf_last_ximage_code: cur_val := pdf_last_ximage; - pdf_last_ximage_pages_code: cur_val := pdf_last_ximage_pages; - pdf_last_annot_code: cur_val := pdf_last_annot; - pdf_last_x_pos_code: cur_val := pdf_last_x_pos; - pdf_last_y_pos_code: cur_val := pdf_last_y_pos; - pdf_retval_code: cur_val := pdf_retval; - pdf_last_ximage_colordepth_code: cur_val := pdf_last_ximage_colordepth; - elapsed_time_code: cur_val := get_microinterval; - end; {there are no other cases} -@z - -@x [455] - pxdimen -else if scan_keyword("ex") then v:=(@) -@.ex@> -@y -else if scan_keyword("ex") then v:=(@) -@.ex@> -else if scan_keyword("px") then v:=pdf_px_dimen -@.px@> -@z - -@x new Didot (1nd=0.375mm), new Cicero (1nc=12nd) -@ The necessary conversion factors can all be specified exactly as -fractions whose numerator and denominator are 32768 or less. -According to the definitions here, $\rm2660\,dd\approx1000.33297\,mm$; -this agrees well with the value $\rm1000.333\,mm$ cited by Bosshard -@^Bosshard, Hans Rudolf@> -in {\sl Technische Grundlagen zur Satzherstellung\/} (Bern, 1980). -@y -@ The necessary conversion factors can all be specified exactly as -fractions whose numerator and denominator are 65535 or less. -According to the definitions here, $\rm2660\,dd\approx1000.33297\,mm$; -this agrees well with the value $\rm1000.333\,mm$ cited by Bosshard -@^Bosshard, Hans Rudolf@> -in {\sl Technische Grundlagen zur Satzherstellung\/} (Bern, 1980). -The Didot point has been newly standardized in 1978; -it's now exactly $\rm 1\,nd=0.375\,mm$. -Conversion uses the equation $0.375=21681/20320/72.27\cdot25.4$. -The new Cicero follows the new Didot point; $\rm 1\,nc=12\,nd$. -@z - -@x new Didot (1nd=0.375mm), new Cicero (1nc=12nd) -else if scan_keyword("cc") then set_conversion(14856)(1157) -@.cc@> -@y -else if scan_keyword("cc") then set_conversion(14856)(1157) -@.cc@> -else if scan_keyword("nd") then set_conversion(21681)(20320) -@.nd@> -else if scan_keyword("nc") then set_conversion(65043)(5080) -@.nc@> -@z - -@x new Didot (1nd=0.375mm), new Cicero (1nc=12nd) - ("cm, mm, dd, cc, bp, or sp; but yours is a new one!")@/ -@y - ("cm, mm, dd, cc, nd, nc, bp, or sp; but yours is a new one!")@/ -@z - -% Define some char token producing pdfTeX primitives -@x [468] -@d number_code=0 {command code for \.{\\number}} -@y -\pdfTeX\ adds \.{\\eTeXrevision}, \.{\\pdftexrevision}, \.{\\pdftexbanner}, -\.{\\pdffontname}, \.{\\pdffontobjnum}, \.{\\pdffontsize}, and \.{\\pdfpageref} -such that |job_name_code| remains last. - -@d number_code=0 {command code for \.{\\number}} -@z - -@x [468] -@d job_name_code=5 {command code for \.{\\jobname}} +@d frozen_null_font=frozen_control_sequence+11 + {permanent `\.{\\nullfont}'} @y -@d pdftex_first_expand_code = 5 {base for \pdfTeX's command codes} -@d pdftex_revision_code = pdftex_first_expand_code + 0 {command code for \.{\\pdftexrevision}} -@d pdftex_banner_code = pdftex_first_expand_code + 1 {command code for \.{\\pdftexbanner}} -@d pdf_font_name_code = pdftex_first_expand_code + 2 {command code for \.{\\pdffontname}} -@d pdf_font_objnum_code = pdftex_first_expand_code + 3 {command code for \.{\\pdffontobjnum}} -@d pdf_font_size_code = pdftex_first_expand_code + 4 {command code for \.{\\pdffontsize}} -@d pdf_page_ref_code = pdftex_first_expand_code + 5 {command code for \.{\\pdfpageref}} -@d pdf_xform_name_code = pdftex_first_expand_code + 6 {command code for \.{\\pdfxformname}} -@d pdf_last_escaped_string_code = pdftex_first_expand_code + 7 {command code for \.{\\pdflastescapedstring}} -@d pdf_last_escaped_name_code = pdftex_first_expand_code + 8 {command code for \.{\\pdflastescapename}} -@d pdftex_convert_codes = pdftex_first_expand_code + 9 {end of \pdfTeX's command codes} -@d job_name_code=pdftex_convert_codes {command code for \.{\\jobname}} +@d frozen_null_font=frozen_control_sequence+12 + {permanent `\.{\\nullfont}'} @z -@x [468] -primitive("jobname",convert,job_name_code);@/ -@y +@x [17.236] l.4960 - first web2c, then e-TeX additional integer parameters +@d int_pars=web2c_int_pars {total number of integer parameters} @# -primitive("pdftexrevision",convert,pdftex_revision_code);@/ -@!@:pdftex_revision_}{\.{\\pdftexrevision} primitive@> -primitive("pdftexbanner",convert,pdftex_banner_code);@/ -@!@:pdftex_banner_}{\.{\\pdftexbanner} primitive@> -primitive("pdffontname",convert,pdf_font_name_code);@/ -@!@:pdf_font_name_}{\.{\\pdffontname} primitive@> -primitive("pdffontobjnum",convert,pdf_font_objnum_code);@/ -@!@:pdf_font_objnum_}{\.{\\pdffontobjnum} primitive@> -primitive("pdffontsize",convert,pdf_font_size_code);@/ -@!@:pdf_font_size_}{\.{\\pdffontsize} primitive@> -primitive("pdfpageref",convert,pdf_page_ref_code);@/ -@!@:pdf_page_ref_}{\.{\\pdfpageref} primitive@> -primitive("pdfxformname",convert,pdf_xform_name_code);@/ -@!@:pdf_xform_name_}{\.{\\pdfxformname} primitive@> -primitive("pdflastescapedstring",convert,pdf_last_escaped_string_code);@/ -@!@:pdf_last_escaped_string_}{\.{\\pdflastescapedstring} primitive@> -primitive("pdflastescapedname",convert,pdf_last_escaped_name_code);@/ -@!@:pdf_last_escaped_name_}{\.{\\pdflastescapedname} primitive@> -@# -primitive("jobname",convert,job_name_code);@/ -@z - -@x [469] - othercases print_esc("jobname") -@y - pdftex_revision_code: print_esc("pdftexrevision"); - pdftex_banner_code: print_esc("pdftexbanner"); - pdf_font_name_code: print_esc("pdffontname"); - pdf_font_objnum_code: print_esc("pdffontobjnum"); - pdf_font_size_code: print_esc("pdffontsize"); - pdf_page_ref_code: print_esc("pdfpageref"); - pdf_xform_name_code: print_esc("pdfxformname"); - pdf_last_escaped_string_code: print_esc("pdflastescapedstring"); - pdf_last_escaped_name_code: print_esc("pdflastescapedname"); - othercases print_esc("jobname") -@z - -@x [465] - pdfescapestring -@!b:pool_pointer; {base of temporary string} -@y -@!b:pool_pointer; {base of temporary string} -@!i, l: integer; {index to access escaped string or name} -@z - -@x [471] -job_name_code: if job_name=0 then open_log_file; -@y -pdftex_revision_code: do_nothing; -pdftex_banner_code: do_nothing; -pdf_font_name_code, pdf_font_objnum_code, pdf_font_size_code: begin - scan_font_ident; - if cur_val = null_font then - pdf_error("font", "invalid font identifier"); - if c <> pdf_font_size_code then begin - pdf_check_vf(cur_val); - if not font_used[cur_val] then - pdf_init_font(cur_val); - end; -end; -pdf_page_ref_code: begin - scan_int; - if cur_val <= 0 then - pdf_error("pageref", "invalid page number"); -end; -pdf_xform_name_code: begin - scan_int; - pdf_check_obj(obj_type_xform, cur_val); -end; -pdf_last_escaped_string_code: do_nothing; -pdf_last_escaped_name_code: do_nothing; -job_name_code: if job_name=0 then open_log_file; -@z - -@x [472] -job_name_code: print(job_name); +@d etex_int_base=tex_int_pars {base for \eTeX's integer parameters} @y -pdftex_revision_code: print(pdftex_revision); -pdftex_banner_code: print(pdftex_banner); -pdf_font_name_code, pdf_font_objnum_code: begin - set_ff(cur_val); - if c = pdf_font_name_code then - print_int(obj_info(pdf_font_num[ff])) - else - print_int(pdf_font_num[ff]); -end; -pdf_font_size_code: begin - print_scaled(font_size[cur_val]); - print("pt"); -end; -pdf_page_ref_code: print_int(get_obj(obj_type_page, cur_val, false)); -pdf_xform_name_code: print_int(obj_info(cur_val)); -pdf_last_escaped_string_code: begin - l := escapedstrlen; - for i := 0 to l - 1 do - print_char(getescapedstrchar(i)); -end; -pdf_last_escaped_name_code: begin - l := escapednamelen; - for i := 0 to l - 1 do - print_char(getescapednamechar(i)); -end; -job_name_code: print(job_name); +@d pdftex_first_integer_code = web2c_int_pars {base for \pdfTeX's integer parameters} @z -@x [577] - tagcode -@= -procedure scan_font_ident; -@y -@= -function get_tag_code(f: internal_font_number; c: eight_bits): integer; -var i:small_number; -begin - if is_valid_char(c) then +% start of tex.pch +@x (MLTeX) l. 12945 begin i := char_tag(char_info(f)(c)); - if i = lig_tag then - get_tag_code := 1 - else if i = list_tag then - get_tag_code := 2 - else if i = ext_tag then - get_tag_code := 4 - else - get_tag_code := 0; - end - else - get_tag_code := -1; -end; -procedure scan_font_ident; -@z - -% Shipping out to PDF -@x [638] -@ The |hlist_out| and |vlist_out| procedures are now complete, so we are -ready for the |ship_out| routine that gets them started in the first place. - -@p procedure ship_out(@!p:pointer); {output the box |p|} @y -@ The |hlist_out| and |vlist_out| procedures are now complete, so we are -ready for the |dvi_ship_out| routine that gets them started in the first place. - -@p procedure dvi_ship_out(@!p:pointer); {output the box |p|} + begin i := char_tag(orig_char_info(f)(c)); @z -@x [644] -@* \[33] Packaging. -@y - -@* \[32a] \pdfTeX\ basic. - -Initialize pdftex's parameters to some useful default value. -Helpful in case one forgets to set them during initex run. - -@= -pdf_h_origin := (one_hundred_inch + 50) div 100; -pdf_v_origin := (one_hundred_inch + 50) div 100; -pdf_compress_level := 9; -pdf_decimal_digits := 4; -pdf_image_resolution := 72; -pdf_minor_version := 4; -pdf_gamma := 1000; -pdf_image_gamma := 2200; -pdf_image_hicolor := 1; -pdf_image_apply_gamma := 0; - -@ The subroutines define the corresponding macros so we can use them -in C. - -@d flushable(#) == (# = str_ptr - 1) - -@d is_valid_char(#)==((font_bc[f] <= #) and (# <= font_ec[f]) and +@x (MLTeX) l. 14640 +@d is_valid_char(#)==((font_bc[f] <= #) and (# <= font_ec[f]) and char_exists(char_info(f)(#))) +@y +@d is_valid_char(#)==((font_bc[f] <= #) and (# <= font_ec[f]) and + char_exists(orig_char_info(f)(#))) +@z -@p function get_pdf_compress_level: integer; -begin - get_pdf_compress_level := pdf_compress_level; -end; - -function get_nullfont: internal_font_number; -begin - get_nullfont := null_font; -end; - -function get_nullcs: pointer; -begin - get_nullcs := null_cs; -end; - -function get_nullptr: pointer; -begin - get_nullptr := null; -end; - -function get_tex_int(code: integer): integer; -begin - get_tex_int := int_par(code); -end; - -function get_tex_dimen(code: integer): scaled; -begin - get_tex_dimen := dimen_par(code); -end; - -function get_x_height(f: internal_font_number): scaled; -begin - get_x_height := x_height(f); -end; - +@x (MLTeX) l. 14678 function get_charwidth(f: internal_font_number; c: eight_bits): scaled; begin if is_valid_char(c) then @@ -769,6085 +117,348 @@ begin else get_chardepth := 0; end; - -function get_quad(f: internal_font_number): scaled; +@y +function get_charwidth(f: internal_font_number; c: eight_bits): scaled; begin - get_quad := quad(f); + if is_valid_char(c) then + get_charwidth := char_width(f)(orig_char_info(f)(c)) + else + get_charwidth := 0; end; -function get_slant(f: internal_font_number): scaled; +function get_charheight(f: internal_font_number; c: eight_bits): scaled; begin - get_slant := slant(f); + if is_valid_char(c) then + get_charheight := char_height(f)(height_depth(orig_char_info(f)(c))) + else + get_charheight := 0; end; -function new_dummy_font: internal_font_number; +function get_chardepth(f: internal_font_number; c: eight_bits): scaled; begin - new_dummy_font := read_font_info(null_cs, "dummy", "", -1000); -end; - -@ Helper for debugging purposes - -@p procedure short_display_n(@!p, m:integer); {prints highlights of list |p|} -var n:integer; {for replacement counts} - i: integer; -begin -i := 0; -font_in_short_display:=null_font; -if p = null then - return; -while p>mem_min do - begin if is_char_node(p) then - begin if p<=mem_end then - begin if font(p)<>font_in_short_display then - begin if (font(p)font_max) then - print_char("*") -@.*\relax@> - else @; - print_char(" "); font_in_short_display:=font(p); - end; - print_ASCII(qo(character(p))); - end; - end - else begin - if (type(p) = glue_node) or - (type(p) = disc_node) or - (type(p) = penalty_node) or - ((type(p) = kern_node) and (subtype(p) = explicit)) then - incr(i); - if i >= m then - return; - if (type(p) = disc_node) then begin - print("|"); - short_display(pre_break(p)); - print("|"); - short_display(post_break(p)); - print("|"); - n:=replace_count(p); - while n>0 do - begin if link(p)<>null then p:=link(p); - decr(n); - end; - end - else - @; - end; - p:=link(p); - if p = null then - return; - end; - update_terminal; + if is_valid_char(c) then + get_chardepth := char_depth(f)(height_depth(orig_char_info(f)(c))) + else + get_chardepth := 0; end; +@z -@ Sometimes it is neccesary to allocate memory for PDF output that cannot -be deallocated then, so we use |pdf_mem| for this purpose. - -@= -@!inf_pdf_mem_size = 10000; {min size of the |pdf_mem| array} -@!sup_pdf_mem_size = 10000000; {max size of the |pdf_mem| array} -@ @= -@!pdf_mem_size: integer; -@!pdf_mem: ^integer; -@!pdf_mem_ptr: integer; +@x (MLTeX) l. 17413 +label reswitch, move_past, fin_rule, next_p; +@y +label reswitch, move_past, fin_rule, next_p, found, continue; +@z -@ @= -pdf_mem_ptr := 1; {the first word is not used so we can use zero as a value for testing -whether a pointer to |pdf_mem| is valid} -pdf_mem_size := inf_pdf_mem_size; {allocated size of |pdf_mem| array} +@x (MLTeX) l. 17462 + if is_valid_char(c) then + output_one_char(c) + else + char_warning(f, c); + cur_h:=cur_h+char_width(f)(char_info(f)(c)); +@y + if is_valid_char(c) then begin + output_one_char(c); + cur_h:=cur_h+char_width(f)(char_info(f)(c)); + goto continue; + end; + if mltex_enabled_p then + @<(\pdfTeX) Output a substitution, |goto continue| if not possible@>; +continue: +@z -@ We use |pdf_get_mem| to allocate memory in |pdf_mem| +@x +@ The |pdf_vlist_out| routine is similar to |pdf_hlist_out|, but a bit simpler. +@y +@ @<(\pdfTeX) Output a substitution, |goto continue| if not possible@>= + begin + @; +found: @; + @<(\pdfTeX) Rebuild character using substitution information@>; + end -@p function pdf_get_mem(s: integer): integer; {allocate |s| words in |pdf_mem|} -var a: integer; -begin - if s > sup_pdf_mem_size - pdf_mem_size then - overflow("PDF memory size (pdf_mem_size)", pdf_mem_size); - if pdf_mem_ptr + s > pdf_mem_size then begin - a := 0.2 * pdf_mem_size; - if pdf_mem_ptr + s > pdf_mem_size + a then - pdf_mem_size := pdf_mem_ptr + s - else if pdf_mem_size < sup_pdf_mem_size - a then - pdf_mem_size := pdf_mem_size + a - else - pdf_mem_size := sup_pdf_mem_size; - pdf_mem := xrealloc_array(pdf_mem, integer, pdf_mem_size); +@ @<(\pdfTeX) Rebuild character using substitution information@>= + base_x_height:=x_height(f); + base_slant:=slant(f)/float_constant(65536); +@^real division@> + accent_slant:=base_slant; {slant of accent character font} + base_width:=char_width(f)(ib_c); + base_height:=char_height(f)(height_depth(ib_c)); + accent_width:=char_width(f)(ia_c); + accent_height:=char_height(f)(height_depth(ia_c)); + @/{compute necessary horizontal shift (don't forget slant)}@/ + delta:=round((base_width-accent_width)/float_constant(2)+ + base_height*base_slant-base_x_height*accent_slant); +@^real multiplication@> +@^real addition@> + @/{1. For centering/horizontal shifting insert a kern node.}@/ + cur_h:=cur_h+delta; + @/{2. Then insert the accent character possibly shifted up or down.}@/ + if ((base_height<>base_x_height) and (accent_height>0)) then + begin {the accent must be shifted up or down} + cur_v:=base_line+(base_x_height-base_height); + output_one_char(accent_c); + cur_v:=base_line; + end + else begin + output_one_char(accent_c); end; - pdf_get_mem := pdf_mem_ptr; - pdf_mem_ptr := pdf_mem_ptr + s; -end; - -@* \[32b] \pdfTeX\ output low-level subroutines. -We use the similiar subroutines to handle the output buffer for -PDF output. When compress is used, the state of writing to buffer -is held in |zip_write_state|. We must write the header of PDF -output file in initialization to ensure that it will be the first -written bytes. - -@= -@!pdf_buf_size = 16384; {size of the PDF buffer} - -@ The following macros are similar as for \.{DVI} buffer handling - -@d pdf_offset == (pdf_gone + pdf_ptr) {the file offset of last byte in PDF -buffer that |pdf_ptr| points to} - -@d no_zip == 0 {no \.{ZIP} compression} -@d zip_writing == 1 {\.{ZIP} compression being used} -@d zip_finish == 2 {finish \.{ZIP} compression} - -@d pdf_quick_out(#) == {output a byte to PDF buffer without checking of -overflow} -begin - pdf_buf[pdf_ptr] := #; - incr(pdf_ptr); -end - -@d pdf_room(#) == {make sure that there are at least |n| bytes free in PDF -buffer} -begin - if pdf_buf_size < # then - overflow("PDF output buffer", pdf_buf_size); - if pdf_ptr + # > pdf_buf_size then - pdf_flush; -end - -@d pdf_out(#) == {do the same as |pdf_quick_out| and flush the PDF -buffer if necessary} -begin - if pdf_ptr > pdf_buf_size then - pdf_flush; - pdf_quick_out(#); -end - -@= -@!pdf_file: byte_file; {the PDF output file} -@!pdf_buf: array[0..pdf_buf_size] of eight_bits; {the PDF buffer} -@!pdf_ptr: integer; {pointer to the first unused byte in the PDF buffer} -@!pdf_gone: integer; {number of bytes that were flushed to output} -@!pdf_save_offset: integer; {to save |pdf_offset|} -@!zip_write_state: integer; {which state of compression we are in} -@!fixed_pdf_minor_version: integer; {fixed minor part of the pdf version} -@!pdf_minor_version_written: boolean; {flag if the pdf version has been written} -@!fixed_pdfoutput: integer; {fixed output format} -@!fixed_pdfoutput_set: boolean; {|fixed_pdfoutput| has been set?} -@!fixed_gamma: integer; -@!fixed_image_gamma: integer; -@!fixed_image_hicolor: boolean; -@!fixed_image_apply_gamma: integer; -@!epochseconds: integer; -@!microseconds: integer; + cur_h:=cur_h+accent_width; + @/{3. For centering/horizontal shifting insert another kern node.}@/ + cur_h:=cur_h+(-accent_width-delta); + @/{4. Output the base character.}@/ + output_one_char(base_c); + cur_h:=cur_h+base_width; -@ @= -pdf_buf[0] := "%"; -pdf_buf[1] := "P"; -pdf_buf[2] := "D"; -pdf_buf[3] := "F"; -pdf_buf[4] := "-"; -pdf_buf[5] := "1"; -pdf_buf[6] := "."; -pdf_buf[7] := "4"; -pdf_buf[8] := pdf_new_line_char; -pdf_buf[9] := "%"; -pdf_buf[10] := 208; -pdf_buf[11] := 212; -pdf_buf[12] := 197; -pdf_buf[13] := 216; -pdf_buf[14] := pdf_new_line_char; -pdf_ptr := 15; -pdf_gone := 0; -zip_write_state := no_zip; -pdf_minor_version_written := false; -fixed_pdfoutput_set := false; -fixed_gamma := 1000; -fixed_image_gamma := 2200; -fixed_image_hicolor := 1; -fixed_image_apply_gamma := 0; - -@ @p -function fix_int(val, min, max: integer): integer; -begin - if val < min then - fix_int := min - else if val > max then - fix_int := max - else - fix_int := val; -end; - -@ This checks that |pdfminorversion| can only be set before any -bytes have been written to the generated pdf file. It should be called -directly after every |ensure_pdf_open|. +@ The |pdf_vlist_out| routine is similar to |pdf_hlist_out|, but a bit simpler. +@z -@p procedure check_pdfminorversion; -begin - if not pdf_minor_version_written then begin - pdf_minor_version_written := true; - if (pdf_minor_version < 0) or (pdf_minor_version > 9) then begin - print_err("pdfTeX error (illegal pdfminorversion)"); - print_ln; - help2 ("The pdfminorversion must be between 0 and 9.")@/ - ("I changed this to 4."); - int_error (pdf_minor_version); - pdf_minor_version := 4; - end; - fixed_pdf_minor_version := pdf_minor_version; - pdf_buf[7] := chr(ord("0") + fixed_pdf_minor_version); - fixed_gamma := fix_int(pdf_gamma, 0, 1000000); - fixed_image_gamma := fix_int(pdf_image_gamma, 0, 1000000); - fixed_image_hicolor := fix_int(pdf_image_hicolor, 0, 1); - fixed_image_apply_gamma := fix_int(pdf_image_apply_gamma, 0, 1); +@x [49.1259] + begin if s>0 then + begin if s=font_size[f] then goto common_ending; + end + else if font_size[f]=xn_over_d(font_dsize[f],-s,1000) then + goto common_ending; end - else begin - if fixed_pdf_minor_version <> pdf_minor_version then - pdf_error("setup", - "\pdfminorversion cannot be changed after data is written to the pdf file"); - end; -end; - -@ Checks that we have a name for the generated pdf file and that it's open. -|check_pdfminorversion| should be called directly -hereafter. +@y + begin + if pdf_font_step[f] = 0 then begin + if s>0 then + begin if s=font_size[f] then goto common_ending; + end + else if font_size[f]=xn_over_d(font_dsize[f],-s,1000) then + goto common_ending; + end + end +@z + +@x (WEB2C!) +param_base:=xmalloc_array(integer, font_max); + +undump_things(font_check[null_font], font_ptr+1-null_font); +@y +param_base:=xmalloc_array(integer, font_max); + +pdf_char_used:=xmalloc_array(char_used_array, font_max); +pdf_font_size:=xmalloc_array(scaled, font_max); +pdf_font_num:=xmalloc_array(integer, font_max); +pdf_font_map:=xmalloc_array(fm_entry_ptr, font_max); +pdf_font_type:=xmalloc_array(eight_bits, font_max); +pdf_font_attr:=xmalloc_array(str_number, font_max); +pdf_font_blink:=xmalloc_array(internal_font_number, font_max); +pdf_font_elink:=xmalloc_array(internal_font_number, font_max); +pdf_font_stretch:=xmalloc_array(integer, font_max); +pdf_font_shrink:=xmalloc_array(integer, font_max); +pdf_font_step:=xmalloc_array(integer, font_max); +pdf_font_expand_ratio:=xmalloc_array(integer, font_max); +pdf_font_auto_expand:=xmalloc_array(boolean, font_max); +pdf_font_lp_base:=xmalloc_array(integer, font_max); +pdf_font_rp_base:=xmalloc_array(integer, font_max); +pdf_font_ef_base:=xmalloc_array(integer, font_max); +pdf_font_kn_bs_base:=xmalloc_array(integer, font_max); +pdf_font_st_bs_base:=xmalloc_array(integer, font_max); +pdf_font_sh_bs_base:=xmalloc_array(integer, font_max); +pdf_font_kn_bc_base:=xmalloc_array(integer, font_max); +pdf_font_kn_ac_base:=xmalloc_array(integer, font_max); +vf_packet_base:=xmalloc_array(integer, font_max); +vf_default_font:=xmalloc_array(internal_font_number, font_max); +vf_local_font_num:=xmalloc_array(internal_font_number, font_max); +vf_e_fnts:=xmalloc_array(integer, font_max); +vf_i_fnts:=xmalloc_array(internal_font_number, font_max); + +for font_k := font_base to font_max do begin + for k := 0 to 31 do + pdf_char_used[font_k, k] := 0; + pdf_font_size[font_k] := 0; + pdf_font_num[font_k] := 0; + pdf_font_map[font_k] := 0; + pdf_font_type[font_k] := new_font_type; + pdf_font_attr[font_k] := ""; + pdf_font_blink[font_k] := null_font; + pdf_font_elink[font_k] := null_font; + pdf_font_stretch[font_k] := null_font; + pdf_font_shrink[font_k] := null_font; + pdf_font_step[font_k] := 0; + pdf_font_expand_ratio[font_k] := 0; + pdf_font_auto_expand[font_k] := false; + pdf_font_lp_base[font_k] := 0; + pdf_font_rp_base[font_k] := 0; + pdf_font_ef_base[font_k] := 0; + pdf_font_kn_bs_base[font_k] := 0; + pdf_font_st_bs_base[font_k] := 0; + pdf_font_sh_bs_base[font_k] := 0; + pdf_font_kn_bc_base[font_k] := 0; + pdf_font_kn_ac_base[font_k] := 0; +end; + +make_pdftex_banner; +undump_things(font_check[null_font], font_ptr+1-null_font); +@z + +@x [51.1] + setup_bound_var (0)('hash_extra')(hash_extra); + +@y + setup_bound_var (0)('hash_extra')(hash_extra); + setup_bound_var (72)('pk_dpi')(pk_dpi); +@z + +@x [51.2] + const_chk (hash_extra); + if error_line > ssup_error_line then error_line := ssup_error_line; +@y + const_chk (hash_extra); + const_chk (obj_tab_size); + const_chk (pdf_mem_size); + const_chk (dest_names_size); + const_chk (pk_dpi); + if error_line > ssup_error_line then error_line := ssup_error_line; +@z + +@x [51.1332] l.24203 (ca.) texarray + line_stack:=xmalloc_array (integer, max_in_open); +@y + line_stack:=xmalloc_array (integer, max_in_open); + eof_seen:=xmalloc_array (boolean, max_in_open); + grp_stack:=xmalloc_array (save_pointer, max_in_open); + if_stack:=xmalloc_array (pointer, max_in_open); +@z + +@x [51.3] + hyph_link :=xmalloc_array (hyph_pointer, hyph_size); +@y + hyph_link :=xmalloc_array (hyph_pointer, hyph_size); + obj_tab:=xmalloc_array (obj_entry, inf_obj_tab_size); {will grow dynamically} + pdf_mem:=xmalloc_array (integer, inf_pdf_mem_size); {will grow dynamically} + dest_names:=xmalloc_array (dest_name_entry, inf_dest_names_size); {will grow dynamically} + pdf_op_buf:=xmalloc_array (eight_bits, pdf_op_buf_size); + pdf_os_buf:=xmalloc_array (eight_bits, inf_pdf_os_buf_size); {will grow dynamically} + pdf_os_objnum:=xmalloc_array (integer, pdf_os_max_objs); + pdf_os_objoff:=xmalloc_array (integer, pdf_os_max_objs); +@z + +@x [51.1337] l.24371 (ca.) texarray + trie_root:=0; trie_c[0]:=si(0); trie_ptr:=0; +@y + trie_root:=0; trie_c[0]:=si(0); trie_ptr:=0; + hyph_root:=0; hyph_start:=0; +@z + + +@x (WEB2C!) + param_base:=xmalloc_array(integer, font_max); + + font_ptr:=null_font; fmem_ptr:=7; +@y +param_base:=xmalloc_array(integer, font_max); + +pdf_char_used:=xmalloc_array(char_used_array,font_max); +pdf_font_size:=xmalloc_array(scaled,font_max); +pdf_font_num:=xmalloc_array(integer,font_max); +pdf_font_map:=xmalloc_array(fm_entry_ptr,font_max); +pdf_font_type:=xmalloc_array(eight_bits,font_max); +pdf_font_attr:=xmalloc_array(str_number,font_max); +pdf_font_blink:=xmalloc_array(internal_font_number,font_max); +pdf_font_elink:=xmalloc_array(internal_font_number,font_max); +pdf_font_stretch:=xmalloc_array(integer,font_max); +pdf_font_shrink:=xmalloc_array(integer,font_max); +pdf_font_step:=xmalloc_array(integer,font_max); +pdf_font_expand_ratio:=xmalloc_array(integer,font_max); +pdf_font_auto_expand:=xmalloc_array(boolean,font_max); +pdf_font_lp_base:=xmalloc_array(integer,font_max); +pdf_font_rp_base:=xmalloc_array(integer,font_max); +pdf_font_ef_base:=xmalloc_array(integer,font_max); +pdf_font_kn_bs_base:=xmalloc_array(integer, font_max); +pdf_font_st_bs_base:=xmalloc_array(integer, font_max); +pdf_font_sh_bs_base:=xmalloc_array(integer, font_max); +pdf_font_kn_bc_base:=xmalloc_array(integer, font_max); +pdf_font_kn_ac_base:=xmalloc_array(integer, font_max); +vf_packet_base:=xmalloc_array(integer,font_max); +vf_default_font:=xmalloc_array(internal_font_number,font_max); +vf_local_font_num:=xmalloc_array(internal_font_number,font_max); +vf_e_fnts:=xmalloc_array(integer,font_max); +vf_i_fnts:=xmalloc_array(internal_font_number,font_max); -@p procedure ensure_pdf_open; -begin - if output_file_name <> 0 then - return; - if job_name = 0 then - open_log_file; - pack_job_name(".pdf"); - while not b_open_out(pdf_file) do - prompt_file_name("file name for output",".pdf"); - output_file_name := b_make_name_string(pdf_file); +for font_k := font_base to font_max do begin + for k := 0 to 31 do + pdf_char_used[font_k, k] := 0; + pdf_font_size[font_k] := 0; + pdf_font_num[font_k] := 0; + pdf_font_map[font_k] := 0; + pdf_font_type[font_k] := new_font_type; + pdf_font_attr[font_k] := ""; + pdf_font_blink[font_k] := null_font; + pdf_font_elink[font_k] := null_font; + pdf_font_stretch[font_k] := null_font; + pdf_font_shrink[font_k] := null_font; + pdf_font_step[font_k] := 0; + pdf_font_expand_ratio[font_k] := 0; + pdf_font_auto_expand[font_k] := false; + pdf_font_lp_base[font_k] := 0; + pdf_font_rp_base[font_k] := 0; + pdf_font_ef_base[font_k] := 0; + pdf_font_kn_bs_base[font_k] := 0; + pdf_font_st_bs_base[font_k] := 0; + pdf_font_sh_bs_base[font_k] := 0; + pdf_font_kn_bc_base[font_k] := 0; + pdf_font_kn_ac_base[font_k] := 0; end; -@ The PDF buffer is flushed by calling |pdf_flush|, which checks the -variable |zip_write_state| and will compress the buffer before flushing if -neccesary. We call |pdf_begin_stream| to begin a stream and |pdf_end_stream| -to finish it. The stream contents will be compressed if compression is turn on. +font_ptr:=null_font; fmem_ptr:=7; +make_pdftex_banner; +@z -@p procedure pdf_flush; {flush out the |pdf_buf|} -begin - ensure_pdf_open; - check_pdfminorversion; - case zip_write_state of - no_zip: if pdf_ptr > 0 then begin - write_pdf(0, pdf_ptr - 1); - pdf_gone := pdf_gone + pdf_ptr; - end; - zip_writing: - write_zip(false); - zip_finish: begin - write_zip(true); - zip_write_state := no_zip; - end; - end; - pdf_ptr := 0; -end; +@x [53a.1379] l.??? -etex command line switch +@!init if (buffer[loc]="*")and(format_ident=" (INITEX)") then +@y +@!init if (etex_p or(buffer[loc]="*"))and(format_ident=" (INITEX)") then +@z -procedure pdf_begin_stream; {begin a stream} -begin - ensure_pdf_open; - check_pdfminorversion; - pdf_print_ln("/Length "); - pdf_stream_length_offset := pdf_offset - 11; - pdf_stream_length := 0; - if pdf_compress_level > 0 then begin - pdf_print_ln("/Filter /FlateDecode"); - pdf_print_ln(">>"); - pdf_print_ln("stream"); - pdf_flush; - zip_write_state := zip_writing; - end - else begin - pdf_print_ln(">>"); - pdf_print_ln("stream"); - pdf_save_offset := pdf_offset; - end; -end; +@x [53a.1379] l.??? -etex command line switch + incr(loc); eTeX_mode:=1; {enter extended mode} +@y + if (buffer[loc]="*") then incr(loc); + eTeX_mode:=1; {enter extended mode} +@z -procedure pdf_end_stream; {end a stream} -begin - if pdf_compress_level > 0 then - zip_write_state := zip_finish - else - pdf_stream_length := pdf_offset - pdf_save_offset; - pdf_flush; - write_stream_length(pdf_stream_length, pdf_stream_length_offset); - if pdf_compress_level > 0 then - pdf_out(pdf_new_line_char); - pdf_print_ln("endstream"); - pdf_end_obj; -end; +@x [53a.1383] l.??? -etex command line switch +@!eTeX_mode: 0..1; {identifies compatibility and extended mode} +@y +@!eTeX_mode: 0..1; {identifies compatibility and extended mode} +@!etex_p: boolean; {was the -etex option specified} +@z -@ Basic printing procedures for PDF output are very similiar to \TeX\ basic -printing ones but the output is going to PDF buffer. Subroutines with -suffix |_ln| append a new-line character to the PDF output. +@x [53a.1391] l.??? texarray +@!eof_seen : array[1..max_in_open] of boolean; {has eof been seen?} +@y +@!eof_seen : ^boolean; {has eof been seen?} +@z -@d pdf_new_line_char == 10 {new-line character for UNIX platforms} +@x [53a.1506] l.??? texarray +@!grp_stack : array[0..max_in_open] of save_pointer; {initial |cur_boundary|} +@!if_stack : array[0..max_in_open] of pointer; {initial |cond_ptr|} +@y +@!grp_stack : ^save_pointer; {initial |cur_boundary|} +@!if_stack : ^pointer; {initial |cond_ptr|} +@z -@d pdf_print_nl == {output a new-line character to PDF buffer} -if (pdf_ptr > 0) and (pdf_buf[pdf_ptr - 1] <> pdf_new_line_char) then - pdf_out(pdf_new_line_char) +@x [53a.1587] l.??? texarray +hyph_root:=0; hyph_start:=0; +@y +@z -@d pdf_print_ln(#) == {print out a string to PDF buffer followed by -a new-line character} -begin - pdf_print(#); - pdf_print_nl; -end +@x [54/web2c.???] l.??? needed earlier +replacement, but always existing character |font_bc[f]|. +@^inner loop@> -@d pdf_print_int_ln(#) == {print out an integer to PDF buffer followed by -a new-line character} -begin - pdf_print_int(#); - pdf_print_nl; -end +@= +function effective_char(@!err_p:boolean; +@y +replacement, but always existing character |font_bc[f]|. +@^inner loop@> -@= -procedure pdf_error(t, p: str_number); -begin - normalize_selector; - print_err("pdfTeX error"); - if t <> 0 then begin - print(" ("); - print(t); - print(")"); - end; - print(": "); print(p); - succumb; -end; +@= +function effective_char(@!err_p:boolean; +@z -procedure pdf_warning(t, p: str_number; append_nl: boolean); -begin - print_err("pdfTeX warning"); - if t <> 0 then begin - print(" ("); - print(t); - print(")"); - end; - print(": "); print(p); - if append_nl then - print_ln; -end; - -procedure remove_last_space; -begin - if (pdf_ptr > 0) and (pdf_buf[pdf_ptr - 1] = 32) then - decr(pdf_ptr); -end; - -procedure pdf_print_octal(n: integer); {prints an integer in octal form to -PDF buffer} -var k:0..23; {index to current digit; we assume that $|n|<10^{23}$} -begin - k:=0; - repeat dig[k]:=n mod 8; n:=n div 8; incr(k); - until n=0; - if k = 1 then begin - pdf_out("0"); - pdf_out("0"); - end; - if k = 2 then - pdf_out("0"); - while k>0 do begin - decr(k); - pdf_out("0"+dig[k]); - end; -end; - -procedure pdf_print_char(f: internal_font_number; c: integer); -{ print out a character to PDF buffer; the character will be printed in octal - form in the following cases: chars <= 32, backslash (92), left parenthesis - (40) and right parenthesis (41) } -begin - if c <= 32 then - c := pdf_char_map[f, c]; - pdf_mark_char(f, c); - if (c <= 32) or (c = 92) or (c = 40) or (c = 41) or (c > 127) then begin - pdf_out(92); {output a backslash} - pdf_print_octal(c); - end - else - pdf_out(c); -end; - -procedure pdf_print(s: str_number); {print out a string to PDF buffer} -var j: pool_pointer; {current character code position} - c: integer; -begin - j := str_start[s]; - while j < str_start[s + 1] do begin - c := str_pool[j]; - pdf_out(c); - incr(j); - end; -end; - -function str_in_str(s, r: str_number; i: integer): boolean; - {test equality of strings} -label not_found; {loop exit} -var j, k: pool_pointer; {running indices} -@!result: boolean; {result of comparison} -begin - str_in_str := false; - if length(s) < i + length(r) then - return; - j := i + str_start[s]; - k := str_start[r]; - while (j < str_start[s + 1]) and (k < str_start[r + 1]) do begin - if str_pool[j] <> str_pool[k] then - return; - incr(j); - incr(k); - end; - str_in_str := true; -end; - -procedure literal(s: str_number; literal_mode: integer; warn: boolean); -var j: pool_pointer; {current character code position} -begin - j:=str_start[s]; - if literal_mode = scan_special then begin - if not (str_in_str(s, "PDF:", 0) or str_in_str(s, "pdf:", 0)) then begin - if warn and not (str_in_str(s, "SRC:", 0) - or str_in_str(s, "src:", 0) - or (length(s) = 0)) then - print_nl("Non-PDF special ignored!"); - return; - end; - j := j + length("PDF:"); - if str_in_str(s, "direct:", length("PDF:")) then begin - j := j + length("direct:"); - literal_mode := direct_always; end - else if str_in_str(s, "page:", length("PDF:")) then begin - j := j + length("page:"); - literal_mode := direct_page; end - else - literal_mode := reset_origin; - end; - case literal_mode of - reset_origin: begin - pdf_end_text; - pdf_set_origin; - end; - direct_page: - pdf_end_text; - direct_always: begin - pdf_first_space_corr := 0; - pdf_end_string; - pdf_print_nl; - end; - othercases confusion("literal1") - endcases; - while j-100000000 then negate(n) - else begin m:=-1-n; n:=m div 10; m:=(m mod 10)+1; k:=1; - if m<10 then dig[0]:=m - else begin dig[0]:=0; incr(n); - end; - end; - end; - repeat dig[k]:=n mod 10; n:=n div 10; incr(k); - until n=0; - pdf_room(k); - while k>0 do begin - decr(k); - pdf_quick_out("0"+dig[k]); - end; -end; - -procedure pdf_print_two(n:integer); {prints two least significant digits in -decimal form to PDF buffer} -begin n:=abs(n) mod 100; pdf_out("0"+(n div 10)); -pdf_out("0"+(n mod 10)); -end; - -function tokens_to_string(p: pointer): str_number; {return a string from tokens -list} -begin - if selector = new_string then - pdf_error("tokens", "tokens_to_string() called while selector = new_string"); - old_setting:=selector; selector:=new_string; - show_token_list(link(p),null,pool_size-pool_ptr); - selector:=old_setting; - last_tokens_string := make_string; - tokens_to_string := last_tokens_string; -end; - - -@ To print |scaled| value to PDF output we need some subroutines to ensure -accurary. - -@d max_integer == @"7FFFFFFF {$2^{31}-1$} -@d call_func(#) == begin if # <> 0 then do_nothing end - -@= -@!one_bp: scaled; {scaled value corresponds to 1bp} -@!one_hundred_bp: scaled; {scaled value corresponds to 100bp} -@!one_hundred_inch: scaled; {scaled value corresponds to 100in} -@!ten_pow: array[0..9] of integer; {$10^0..10^9$} -@!scaled_out: integer; {amount of |scaled| that was taken out in -|divide_scaled|} -@!init_pdf_output: boolean; - -@ @= -one_bp := 65782; {65781.76} -one_hundred_bp := 6578176; -one_hundred_inch := 473628672; -ten_pow[0] := 1; -for i := 1 to 9 do - ten_pow[i] := 10*ten_pow[i - 1]; -init_pdf_output := false; - - -@ The following function divides |s| by |m|. |dd| is number of decimal digits. - -@= -function divide_scaled(s, m: scaled; dd: integer): scaled; -var q, r: scaled; - sign, i: integer; -begin - sign := 1; - if s < 0 then begin - sign := -sign; - s := -s; - end; - if m < 0 then begin - sign := -sign; - m := -m; - end; - if m = 0 then - pdf_error("arithmetic", "divided by zero") - else if m >= (max_integer div 10) then - pdf_error("arithmetic", "number too big"); - q := s div m; - r := s mod m; - for i := 1 to dd do begin - q := 10*q + (10*r) div m; - r := (10*r) mod m; - end; - if 2*r >= m then begin - incr(q); - r := r - m; - end; - scaled_out := sign*(s - (r div ten_pow[dd])); - divide_scaled := sign*q; -end; - -function round_xn_over_d(@!x:scaled; @!n,@!d:integer):scaled; -var positive:boolean; {was |x>=0|?} -@!t,@!u,@!v:nonnegative_integer; {intermediate quantities} -begin if x>=0 then positive:=true -else begin negate(x); positive:=false; - end; -t:=(x mod @'100000)*n; -u:=(x div @'100000)*n+(t div @'100000); -v:=(u mod d)*@'100000 + (t mod @'100000); -if u div d>=@'100000 then arith_error:=true -else u:=@'100000*(u div d) + (v div d); -v := v mod d; -if 2*v >= d then - incr(u); -if positive then - round_xn_over_d := u -else - round_xn_over_d := -u; -end; - - -@ Next subroutines are needed for controling spacing in PDF page description. -The procedure |adv_char_width| advances |pdf_h| by the amount |w|, which is -the character width. We cannot simply add |w| to |pdf_h|, but must -calculate the amount corresponding to |w| in the PDF output. For PK fonts -things are more complicated, as we have to deal with scaling bitmaps as well. - -@p -procedure adv_char_width(f: internal_font_number; w: scaled); {update |pdf_h| -by character width |w| from font |f|} -begin - if hasfmentry(f) then begin - call_func(divide_scaled(w, pdf_font_size[f], 3)); - pdf_h := pdf_h + scaled_out; - end - else - pdf_h := pdf_h + get_pk_char_width(f, w); -end; - -procedure pdf_print_real(m, d: integer); {print $m/10^d$ as real} -var n: integer; -begin - if m < 0 then begin - pdf_out("-"); - m := -m; - end; - n := ten_pow[d]; - pdf_print_int(m div n); - m := m mod n; - if m > 0 then begin - pdf_out("."); - n := n div 10; - while m < n do begin - pdf_out("0"); - n := n div 10; - end; - while m mod 10 = 0 do - m := m div 10; - pdf_print_int(m); - end; -end; - -procedure pdf_print_bp(s: scaled); {print scaled as |bp|} -begin - pdf_print_real(divide_scaled(s, one_hundred_bp, fixed_decimal_digits + 2), - fixed_decimal_digits); -end; - -procedure pdf_print_mag_bp(s: scaled); {take |mag| into account} -begin - prepare_mag; - if mag <> 1000 then - s := round_xn_over_d(s, mag, 1000); - pdf_print_bp(s); -end; - -@* \[32c] PDF page description. - -@d pdf_x(#) == ((#) - pdf_origin_h) {convert $x$-coordinate from \.{DVI} to -PDF} -@d pdf_y(#) == (pdf_origin_v - (#)) {convert $y$-coordinate from \.{DVI} to -PDF} - -@= -@!pdf_f: internal_font_number; {the current font in PDF output page} -@!pdf_h: scaled; {current horizontal coordinate in PDF output page} -@!pdf_v: scaled; {current vertical coordinate in PDF output page} -@!pdf_last_h: scaled; {last horizontal coordinate in PDF output page} -@!pdf_last_v: scaled; {last vertical coordinate in PDF output page} -@!pdf_origin_h: scaled; {current horizontal origin in PDF output page} -@!pdf_origin_v: scaled; {current vertical origin in PDF output page} -@!pdf_first_space_corr: integer; {amount of first word spacing while drawing a string; -for some reason it is not taken into account of the length of the string, so we -have to save it in order to adjust spacing when string drawing is finished} -@!pdf_doing_string: boolean; {we are writing string to PDF file?} -@!pdf_doing_text: boolean; {we are writing text section to PDF file?} -@!pdf_font_changed: boolean; {current font has been changed?} -@!min_bp_val: scaled; -@!fixed_pk_resolution: integer; -@!fixed_decimal_digits: integer; -@!pk_scale_factor: integer; -@!pdf_output_option: integer; -@!pdf_output_value: integer; - -@ Following procedures implement low-level subroutines to convert \TeX{} -internal structures to PDF page description. - -@p procedure pdf_set_origin; {set the origin to |cur_h|, |cur_v|} -begin - if (abs(cur_h - pdf_origin_h) >= min_bp_val) or - (abs(cur_v - pdf_origin_v) >= min_bp_val) then begin - pdf_print("1 0 0 1 "); - pdf_print_bp(cur_h - pdf_origin_h); - pdf_origin_h := pdf_origin_h + scaled_out; - pdf_out(" "); - pdf_print_bp(pdf_origin_v - cur_v); - pdf_origin_v := pdf_origin_v - scaled_out; - pdf_print_ln(" cm"); - end; - pdf_h := pdf_origin_h; - pdf_last_h := pdf_origin_h; - pdf_v := pdf_origin_v; - pdf_last_v := pdf_origin_v; -end; - -procedure pdf_end_string; {end the current string} -begin - if pdf_doing_string then begin - pdf_print(")]TJ"); - if pdf_first_space_corr <> 0 then begin - pdf_h := pdf_h - pdf_first_space_corr; - pdf_first_space_corr := 0; - end; - pdf_doing_string := false; - end; -end; - -procedure pdf_moveto(v, v_out: scaled); {set the next starting point to |cur_h|, |cur_v|} -begin - pdf_out(" "); - pdf_print_bp(cur_h - pdf_last_h); - pdf_h := pdf_last_h + scaled_out; - pdf_out(" "); - pdf_print_real(v, fixed_decimal_digits); - pdf_print(" Td"); - pdf_v := pdf_last_v - v_out; - pdf_last_h := pdf_h; - pdf_last_v := pdf_v; -end; - -procedure pdf_begin_text; forward; - -procedure pdf_print_font_tag(f: internal_font_number); -begin - if pdf_font_expand_ratio[f] = 0 then - return; - if pdf_font_expand_ratio[f] > 0 then - pdf_print("+"); {minus sign will be printed by |pdf_print_int|} - pdf_print_int(pdf_font_expand_ratio[f]); -end; - -procedure pdf_set_font(f: internal_font_number); -label found; -var p: pointer; - k: internal_font_number; -begin - if not font_used[f] then - pdf_init_font(f); - set_ff(f); - k := ff; - p := pdf_font_list; - while p <> null do begin - set_ff(info(p)); - if ff = k then - goto found; - p := link(p); - end; - pdf_append_list(f)(pdf_font_list); {|f| not found in |pdf_font_list|} -found: - pdf_begin_text; - pdf_print("/F"); - pdf_print_int(k); - pdf_print_font_tag(f); - pdf_print_resname_prefix; - pdf_out(" "); - pdf_print_bp(font_size[f]); - pdf_print(" Tf"); - pdf_f := f; - pdf_font_changed := true; -end; - -procedure pdf_begin_text; {begin a text section} -var temp_cur_h, temp_cur_v: scaled; -begin - if not pdf_doing_text then begin - temp_cur_h := cur_h; - temp_cur_v := cur_v; - cur_h := 0; - cur_v := cur_page_height; - pdf_set_origin; - cur_h := temp_cur_h; - cur_v := temp_cur_v; - pdf_print_ln("BT"); - pdf_doing_text := true; - pdf_f := null_font; - pdf_first_space_corr := 0; - pdf_doing_string := false; - end; -end; - -procedure pdf_begin_string(f: internal_font_number); {begin to draw a string} -var b: boolean; {|b| is true only when we must adjust word spacing -at the beginning of string} - s, s_out, v, v_out: scaled; -begin - pdf_begin_text; - if f <> pdf_f then begin - pdf_end_string; - pdf_set_font(f); - end; - b := false; - s := divide_scaled(cur_h - pdf_h, pdf_font_size[f], 3); - s_out := scaled_out; - if abs(cur_v - pdf_v) >= min_bp_val then begin - v := divide_scaled(pdf_last_v - cur_v, one_hundred_bp, - fixed_decimal_digits + 2); - v_out := scaled_out; - end - else begin - v := 0; - v_out := 0; - end; - if pdf_font_changed or (v <> 0) or (abs(s) >= @'100000) then begin - pdf_end_string; - pdf_moveto(v, v_out); - pdf_font_changed := false; - s := 0; - s_out := 0; - end; - if pdf_doing_string then begin - if s <> 0 then - pdf_out(")"); - end - else begin - pdf_out("["); - if s <> 0 then - b := true - else - pdf_out("("); - pdf_doing_string := true; - end; - if s <> 0 then begin - pdf_print_int(-s); - if b then - pdf_first_space_corr := s_out; - pdf_out("("); - pdf_h := pdf_h + s_out; - end; -end; - -procedure pdf_end_text; {end a text section} -begin - if pdf_doing_text then begin - pdf_end_string; - pdf_print_nl; - pdf_print_ln("ET"); - pdf_doing_text := false; - end; -end; - -procedure pdf_set_rule(x, y, w, h: scaled); {draw a rule} -begin - pdf_end_text; - pdf_set_origin; - if h <= one_bp then begin - pdf_print_ln("q"); - pdf_print_ln("[]0 d"); - pdf_print_ln("0 J"); - pdf_print_bp(h); pdf_print_ln(" w"); - pdf_print("0 "); pdf_print_bp((h + 1)/2); pdf_print_ln(" m"); - pdf_print_bp(w); pdf_print(" "); pdf_print_bp((h + 1)/2); - pdf_print_ln(" l"); - pdf_print_ln("S"); - pdf_print_ln("Q"); - end - else if w <= one_bp then begin - pdf_print_ln("q"); - pdf_print_ln("[]0 d"); - pdf_print_ln("0 J"); - pdf_print_bp(w); pdf_print_ln(" w"); - pdf_print_bp((w + 1)/2); pdf_print_ln(" 0 m"); - pdf_print_bp((w + 1)/2); pdf_print(" "); pdf_print_bp(h); - pdf_print_ln(" l"); - pdf_print_ln("S"); - pdf_print_ln("Q"); - end - else begin - pdf_print_bp(pdf_x(x)); pdf_out(" "); - pdf_print_bp(pdf_y(y)); pdf_out(" "); - pdf_print_bp(w); pdf_out(" "); - pdf_print_bp(h); pdf_print_ln(" re f"); - end; -end; - - -procedure pdf_rectangle(left, top, right, bottom: scaled); {output a -rectangle specification to PDF file} -begin - prepare_mag; - pdf_print("/Rect ["); - pdf_print_mag_bp(pdf_x(left)); pdf_out(" "); - pdf_print_mag_bp(pdf_y(bottom)); pdf_out(" "); - pdf_print_mag_bp(pdf_x(right)); pdf_out(" "); - pdf_print_mag_bp(pdf_y(top)); - pdf_print_ln("]"); -end; - -@* \[32d] The cross-reference table. - -The cross-reference table |obj_tab| is an array of |obj_tab_size| of -|tab_entry|. Each entry contains four integer fields and represents an object -in PDF file whose object number is the index of this entry in |obj_tab|. -Objects in |obj_tab| maybe linked into list; objects in such a linked list have -the same type. - -@= -@!obj_entry = record@;@/ - int0, int1, int2, int3: integer; -end; - -@ The first field contains information representing identifier of this object. -It is usally a number for most of object types, but it may be a string number -for named destination or named thread. - -The second field of |obj_entry| contains link to the next -object in |obj_tab| if this object in linked in a list. - -The third field holds the byte offset of the object in the output PDF file. -Objects that have been not written yet have this field set to zero. However -sometimes we have to use this field to store some info before the object is -written out. - -The last field usually represents the pointer to some auxiliary data -structure depending on the object type; however it may be used as a counter as -well. - -@d obj_info(#) == obj_tab[#].int0 {information representing identifier of this object} -@d obj_link(#) == obj_tab[#].int1 {link to the next entry in linked list} -@d obj_offset(#) == obj_tab[#].int2 {byte offset of this object in PDF output file} -@d obj_aux(#) == obj_tab[#].int3 {auxiliary pointer} -@d is_obj_written(#) == (obj_offset(#) <> 0) - -@# {types of objects} -@d obj_type_others == 0 {objects which are not linked in any list} -@d obj_type_page == 1 {index of linked list of Page objects} -@d obj_type_pages == 2 {index of linked list of Pages objects} -@d obj_type_font == 3 {index of linked list of Fonts objects} -@d obj_type_outline == 4 {index of linked list of outline objects} -@d obj_type_dest == 5 {index of linked list of destination objects} -@d obj_type_obj == 6 {index of linked list of raw objects} -@d obj_type_xform == 7 {index of linked list of XObject forms} -@d obj_type_ximage == 8 {index of linked list of XObject image} -@d obj_type_thread == 9 {index of linked list of num article threads} -@d head_tab_max == obj_type_thread {max index of |head_tab|} - -@# {max number of kids for balanced trees} -@d pages_tree_kids_max == 6 {max number of kids of Pages tree node} -@d name_tree_kids_max == @'100000 {max number of kids of node of name tree for -name destinations} - -@# {when a whatsit node representing annotation is created, words |1..3| are -width, height and depth of this annotation; after shipping out words |1..4| -are rectangle specification of annotation. For whatsit node representing -destination |pdf_left| and |pdf_top| are used for some types of destinations} - -@# {coordinates of destinations/threads/annotations (in whatsit node)} -@d pdf_left(#) == mem[# + 1].sc -@d pdf_top(#) == mem[# + 2].sc -@d pdf_right(#) == mem[# + 3].sc -@d pdf_bottom(#) == mem[# + 4].sc - -@# {dimesion of destinations/threads/annotations (in whatsit node)} -@d pdf_width(#) == mem[# + 1].sc -@d pdf_height(#) == mem[# + 2].sc -@d pdf_depth(#) == mem[# + 3].sc - -@# {data structure for \.{\\pdfliteral}} -@d pdf_literal_data(#) == link(#+1) {data} -@d pdf_literal_mode(#) == info(#+1) {mode of resetting the text matrix - while writing data to the page stream} -@# {modes of resetting the text matrix} -@d reset_origin == 0 {end text (ET) if needed, reset text matrix} -@d direct_page == 1 {end text (ET) if needed, don't reset text matrix} -@d direct_always == 2 {don't reset text matrix} -@d scan_special == 3 {look into special text} - -@# {data structure for \.{\\pdfobj} and \.{\\pdfrefobj}} -@d pdf_refobj_node_size == 2 {size of whatsit node representing the raw object} -@d pdf_obj_objnum(#) == info(# + 1) {number of the raw object} -@d obj_data_ptr == obj_aux {pointer to |pdf_mem|} -@d pdfmem_obj_size == 4 {size of memory in |pdf_mem| which - |obj_data_ptr| holds} -@d obj_obj_data(#) == pdf_mem[obj_data_ptr(#) + 0] {object data} -@d obj_obj_is_stream(#) == pdf_mem[obj_data_ptr(#) + 1] {will this object - be written as a stream instead of a dictionary?} -@d obj_obj_stream_attr(#) == pdf_mem[obj_data_ptr(#) + 2] {additional - object attributes for streams} -@d obj_obj_is_file(#) == pdf_mem[obj_data_ptr(#) + 3] {data should be - read from an external file?} - -@# {data structure for \.{\\pdfxform} and \.{\\pdfrefxform}} -@d pdf_refxform_node_size == 5 {size of whatsit node for xform; words 1..3 are - form dimensions} -@d pdf_xform_objnum(#) == info(# + 4) {object number} -@d pdfmem_xform_size == 6 {size of memory in |pdf_mem| which - |obj_data_ptr| holds} -@d obj_xform_width(#) == pdf_mem[obj_data_ptr(#) + 0] -@d obj_xform_height(#) == pdf_mem[obj_data_ptr(#) + 1] -@d obj_xform_depth(#) == pdf_mem[obj_data_ptr(#) + 2] -@d obj_xform_box(#) == pdf_mem[obj_data_ptr(#) + 3] {this field holds - pointer to the corresponding box} -@d obj_xform_attr(#) == pdf_mem[obj_data_ptr(#) + 4] {additional xform - attributes} -@d obj_xform_resources(#) == pdf_mem[obj_data_ptr(#) + 5] {additional xform - Resources} - -@# {data structure for \.{\\pdfximage} and \.{\\pdfrefximage}} -@d pdf_refximage_node_size == 5 {size of whatsit node for ximage; words 1..3 - are image dimensions} -@d pdf_ximage_objnum(#) == info(# + 4) {object number} -@d pdfmem_ximage_size == 5 {size of memory in |pdf_mem| which - |obj_data_ptr| holds} -@d obj_ximage_width(#) == pdf_mem[obj_data_ptr(#) + 0] -@d obj_ximage_height(#) == pdf_mem[obj_data_ptr(#) + 1] -@d obj_ximage_depth(#) == pdf_mem[obj_data_ptr(#) + 2] -@d obj_ximage_attr(#) == pdf_mem[obj_data_ptr(#) + 3] {additional ximage attributes} -@d obj_ximage_data(#) == pdf_mem[obj_data_ptr(#) + 4] {pointer to image data} - -@# {data structure of annotations; words 1..4 represent the coordinates of - the annotation} -@d obj_annot_ptr == obj_aux {pointer to corresponding whatsit node} -@d pdf_annot_node_size == 7 {size of whatsit node representing annotation} -@d pdf_annot_data(#) == info(# + 5) {raw data of general annotations} -@d pdf_link_attr(#) == info(# + 5) {attributes of link annotations} -@d pdf_link_action(#) == link(# + 5) {pointer to action structure} -@d pdf_annot_objnum(#) == mem[# + 6].int {object number of corresponding object} - -@# {types of actions} -@d pdf_action_page == 0 {Goto action} -@d pdf_action_goto == 1 {Goto action} -@d pdf_action_thread == 2 {Thread action} -@d pdf_action_user == 3 {user-defined action} - -@# {data structure of actions} -@d pdf_action_size == 3 {size of action structure in |mem|} -@d pdf_action_type == type {action type} -@d pdf_action_named_id == subtype {identifier is type of name} -@d pdf_action_id == link {destination/thread name identifier} -@d pdf_action_file(#) == info(# + 1) {file name for external action} -@d pdf_action_new_window(#)== link(# + 1) {open a new window?} -@d pdf_action_page_tokens(#) == info(# + 2) {specification of goto page action} -@d pdf_action_user_tokens(#) == info(# + 2) {user-defined action string} -@d pdf_action_refcount(#) == link(# + 2) {counter of references to this action} - -@# {data structure of outlines; it's not able to write out outline entries -before all outline entries are defined, so memory allocated for outline -entries can't not be deallocated and will stay in memory. For this reason we -will store data of outline entries in |pdf_mem| instead of |mem|} - -@d pdfmem_outline_size == 8 {size of memory in |pdf_mem| which -|obj_outline_ptr| points to} -@d obj_outline_count == obj_info{count of all opened children} -@d obj_outline_ptr == obj_aux {pointer to |pdf_mem|} -@d obj_outline_title(#) == pdf_mem[obj_outline_ptr(#)] -@d obj_outline_parent(#) == pdf_mem[obj_outline_ptr(#) + 1] -@d obj_outline_prev(#) == pdf_mem[obj_outline_ptr(#) + 2] -@d obj_outline_next(#) == pdf_mem[obj_outline_ptr(#) + 3] -@d obj_outline_first(#) == pdf_mem[obj_outline_ptr(#) + 4] -@d obj_outline_last(#) == pdf_mem[obj_outline_ptr(#) + 5] -@d obj_outline_action_objnum(#) == pdf_mem[obj_outline_ptr(#) + 6] {object number of -action} -@d obj_outline_attr(#) == pdf_mem[obj_outline_ptr(#) + 7] - -@# {types of destinations} -@d pdf_dest_xyz == 0 -@d pdf_dest_fit == 1 -@d pdf_dest_fith == 2 -@d pdf_dest_fitv == 3 -@d pdf_dest_fitb == 4 -@d pdf_dest_fitbh == 5 -@d pdf_dest_fitbv == 6 -@d pdf_dest_fitr == 7 - -@# {data structure of destinations} -@d obj_dest_ptr == obj_aux {pointer to |pdf_dest_node|} -@d pdf_dest_node_size == 7 {size of whatsit node for destination; -words |1..4| hold dest dimensions, word |6| identifier type, subtype -and identifier of destination, word |6| the corresponding object number} -@d pdf_dest_type(#) == type(# + 5) {type of destination} -@d pdf_dest_named_id(#) == subtype(# + 5) {is named identifier?} -@d pdf_dest_id(#) == link(# + 5) {destination identifier} -@d pdf_dest_xyz_zoom(#) == info(# + 6) {zoom factor for |destxyz| destination} -@d pdf_dest_objnum(#) == link(# + 6) {object number of corresponding -object} - -@# {data structure of threads; words 1..4 represent the coordinates of the - corners} -@d pdf_thread_node_size == 7 -@d pdf_thread_named_id(#) == subtype(# + 5) {is a named identifier} -@d pdf_thread_id(#) == link(# + 5) {thread identifier} -@d pdf_thread_attr(#) == info(# + 6) {attributes of thread} -@d obj_thread_first == obj_aux {pointer to the first bead} - -@# {data structure of beads} -@d pdfmem_bead_size == 5 {size of memory in |pdf_mem| which - |obj_bead_ptr| points to} -@d obj_bead_ptr == obj_aux {pointer to |pdf_mem|} -@d obj_bead_rect(#) == pdf_mem[obj_bead_ptr(#)] -@d obj_bead_page(#) == pdf_mem[obj_bead_ptr(#) + 1] -@d obj_bead_next(#) == pdf_mem[obj_bead_ptr(#) + 2] -@d obj_bead_prev(#) == pdf_mem[obj_bead_ptr(#) + 3] -@d obj_bead_attr(#) == pdf_mem[obj_bead_ptr(#) + 4] -@d obj_bead_data == obj_bead_rect {pointer to the corresponding -whatsit node; |obj_bead_rect| is needed only when the bead rectangle has -been written out and after that |obj_bead_data| is not needed any more -so we can use this field for both} - -@= -@!inf_obj_tab_size = 32000; {min size of the cross-reference table for PDF output} -@!sup_obj_tab_size = 8388607; {max size of the cross-reference table for PDF output} -@!inf_dest_names_size = 10000; {min size of the destination names table for PDF output} -@!sup_dest_names_size = 131072; {max size of the destination names table for PDF output} -@!pdf_objtype_max = head_tab_max; -@!pdf_pdf_box_spec_media = 0; -@!pdf_pdf_box_spec_crop = 1; -@!pdf_pdf_box_spec_bleed = 2; -@!pdf_pdf_box_spec_trim = 3; -@!pdf_pdf_box_spec_art = 4; - -@ @= -@!obj_tab_size:integer; -@!obj_tab:^obj_entry; -@!head_tab: array[1..head_tab_max] of integer; -@!obj_ptr: integer; {objects counter} -@!pdf_last_pages: integer; {pointer to most recently generated pages object} -@!pdf_last_page: integer; {pointer to most recently generated page object} -@!pdf_last_stream: integer; {pointer to most recently generated stream} -@!pdf_stream_length: integer; {length of most recently generated stream} -@!pdf_stream_length_offset: integer; {file offset of the last stream length} -@!pdf_append_list_arg: integer; {for use with |pdf_append_list|} -@!ff: integer; {for use with |set_ff|} - -@ @= -obj_ptr := 0; -for k := 1 to head_tab_max do - head_tab[k] := 0; - -@ Here we implement subroutines for work with objects and related things. -Some of them are used in former parts too, so we need to declare them -forward. - -@d pdf_append_list_end(#) == # := append_ptr(#, pdf_append_list_arg); end -@d pdf_append_list(#) == begin pdf_append_list_arg := #; pdf_append_list_end -@d set_ff(#) == begin - if pdf_font_num[#] < 0 then - ff := -pdf_font_num[#] - else - ff := #; -end - -@= -procedure append_dest_name(s: str_number; n: integer); -begin - if pdf_dest_names_ptr = dest_names_size then - overflow("number of destination names", dest_names_size); - dest_names[pdf_dest_names_ptr].objname := s; - dest_names[pdf_dest_names_ptr].objnum := n; - incr(pdf_dest_names_ptr); -end; - -procedure pdf_create_obj(t, i: integer); {create an object with type |t| and -identifier |i|} -label done; -var p, q: integer; -begin - if obj_ptr = obj_tab_size then - overflow("indirect objects table size", obj_tab_size); - incr(obj_ptr); - obj_info(obj_ptr) := i; - obj_offset(obj_ptr) := 0; - obj_aux(obj_ptr) := 0; - avl_put_obj(obj_ptr, t); - if t = obj_type_page then begin - p := head_tab[t]; - {find the right position to insert newly created object}@/ - if (p = 0) or (obj_info(p) < i) then begin - obj_link(obj_ptr) := p; - head_tab[t] := obj_ptr; - end - else begin - while p <> 0 do begin - if obj_info(p) < i then - goto done; - q := p; - p := obj_link(p); - end; -done: - obj_link(q) := obj_ptr; - obj_link(obj_ptr) := p; - end; - end - else if t <> obj_type_others then begin - obj_link(obj_ptr) := head_tab[t]; - head_tab[t] := obj_ptr; - if (t = obj_type_dest) and (i < 0) then - append_dest_name(-obj_info(obj_ptr), obj_ptr); - end; -end; - -function pdf_new_objnum: integer; {create a new object and return its number} -begin - pdf_create_obj(obj_type_others, 0); - pdf_new_objnum := obj_ptr; -end; - -procedure pdf_begin_obj(i: integer); {begin a PDF object} -begin - ensure_pdf_open; - check_pdfminorversion; - obj_offset(i) := pdf_offset; - pdf_print_int(i); - pdf_print_ln(" 0 obj"); -end; - -procedure pdf_end_obj; -begin - pdf_print_ln("endobj"); {end a PDF object} -end; - -procedure pdf_begin_dict(i: integer); {begin a PDF dictionary object} -begin - obj_offset(i) := pdf_offset; - pdf_print_int(i); - pdf_print_ln(" 0 obj <<"); -end; - -procedure pdf_end_dict; {end a PDF object of type dictionary} -begin - pdf_print_ln(">> endobj"); -end; - -procedure pdf_new_obj(t, i: integer); {begin to a new object} -begin - pdf_create_obj(t, i); - pdf_begin_obj(obj_ptr); -end; - -procedure pdf_new_dict(t, i: integer); {begin a new object with type dictionary} -begin - pdf_create_obj(t, i); - pdf_begin_dict(obj_ptr); -end; - -function append_ptr(p: pointer; i: integer): pointer; {appends a pointer with -info |i| to the end of linked list with head |p|} -var q: pointer; -begin - append_ptr := p; - fast_get_avail(q); - info(q) := i; - link(q) := null; - if p = null then begin - append_ptr := q; - return; - end; - while link(p) <> null do - p := link(p); - link(p) := q; -end; - -function pdf_lookup_list(p: pointer; i: integer): pointer; {looks up for pointer -with info |i| in list |p|} -begin - pdf_lookup_list := null; - while p <> null do begin - if info(p) = i then begin - pdf_lookup_list := p; - return; - end; - p := link(p); - end; -end; - -@ @= -@!pdf_image_procset: integer; {collection of image types used in current page/form} -@!pdf_text_procset: boolean; {mask of used ProcSet's in the current page/form} - -@ Subroutines to print out various PDF objects - -@d is_hex_char(#) == (((# >= '0') and (# <= '9')) or - ((# >= 'A') and (# <= 'F')) or - ((# >= 'a') and (# <= 'f'))) - -@p procedure pdf_print_fw_int(n, w: integer); {print out an integer with -fixed width; used for outputting cross-reference table} -var k:integer; {$0\le k\le23$} -begin - k:=0; - repeat dig[k]:=n mod 10; n:=n div 10; incr(k); - until k = w; - pdf_room(k); - while k>0 do begin - decr(k); - pdf_quick_out("0"+dig[k]); - end; -end; - -procedure pdf_int_entry(s: str_number; v: integer); {print out an entry in -dictionary with integer value to PDF buffer} -begin - pdf_out("/"); - pdf_print(s); - pdf_out(" "); - pdf_print_int(v); -end; - -procedure pdf_int_entry_ln(s: str_number; v: integer); -begin - pdf_int_entry(s, v); - pdf_print_nl; -end; - -procedure pdf_indirect(s: str_number; o: integer); {print out an indirect -entry in dictionary} -begin - pdf_out("/"); - pdf_print(s); - pdf_out(" "); - pdf_print_int(o); - pdf_print(" 0 R"); -end; - -procedure pdf_indirect_ln(s: str_number; o: integer); -begin - pdf_indirect(s, o); - pdf_print_nl; -end; - -procedure pdf_print_str(s: str_number); {print out |s| as string in PDF -output} -label done; -var i, j, k: pool_pointer; - is_hex_string: boolean; -begin - i := str_start[s]; - j := i + length(s) - 1; - if i > j then begin - pdf_print("()"); {null string} - return; - end; - if (str_pool[i] = '(') and (str_pool[j] = ')') then begin - pdf_print(s); - return; - end; - is_hex_string := false; - if (str_pool[i] <> '<') or (str_pool[j] <> '>') or odd(length(s)) then - goto done; - incr(i); - decr(j); - while i < j do begin - if is_hex_char(str_pool[i]) and is_hex_char(str_pool[i + 1]) then - i := i + 2 - else - goto done; - end; - is_hex_string := true; -done: - if is_hex_string then - pdf_print(s) - else begin - pdf_out("("); - pdf_print(s); - pdf_out(")"); - end; -end; - -procedure pdf_print_str_ln(s: str_number); {print out |s| as string in PDF -output} -begin - pdf_print_str(s); - pdf_print_nl; -end; - -procedure pdf_str_entry(s, v: str_number); {print out an entry in -dictionary with string value to PDF buffer} -begin - if v = 0 then - return; - pdf_out("/"); - pdf_print(s); - pdf_out(" "); - pdf_print_str(v); -end; - -procedure pdf_str_entry_ln(s, v: str_number); -begin - if v = 0 then - return; - pdf_str_entry(s, v); - pdf_print_nl; -end; - -@* \[32e] Font processing. - -As pdfTeX should also act as a back-end driver, it needs to support virtual -font too. Information about virtual font can be found in source of some -\.{DVI}-related programs. - -Whenever we want to write out a character in a font to PDF output, we -should check whether the used font is new font (has not been used yet), -virtual font or real font. The array |pdf_font_type| holds flag of each used -font. After initialization flag of each font is set to |new_font_type|. -The first time when a character of a font is written out, pdfTeX looks for -the corresponding virtual font. If the corresponding virtual font exists, then -the font type is set to |virtual_font_type|; otherwise it will be set to -|real_font_type|. |subst_font_type| indicates fonts that have been substituted -during adjusting spacing. Such fonts are linked via the |pdf_font_elink| array. - -@d new_font_type = 0 {new font (has not been used yet)} -@d virtual_font_type = 1 {virtual font} -@d real_font_type = 2 {real font} -@d subst_font_type = 3 {substituted font} -@d pdf_init_font(#) == begin tmp_f := #; pdf_create_font_obj; end -@d pdf_check_vf(#) == begin - tmp_f := #; - do_vf; - if pdf_font_type[#] = virtual_font_type then - pdf_error("font", "command cannot be used with virtual font"); -end - -@d pdf_check_new_font(#) == - if pdf_font_type[#] = new_font_type then begin - tmp_f := #; - do_vf; - end - -@= -procedure pdf_create_font_obj; forward; -procedure do_vf; forward; -procedure scan_pdf_ext_toks; forward; - -@ @= -@!pdf_font_type: ^eight_bits; {the type of font} -@!pdf_font_attr: ^str_number; {pointer to additional attributes} -@!tmp_f: internal_font_number; {for use with |pdf_init_font|} - -@ To set PDF font we need to find out fonts with the same name, because \TeX\ -can load the same font several times for various sizes. For such fonts we -define only one font resource. The array |pdf_font_num| holds the object -number of font resource. A negative value of an entry of |pdf_font_num| -indicates that the corresponding font shares the font resource with the font -whose internal number is the absolute value of the entry. For partial -downloading we also need to hold flags indicating which characters in particular -font are used in array |pdf_char_used|. - -@d pdf_print_resname_prefix == -if pdf_resname_prefix <> 0 then - pdf_print(pdf_resname_prefix) - -@p procedure pdf_use_font(f: internal_font_number; fontnum: integer); -begin - call_func(divide_scaled(font_size[f], one_hundred_bp, - fixed_decimal_digits + 2)); - pdf_font_size[f] := scaled_out; - font_used[f] := true; - pdf_font_num[f] := fontnum; - if pdf_move_chars > 0 then begin - pdf_warning(0,"Primitive \pdfmovechars is obsolete.",true); - pdf_move_chars := 0; {warn only once} - end; -end; - -procedure pdf_create_font_obj; -var f, k: internal_font_number; -begin - f := tmp_f; - if not font_used[f] then begin - if hasfmentry(f) then begin - {this font is scaleable, so different sizes can share a single font object} - k := tfm_of_fm(pdf_font_map[f]); - if (k <> f) and str_eq_str(font_name[k], font_name[f]) then begin - pdf_use_font(f, -k); - return; - end; - end; - pdf_create_obj(obj_type_font, f); - pdf_use_font(f, obj_ptr); - end; -end; - -@ We need to hold information about used characters in each font for partial -downloading. - -@= -char_used_array = array[0..31] of eight_bits; -char_map_array = array[0..32] of eight_bits; {move chars in range 0..32} -fm_entry_ptr = ^integer; - -@ @= -@!pdf_char_used: ^char_used_array; {to mark used chars} -@!pdf_char_map: ^char_map_array; {where to map chars 0..32} -@!pdf_font_size: ^scaled; {used size of font in PDF file} -@!pdf_font_num: ^integer; {mapping between internal font number in \TeX\ and - font name defined in resources in PDF file} -@!pdf_font_map: ^fm_entry_ptr; {pointer into AVL tree of font mappings} -@!pdf_font_list: pointer; {list of used fonts in current page} -@!pdf_resname_prefix: str_number; {global prefix of resources name} -@!last_tokens_string: str_number; {the number of the most recently string -created by |tokens_to_string|} - -@ @= -pdf_resname_prefix := 0; -last_tokens_string := 0; - - -@ Here we implement reading information from \.{VF} file. - -@d vf_max_packet_length = 10000 {max length of character packet in \.{VF} file} - -@# -@d vf_error = 61 {label to go to when an error occur} -@d do_char = 70 {label to go to typesetting a character of virtual font} -@# -@d long_char = 242 {\.{VF} command for general character packet} -@d vf_id = 202 {identifies \.{VF} files} -@d put1=133 {typeset a character} - -@# -@d vf_byte == getc(vf_file) {get a byte from\.{VF} file} -@d vf_packet(#) == vf_packet_start[vf_packet_base[#] + vf_packet_end -@d vf_packet_end(#) == #] - -@# -@d bad_vf(#) == begin vf_err_str := #; goto vf_error; end {go out \.{VF} -processing with an error message} -@d four_cases(#) == #,#+1,#+2,#+3 - -@# -@d tmp_b0 == tmp_w.qqqq.b0 -@d tmp_b1 == tmp_w.qqqq.b1 -@d tmp_b2 == tmp_w.qqqq.b2 -@d tmp_b3 == tmp_w.qqqq.b3 -@d tmp_int == tmp_w.int - -@# -@d scaled3u == {convert |tmp_b1..tmp_b3| to an unsigned scaled dimension} -(((((tmp_b3*vf_z)div@'400)+(tmp_b2*vf_z))div@'400)+(tmp_b1*vf_z))div vf_beta -@d scaled4(#) == {convert |tmp_b0..tmp_b3| to a scaled dimension} - #:=scaled3u; - if tmp_b0>0 then if tmp_b0=255 then # := # - vf_alpha -@d scaled3(#) == {convert |tmp_b1..tmp_b3| to a scaled dimension} - #:=scaled3u; @+ if tmp_b1>127 then # := # - vf_alpha -@d scaled2 == {convert |tmp_b2..tmp_b3| to a scaled dimension} - if tmp_b2>127 then tmp_b1:=255 else tmp_b1:=0; - scaled3 -@d scaled1 == {convert |tmp_b3| to a scaled dimension} - if tmp_b3>127 then tmp_b1:=255 else tmp_b1:=0; - tmp_b2:=tmp_b1; scaled3 - -@= -@!vf_packet_base: ^integer; {base addresses of character packets from virtual fonts} -@!vf_default_font: ^internal_font_number; {default font in a \.{VF} file} -@!vf_local_font_num: ^internal_font_number; {number of local fonts in a \.{VF} file} -@!vf_packet_length: integer; {length of the current packet} -@!vf_file: byte_file; -@!vf_nf: internal_font_number; {the local fonts counter} -@!vf_e_fnts: ^integer; {external font numbers} -@!vf_i_fnts: ^internal_font_number; {corresponding internal font numbers} -@!tmp_w: memory_word; {accumulator} -@!vf_z: integer; {multiplier} -@!vf_alpha: integer; {correction for negative values} -@!vf_beta: 1..16; {divisor} - -@ @= -vf_nf := 0; - -@ The |do_vf| procedure attempts to read the \.{VF} file for a font, and sets -|pdf_font_type| to |real_font_type| if the \.{VF} file could not be found -or loaded, otherwise sets |pdf_font_type| to |virtual_font_type|. At this -time, |tmp_f| is the internal font number of the current \.{TFM} font. To -process font definitions in virtual font we call |vf_def_font|. - -@p procedure vf_replace_z; -begin - vf_alpha:=16; - while vf_z>=@'40000000 do begin - vf_z:=vf_z div 2; - vf_alpha:=vf_alpha+vf_alpha; - end; - vf_beta:=256 div vf_alpha; - vf_alpha:=vf_alpha*vf_z; -end; - -function vf_read(k: integer): integer; {read |k| bytes as an integer from \.{VF} file} -var i: integer; -begin - i := 0; - while k > 0 do begin - i := i*256 + vf_byte; - decr(k); - end; - vf_read := i; -end; - -procedure vf_local_font_warning(f, k: internal_font_number; s: str_number); -{print a warning message if an error ocurrs during processing local fonts in -\.{VF} file} -begin - print_nl(s); - print(" in local font "); - print(font_name[k]); - print(" in virtual font "); - print(font_name[f]); - print(".vf ignored."); -end; - -function vf_def_font(f: internal_font_number): internal_font_number; -{process a local font in \.{VF} file} -var k: internal_font_number; - s: str_number; - ds, fs: scaled; - cs: four_quarters; - c: integer; -begin - cs.b0 := vf_byte; cs.b1 := vf_byte; cs.b2 := vf_byte; cs.b3 := vf_byte; - tmp_b0 := vf_byte; tmp_b1 := vf_byte; tmp_b2 := vf_byte; tmp_b3 := vf_byte; - scaled4(fs); - ds := vf_read(4) div @'20; - tmp_b0 := vf_byte; - tmp_b1 := vf_byte; - while tmp_b0 > 0 do begin - decr(tmp_b0); - if vf_byte > 0 then - do_nothing; {skip the font path} - end; - str_room(tmp_b1); - while tmp_b1 > 0 do begin - decr(tmp_b1); - append_char(vf_byte); - end; - s := make_string; - k := tfm_lookup(s, fs); - if k = null_font then - k := read_font_info(null_cs, s, "", fs); - if k <> null_font then begin - if ((cs.b0 <> 0) or (cs.b1 <> 0) or (cs.b2 <> 0) or (cs.b3 <> 0)) and - ((font_check[k].b0 <> 0) or (font_check[k].b1 <> 0) or - (font_check[k].b2 <> 0) or (font_check[k].b3 <> 0)) and - ((cs.b0 <> font_check[k].b0) or (cs.b1 <> font_check[k].b1) or - (cs.b2 <> font_check[k].b2) or (cs.b3 <> font_check[k].b3)) then - vf_local_font_warning(f, k, "checksum mismatch"); - if ds <> font_dsize[k] then - vf_local_font_warning(f, k, "design size mismatch"); - end; - if pdf_font_expand_ratio[f] <> 0 then - set_expand_param(k, f, pdf_font_expand_ratio[f]); - vf_def_font := k; -end; - -procedure do_vf; {process \.{VF} file with font internal number |f|} -label vf_error; -var cmd, k, n: integer; - cc, cmd_length: integer; - tfm_width: scaled; - vf_err_str, s: str_number; - stack_level: vf_stack_index; - save_vf_nf: internal_font_number; - f: internal_font_number; - -begin - f := tmp_f; - pdf_font_type[f] := real_font_type; - if auto_expand_vf(f) then - return; {auto-expanded virtual font} - stack_level := 0; - @; - @;@/ - @;@/ - @;@/ - while cmd <= long_char do begin@/ - @;@/ - end; - if cmd <> post then - bad_vf("POST command expected"); - b_close(vf_file); - pdf_font_type[f] := virtual_font_type; - return; -vf_error: - print_nl("Error in processing VF font ("); - print(font_name[f]); - print(".vf): "); - print(vf_err_str); - print(", virtual font will be ignored"); - print_ln; - b_close(vf_file); - update_terminal; -end; - -@ @= -pack_file_name(font_name[f], "", ".vf"); -if not vf_b_open_in(vf_file) then - return - -@ @= -if vf_byte <> pre then - bad_vf("PRE command expected"); -if vf_byte <> vf_id then - bad_vf("wrong id byte"); -cmd_length := vf_byte; -for k := 1 to cmd_length do - tmp_int := vf_byte; -tmp_b0 := vf_byte; tmp_b1 := vf_byte; tmp_b2 := vf_byte; tmp_b3 := vf_byte; -if ((tmp_b0 <> 0) or (tmp_b1 <> 0) or (tmp_b2 <> 0) or (tmp_b3 <> 0)) and - ((font_check[f].b0 <> 0) or (font_check[f].b1 <> 0) or - (font_check[f].b2 <> 0) or (font_check[f].b3 <> 0)) and - ((tmp_b0 <> font_check[f].b0) or (tmp_b1 <> font_check[f].b1) or - (tmp_b2 <> font_check[f].b2) or (tmp_b3 <> font_check[f].b3)) then begin - print_nl("checksum mismatch in font "); - print(font_name[f]); - print(".vf ignored"); -end; -if vf_read(4) div @'20 <> font_dsize[f] then begin - print_nl("design size mismatch in font "); - print(font_name[f]); - print(".vf ignored"); -end; -update_terminal; -vf_z := font_size[f]; -vf_replace_z - -@ @= -cmd := vf_byte; -save_vf_nf := vf_nf; -while (cmd >= fnt_def1) and (cmd <= fnt_def1 + 3) do begin - vf_e_fnts[vf_nf] := vf_read(cmd - fnt_def1 + 1); - vf_i_fnts[vf_nf] := vf_def_font(f); - incr(vf_nf); - cmd := vf_byte; -end; -vf_default_font[f] := save_vf_nf; -vf_local_font_num[f] := vf_nf - save_vf_nf; - -@ @= - vf_packet_base[f] := new_vf_packet(f) - -@ @= -if cmd = long_char then begin - vf_packet_length := vf_read(4); - cc := vf_read(4); - if not is_valid_char(cc) then - bad_vf("invalid character code"); - tmp_b0 := vf_byte; tmp_b1 := vf_byte; tmp_b2 := vf_byte; tmp_b3 := vf_byte; - scaled4(tfm_width); -end -else begin - vf_packet_length := cmd; - cc := vf_byte; - if not is_valid_char(cc) then - bad_vf("invalid character code"); - tmp_b1 := vf_byte; tmp_b2 := vf_byte; tmp_b3 := vf_byte; - scaled3(tfm_width); -end; -if vf_packet_length < 0 then - bad_vf("negative packet length"); -if vf_packet_length > vf_max_packet_length then - bad_vf("packet length too long"); -if (tfm_width <> char_width(f)(char_info(f)(cc))) then begin - print_nl("character width mismatch in font "); - print(font_name[f]); - print(".vf ignored"); -end; -str_room(vf_packet_length); -while vf_packet_length > 0 do begin - cmd := vf_byte; - decr(vf_packet_length); - @; - if cmd <> nop then - append_char(cmd); - vf_packet_length := vf_packet_length - cmd_length; - while cmd_length > 0 do begin - decr(cmd_length); - append_char(vf_byte); - end; -end; -if stack_level <> 0 then - bad_vf("more PUSHs than POPs in character packet"); -if vf_packet_length <> 0 then - bad_vf("invalid packet length or DVI command in packet"); -@; -cmd := vf_byte - -@ @= -s := make_string; -storepacket(f, cc, s); -flush_str(s) - -@ @= -if (cmd >= set_char_0) and (cmd <= set_char_0 + 127) then - cmd_length := 0 -else if ((fnt_num_0 <= cmd) and (cmd <= fnt_num_0 + 63)) or - ((fnt1 <= cmd) and (cmd <= fnt1 + 3)) then begin - if cmd >= fnt1 then begin - k := vf_read(cmd - fnt1 + 1); - vf_packet_length := vf_packet_length - (cmd - fnt1 + 1); - end - else - k := cmd - fnt_num_0; - if k >= 256 then - bad_vf("too many local fonts"); - n := 0; - while (n < vf_local_font_num[f]) and - (vf_e_fnts[vf_default_font[f] + n] <> k) do - incr(n); - if n = vf_local_font_num[f] then - bad_vf("undefined local font"); - if k <= 63 then - append_char(fnt_num_0 + k) - else begin - append_char(fnt1); - append_char(k); - end; - cmd_length := 0; - cmd := nop; -end -else case cmd of -set_rule, put_rule: cmd_length := 8; -four_cases(set1): cmd_length := cmd - set1 + 1; -four_cases(put1): cmd_length := cmd - put1 + 1; -four_cases(right1): cmd_length := cmd - right1 + 1; -four_cases(w1): cmd_length := cmd - w1 + 1; -four_cases(x1): cmd_length := cmd - x1 + 1; -four_cases(down1): cmd_length := cmd - down1 + 1; -four_cases(y1): cmd_length := cmd - y1 + 1; -four_cases(z1): cmd_length := cmd - z1 + 1; -four_cases(xxx1): begin - cmd_length := vf_read(cmd - xxx1 + 1); - vf_packet_length := vf_packet_length - (cmd - xxx1 + 1); - if cmd_length > vf_max_packet_length then - bad_vf("packet length too long"); - if cmd_length < 0 then - bad_vf("string of negative length"); - append_char(xxx1); - append_char(cmd_length); - cmd := nop; {|cmd| has been already stored above as |xxx1|} -end; -w0, x0, y0, z0, nop: - cmd_length := 0; -push, pop: begin - cmd_length := 0; - if cmd = push then - if stack_level = vf_stack_size then - overflow("virtual font stack size", vf_stack_size) - else - incr(stack_level) - else - if stack_level = 0 then - bad_vf("more POPs than PUSHs in character") - else - decr(stack_level); -end; -othercases - bad_vf("improver DVI command"); -endcases - -@ The |do_vf_packet| procedure is called in order to interpret the -character packet for a virtual character. Such a packet may contain the -instruction to typeset a character from the same or an other virtual -font; in such cases |do_vf_packet| calls itself recursively. The -recursion level, i.e., the number of times this has happened, is kept -in the global variable |vf_cur_s| and should not exceed |vf_max_recursion|. - -@= -@!vf_max_recursion=10; {\.{VF} files shouldn't recurse beyond this level} -@!vf_stack_size=100; {\.{DVI} files shouldn't |push| beyond this depth} - -@ @= -@!vf_stack_index=0..vf_stack_size; {an index into the stack} -@!vf_stack_record=record - stack_h, stack_v, stack_w, stack_x, stack_y, stack_z: scaled; -end; - -@ @= -@!vf_cur_s: 0..vf_max_recursion; {current recursion level} -@!vf_stack: array [vf_stack_index] of vf_stack_record; -@!vf_stack_ptr: vf_stack_index; {pointer into |vf_stack|} - -@ @= -vf_cur_s := 0; -vf_stack_ptr := 0; - -@ Some functions for processing character packets. - -@p function packet_read(k: integer): integer; {read |k| bytes as an integer from -character packet} -var i: integer; -begin - i := 0; - while k > 0 do begin - i := i*256 + packet_byte; - decr(k); - end; - packet_read := i; -end; - -function packet_scaled(k: integer): integer; {get |k| bytes from packet as a -scaled} -var s: scaled; -begin - case k of - 1: begin - tmp_b3 := packet_byte; - scaled1(s); - end; - 2: begin - tmp_b2 := packet_byte; - tmp_b3 := packet_byte; - scaled2(s); - end; - 3: begin - tmp_b1 := packet_byte; - tmp_b2 := packet_byte; - tmp_b3 := packet_byte; - scaled3(s); - end; - 4: begin - tmp_b0 := packet_byte; - tmp_b1 := packet_byte; - tmp_b2 := packet_byte; - tmp_b3 := packet_byte; - scaled4(s); - end; - othercases pdf_error("vf", "invalid number size"); - endcases; - packet_scaled := s; -end; - -procedure do_vf_packet(f: internal_font_number; c: eight_bits); {typeset the \.{DVI} commands in the -character packet for character |c| in current font |f|} -label do_char, continue; -var save_packet_ptr, save_packet_length: pool_pointer; - save_vf, k, n: internal_font_number; - base_line, save_h, save_v: scaled; - cmd: integer; - char_move: boolean; - w, x, y, z: scaled; - s: str_number; -begin - incr(vf_cur_s); - if vf_cur_s > vf_max_recursion then - overflow("max level recursion of virtual fonts", vf_max_recursion); - push_packet_state; - start_packet(f, c); - vf_z := font_size[f]; - vf_replace_z; - save_vf := f; - f := vf_i_fnts[vf_default_font[save_vf]]; - save_v := cur_v; - save_h := cur_h; - w := 0; x := 0; y := 0; z := 0; - while vf_packet_length > 0 do begin - cmd := packet_byte; - @; -continue: - end; - cur_h := save_h; - cur_v := save_v; - pop_packet_state; - vf_z := font_size[f]; - vf_replace_z; - decr(vf_cur_s); -end; - -@ The following code typesets a character to PDF output. - -@d output_one_char(#)==begin - pdf_check_new_font(f); - if pdf_font_type[f] = virtual_font_type then - do_vf_packet(f, #) - else begin - pdf_begin_string(f); - pdf_print_char(f, #); - adv_char_width(f, char_width(f)(char_info(f)(#))); - end; -end - - -@= -if (cmd >= set_char_0) and (cmd <= set_char_0 + 127) then begin - if not is_valid_char(cmd) then begin - char_warning(f, cmd); - goto continue; - end; - c := cmd; - char_move := true; - goto do_char; -end -else if ((fnt_num_0 <= cmd) and (cmd <= fnt_num_0 + 63)) or (cmd = fnt1) then begin - if cmd = fnt1 then - k := packet_byte - else - k := cmd - fnt_num_0; - n := 0; - while (n < vf_local_font_num[save_vf]) and - (vf_e_fnts[vf_default_font[save_vf] + n] <> k) do - incr(n); - if (n = vf_local_font_num[save_vf]) then - f := null_font - else - f := vf_i_fnts[vf_default_font[save_vf] + n]; -end -else case cmd of -push: begin - vf_stack[vf_stack_ptr].stack_h := cur_h; - vf_stack[vf_stack_ptr].stack_v := cur_v; - vf_stack[vf_stack_ptr].stack_w := w; - vf_stack[vf_stack_ptr].stack_x := x; - vf_stack[vf_stack_ptr].stack_y := y; - vf_stack[vf_stack_ptr].stack_z := z; - incr(vf_stack_ptr); -end; -pop: begin - decr(vf_stack_ptr); - cur_h := vf_stack[vf_stack_ptr].stack_h; - cur_v := vf_stack[vf_stack_ptr].stack_v; - w := vf_stack[vf_stack_ptr].stack_w; - x := vf_stack[vf_stack_ptr].stack_x; - y := vf_stack[vf_stack_ptr].stack_y; - z := vf_stack[vf_stack_ptr].stack_z; -end; -four_cases(set1), four_cases(put1): begin - if (set1 <= cmd) and (cmd <= set1 + 3) then begin - tmp_int := packet_read(cmd - set1 + 1); - char_move := true; - end - else begin - tmp_int := packet_read(cmd - put1 + 1); - char_move := false; - end; - if not is_valid_char(tmp_int) then begin - char_warning(f, tmp_int); - goto continue; - end; - c := tmp_int; - goto do_char; -end; -set_rule, put_rule: begin - rule_ht := packet_scaled(4); - rule_wd := packet_scaled(4); - if (rule_wd > 0) and (rule_ht > 0) then begin - pdf_set_rule(cur_h, cur_v, rule_wd, rule_ht); - if cmd = set_rule then - cur_h := cur_h + rule_wd; - end; -end; -four_cases(right1): - cur_h := cur_h + packet_scaled(cmd - right1 + 1); -w0, four_cases(w1): begin - if cmd > w0 then - w := packet_scaled(cmd - w0); - cur_h := cur_h + w; -end; -x0, four_cases(x1): begin - if cmd > x0 then - x := packet_scaled(cmd - x0); - cur_h := cur_h + x; -end; -four_cases(down1): - cur_v := cur_v + packet_scaled(cmd - down1 + 1); -y0, four_cases(y1): begin - if cmd > y0 then - y := packet_scaled(cmd - y0); - cur_v := cur_v + y; -end; -z0, four_cases(z1): begin - if cmd > z0 then - z := packet_scaled(cmd - z0); - cur_v := cur_v + z; -end; -four_cases(xxx1): begin - tmp_int := packet_read(cmd - xxx1 + 1); - str_room(tmp_int); - while tmp_int > 0 do begin - decr(tmp_int); - append_char(packet_byte); - end; - s := make_string; - literal(s, scan_special, false); - flush_str(s); -end; -othercases pdf_error("vf", "invalid DVI command"); -endcases; -goto continue; -do_char: -if is_valid_char(c) then - output_one_char(c) -else - char_warning(f, c); -if char_move then - cur_h := cur_h + char_width(f)(char_info(f)(c)) - -@* \[32f] PDF shipping out. -To ship out a \TeX\ box to PDF page description we need to implement -|pdf_hlist_out|, |pdf_vlist_out| and |pdf_ship_out|, which are equivalent to -the \TeX' original |hlist_out|, |vlist_out| and |ship_out| resp. But first we -need to declare some procedures needed in |pdf_hlist_out| and |pdf_vlist_out|. - -@= -procedure pdf_out_literal(p:pointer); -var old_setting:0..max_selector; {holds print |selector|} - s: str_number; -begin - old_setting:=selector; selector:=new_string; - show_token_list(link(pdf_literal_data(p)),null,pool_size-pool_ptr); - selector:=old_setting; - s := make_string; - literal(s, pdf_literal_mode(p), false); - flush_str(s); -end; - -procedure pdf_special(p: pointer); -var old_setting:0..max_selector; {holds print |selector|} - s: str_number; -begin - old_setting:=selector; selector:=new_string; - show_token_list(link(write_tokens(p)),null,pool_size-pool_ptr); - selector:=old_setting; - s := make_string; - literal(s, scan_special, true); - flush_str(s); -end; - -procedure pdf_print_toks(p: pointer); {print tokens list |p|} -var s: str_number; -begin - s := tokens_to_string(p); - if length(s) > 0 then - pdf_print(s); - flush_str(s); -end; - -procedure pdf_print_toks_ln(p: pointer); {print tokens list |p|} -var s: str_number; -begin - s := tokens_to_string(p); - if length(s) > 0 then begin - pdf_print(s); - pdf_print_nl; - end; - flush_str(s); -end; - -@ Similiar to |vlist_out|, |pdf_vlist_out| needs to be declared forward - -@p procedure@?pdf_vlist_out; forward; - -@ The implementation of procedure |pdf_hlist_out| is similiar to |hlist_out| - -@p @t\4@>@@t@>@/ - -procedure pdf_hlist_out; {output an |hlist_node| box} -label reswitch, move_past, fin_rule, next_p; -var base_line: scaled; {the baseline coordinate for this box} -@!left_edge: scaled; {the left coordinate for this box} -@!save_h: scaled; {what |cur_h| should pop to} -@!this_box: pointer; {pointer to containing box} -@!g_order: glue_ord; {applicable order of infinity for glue} -@!g_sign: normal..shrinking; {selects type of glue} -@!p:pointer; {current position in the hlist} -@!leader_box:pointer; {the leader box being replicated} -@!leader_wd:scaled; {width of leader box being replicated} -@!lx:scaled; {extra space between leader boxes} -@!outer_doing_leaders:boolean; {were we doing leaders?} -@!edge:scaled; {left edge of sub-box, or right edge of leader space} -@!running_link: pointer; {pointer to `running' link if exists} -@!glue_temp:real; {glue value before rounding} -@!cur_glue:real; {glue seen so far} -@!cur_g:scaled; {rounded equivalent of |cur_glue| times the glue ratio} -begin cur_g:=0; cur_glue:=float_constant(0); -this_box:=temp_ptr; g_order:=glue_order(this_box); -g_sign:=glue_sign(this_box); p:=list_ptr(this_box); -incr(cur_s); -base_line:=cur_v; left_edge:=cur_h; -running_link := null; -@; -while p<>null do - @; -decr(cur_s); -end; - -@ @= -if running_link = null then begin - if is_running(pdf_link_wd) and (pdf_link_level = cur_s) then begin - append_link(this_box, left_edge, base_line); - running_link := last_link; - end; -end -else - last_link := running_link - -@ @= -reswitch: if is_char_node(p) then - begin - repeat f:=font(p); c:=character(p); - if is_valid_char(c) then - output_one_char(c) - else - char_warning(f, c); - cur_h:=cur_h+char_width(f)(char_info(f)(c)); - p:=link(p); - until not is_char_node(p); - end -else @ - -@ @= -begin case type(p) of -hlist_node,vlist_node:@<(\pdfTeX) Output a box in an hlist@>; -rule_node: begin rule_ht:=height(p); rule_dp:=depth(p); rule_wd:=width(p); - goto fin_rule; - end; -whatsit_node: @; -glue_node: @<(\pdfTeX) Move right or output leaders@>; -kern_node,math_node:cur_h:=cur_h+width(p); -ligature_node: @; -othercases do_nothing -endcases;@/ -goto next_p; -fin_rule: @<(\pdfTeX) Output a rule in an hlist@>; -move_past: cur_h:=cur_h+rule_wd; -next_p:p:=link(p); -end - -@ @<(\pdfTeX) Output a box in an hlist@>= -if list_ptr(p)=null then cur_h:=cur_h+width(p) -else begin - cur_v:=base_line+shift_amount(p); {shift the box down} - temp_ptr:=p; edge:=cur_h; - if type(p)=vlist_node then pdf_vlist_out@+else pdf_hlist_out; - cur_h:=edge+width(p); cur_v:=base_line; - end - -@ @<(\pdfTeX) Output a rule in an hlist@>= -if is_running(rule_ht) then rule_ht:=height(this_box); -if is_running(rule_dp) then rule_dp:=depth(this_box); -rule_ht:=rule_ht+rule_dp; {this is the rule thickness} -if (rule_ht>0)and(rule_wd>0) then {we don't output empty rules} - begin cur_v:=base_line+rule_dp; - pdf_set_rule(cur_h, cur_v, rule_wd, rule_ht); - cur_v:=base_line; - end - -@ @<(\pdfTeX) Move right or output leaders@>= -begin g:=glue_ptr(p); rule_wd:=width(g)-cur_g; -if g_sign<>normal then - begin if g_sign=stretching then - begin if stretch_order(g)=g_order then - begin cur_glue:=cur_glue+stretch(g); - vet_glue(float(glue_set(this_box))*cur_glue); -@^real multiplication@> - cur_g:=round(glue_temp); - end; - end - else if shrink_order(g)=g_order then - begin cur_glue:=cur_glue-shrink(g); - vet_glue(float(glue_set(this_box))*cur_glue); - cur_g:=round(glue_temp); - end; - end; -rule_wd:=rule_wd+cur_g; -if subtype(p)>=a_leaders then - @<(\pdfTeX) Output leaders in an hlist, |goto fin_rule| if a rule - or to |next_p| if done@>; -goto move_past; -end - -@ @<(\pdfTeX) Output leaders in an hlist...@>= -begin leader_box:=leader_ptr(p); -if type(leader_box)=rule_node then - begin rule_ht:=height(leader_box); rule_dp:=depth(leader_box); - goto fin_rule; - end; -leader_wd:=width(leader_box); -if (leader_wd>0)and(rule_wd>0) then - begin rule_wd:=rule_wd+10; {compensate for floating-point rounding} - edge:=cur_h+rule_wd; lx:=0; - @; - while cur_h+leader_wd<=edge do - @<(\pdfTeX) Output a leader box at |cur_h|, - then advance |cur_h| by |leader_wd+lx|@>; - cur_h:=edge-10; goto next_p; - end; -end - -@ @<(\pdfTeX) Output a leader box at |cur_h|, ...@>= -begin cur_v:=base_line+shift_amount(leader_box);@/ -save_h:=cur_h; temp_ptr:=leader_box; -outer_doing_leaders:=doing_leaders; doing_leaders:=true; -if type(leader_box)=vlist_node then pdf_vlist_out@+else pdf_hlist_out; -doing_leaders:=outer_doing_leaders; -cur_v:=base_line; -cur_h:=save_h+leader_wd+lx; -end - -@ The |pdf_vlist_out| routine is similar to |pdf_hlist_out|, but a bit simpler. -@p procedure pdf_vlist_out; {output a |pdf_vlist_node| box} -label move_past, fin_rule, next_p; -var left_edge: scaled; {the left coordinate for this box} -@!top_edge: scaled; {the top coordinate for this box} -@!save_v: scaled; {what |cur_v| should pop to} -@!this_box: pointer; {pointer to containing box} -@!g_order: glue_ord; {applicable order of infinity for glue} -@!g_sign: normal..shrinking; {selects type of glue} -@!p:pointer; {current position in the vlist} -@!leader_box:pointer; {the leader box being replicated} -@!leader_ht:scaled; {height of leader box being replicated} -@!lx:scaled; {extra space between leader boxes} -@!outer_doing_leaders:boolean; {were we doing leaders?} -@!edge:scaled; {bottom boundary of leader space} -@!glue_temp:real; {glue value before rounding} -@!cur_glue:real; {glue seen so far} -@!cur_g:scaled; {rounded equivalent of |cur_glue| times the glue ratio} -begin cur_g:=0; cur_glue:=float_constant(0); -this_box:=temp_ptr; g_order:=glue_order(this_box); -g_sign:=glue_sign(this_box); p:=list_ptr(this_box); -incr(cur_s); -left_edge:=cur_h; cur_v:=cur_v-height(this_box); top_edge:=cur_v; -@; -while p<>null do - @; -decr(cur_s); -end; - -@ @= -if (last_thread <> null) and is_running(pdf_thread_dp) and - (pdf_thread_level = cur_s) then - append_thread(this_box, left_edge, top_edge + height(this_box)) - -@ @= -begin if is_char_node(p) then confusion("pdfvlistout") -@:this can't happen pdfvlistout}{\quad pdfvlistout@> -else @; -next_p:p:=link(p); -end - -@ @= -begin case type(p) of -hlist_node,vlist_node:@<(\pdfTeX) Output a box in a vlist@>; -rule_node: begin rule_ht:=height(p); rule_dp:=depth(p); rule_wd:=width(p); - goto fin_rule; - end; -whatsit_node: @; -glue_node: @<(\pdfTeX) Move down or output leaders@>; -kern_node:cur_v:=cur_v+width(p); -othercases do_nothing -endcases;@/ -goto next_p; -fin_rule: @<(\pdfTeX) Output a rule in a vlist, |goto next_p|@>; -move_past: cur_v:=cur_v+rule_ht; -end - -@ @<(\pdfTeX) Output a box in a vlist@>= -if list_ptr(p)=null then cur_v:=cur_v+height(p)+depth(p) -else begin cur_v:=cur_v+height(p); save_v:=cur_v; - cur_h:=left_edge+shift_amount(p); {shift the box right} - temp_ptr:=p; - if type(p)=vlist_node then pdf_vlist_out@+else pdf_hlist_out; - cur_v:=save_v+depth(p); cur_h:=left_edge; - end - -@ @<(\pdfTeX) Output a rule in a vlist...@>= -if is_running(rule_wd) then rule_wd:=width(this_box); -rule_ht:=rule_ht+rule_dp; {this is the rule thickness} -cur_v:=cur_v+rule_ht; -if (rule_ht>0)and(rule_wd>0) then {we don't output empty rules} - pdf_set_rule(cur_h, cur_v, rule_wd, rule_ht); -goto next_p - -@ @<(\pdfTeX) Move down or output leaders@>= -begin g:=glue_ptr(p); rule_ht:=width(g)-cur_g; -if g_sign<>normal then - begin if g_sign=stretching then - begin if stretch_order(g)=g_order then - begin cur_glue:=cur_glue+stretch(g); - vet_glue(float(glue_set(this_box))*cur_glue); -@^real multiplication@> - cur_g:=round(glue_temp); - end; - end - else if shrink_order(g)=g_order then - begin cur_glue:=cur_glue-shrink(g); - vet_glue(float(glue_set(this_box))*cur_glue); - cur_g:=round(glue_temp); - end; - end; -rule_ht:=rule_ht+cur_g; -if subtype(p)>=a_leaders then - @<(\pdfTeX) Output leaders in a vlist, |goto fin_rule| if a rule - or to |next_p| if done@>; -goto move_past; -end - -@ @<(\pdfTeX) Output leaders in a vlist...@>= -begin leader_box:=leader_ptr(p); -if type(leader_box)=rule_node then - begin rule_wd:=width(leader_box); rule_dp:=0; - goto fin_rule; - end; -leader_ht:=height(leader_box)+depth(leader_box); -if (leader_ht>0)and(rule_ht>0) then - begin rule_ht:=rule_ht+10; {compensate for floating-point rounding} - edge:=cur_v+rule_ht; lx:=0; - @; - while cur_v+leader_ht<=edge do - @<(\pdfTeX) Output a leader box at |cur_v|, - then advance |cur_v| by |leader_ht+lx|@>; - cur_v:=edge-10; goto next_p; - end; -end - -@ @<(\pdfTeX) Output a leader box at |cur_v|, ...@>= -begin cur_h:=left_edge+shift_amount(leader_box);@/ -cur_v:=cur_v+height(leader_box); save_v:=cur_v; -temp_ptr:=leader_box; -outer_doing_leaders:=doing_leaders; doing_leaders:=true; -if type(leader_box)=vlist_node then pdf_vlist_out@+else pdf_hlist_out; -doing_leaders:=outer_doing_leaders; -cur_h:=left_edge; -cur_v:=save_v-height(leader_box)+leader_ht+lx; -end - -@ |fix_pdfoutput| freezes |pdfoutput| when something has been written to -the output. - -@p procedure fix_pdfoutput; -begin - if not fixed_pdfoutput_set then begin - fixed_pdfoutput := pdf_output; - fixed_pdfoutput_set := true; - end - else if fixed_pdfoutput <> pdf_output then - pdf_error("setup", - "\pdfoutput can only be changed before anything is written to the output"); -end; - - -@ |pdf_ship_out| is used instead of |ship_out| to shipout a box to PDF -output. If |shipping_page| is not set then the output will be a Form object, -otherwise it will be a Page object. - -@p procedure pdf_ship_out(p: pointer; shipping_page: boolean); {output the box |p|} -label done, done1; -var i,j,k:integer; {general purpose accumulators} -r: integer; {accumulator to copy node for pending link annotation} -save_font_list: pointer; {to save |pdf_font_list| during flushing pending forms} -save_obj_list: pointer; {to save |pdf_obj_list|} -save_ximage_list: pointer; {to save |pdf_ximage_list|} -save_xform_list: pointer; {to save |pdf_xform_list|} -save_image_procset: integer; {to save |pdf_image_procset|} -save_text_procset: integer; {to save |pdf_text_procset|} -pdf_last_resources: integer; {pointer to most recently generated Resources object} -s:str_number; -old_setting:0..max_selector; {saved |selector| setting} -begin if tracing_output>0 then - begin print_nl(""); print_ln; - print("Completed box being shipped out"); -@.Completed box...@> - end; -if not init_pdf_output then begin - @; - init_pdf_output := true; -end; -is_shipping_page := shipping_page; -if shipping_page then begin - if term_offset>max_print_line-9 then print_ln - else if (term_offset>0)or(file_offset>0) then print_char(" "); - print_char("["); j:=9; - while (count(j)=0)and(j>0) do decr(j); - for k:=0 to j do - begin print_int(count(k)); - if k0 then - begin if shipping_page then print_char("]"); - begin_diagnostic; show_box(p); end_diagnostic(true); - end; -@<(\pdfTeX) Ship box |p| out@>; -if (tracing_output<=0) and shipping_page then print_char("]"); -dead_cycles:=0; -update_terminal; {progress report} -@; -end; - -@ @<(\pdfTeX) Ship box |p| out@>= -@; -@; -if type(p)=vlist_node then pdf_vlist_out@+else pdf_hlist_out; -if shipping_page then - incr(total_pages); -cur_s:=-1; -@; -done: - -@ @= -fix_pdfoutput; -temp_ptr:=p; -prepare_mag; -pdf_last_resources := pdf_new_objnum; -@; -if not shipping_page then begin - pdf_xform_width := width(p); - pdf_xform_height := height(p); - pdf_xform_depth := depth(p); - pdf_begin_dict(pdf_cur_form); - pdf_last_stream := pdf_cur_form; - cur_v := height(p); - cur_h := 0; - pdf_origin_h := 0; - pdf_origin_v := pdf_xform_height + pdf_xform_depth; -end -else begin - @; - pdf_last_page := get_obj(obj_type_page, total_pages + 1, 0); - obj_aux(pdf_last_page) := 1; {mark that this page has beed created} - pdf_new_dict(obj_type_others, 0); - pdf_last_stream := obj_ptr; - cur_h := cur_h_offset; - cur_v := height(p) + cur_v_offset; - pdf_origin_h := 0; - pdf_origin_v := cur_page_height; - @; -end; -if not shipping_page then begin - @; -end; -@ - -@ @= -pdf_font_list := null; -pdf_obj_list := null; -pdf_xform_list := null; -pdf_ximage_list := null; -pdf_text_procset := false; -pdf_image_procset := 0 - -@ @= -pdf_annot_list := null; -pdf_link_list := null; -pdf_dest_list := null; -pdf_bead_list := null; -last_link := null; -last_thread := null - -@ @= -cur_h_offset := pdf_h_origin + h_offset; -cur_v_offset := pdf_v_origin + v_offset; -if pdf_page_width <> 0 then - cur_page_width := pdf_page_width -else - cur_page_width := width(p) + 2*cur_h_offset; -if pdf_page_height <> 0 then - cur_page_height := pdf_page_height -else - cur_page_height := height(p) + depth(p) + 2*cur_v_offset - -@ Here we write out the header for Form. - -@= -pdf_print_ln("/Type /XObject"); -pdf_print_ln("/Subtype /Form"); -if obj_xform_attr(pdf_cur_form) <> null then begin - pdf_print_toks_ln(obj_xform_attr(pdf_cur_form)); - delete_toks(obj_xform_attr(pdf_cur_form)); -end; -pdf_print("/BBox ["); -pdf_print("0 0 "); -pdf_print_bp(pdf_xform_width); pdf_out(" "); -pdf_print_bp(pdf_xform_height + pdf_xform_depth); pdf_print_ln("]"); -pdf_print_ln("/FormType 1"); -pdf_print_ln("/Matrix [1 0 0 1 0 0]"); -pdf_indirect_ln("Resources", pdf_last_resources) - -@ @= -pdf_begin_stream; -if shipping_page then begin - @; -end - -@ @= -prepare_mag; -if mag <> 1000 then begin - pdf_print_real(mag, 3); - pdf_print(" 0 0 "); - pdf_print_real(mag, 3); - pdf_print_ln(" 0 0 cm"); -end - -@ @= -@; -if shipping_page then begin - @; -end; -@; -@; -@; -if shipping_page then begin - @; -end; -@; -@; -if shipping_page then begin - @; -end - -@ @= -pdf_end_text; -pdf_end_stream - -@ @= -pdf_begin_dict(pdf_last_resources); -@; -@; -@; -@; -pdf_end_dict - -@ @= -if shipping_page then begin - if pdf_page_resources <> null then - pdf_print_toks_ln(pdf_page_resources); -end -else begin - if obj_xform_resources(pdf_cur_form) <> null then begin - pdf_print_toks_ln(obj_xform_resources(pdf_cur_form)); - delete_toks(obj_xform_resources(pdf_cur_form)); - end; -end - -@ In the end of shipping out a page we reset all the lists holding objects -have been created during the page shipping. - -@d delete_toks(#) == begin delete_token_ref(#); # := null; end - -@= -flush_list(pdf_font_list); -flush_list(pdf_obj_list); -flush_list(pdf_xform_list); -flush_list(pdf_ximage_list) - -@ @= -flush_list(pdf_annot_list); -flush_list(pdf_link_list); -flush_list(pdf_dest_list); -flush_list(pdf_bead_list) - -@ @= -if pdf_font_list <> null then begin - pdf_print("/Font << "); - k := pdf_font_list; - while k <> null do begin - pdf_print("/F"); - set_ff(info(k)); - pdf_print_int(ff); - pdf_print_font_tag(info(k)); - pdf_print_resname_prefix; - pdf_out(" "); - pdf_print_int(pdf_font_num[ff]); - pdf_print(" 0 R "); - k := link(k); - end; - pdf_print_ln(">>"); - pdf_text_procset := true; -end - -@ @= -if (pdf_xform_list <> null) or (pdf_ximage_list <> null) then begin - pdf_print("/XObject << "); - k := pdf_xform_list; - while k <> null do begin - pdf_print("/Fm"); - pdf_print_int(obj_info(info(k))); - pdf_print_resname_prefix; - pdf_out(" "); - pdf_print_int(info(k)); - pdf_print(" 0 R "); - k := link(k); - end; - k := pdf_ximage_list; - while k <> null do begin - pdf_print("/Im"); - pdf_print_int(obj_info(info(k))); - pdf_print_resname_prefix; - pdf_out(" "); - pdf_print_int(info(k)); - pdf_print(" 0 R "); - update_image_procset(obj_ximage_data(info(k))); - k := link(k); - end; - pdf_print_ln(">>"); -end - -@ @= -pdf_print("/ProcSet [ /PDF"); -if pdf_text_procset then - pdf_print(" /Text"); -if check_image_b(pdf_image_procset) then - pdf_print(" /ImageB"); -if check_image_c(pdf_image_procset) then - pdf_print(" /ImageC"); -if check_image_i(pdf_image_procset) then - pdf_print(" /ImageI"); -pdf_print_ln(" ]") - -@ @= -pdf_begin_dict(pdf_last_page); -pdf_print_ln("/Type /Page"); -pdf_indirect_ln("Contents", pdf_last_stream); -pdf_indirect_ln("Resources", pdf_last_resources); -pdf_print("/MediaBox [0 0 "); -pdf_print_mag_bp(cur_page_width); pdf_out(" "); -pdf_print_mag_bp(cur_page_height); -pdf_print_ln("]"); -if pdf_page_attr <> null then - pdf_print_toks_ln(pdf_page_attr); -@; -@; -pdf_end_dict - -@ @= -if total_pages mod pages_tree_kids_max = 1 then begin - pdf_create_obj(obj_type_pages, pages_tree_kids_max); - pdf_last_pages := obj_ptr; -end; -pdf_indirect_ln("Parent", pdf_last_pages) - -@ @= -if (pdf_annot_list <> null) or (pdf_link_list <> null) then begin - pdf_print("/Annots [ "); - k := pdf_annot_list; - while k <> null do begin - pdf_print_int(info(k)); - pdf_print(" 0 R "); - k := link(k); - end; - k := pdf_link_list; - while k <> null do begin - pdf_print_int(info(k)); - pdf_print(" 0 R "); - k := link(k); - end; - pdf_print_ln("]"); -end; -if pdf_bead_list <> null then begin - k := pdf_bead_list; - pdf_print("/B [ "); - while k <> null do begin - pdf_print_int(info(k)); - pdf_print(" 0 R "); - k := link(k); - end; - pdf_print_ln("]"); -end - -@ @= -procedure pdf_write_obj(n: integer); {write a raw PDF object} -var s: str_number; - f: byte_file; -begin - s := tokens_to_string(obj_obj_data(n)); - delete_toks(obj_obj_data(n)); - if obj_obj_is_stream(n) > 0 then begin - pdf_begin_dict(n); - if obj_obj_stream_attr(n) <> null then begin - pdf_print_toks_ln(obj_obj_stream_attr(n)); - delete_toks(obj_obj_stream_attr(n)); - end; - pdf_begin_stream; - end - else - pdf_begin_obj(n); - if obj_obj_is_file(n) > 0 then begin - cur_name := s; - cur_area := ""; - cur_ext := ""; - pack_cur_name; - if not tex_b_openin(f) then - pdf_error("ext5", "cannot open file for embedding"); - print("<<"); - print(s); - while not eof(f) do - pdf_out(getc(f)); - print(">>"); - b_close(f); - end - else if obj_obj_is_stream(n) > 0 then - pdf_print(s) - else - pdf_print_ln(s); - if obj_obj_is_stream(n) > 0 then - pdf_end_stream - else - pdf_end_obj; - flush_str(s); -end; - -procedure flush_whatsit_node(p: pointer; s: small_number); -begin - type(p) := whatsit_node; - subtype(p) := s; - if link(p) <> null then - pdf_error("flush_whatsit_node", "link(p) is not null"); - flush_node_list(p); -end; - -@ @= -if pdf_obj_list <> null then begin - k := pdf_obj_list; - while k <> null do begin - if not is_obj_written(info(k)) then - pdf_write_obj(info(k)); - k := link(k); - end; -end - -@ When flushing pending forms we need to save and restore resources lists - (|pdf_font_list|, |pdf_obj_list|, |pdf_xform_list| and |pdf_ximage_list|), - which are also used by page shipping. - -@= -if pdf_xform_list <> null then begin - k := pdf_xform_list; - while k <> null do begin - if not is_obj_written(info(k)) then begin - pdf_cur_form := info(k); - @; - @; - pdf_ship_out(obj_xform_box(pdf_cur_form), false); - @; - end; - k := link(k); - end; -end - -@ @= -save_font_list := pdf_font_list; -save_obj_list := pdf_obj_list; -save_xform_list := pdf_xform_list; -save_ximage_list := pdf_ximage_list; -save_text_procset := pdf_text_procset; -save_image_procset := pdf_image_procset - -@ @= -pdf_font_list := save_font_list; -pdf_obj_list := save_obj_list; -pdf_xform_list := save_xform_list; -pdf_ximage_list := save_ximage_list; -pdf_text_procset := save_text_procset; -pdf_image_procset := save_image_procset - -@ @= -procedure pdf_write_image(n: integer); {write an image} -begin - pdf_begin_dict(n); - if obj_ximage_attr(n) <> null then begin - pdf_print_toks_ln(obj_ximage_attr(n)); - delete_toks(obj_ximage_attr(n)); - end; - write_image(obj_ximage_data(n)); - delete_image(obj_ximage_data(n)); -end; - -@ @= -if pdf_ximage_list <> null then begin - k := pdf_ximage_list; - while k <> null do begin - if not is_obj_written(info(k)) then - pdf_write_image(info(k)); - k := link(k); - end; -end - -@ @= -pdf_origin_h := 0; -pdf_origin_v := cur_page_height; -@; -@; -@; -@ - -@ @= -if pdf_annot_list <> null then begin - k := pdf_annot_list; - while k <> null do begin - i := obj_annot_ptr(info(k)); {|i| points to |pdf_annot_node|} - pdf_begin_dict(info(k)); - pdf_print_ln("/Type /Annot"); - pdf_print_toks_ln(pdf_annot_data(i)); - pdf_rectangle(pdf_left(i), pdf_top(i), pdf_right(i), pdf_bottom(i)); - pdf_end_dict; - k := link(k); - end; -end - -@ @= -if pdf_link_list <> null then begin - @; - @; -end - -@ @= -k := pdf_link_list; -while k <> null do begin - i := obj_annot_ptr(info(k)); - pdf_begin_dict(info(k)); - pdf_print_ln("/Type /Annot"); - if pdf_link_attr(i) <> null then - pdf_print_toks_ln(pdf_link_attr(i)); - pdf_rectangle(pdf_left(i), pdf_top(i), pdf_right(i), pdf_bottom(i)); - if pdf_action_type(pdf_link_action(i)) <> pdf_action_user - then begin - pdf_print_ln("/Subtype /Link"); - pdf_print("/A "); - end; - write_action(pdf_link_action(i)); - pdf_end_dict; - k := link(k); -end - -@ @= -k := pdf_link_list; -while k <> null do begin - i := obj_annot_ptr(info(k)); - {nodes with |info = null| were created by |append_link| and - must be flushed here, as they are not linked in any list} - if info(i) = max_halfword then - flush_whatsit_node(i, pdf_start_link_node); - k := link(k); -end - -@ @= -if pdf_dest_list <> null then begin - k := pdf_dest_list; - while k <> null do begin - if is_obj_written(info(k)) then - pdf_error("ext5", - "destination has been already written (this shouldn't happen)") - else begin - i := obj_dest_ptr(info(k)); - if pdf_dest_named_id(i) > 0 then begin - pdf_begin_dict(info(k)); - pdf_print("/D "); - end - else - pdf_begin_obj(info(k)); - pdf_out("["); pdf_print_int(pdf_last_page); pdf_print(" 0 R "); - case pdf_dest_type(i) of - pdf_dest_xyz: begin - pdf_print("/XYZ "); - pdf_print_mag_bp(pdf_x(pdf_left(i))); pdf_out(" "); - pdf_print_mag_bp(pdf_y(pdf_top(i))); pdf_out(" "); - if pdf_dest_xyz_zoom(i) = null then - pdf_print("null") - else begin - pdf_print_int(pdf_dest_xyz_zoom(i) div 1000); - pdf_out("."); - pdf_print_int((pdf_dest_xyz_zoom(i) mod 1000)); - end; - end; - pdf_dest_fit: - pdf_print("/Fit"); - pdf_dest_fith: begin - pdf_print("/FitH "); - pdf_print_mag_bp(pdf_y(pdf_top(i))); - end; - pdf_dest_fitv: begin - pdf_print("/FitV "); - pdf_print_mag_bp(pdf_x(pdf_left(i))); - end; - pdf_dest_fitb: - pdf_print("/FitB"); - pdf_dest_fitbh: begin - pdf_print("/FitBH "); - pdf_print_mag_bp(pdf_y(pdf_top(i))); - end; - pdf_dest_fitbv: begin - pdf_print("/FitBV "); - pdf_print_mag_bp(pdf_x(pdf_left(i))); - end; - pdf_dest_fitr: begin - pdf_print("/FitR "); - pdf_print_rect_spec(i); - end; - othercases pdf_error("ext5", "unknown dest type"); - endcases; - pdf_print_ln("]"); - if pdf_dest_named_id(i) > 0 then - pdf_end_dict - else - pdf_end_obj; - end; - k := link(k); - end; -end - -@ @= -procedure pdf_print_rect_spec(r: pointer); {prints a rect spec} -begin - pdf_print_mag_bp(pdf_x(pdf_left(r))); - pdf_out(" "); - pdf_print_mag_bp(pdf_y(pdf_bottom(r))); - pdf_out(" "); - pdf_print_mag_bp(pdf_x(pdf_right(r))); - pdf_out(" "); - pdf_print_mag_bp(pdf_y(pdf_top(r))); -end; - -@ @= -if pdf_bead_list <> null then begin - k := pdf_bead_list; - while k <> null do begin - pdf_new_obj(obj_type_others, 0); - pdf_out("["); - i := obj_bead_data(info(k)); {pointer to a whatsit or whatsit-like node} - pdf_print_rect_spec(i); - if info(i) = max_halfword then {not a whatsit node, so must be destroyed here} - flush_whatsit_node(i, pdf_start_thread_node); - pdf_print_ln("]"); - obj_bead_rect(info(k)) := obj_ptr; {rewrite |obj_bead_data|} - pdf_end_obj; - k := link(k); - end; -end - -@ In the end we must flush PDF objects that cannot be written out -immediately after shipping out pages. - -@ @= -if pdf_first_outline <> 0 then begin - pdf_new_dict(obj_type_others, 0); - outlines := obj_ptr; - l := pdf_first_outline; k := 0; - repeat - incr(k); - a := open_subentries(l); - if obj_outline_count(l) > 0 then - k := k + a; - obj_outline_parent(l) := obj_ptr; - l := obj_outline_next(l); - until l = 0; - pdf_print_ln("/Type /Outlines"); - pdf_indirect_ln("First", pdf_first_outline); - pdf_indirect_ln("Last", pdf_last_outline); - pdf_int_entry_ln("Count", k); - pdf_end_dict; - @; -end -else - outlines := 0 - -@ @= -k := head_tab[obj_type_outline]; -while k <> 0 do begin - if obj_outline_parent(k) = pdf_parent_outline then begin - if obj_outline_prev(k) = 0 then - pdf_first_outline := k; - if obj_outline_next(k) = 0 then - pdf_last_outline := k; - end; - pdf_begin_dict(k); - pdf_indirect_ln("Title", obj_outline_title(k)); - pdf_indirect_ln("A", obj_outline_action_objnum(k)); - if obj_outline_parent(k) <> 0 then - pdf_indirect_ln("Parent", obj_outline_parent(k)); - if obj_outline_prev(k) <> 0 then - pdf_indirect_ln("Prev", obj_outline_prev(k)); - if obj_outline_next(k) <> 0 then - pdf_indirect_ln("Next", obj_outline_next(k)); - if obj_outline_first(k) <> 0 then - pdf_indirect_ln("First", obj_outline_first(k)); - if obj_outline_last(k) <> 0 then - pdf_indirect_ln("Last", obj_outline_last(k)); - if obj_outline_count(k) <> 0 then - pdf_int_entry_ln("Count", obj_outline_count(k)); - if obj_outline_attr(k) <> 0 then begin - pdf_print_toks_ln(obj_outline_attr(k)); - delete_toks(obj_outline_attr(k)); - end; - pdf_end_dict; - k := obj_link(k); -end - -@ @= -if head_tab[obj_type_thread] <> 0 then begin - pdf_new_obj(obj_type_others, 0); - threads := obj_ptr; - pdf_out("["); - k := head_tab[obj_type_thread]; - while k <> 0 do begin - pdf_print_int(k); - pdf_print(" 0 R "); - k := obj_link(k); - end; - remove_last_space; - pdf_print_ln("]"); - pdf_end_obj; - k := head_tab[obj_type_thread]; - while k <> 0 do begin - out_thread(k); - k := obj_link(k); - end; -end -else - threads := 0 - -@ Now we are ready to declare our new procedure |ship_out|. It will call -|pdf_ship_out| if the integer parameter |pdf_output| is positive; otherwise it -will call |dvi_ship_out|, which is the \TeX\ original |ship_out|. - -@p procedure ship_out(p:pointer); {output the box |p|} -begin - fix_pdfoutput; - if pdf_output > 0 then - pdf_ship_out(p, true) - else - dvi_ship_out(p); -end; - -@ @= -ensure_pdf_open; -check_pdfminorversion; -prepare_mag; -fixed_decimal_digits := fix_int(pdf_decimal_digits, 0, 4); -min_bp_val := - divide_scaled(one_hundred_bp, ten_pow[fixed_decimal_digits + 2], 0); -fixed_pk_resolution := fix_int(pdf_pk_resolution, 72, 2400); -pk_scale_factor := - divide_scaled(72, fixed_pk_resolution, 5 + fixed_decimal_digits); -if pdf_pk_mode <> null then begin - kpse_init_prog('PDFTEX', fixed_pk_resolution, - make_cstring(tokens_to_string(pdf_pk_mode)), nil); - flush_string; -end else - kpse_init_prog('PDFTEX', fixed_pk_resolution, nil, nil); -if not kpse_var_value('MKTEXPK') then - kpse_set_program_enabled (kpse_pk_format, 1, kpse_src_cmdline); -set_job_id(year, month, day, time, pdftex_version, pdftex_revision); -if (pdf_unique_resname > 0) and (pdf_resname_prefix = 0) then - pdf_resname_prefix := get_resname_prefix - -@ Finishing the PDF output file. - -The following procedures sort the table of destination names -@p function str_less_str(s1, s2: str_number): boolean; {compare two strings} -var j1, j2: pool_pointer; - l, i: integer; -begin - j1 := str_start[s1]; - j2 := str_start[s2]; - if length(s1) < length(s2) then - l := length(s1) - else - l := length(s2); - i := 0; - while (i < l) and (str_pool[j1 + i] = str_pool[j2 + i]) do - incr(i); - if ((i < l) and (str_pool[j1 + i] < str_pool[j2 + i])) or - ((i = l) and (length(s1) < length(s2))) then - str_less_str := true - else - str_less_str := false; -end; - -procedure sort_dest_names(l, r: integer); {sorts |dest_names| by names} -var i, j: integer; - s: str_number; - x, y: integer; - e: dest_name_entry; -begin - i := l; - j := r; - s := dest_names[(l + r) div 2].objname; - repeat - while str_less_str(dest_names[i].objname, s) do - incr(i); - while str_less_str(s, dest_names[j].objname) do - decr(j); - if i <= j then begin - e := dest_names[i]; - dest_names[i] := dest_names[j]; - dest_names[j] := e; - incr(i); - decr(j); - end; - until i > j; - if l < j then - sort_dest_names(l, j); - if i < r then - sort_dest_names(i, r); -end; - -@ Now the finish of PDF output file. At this moment all Page objects -are already written completely to PDF output file. - -@= -if total_pages=0 then print_nl("No pages of output.") -@.No pages of output@> -else begin - pdf_flush; {to make sure that the output file name has been already - created} - if total_pages mod pages_tree_kids_max <> 0 then - obj_info(pdf_last_pages) := total_pages mod pages_tree_kids_max; - {last pages object may have less than |pages_tree_kids_max| chilrend} - @; - @; - @; - @; - @; - @; - @; - @; - @; - pdf_print_info; - @; - @; - pdf_flush; - print_nl("Output written on "); print_file_name(0, output_file_name, 0); - @.Output written on x@> - print(" ("); print_int(total_pages); print(" page"); - if total_pages<>1 then print_char("s"); - print(", "); print_int(pdf_offset); print(" bytes)."); - libpdffinish; - b_close(pdf_file); -end - -@ Destinations that have been referenced but don't exists have -|obj_dest_ptr=null|. Leaving them undefined might cause troubles for -PDF browsers, so we need to fix them. - -@p procedure pdf_fix_dest(k: integer); -begin - if obj_dest_ptr(k) <> null then - return; - pdf_warning("dest", "", false); - if obj_info(k) < 0 then begin - print("name{"); - print(-obj_info(k)); - print("}"); - end - else begin - print("num"); - print_int(obj_info(k)); - end; - print(" has been referenced but does not exist, replaced by a fixed one"); - print_ln; print_ln; - pdf_begin_obj(k); - pdf_out("["); - pdf_print_int(head_tab[obj_type_page]); - pdf_print_ln(" 0 R /Fit]"); - pdf_end_obj; -end; - -@ @= -k := head_tab[obj_type_dest]; -while k <> 0 do begin - pdf_fix_dest(k); - k := obj_link(k); -end - -@ @= -k := head_tab[obj_type_page]; -while obj_aux(k) = 0 do begin - pdf_warning("dest", "Page ", false); - print_int(obj_info(k)); - print(" has been referenced but does not exist!"); - print_ln; print_ln; - k := obj_link(k); -end; -head_tab[obj_type_page] := k - -@ @= -k := head_tab[obj_type_page]; -l := 0; -repeat - i := obj_link(k); - obj_link(k) := l; - l := k; - k := i; -until k = 0; -head_tab[obj_type_page] := l; -k := head_tab[obj_type_pages]; -l := 0; -repeat - i := obj_link(k); - obj_link(k) := l; - l := k; - k := i; -until k = 0; -head_tab[obj_type_pages] := l - -@ @= -for k := font_base + 1 to font_ptr do - if font_used[k] and hasfmentry(k) then begin - if pdf_font_num[k] < 0 then - i := -pdf_font_num[k] - else - i := tfm_of_fm(pdf_font_map[k]); - if i <> k then - for j := 0 to 255 do - if pdf_char_marked(k, j) then - pdf_mark_char(i, j); - end; -k := head_tab[obj_type_font]; -while k <> 0 do begin - f := obj_info(k); - do_pdf_font(k, f); - k := obj_link(k); -end - -@ We will generate in each single step the parents of all Pages/Page objects in -the previous level. These new generated Pages object will create a new level of -Pages tree. We will repeat this until search only one Pages object. This one -will be the Root object. - -@= -a := obj_ptr + 1; {all Pages objects whose children are not Page objects -should have index greater than |a|} -l := head_tab[obj_type_pages]; {|l| is the index of current Pages object which is -being output} -k := head_tab[obj_type_page]; {|k| is the index of current child of |l|} -if obj_link(l) = 0 then - is_root := true {only Pages object; total pages is not greater than - |pages_tree_kids_max|} -else - is_root := false; -b := obj_ptr + 1; {to init |c| in next step} -repeat - i := 0; {counter of Pages object in current level} - c := b; {first Pages object in previous level} - b := obj_ptr + 1; {succcesor of last created object} - repeat - if not is_root then begin - if i mod pages_tree_kids_max = 0 then begin {create a new Pages object for next level} - pdf_last_pages := pdf_new_objnum; - obj_info(pdf_last_pages) := obj_info(l); - end - else - obj_info(pdf_last_pages) := obj_info(pdf_last_pages) + - obj_info(l); - end; - @; - incr(i); - if l < a then - l := obj_link(l) - else - incr(l); - until (l = 0) or (l = b); - if l = 0 then - l := a; - if b = obj_ptr then - is_root := true; -until false; -done: - -@ @= -pdf_begin_dict(l); -pdf_print_ln("/Type /Pages"); -pdf_int_entry_ln("Count", obj_info(l)); -if not is_root then - pdf_indirect_ln("Parent", pdf_last_pages); -pdf_print("/Kids ["); -j := 0; -repeat - pdf_print_int(k); - pdf_print(" 0 R "); - if k < a then {the next Pages/Page object must be |obj_link(k)|} - k := obj_link(k) - else {|k >= a|, the next Pages object is |k + 1|} - incr(k); - incr(j); -until ((l < a) and (j = obj_info(l))) or - (k = 0) or (k = c) or - (j = pages_tree_kids_max); -remove_last_space; -pdf_print_ln("]"); -if k = 0 then begin - if head_tab[obj_type_pages] <> 0 then begin {we are in Page objects list} - k := head_tab[obj_type_pages]; - head_tab[obj_type_pages] := 0; - end - else {we are in Pages objects list} - k := a; -end; -if is_root and (pdf_pages_attr <> null) then - pdf_print_toks_ln(pdf_pages_attr); -pdf_end_dict; -if is_root then - goto done - -@ The name tree is very similiar to Pages tree so its construction should be -certain from Pages tree construction. For intermediate node |obj_info| will be -the first name and |obj_link| will be the last name in \.{\\Limits} array. -Note that |pdf_dest_names_ptr| will be less than |obj_ptr|, so we test if -|k < pdf_dest_names_ptr| then |k| is index of leaf in |dest_names|; else -|k| will be index in |obj_tab| of some intermediate node. - -@= -if pdf_dest_names_ptr = 0 then begin - dests := 0; - goto done1; -end; -sort_dest_names(0, pdf_dest_names_ptr - 1); -a := obj_ptr + 1; {first intermediate node of name tree} -l := a; {index of node being output} -k := 0; {index of current child of |l|; if |k < pdf_dest_names_ptr| then this is -pointer to |dest_names| array; otherwise it is the pointer to |obj_tab| -(object number) } -repeat - c := obj_ptr + 1; {first node in current level} - repeat - pdf_create_obj(obj_type_others, 0); {create a new node for next level} - @; - incr(l); - incr(i); - until k = c; -until false; -done1: -if (dests <> 0) or (pdf_names_toks <> null) then begin - pdf_new_dict(obj_type_others, 0); - if (dests <> 0) then - pdf_indirect_ln("Dests", dests); - if pdf_names_toks <> null then begin - pdf_print_toks_ln(pdf_names_toks); - delete_toks(pdf_names_toks); - end; - pdf_end_dict; - names_tree := obj_ptr; -end -else - names_tree := 0 - -@ @= -pdf_begin_dict(l); -j := 0; -if k < pdf_dest_names_ptr then begin - obj_info(l) := dest_names[k].objname; - pdf_print("/Names ["); - repeat - pdf_print_str(dest_names[k].objname); - pdf_out(" "); - pdf_print_int(dest_names[k].objnum); - pdf_print(" 0 R "); - incr(j); - incr(k); - until (j = name_tree_kids_max) or (k = pdf_dest_names_ptr); - remove_last_space; - pdf_print_ln("]"); - obj_link(l) := dest_names[k - 1].objname; - if k = pdf_dest_names_ptr then - k := a; -end -else begin - obj_info(l) := obj_info(k); - pdf_print("/Kids ["); - repeat - pdf_print_int(k); - pdf_print(" 0 R "); - incr(j); - incr(k); - until (j = name_tree_kids_max) or (k = c); - remove_last_space; - pdf_print_ln("]"); - obj_link(l) := obj_link(k - 1); -end; -if (l > k) or (l = a) then begin - pdf_print("/Limits ["); - pdf_print_str(obj_info(l)); - pdf_out(" "); - pdf_print_str(obj_link(l)); - pdf_print_ln("]"); - pdf_end_dict; -end -else begin - pdf_end_dict; - dests := l; - goto done1; -end - -@ @= -pdf_new_dict(obj_type_others, 0); -root := obj_ptr; -pdf_print_ln("/Type /Catalog"); -pdf_indirect_ln("Pages", pdf_last_pages); -if threads <> 0 then - pdf_indirect_ln("Threads", threads); -if outlines <> 0 then - pdf_indirect_ln("Outlines", outlines); -if names_tree <> 0 then - pdf_indirect_ln("Names", names_tree); -if pdf_catalog_toks <> null then begin - pdf_print_toks_ln(pdf_catalog_toks); - delete_toks(pdf_catalog_toks); -end; -if pdf_catalog_openaction <> 0 then - pdf_indirect_ln("OpenAction", pdf_catalog_openaction); -pdf_end_dict - -@ If the same keys in a dictionary are given several times, -then it is not defined which value is choosen by an application. -Therefore the keys |/Producer| and |/Creator| are only set, -if the token list |pdf_info_toks|, converted to a string does -not contain these key strings. - -@p function substr_of_str(s, t: str_number):boolean; -label continue,exit; -var j, k, kk: pool_pointer; {running indices} -begin - k:=str_start[t]; - while (k < str_start[t+1] - length(s)) do begin - j:=str_start[s]; - kk:=k; - while (j < str_start[s+1]) do begin - if str_pool[j] <> str_pool[kk] then - goto continue; - incr(j); - incr(kk); - end; - substr_of_str:=true; - return; - continue: incr(k); - end; - substr_of_str:=false; -end; - -procedure pdf_print_info; {print info object} -var s: str_number; - creator_given, producer_given, creationdate_given: boolean; -begin - pdf_new_dict(obj_type_others, 0); - creator_given:=false; - producer_given:=false; - creationdate_given:=false; - if pdf_info_toks <> null then begin - s:=tokens_to_string(pdf_info_toks); - creator_given:=substr_of_str("/Creator", s); - producer_given:=substr_of_str("/Producer", s); - creationdate_given:=substr_of_str("/CreationDate", s); - end; - if not producer_given then begin - @; - end; - if pdf_info_toks <> null then begin - if length(s) > 0 then begin - pdf_print(s); - pdf_print_nl; - end; - flush_str(s); - delete_toks(pdf_info_toks); - end; - if not creator_given then - pdf_str_entry_ln("Creator", "TeX"); - if not creationdate_given then begin - @; - end; - pdf_str_entry_ln("PTEX.Fullbanner", pdftex_banner); - pdf_end_dict; -end; - -@ @= -pdf_print("/Producer (pdfTeX-"); -pdf_print_int(pdftex_version div 100); -pdf_out("."); -pdf_print_int(pdftex_version mod 100); -pdf_out("."); -pdf_print(pdftex_revision); -pdf_print_ln(")") - -@ @= -print_creation_date; - -@ @= -@!pdftex_banner: str_number; {the complete banner} - -@ @= -l := 0; -for k := 1 to obj_ptr do - if obj_offset(k) = 0 then begin - obj_link(l) := k; - l := k; - end; -obj_link(l) := 0; -pdf_save_offset := pdf_offset; -pdf_print_ln("xref"); -pdf_print("0 "); pdf_print_int_ln(obj_ptr + 1); -pdf_print_fw_int(obj_link(0), 10); -pdf_print_ln(" 65535 f "); -for k := 1 to obj_ptr do begin - if obj_offset(k) = 0 then begin - pdf_print_fw_int(obj_link(k), 10); - pdf_print_ln(" 00000 f "); - end - else begin - pdf_print_fw_int(obj_offset(k), 10); - pdf_print_ln(" 00000 n "); - end; -end - -@ @= -pdf_print_ln("trailer"); -pdf_print("<< "); -pdf_int_entry_ln("Size", obj_ptr + 1); -pdf_indirect_ln("Root", root); -pdf_indirect_ln("Info", obj_ptr); -if pdf_trailer_toks <> null then begin - pdf_print_toks_ln(pdf_trailer_toks); - delete_toks(pdf_trailer_toks); -end; -print_ID(output_file_name); -pdf_print_ln(" >>"); -pdf_print_ln("startxref"); -pdf_print_int_ln(pdf_save_offset); -pdf_print_ln("%%EOF") - -@* \[33] Packaging. -@z - -@x [649] -exit: hpack:=r; -@y -exit: {|hpack|} -hpack:=r; -@z - -@x [651] - kern_node,math_node: x:=x+width(p); -@y - kern_node: x:=x+width(p); - math_node: x:=x+width(p); -@z - -@x [888] - fixlinedimen -append_to_vlist(just_box); -@y -if pdf_each_line_height <> 0 then - height(just_box) := pdf_each_line_height; -if pdf_each_line_depth <> 0 then - depth(just_box) := pdf_each_line_depth; -if (pdf_first_line_height <> 0) and (cur_line = prev_graf + 1) then - height(just_box) := pdf_first_line_height; -if (pdf_last_line_depth <> 0) and (cur_line + 1 = best_line) then - depth(just_box) := pdf_last_line_depth; -append_to_vlist(just_box); -@z - -@x [889] -adjust_tail:=adjust_head; just_box:=hpack(q,cur_width,exactly); -@y -adjust_tail:=adjust_head; -just_box:=hpack(q,cur_width,exactly); -@z - -@x [1088] - quitvmode -@ A paragraph begins when horizontal-mode material occurs in vertical mode, -or when the paragraph is explicitly started by `\.{\\indent}' or -`\.{\\noindent}'. - -@= -primitive("indent",start_par,1); -@!@:indent_}{\.{\\indent} primitive@> -primitive("noindent",start_par,0); -@!@:no_indent_}{\.{\\noindent} primitive@> -@y -@ Here is a really small patch to add a new primitive called -\.{\\quitvmode}. In vertical modes, it is identical to \.{\\indent}, -but in horizontal and math modes it is really a no-op (as opposed to -\.{\\indent}, which executes the |indent_in_hmode| procedure). - -A paragraph begins when horizontal-mode material occurs in vertical mode, -or when the paragraph is explicitly started by `\.{\\quitvmode}', -`\.{\\indent}' or `\.{\\noindent}'. - -@= -primitive("indent",start_par,1); -@!@:indent_}{\.{\\indent} primitive@> -primitive("noindent",start_par,0); -@!@:no_indent_}{\.{\\noindent} primitive@> -primitive("quitvmode",start_par,2); -@!@:quit_vmode_}{\.{\\quitvmode} primitive@> -@z - -@x [1089] - quitvmode -start_par: if chr_code=0 then print_esc("noindent")@+ else print_esc("indent"); -@y -start_par: if chr_code=0 then print_esc("noindent")@+ else if chr_code=1 then print_esc("indent")@+ else print_esc("quitvmode"); -@z - -@x [1092] - quitvmode -hmode+start_par,mmode+start_par: indent_in_hmode; -@y -hmode+start_par,mmode+start_par: if cur_chr<>2 then indent_in_hmode; -@z - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Declare the necessary variables for finishing PDF file -% Close PDF output if necessary -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -@x [1333] -procedure close_files_and_terminate; -var k:integer; {all-purpose index} -begin @; -@!stat if tracing_stats>0 then @;@;@+tats@/ -wake_up_terminal; @; -@y -procedure close_files_and_terminate; -label done, done1; -var a, b, c, i, j, k, l: integer; {all-purpose index} - is_root: boolean; {|pdf_last_pages| is root of Pages tree?} - root, outlines, threads, names_tree, dests, fixed_dest: integer; -begin @; -@!stat if tracing_stats>0 then @;@;@+tats@/ -wake_up_terminal; -if fixed_pdfoutput > 0 then begin - if history = fatal_error_stop then - print_err(" ==> Fatal error occurred, the output PDF file is not finished!") - else begin - @; - end; -end -else begin - @; -end; -@z - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Output statistics about the pdftex specific sizes only in PDF mode. -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -@x [1334] - wlog_ln(' ',max_in_stack:1,'i,',max_nest_stack:1,'n,',@| - max_param_stack:1,'p,',@| - max_buf_stack+1:1,'b,',@| - max_save_stack+6:1,'s stack positions out of ',@| - stack_size:1,'i,', - nest_size:1,'n,', - param_size:1,'p,', - buf_size:1,'b,', - save_size:1,'s'); - end -@y - wlog_ln(' ',max_in_stack:1,'i,',max_nest_stack:1,'n,',@| - max_param_stack:1,'p,',@| - max_buf_stack+1:1,'b,',@| - max_save_stack+6:1,'s stack positions out of ',@| - stack_size:1,'i,', - nest_size:1,'n,', - param_size:1,'p,', - buf_size:1,'b,', - save_size:1,'s'); - if (pdf_output > 0) then begin - wlog_ln('PDF statistics:'); - wlog_ln(' ',obj_ptr:1,' PDF objects out of ',obj_tab_size:1); - wlog_ln(' ',pdf_dest_names_ptr:1,' named destinations out of ',dest_names_size:1); - wlog_ln(' ',pdf_mem_ptr:1,' words of extra memory for PDF output out of ',pdf_mem_size:1); - end; - end -@z - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Override pdf_output value from format file with command line option -% and initialize other options -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -@x - while (loc 0) then pdf_output := pdf_output_value; -pdf_init_map_file('pdftex.map'); -@z - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% PDF-speficic extensions that don't fall to any previous category -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -@x [1344] -@d set_language_code=5 {command modifier for \.{\\setlanguage}} -@y -@d set_language_code=5 {command modifier for \.{\\setlanguage}} -@d pdftex_first_extension_code = 6 -@d pdf_literal_node == pdftex_first_extension_code + 0 -@d pdf_obj_code == pdftex_first_extension_code + 1 -@d pdf_refobj_node == pdftex_first_extension_code + 2 -@d pdf_xform_code == pdftex_first_extension_code + 3 -@d pdf_refxform_node == pdftex_first_extension_code + 4 -@d pdf_ximage_code == pdftex_first_extension_code + 5 -@d pdf_refximage_node == pdftex_first_extension_code + 6 -@d pdf_annot_node == pdftex_first_extension_code + 7 -@d pdf_start_link_node == pdftex_first_extension_code + 8 -@d pdf_end_link_node == pdftex_first_extension_code + 9 -@d pdf_outline_code == pdftex_first_extension_code + 10 -@d pdf_dest_node == pdftex_first_extension_code + 11 -@d pdf_thread_node == pdftex_first_extension_code + 12 -@d pdf_start_thread_node == pdftex_first_extension_code + 13 -@d pdf_end_thread_node == pdftex_first_extension_code + 14 -@d pdf_save_pos_node == pdftex_first_extension_code + 15 -@d pdf_info_code == pdftex_first_extension_code + 16 -@d pdf_catalog_code == pdftex_first_extension_code + 17 -@d pdf_names_code == pdftex_first_extension_code + 18 -@d pdf_font_attr_code == pdftex_first_extension_code + 19 -@d pdf_include_chars_code == pdftex_first_extension_code + 20 -@d pdf_map_file_code == pdftex_first_extension_code + 21 -@d pdf_map_line_code == pdftex_first_extension_code + 22 -@d pdf_trailer_code == pdftex_first_extension_code + 23 -@d pdf_strcmp_code == pdftex_first_extension_code + 24 -@d pdf_escape_string_code == pdftex_first_extension_code + 25 -@d pdf_escape_name_code == pdftex_first_extension_code + 26 -@d reset_timer_code == pdftex_first_extension_code + 27 -@d pdftex_last_extension_code == pdftex_first_extension_code + 27 -@z - -@x [1344] -primitive("setlanguage",extension,set_language_code);@/ -@!@:set_language_}{\.{\\setlanguage} primitive@> -@y -primitive("setlanguage",extension,set_language_code);@/ -@!@:set_language_}{\.{\\setlanguage} primitive@> -primitive("pdfliteral",extension,pdf_literal_node);@/ -@!@:pdf_literal_}{\.{\\pdfliteral} primitive@> -primitive("pdfobj",extension,pdf_obj_code);@/ -@!@:pdf_obj_}{\.{\\pdfobj} primitive@> -primitive("pdfrefobj",extension,pdf_refobj_node);@/ -@!@:pdf_refobj_}{\.{\\pdfrefobj} primitive@> -primitive("pdfxform",extension,pdf_xform_code);@/ -@!@:pdf_xform_}{\.{\\pdfxform} primitive@> -primitive("pdfrefxform",extension,pdf_refxform_node);@/ -@!@:pdf_refxform_}{\.{\\pdfrefxform} primitive@> -primitive("pdfximage",extension,pdf_ximage_code);@/ -@!@:pdf_ximage_}{\.{\\pdfximage} primitive@> -primitive("pdfrefximage",extension,pdf_refximage_node);@/ -@!@:pdf_refximage_}{\.{\\pdfrefximage} primitive@> -primitive("pdfannot",extension,pdf_annot_node);@/ -@!@:pdf_annot_}{\.{\\pdfannot} primitive@> -primitive("pdfstartlink",extension,pdf_start_link_node);@/ -@!@:pdf_start_link_}{\.{\\pdfstartlink} primitive@> -primitive("pdfendlink",extension,pdf_end_link_node);@/ -@!@:pdf_end_link_}{\.{\\pdfendlink} primitive@> -primitive("pdfoutline",extension,pdf_outline_code);@/ -@!@:pdf_outline_}{\.{\\pdfoutline} primitive@> -primitive("pdfdest",extension,pdf_dest_node);@/ -@!@:pdf_dest_}{\.{\\pdfdest} primitive@> -primitive("pdfthread",extension,pdf_thread_node);@/ -@!@:pdf_thread_}{\.{\\pdfthread} primitive@> -primitive("pdfstartthread",extension,pdf_start_thread_node);@/ -@!@:pdf_start_thread_}{\.{\\pdfstartthread} primitive@> -primitive("pdfendthread",extension,pdf_end_thread_node);@/ -@!@:pdf_end_thread_}{\.{\\pdfendthread} primitive@> -primitive("pdfsavepos",extension,pdf_save_pos_node);@/ -@!@:pdf_save_pos_}{\.{\\pdfsavepos} primitive@> -primitive("pdfinfo",extension,pdf_info_code);@/ -@!@:pdf_info_}{\.{\\pdfinfo} primitive@> -primitive("pdfcatalog",extension,pdf_catalog_code);@/ -@!@:pdf_catalog_}{\.{\\pdfcatalog} primitive@> -primitive("pdfnames",extension,pdf_names_code);@/ -@!@:pdf_names_}{\.{\\pdfnames} primitive@> -primitive("pdfincludechars",extension,pdf_include_chars_code);@/ -@!@:pdf_include_chars_}{\.{\\pdfincludechars} primitive@> -primitive("pdffontattr",extension,pdf_font_attr_code);@/ -@!@:pdf_font_attr_}{\.{\\pdffontattr} primitive@> -primitive("pdfmapfile",extension,pdf_map_file_code);@/ -@!@:pdf_map_file_}{\.{\\pdfmapfile} primitive@> -primitive("pdfmapline",extension,pdf_map_line_code);@/ -@!@:pdf_map_line_}{\.{\\pdfmapline} primitive@> -primitive("pdftrailer",extension,pdf_trailer_code);@/ -@!@:pdf_trailer_}{\.{\\pdftrailer} primitive@> -primitive("pdfstrcmp",extension,pdf_strcmp_code);@/ -@!@:pdf_strcmp_}{\.{\\pdfstrcmp} primitive@> -primitive("pdfescapestring",extension,pdf_escape_string_code);@/ -@!@:pdf_escape_string_}{\.{\\pdfescapestring} primitive@> -primitive("pdfescapename",extension,pdf_escape_name_code);@/ -@!@:pdf_escape_name_}{\.{\\pdfescapename} primitive@> -primitive("pdfresettimer",extension,reset_timer_code);@/ -@!@:reset_timer_}{\.{\\pdfresettimer} primitive@> -@z - -@x [1346] - set_language_code:print_esc("setlanguage"); -@y - set_language_code:print_esc("setlanguage"); - pdf_literal_node: print_esc("pdfliteral"); - pdf_obj_code: print_esc("pdfobj"); - pdf_refobj_node: print_esc("pdfrefobj"); - pdf_xform_code: print_esc("pdfxform"); - pdf_refxform_node: print_esc("pdfrefxform"); - pdf_ximage_code: print_esc("pdfximage"); - pdf_refximage_node: print_esc("pdfrefximage"); - pdf_annot_node: print_esc("pdfannot"); - pdf_start_link_node: print_esc("pdfstartlink"); - pdf_end_link_node: print_esc("pdfendlink"); - pdf_outline_code: print_esc("pdfoutline"); - pdf_dest_node: print_esc("pdfdest"); - pdf_thread_node: print_esc("pdfthread"); - pdf_start_thread_node: print_esc("pdfstartthread"); - pdf_end_thread_node: print_esc("pdfendthread"); - pdf_save_pos_node: print_esc("pdfsavepos"); - pdf_info_code: print_esc("pdfinfo"); - pdf_catalog_code: print_esc("pdfcatalog"); - pdf_names_code: print_esc("pdfnames"); - pdf_include_chars_code: print_esc("pdfincludechars"); - pdf_font_attr_code: print_esc("pdffontattr"); - pdf_map_file_code: print_esc("pdfmapfile"); - pdf_map_line_code: print_esc("pdfmapline"); - pdf_trailer_code: print_esc("pdftrailer"); - pdf_strcmp_code: print_esc("pdfstrcmp"); - pdf_escape_string_code: print_esc("pdfescapestring"); - pdf_escape_name_code: print_esc("pdfescapename"); - reset_timer_code: print_esc("pdfresettimer"); -@z - -@x [1348] -immediate_code:@; -set_language_code:@; -@y -immediate_code:@; -set_language_code:@; -pdf_literal_node: @; -pdf_obj_code: @; -pdf_refobj_node: @; -pdf_xform_code: @; -pdf_refxform_node: @; -pdf_ximage_code: @; -pdf_refximage_node: @; -pdf_annot_node: @; -pdf_start_link_node: @; -pdf_end_link_node: @; -pdf_outline_code: @; -pdf_dest_node: @; -pdf_thread_node: @; -pdf_start_thread_node: @; -pdf_end_thread_node: @; -pdf_save_pos_node: @; -pdf_info_code: @; -pdf_catalog_code: @; -pdf_names_code: @; -pdf_include_chars_code: @; -pdf_font_attr_code: @; -pdf_map_file_code: @; -pdf_map_line_code: @; -pdf_trailer_code: @; -pdf_strcmp_code: @; -pdf_escape_string_code: @; -pdf_escape_name_code: @; -reset_timer_code: @; -@z - -@x [1354] -@= -begin new_whatsit(special_node,write_node_size); write_stream(tail):=null; -p:=scan_toks(false,true); write_tokens(tail):=def_ref; -end -@y -@= -begin new_whatsit(special_node,write_node_size); write_stream(tail):=null; -p:=scan_toks(false,true); write_tokens(tail):=def_ref; -end - -@ The following macros are needed for further manipulation with whatsit nodes -for \pdfTeX{} extensions (copying, destroying etc.) - -@d add_action_ref(#) == incr(pdf_action_refcount(#)) {increase count of -references to this action} - -@d delete_action_ref(#) == {decrease count of references to this -action; free it if there is no reference to this action} -begin - if pdf_action_refcount(#) = null then begin - if pdf_action_type(#) = pdf_action_user then - delete_token_ref(pdf_action_user_tokens(#)) - else begin - if pdf_action_file(#) <> null then - delete_token_ref(pdf_action_file(#)); - if pdf_action_type(#) = pdf_action_page then - delete_token_ref(pdf_action_page_tokens(#)) - else if pdf_action_named_id(#) > 0 then - delete_token_ref(pdf_action_id(#)); - end; - free_node(#, pdf_action_size); - end - else - decr(pdf_action_refcount(#)); -end - -@ We have to check whether \.{\\pdfoutput} is set for using \pdfTeX{} - extensions. -@= -procedure check_pdfoutput(s: str_number; is_error : boolean); -begin - if pdf_output <= 0 then - if is_error then - pdf_error(s, "used while \pdfoutput is not set") - else - pdf_warning(s, "used while \pdfoutput is not set; ignoring it", true); -end; - -procedure scan_pdf_ext_toks; -begin - call_func(scan_toks(false, true)); {like \.{\\special}} -end; - -procedure compare_strings; {to implement \.{\\pdfstrcmp}} -label done; -var s1, s2: str_number; - i1, i2, j1, j2: pool_pointer; - result: integer; -begin - call_func(scan_toks(false, true)); - s1 := tokens_to_string(def_ref); - delete_token_ref(def_ref); - call_func(scan_toks(false, true)); - s2 := tokens_to_string(def_ref); - delete_token_ref(def_ref); - i1 := str_start[s1]; - j1 := str_start[s1 + 1]; - i2 := str_start[s2]; - j2 := str_start[s2 + 1]; - while (i1 < j1) and (i2 < j2) do begin - if str_pool[i1] < str_pool[i2] then begin - result := -1; - goto done; - end; - if str_pool[i1] > str_pool[i2] then begin - result := 1; - goto done; - end; - incr(i1); - incr(i2); - end; - if (i1 = j1) and (i2 = j2) then - result := 0 - else if i1 < j1 then - result := 1 - else - result := -1; -done: - pdf_retval := result; - flush_str(s2); - flush_str(s1); -end; - -@ @= -begin - check_pdfoutput("\pdfliteral", true); - new_whatsit(pdf_literal_node, write_node_size); - if scan_keyword("direct") then - pdf_literal_mode(tail) := direct_always - else if scan_keyword("page") then - pdf_literal_mode(tail) := direct_page - else - pdf_literal_mode(tail) := reset_origin; - scan_pdf_ext_toks; - pdf_literal_data(tail) := def_ref; -end - -@ The \.{\\pdfobj} primitive is used to create a ``raw'' object in the PDF - output file. The object contents will be hold in memory and will be written - out only when the object is referenced by \.{\\pdfrefobj}. When \.{\\pdfobj} - is used with \.{\\immediate}, the object contents will be written out - immediately. Objects referenced in the current page are appended into - |pdf_obj_list|. - -@= -@!pdf_last_obj: integer; - -@ @= -begin - check_pdfoutput("\pdfobj", true); - if scan_keyword("reserveobjnum") then begin - @; - incr(pdf_obj_count); - pdf_create_obj(obj_type_obj, pdf_obj_count); - pdf_last_obj := obj_ptr; - end - else begin - k := -1; - if scan_keyword("useobjnum") then begin - scan_int; - k := cur_val; - if (k <= 0) or (k > obj_ptr) or (obj_data_ptr(k) <> 0) then begin - pdf_warning("\pdfobj", "invalid object number being ignored", true); - pdf_retval := -1; {signal the problem} - k := -1; {will be generated again} - end; - end; - if k < 0 then begin - incr(pdf_obj_count); - pdf_create_obj(obj_type_obj, pdf_obj_count); - k := obj_ptr; - end; - obj_data_ptr(k) := pdf_get_mem(pdfmem_obj_size); - if scan_keyword("stream") then begin - obj_obj_is_stream(k) := 1; - if scan_keyword("attr") then begin - scan_pdf_ext_toks; - obj_obj_stream_attr(k) := def_ref; - end - else - obj_obj_stream_attr(k) := null; - end - else - obj_obj_is_stream(k) := 0; - if scan_keyword("file") then - obj_obj_is_file(k) := 1 - else - obj_obj_is_file(k) := 0; - scan_pdf_ext_toks; - obj_obj_data(k) := def_ref; - pdf_last_obj := k; - end; -end - -@ We need to check whether the referenced object exists. - -@= -procedure pdf_check_obj(t, n: integer); -var k: integer; -begin - k := head_tab[t]; - while (k <> 0) and (k <> n) do - k := obj_link(k); - if k = 0 then - pdf_error("ext1", "cannot find referenced object"); -end; - -@ @= -begin - check_pdfoutput("\pdfrefobj", true); - scan_int; - pdf_check_obj(obj_type_obj, cur_val); - new_whatsit(pdf_refobj_node, pdf_refobj_node_size); - pdf_obj_objnum(tail) := cur_val; -end - -@ \.{\\pdfxform} and \.{\\pdfrefxform} are similiar to \.{\\pdfobj} and - \.{\\pdfrefobj} - -@= -@!pdf_last_xform: integer; - -@ @= -begin - check_pdfoutput("\pdfxform", true); - incr(pdf_xform_count); - pdf_create_obj(obj_type_xform, pdf_xform_count); - k := obj_ptr; - obj_data_ptr(k) := pdf_get_mem(pdfmem_xform_size); - if scan_keyword("attr") then begin - scan_pdf_ext_toks; - obj_xform_attr(k) := def_ref; - end - else - obj_xform_attr(k) := null; - if scan_keyword("resources") then begin - scan_pdf_ext_toks; - obj_xform_resources(k) := def_ref; - end - else - obj_xform_resources(k) := null; - scan_int; - if box(cur_val) = null then - pdf_error("ext1", "\pdfxform cannot be used with a void box"); - obj_xform_width(k) := width(box(cur_val)); - obj_xform_height(k) := height(box(cur_val)); - obj_xform_depth(k) := depth(box(cur_val)); - obj_xform_box(k) := box(cur_val); {save pointer to the box} - box(cur_val) := null; - pdf_last_xform := k; -end - -@ @= -begin - check_pdfoutput("\pdfrefxform", true); - scan_int; - pdf_check_obj(obj_type_xform, cur_val); - new_whatsit(pdf_refxform_node, pdf_refxform_node_size); - pdf_xform_objnum(tail) := cur_val; - pdf_width(tail) := obj_xform_width(cur_val); - pdf_height(tail) := obj_xform_height(cur_val); - pdf_depth(tail) := obj_xform_depth(cur_val); -end - -@ \.{\\pdfximage} and \.{\\pdfrefximage} are similiar to \.{\\pdfxform} and - \.{\\pdfrefxform}. As we have to scan || quite often, it is better - have a |rule_node| that holds the most recently scanned ||. - -@= -@!pdf_last_ximage: integer; -@!pdf_last_ximage_pages: integer; -@!pdf_last_ximage_colordepth: integer; -@!alt_rule: pointer; -@!pdf_last_pdf_box_spec: integer; - -@ @= -alt_rule := null; - -@ @= -procedure scale_image(n: integer); -var x, y, xr, yr: integer; {size and resolution of image} - w, h: scaled; {indeed size corresponds to image resolution} - default_res: integer; -begin - x := image_width(obj_ximage_data(n)); - y := image_height(obj_ximage_data(n)); - xr := image_x_res(obj_ximage_data(n)); - yr := image_y_res(obj_ximage_data(n)); - if (xr > 65535) or (yr > 65535) then begin - xr := 0; - yr := 0; - pdf_warning("ext1", "too large image resolution ignored", true); - end; - if (x <= 0) or (y <= 0) or (xr < 0) or (yr < 0) then - pdf_error("ext1", "invalid image dimensions"); - if is_pdf_image(obj_ximage_data(n)) then begin - w := x; - h := y; - end - else begin - default_res := fix_int(pdf_image_resolution, 0, 65535); - if (default_res > 0) and ((xr = 0) or (yr = 0)) then begin - xr := default_res; - yr := default_res; - end; - if is_running(obj_ximage_width(n)) and - is_running(obj_ximage_height(n)) then - begin - if (xr > 0) and (yr > 0) then begin - w := ext_xn_over_d(one_hundred_inch, x, 100*xr); - h := ext_xn_over_d(one_hundred_inch, y, 100*yr); - end - else begin - w := ext_xn_over_d(one_hundred_inch, x, 7200); - h := ext_xn_over_d(one_hundred_inch, y, 7200); - end; - end; - end; - if is_running(obj_ximage_width(n)) and is_running(obj_ximage_height(n)) and - is_running(obj_ximage_depth(n)) then begin - obj_ximage_width(n) := w; - obj_ximage_height(n) := h; - obj_ximage_depth(n) := 0; - end - else if is_running(obj_ximage_width(n)) then begin - {image depth or height is explicitly specified} - if is_running(obj_ximage_height(n)) then begin - {image depth is explicitly specified} - obj_ximage_width(n) := ext_xn_over_d(h, x, y); - obj_ximage_height(n) := h - obj_ximage_depth(n); - end - else if is_running(obj_ximage_depth(n)) then begin - {image height is explicitly specified} - obj_ximage_width(n) := ext_xn_over_d(obj_ximage_height(n), x, y); - obj_ximage_depth(n) := 0; - end - else begin - {both image depth and height are explicitly specified} - obj_ximage_width(n) := ext_xn_over_d(obj_ximage_height(n) + - obj_ximage_depth(n), x, y); - end; - end - else begin - {image width is explicitly specified} - if is_running(obj_ximage_height(n)) and - is_running(obj_ximage_depth(n)) then begin - {both image depth and height are not specified} - obj_ximage_height(n) := ext_xn_over_d(obj_ximage_width(n), y, x); - obj_ximage_depth(n) := 0; - end - {image depth is explicitly specified} - else if is_running(obj_ximage_height(n)) then begin - obj_ximage_height(n) := - ext_xn_over_d(obj_ximage_width(n), y, x) - obj_ximage_depth(n); - end - {image height is explicitly specified} - else if is_running(obj_ximage_depth(n)) then begin - obj_ximage_depth(n) := 0; - end - {both image depth and height are explicitly specified} - else - do_nothing; - end; -end; - -procedure scan_pdf_box_spec; {scans pdf-box-spec to |pdf_last_pdf_box_spec|} -begin - pdf_last_pdf_box_spec := pdf_pdf_box_spec_crop; - - if scan_keyword("mediabox") then - pdf_last_pdf_box_spec := pdf_pdf_box_spec_media - else if scan_keyword("cropbox") then - pdf_last_pdf_box_spec := pdf_pdf_box_spec_crop - else if scan_keyword("bleedbox") then - pdf_last_pdf_box_spec := pdf_pdf_box_spec_bleed - else if scan_keyword("trimbox") then - pdf_last_pdf_box_spec := pdf_pdf_box_spec_trim - else if scan_keyword("artbox") then - pdf_last_pdf_box_spec := pdf_pdf_box_spec_art -end; - -procedure scan_alt_rule; {scans rule spec to |alt_rule|} -label reswitch; -begin - if alt_rule = null then - alt_rule := new_rule; - width(alt_rule) := null_flag; - height(alt_rule) := null_flag; - depth(alt_rule) := null_flag; -reswitch: - if scan_keyword("width") then begin - scan_normal_dimen; - width(alt_rule) := cur_val; - goto reswitch; - end; - if scan_keyword("height") then begin - scan_normal_dimen; - height(alt_rule) := cur_val; - goto reswitch; - end; - if scan_keyword("depth") then begin - scan_normal_dimen; - depth(alt_rule) := cur_val; - goto reswitch; - end; -end; - -procedure scan_image; -label reswitch; -var p: pointer; - k: integer; - named: str_number; - s: str_number; - page, colorspace: integer; -begin - incr(pdf_ximage_count); - pdf_create_obj(obj_type_ximage, pdf_ximage_count); - k := obj_ptr; - obj_data_ptr(k) := pdf_get_mem(pdfmem_ximage_size); - scan_alt_rule; {scans || to |alt_rule|} - obj_ximage_width(k) := width(alt_rule); - obj_ximage_height(k) := height(alt_rule); - obj_ximage_depth(k) := depth(alt_rule); - if scan_keyword("attr") then begin - scan_pdf_ext_toks; - obj_ximage_attr(k) := def_ref; - end - else - obj_ximage_attr(k) := null; - named := 0; - if scan_keyword("named") then begin - scan_pdf_ext_toks; - named := tokens_to_string(def_ref); - delete_token_ref(def_ref); - end - else if scan_keyword("page") then begin - scan_int; - page := cur_val; - end - else - page := 1; - if scan_keyword("colorspace") then begin - scan_int; - colorspace := cur_val; - end - else - colorspace := 0; - scan_pdf_box_spec; {scans pdf-box-spec to |pdf_last_pdf_box_spec|} - scan_pdf_ext_toks; - s := tokens_to_string(def_ref); - delete_token_ref(def_ref); - if pdf_option_always_use_pdfpagebox <> 0 then begin - pdf_warning(0,"Primitive \pdfoptionalwaysusepdfpagebox is obsolete; use \pdfforcepagebox instead.",true); - pdf_force_pagebox := pdf_option_always_use_pdfpagebox; - pdf_option_always_use_pdfpagebox := 0; {warn once} - end; - if pdf_option_pdf_inclusion_errorlevel <> 0 then begin - pdf_warning(0,"Primitive \pdfoptionpdfinclusionerrorlevel is obsolete; use \pdfinclusionerrorlevel instead.",true); - pdf_inclusion_errorlevel := pdf_option_pdf_inclusion_errorlevel; - pdf_option_pdf_inclusion_errorlevel := 0; {warn once} - end; - if pdf_force_pagebox > 0 then begin - print_err("pdfTeX warning (image inclusion): "); - print ("\pdfforcepagebox is in use ("); - print_int (pdf_force_pagebox); - print (")"); - print_ln; - end; - obj_ximage_data(k) := read_image(s, page, named, colorspace, - pdf_minor_version, - pdf_force_pagebox, - pdf_inclusion_errorlevel); - if named <> 0 then flush_str(named); - flush_str(s); - scale_image(k); - pdf_last_ximage := k; - pdf_last_ximage_pages := image_pages(obj_ximage_data(k)); - pdf_last_ximage_colordepth := image_colordepth(obj_ximage_data(k)); -end; - -@ @= -begin - check_pdfoutput("\pdfximage", true); - check_pdfminorversion; - scan_image; -end - -@ @= -begin - check_pdfoutput("\pdfrefximage", true); - scan_int; - pdf_check_obj(obj_type_ximage, cur_val); - new_whatsit(pdf_refximage_node, pdf_refximage_node_size); - pdf_ximage_objnum(tail) := cur_val; - pdf_width(tail) := obj_ximage_width(cur_val); - pdf_height(tail) := obj_ximage_height(cur_val); - pdf_depth(tail) := obj_ximage_depth(cur_val); -end - -@ The following function finds object with identifier |i| and type |t|. - |i < 0| indicates that |-i| should be treated as a string number. If no - such object exists then it will be created. This function is used mainly to - find destination for link annotations and outlines; however it is also used - in |pdf_ship_out| (to check whether a Page object already exists) so we need - to declare it together with subroutines needed in |pdf_hlist_out| and - |pdf_vlist_out|. - -@= -function find_obj(t, i: integer; byname: boolean): integer; -begin - find_obj := avl_find_obj(t, i, byname); -end; - -procedure flush_str(s: str_number); {flush a string if possible} -begin - if flushable(s) then - flush_string; -end; - -function get_obj(t, i: integer; byname: boolean): integer; -var r: integer; - s: str_number; -begin - if byname > 0 then begin - s := tokens_to_string(i); - r := find_obj(t, s, true); - end - else begin - s := 0; - r := find_obj(t, i, false); - end; - if r = 0 then begin - if byname > 0 then begin - pdf_create_obj(t, -s); - s := 0; - end - else - pdf_create_obj(t, i); - r := obj_ptr; - if t = obj_type_dest then - obj_dest_ptr(r) := null; - end; - if s <> 0 then - flush_str(s); - get_obj := r; -end; - -function get_microinterval:integer; -var s,@!m:integer; {seconds and microseconds} -begin - seconds_and_micros(s,m); - if (s-epochseconds)>32767 then - get_microinterval := max_integer - else if (microseconds>m) then - get_microinterval := ((s-1-epochseconds)*65536)+ (((m+1000000-microseconds)/100)*65536)/10000 - else - get_microinterval := ((s-epochseconds)*65536) + (((m-microseconds)/100)*65536)/10000; -end; - - -@ @= -function scan_action: pointer; {read an action specification} -var p, t: integer; - s: str_number; -begin - p := get_node(pdf_action_size); - scan_action := p; - pdf_action_file(p) := null; - pdf_action_refcount(p) := null; - if scan_keyword("user") then - pdf_action_type(p) := pdf_action_user - else if scan_keyword("goto") then - pdf_action_type(p) := pdf_action_goto - else if scan_keyword("thread") then - pdf_action_type(p) := pdf_action_thread - else - pdf_error("ext1", "action type missing"); - if pdf_action_type(p) = pdf_action_user then begin - scan_pdf_ext_toks; - pdf_action_user_tokens(p) := def_ref; - return; - end; - if scan_keyword("file") then begin - scan_pdf_ext_toks; - pdf_action_file(p) := def_ref; - end; - if scan_keyword("page") then begin - if pdf_action_type(p) <> pdf_action_goto then - pdf_error("ext1", "only GoTo action can be used with `page'"); - pdf_action_type(p) := pdf_action_page; - scan_int; - if cur_val <= 0 then - pdf_error("ext1", "page number must be positive"); - pdf_action_id(p) := cur_val; - pdf_action_named_id(p) := 0; - scan_pdf_ext_toks; - pdf_action_page_tokens(p) := def_ref; - end - else if scan_keyword("name") then begin - scan_pdf_ext_toks; - pdf_action_named_id(p) := 1; - pdf_action_id(p) := def_ref; - end - else if scan_keyword("num") then begin - if (pdf_action_type(p) = pdf_action_goto) and - (pdf_action_file(p) <> null) then - pdf_error("ext1", - "`goto' option cannot be used with both `file' and `num'"); - scan_int; - if cur_val <= 0 then - pdf_error("ext1", "num identifier must be positive"); - pdf_action_named_id(p) := 0; - pdf_action_id(p) := cur_val; - end - else - pdf_error("ext1", "identifier type missing"); - if scan_keyword("newwindow") then begin - pdf_action_new_window(p) := 1; - @; end - else if scan_keyword("nonewwindow") then begin - pdf_action_new_window(p) := 2; - @; end - else - pdf_action_new_window(p) := 0; - if (pdf_action_new_window(p) > 0) and - (((pdf_action_type(p) <> pdf_action_goto) and - (pdf_action_type(p) <> pdf_action_page)) or - (pdf_action_file(p) = null)) then - pdf_error("ext1", - "`newwindow'/`nonewwindow' must be used with `goto' and `file' option"); -end; - -procedure new_annot_whatsit(w, s: small_number); {create a new whatsit node for -annotation} -var p: pointer; -begin - new_whatsit(w, s); - scan_alt_rule; {scans || to |alt_rule|} - pdf_width(tail) := width(alt_rule); - pdf_height(tail) := height(alt_rule); - pdf_depth(tail) := depth(alt_rule); - if (w = pdf_start_link_node) then begin - if scan_keyword("attr") then begin - scan_pdf_ext_toks; - pdf_link_attr(tail) := def_ref; - end - else - pdf_link_attr(tail) := null; - end; - if (w = pdf_thread_node) or (w = pdf_start_thread_node) then begin - if scan_keyword("attr") then begin - scan_pdf_ext_toks; - pdf_thread_attr(tail) := def_ref; - end - else - pdf_thread_attr(tail) := null; - end; -end; - -@ @= -@!pdf_last_annot: integer; - -@ @= -begin - check_pdfoutput("\pdfannot", true); - if scan_keyword("reserveobjnum") then begin - pdf_last_annot := pdf_new_objnum; - @; end - else begin - if scan_keyword("useobjnum") then begin - scan_int; - k := cur_val; - if (k <= 0) or (k > obj_ptr) or (obj_annot_ptr(k) <> 0) then - pdf_error("ext1", "invalid object number"); - end - else - k := pdf_new_objnum; - new_annot_whatsit(pdf_annot_node, pdf_annot_node_size); - pdf_annot_objnum(tail) := k; - scan_pdf_ext_toks; - pdf_annot_data(tail) := def_ref; - pdf_last_annot := k; - end -end - -@ @= -begin - check_pdfoutput("\pdfstartlink", true); - if abs(mode) = vmode then - pdf_error("ext1", "\pdfstartlink cannot be used in vertical mode"); - new_annot_whatsit(pdf_start_link_node, pdf_annot_node_size); - pdf_link_action(tail) := scan_action; -end - -@ @= -begin - check_pdfoutput("\pdfendlink", true); - if abs(mode) = vmode then - pdf_error("ext1", "\pdfendlink cannot be used in vertical mode"); - new_whatsit(pdf_end_link_node, small_node_size); -end - -@ @= -function outline_list_count(p: pointer): integer; {return number of outline -entries in the same level with |p|} -var k: integer; -begin - k := 1; - while obj_outline_prev(p) <> 0 do begin - incr(k); - p := obj_outline_prev(p); - end; - outline_list_count := k; -end; - -@ @= -begin - check_pdfoutput("\pdfoutline", true); - if scan_keyword("attr") then begin - scan_pdf_ext_toks; - r := def_ref; - end - else - r := 0; - p := scan_action; - if scan_keyword("count") then begin - scan_int; - i := cur_val; - end - else - i := 0; - scan_pdf_ext_toks; - q := def_ref; - pdf_new_obj(obj_type_others, 0); - j := obj_ptr; - write_action(p); - pdf_end_obj; - delete_action_ref(p); - pdf_create_obj(obj_type_outline, 0); - k := obj_ptr; - obj_outline_ptr(k) := pdf_get_mem(pdfmem_outline_size); - obj_outline_action_objnum(k) := j; - obj_outline_count(k) := i; - pdf_new_obj(obj_type_others, 0); - pdf_print_str_ln(tokens_to_string(q)); - flush_str(last_tokens_string); - delete_token_ref(q); - pdf_end_obj; - obj_outline_title(k) := obj_ptr; - obj_outline_prev(k) := 0; - obj_outline_next(k) := 0; - obj_outline_first(k) := 0; - obj_outline_last(k) := 0; - obj_outline_parent(k) := pdf_parent_outline; - obj_outline_attr(k) := r; - if pdf_first_outline = 0 then - pdf_first_outline := k; - if pdf_last_outline = 0 then begin - if pdf_parent_outline <> 0 then - obj_outline_first(pdf_parent_outline) := k; - end - else begin - obj_outline_next(pdf_last_outline) := k; - obj_outline_prev(k) := pdf_last_outline; - end; - pdf_last_outline := k; - if obj_outline_count(k) <> 0 then begin - pdf_parent_outline := k; - pdf_last_outline := 0; - end - else if (pdf_parent_outline <> 0) and - (outline_list_count(k) = abs(obj_outline_count(pdf_parent_outline))) then - begin - j := pdf_last_outline; - repeat - obj_outline_last(pdf_parent_outline) := j; - j := pdf_parent_outline; - pdf_parent_outline := obj_outline_parent(pdf_parent_outline); - until (pdf_parent_outline = 0) or - (outline_list_count(j) < abs(obj_outline_count(pdf_parent_outline))); - if pdf_parent_outline = 0 then - pdf_last_outline := pdf_first_outline - else - pdf_last_outline := obj_outline_first(pdf_parent_outline); - while obj_outline_next(pdf_last_outline) <> 0 do - pdf_last_outline := obj_outline_next(pdf_last_outline); - end; -end - -@ When a destination is created we need to check whether another destination -with the same identifier already exists and give a warning if needed. - -@= -procedure warn_dest_dup(id: integer; byname: small_number; s1, s2: str_number); -begin - pdf_warning(s1, "destination with the same identifier (", false); - if byname > 0 then begin - print("name"); - print_mark(id); - end - else begin - print("num"); - print_int(id); - end; - print(") "); - print(s2); - print_ln; - show_context; -end; - -@ Notice that |scan_keyword| doesn't care if two words have same prefix; so -we should be careful when scan keywords with same prefix. The main rule: if -there are two or more keywords with the same prefix, then always test in -order from the longest one to the shortest one. - -@= -begin - check_pdfoutput("\pdfdest", true); - q := tail; - new_whatsit(pdf_dest_node, pdf_dest_node_size); - if scan_keyword("num") then begin - scan_int; - if cur_val <= 0 then - pdf_error("ext1", "num identifier must be positive"); - if cur_val > max_halfword then - pdf_error("ext1", "number too big"); - pdf_dest_id(tail) := cur_val; - pdf_dest_named_id(tail) := 0; - end - else if scan_keyword("name") then begin - scan_pdf_ext_toks; - pdf_dest_id(tail) := def_ref; - pdf_dest_named_id(tail) := 1; - end - else - pdf_error("ext1", "identifier type missing"); - if scan_keyword("xyz") then begin - pdf_dest_type(tail) := pdf_dest_xyz; - if scan_keyword("zoom") then begin - scan_int; - if cur_val > max_halfword then - pdf_error("ext1", "number too big"); - pdf_dest_xyz_zoom(tail) := cur_val; - end - else - pdf_dest_xyz_zoom(tail) := null; - end - else if scan_keyword("fitbh") then - pdf_dest_type(tail) := pdf_dest_fitbh - else if scan_keyword("fitbv") then - pdf_dest_type(tail) := pdf_dest_fitbv - else if scan_keyword("fitb") then - pdf_dest_type(tail) := pdf_dest_fitb - else if scan_keyword("fith") then - pdf_dest_type(tail) := pdf_dest_fith - else if scan_keyword("fitv") then - pdf_dest_type(tail) := pdf_dest_fitv - else if scan_keyword("fitr") then - pdf_dest_type(tail) := pdf_dest_fitr - else if scan_keyword("fit") then - pdf_dest_type(tail) := pdf_dest_fit - else - pdf_error("ext1", "destination type missing"); - @; - if pdf_dest_type(tail) = pdf_dest_fitr then begin - scan_alt_rule; {scans || to |alt_rule|} - pdf_width(tail) := width(alt_rule); - pdf_height(tail) := height(alt_rule); - pdf_depth(tail) := depth(alt_rule); - end; - if pdf_dest_named_id(tail) <> 0 then begin - i := tokens_to_string(pdf_dest_id(tail)); - k := find_obj(obj_type_dest, i, true); - flush_str(i); - end - else - k := find_obj(obj_type_dest, pdf_dest_id(tail), false); - if (k <> 0) and (obj_dest_ptr(k) <> null) then begin - warn_dest_dup(pdf_dest_id(tail), pdf_dest_named_id(tail), - "ext4", "has been already used, duplicate ignored"); - flush_node_list(tail); - tail := q; - link(q) := null; - end; -end - -@ @= -procedure scan_thread_id; -begin - if scan_keyword("num") then begin - scan_int; - if cur_val <= 0 then - pdf_error("ext1", "num identifier must be positive"); - if cur_val > max_halfword then - pdf_error("ext1", "number too big"); - pdf_thread_id(tail) := cur_val; - pdf_thread_named_id(tail) := 0; - end - else if scan_keyword("name") then begin - scan_pdf_ext_toks; - pdf_thread_id(tail) := def_ref; - pdf_thread_named_id(tail) := 1; - end - else - pdf_error("ext1", "identifier type missing"); -end; - -@ @= -begin - check_pdfoutput("\pdfthread", true); - new_annot_whatsit(pdf_thread_node, pdf_thread_node_size); - scan_thread_id; -end - -@ @= -begin - check_pdfoutput("\pdfstartthread", true); - new_annot_whatsit(pdf_start_thread_node, pdf_thread_node_size); - scan_thread_id; -end - -@ @= -begin - check_pdfoutput("\pdfendthread", true); - new_whatsit(pdf_end_thread_node, small_node_size); -end - -@ @= -@!pdf_last_x_pos: integer; -@!pdf_last_y_pos: integer; - -@ @= -begin - check_pdfoutput("\pdfsavepos", true); - new_whatsit(pdf_save_pos_node, small_node_size); -end - -@ To implement primitives as \.{\\pdfinfo}, \.{\\pdfcatalog} or -\.{\\pdfnames} we need to concatenate tokens lists. - -@= -function concat_tokens(q, r: pointer): pointer; {concat |q| and |r| and -returns the result tokens list} -var p: pointer; -begin - if q = null then begin - concat_tokens := r; - return; - end; - p := q; - while link(p) <> null do - p := link(p); - link(p) := link(r); - free_avail(r); - concat_tokens := q; -end; - -@ @= -begin - check_pdfoutput("\pdfinfo", false); - scan_pdf_ext_toks; - if pdf_output > 0 then - pdf_info_toks := concat_tokens(pdf_info_toks, def_ref); -end - -@ @= -begin - check_pdfoutput("\pdfcatalog", false); - scan_pdf_ext_toks; - if pdf_output > 0 then - pdf_catalog_toks := concat_tokens(pdf_catalog_toks, def_ref); - if scan_keyword("openaction") then - if pdf_catalog_openaction <> 0 then - pdf_error("ext1", "duplicate of openaction") - else begin - p := scan_action; - pdf_new_obj(obj_type_others, 0); - if pdf_output > 0 then - pdf_catalog_openaction := obj_ptr; - write_action(p); - pdf_end_obj; - delete_action_ref(p); - end; -end - -@ @= -begin - check_pdfoutput("\pdfnames", true); - scan_pdf_ext_toks; - pdf_names_toks := concat_tokens(pdf_names_toks, def_ref); -end - -@ @= -begin - check_pdfoutput("\pdftrailer", false); - scan_pdf_ext_toks; - if pdf_output > 0 then - pdf_trailer_toks := concat_tokens(pdf_trailer_toks, def_ref); -end - -@ @= -begin - scan_pdf_ext_toks; - escapestr(tokens_to_string(def_ref)); - flush_str(last_tokens_string); - delete_token_ref(def_ref); -end - -@ @= -begin - scan_pdf_ext_toks; - escapename(tokens_to_string(def_ref)); - flush_str(last_tokens_string); - delete_token_ref(def_ref); -end - -@ @= -@!pdf_retval: integer; {global multi-purpose return value} - -@ @= -compare_strings - -@ @= - seconds_and_micros(epochseconds,microseconds); - -@ @= -begin - seconds_and_micros(epochseconds,microseconds); -end - - -@ The following subroutines are about PDF-specific font issues. - -@= -procedure pdf_include_chars; -var s: str_number; - k: pool_pointer; {running indices} - f: internal_font_number; -begin - scan_font_ident; - f := cur_val; - if f = null_font then - pdf_error("font", "invalid font identifier"); - pdf_check_vf(f); - if not font_used[f] then - pdf_init_font(f); - scan_pdf_ext_toks; - s := tokens_to_string(def_ref); - delete_token_ref(def_ref); - k := str_start[s]; - while k < str_start[s + 1] do begin - pdf_mark_char(f, str_pool[k]); - incr(k); - end; - flush_str(s); -end; - - -@ @= -begin - check_pdfoutput("\pdfincludechars", true); - pdf_include_chars; -end - -@ @= -begin - check_pdfoutput("\pdffontattr", true); - scan_font_ident; - k := cur_val; - if k = null_font then - pdf_error("font", "invalid font identifier"); - scan_pdf_ext_toks; - pdf_font_attr[k] := tokens_to_string(def_ref); -end - -@ @= -begin - check_pdfoutput("\pdfmapfile", true); - scan_pdf_ext_toks; - pdfmapfile(def_ref); - delete_token_ref(def_ref); -end - -@ @= -begin - check_pdfoutput("\pdfmapline", true); - scan_pdf_ext_toks; - pdfmapline(def_ref); - delete_token_ref(def_ref); -end - -@ The following function are needed for outputing article thread. - -@= -procedure thread_title(thread: integer); -begin - pdf_print("/Title ("); - if obj_info(thread) < 0 then - pdf_print(-obj_info(thread)) - else - pdf_print_int(obj_info(thread)); - pdf_print_ln(")"); -end; - -procedure pdf_fix_thread(thread: integer); -var a: pointer; -begin - pdf_warning("thread", "destination ", false); - if obj_info(thread) < 0 then begin - print("name{"); - print(-obj_info(thread)); - print("}"); - end - else begin - print("num"); - print_int(obj_info(thread)); - end; - print(" has been referenced but does not exist, replaced by a fixed one"); - print_ln; print_ln; - pdf_new_dict(obj_type_others, 0); - a := obj_ptr; - pdf_indirect_ln("T", thread); - pdf_indirect_ln("V", a); - pdf_indirect_ln("N", a); - pdf_indirect_ln("P", head_tab[obj_type_page]); - pdf_print("/R [0 0 "); - pdf_print_bp(pdf_page_width); pdf_out(" "); - pdf_print_bp(pdf_page_height); - pdf_print_ln("]"); - pdf_end_dict; - pdf_begin_dict(thread); - pdf_print_ln("/I << "); - thread_title(thread); - pdf_print_ln(">>"); - pdf_indirect_ln("F", a); - pdf_end_dict; -end; - -procedure out_thread(thread: integer); -var a, b, c: pointer; - last_attr: integer; -begin - if obj_thread_first(thread) = 0 then begin - pdf_fix_thread(thread); - return; - end; - pdf_begin_dict(thread); - a := obj_thread_first(thread); - b := a; - last_attr := 0; - repeat - if obj_bead_attr(a) <> 0 then - last_attr := obj_bead_attr(a); - a := obj_bead_next(a); - until a = b; - if last_attr <> 0 then - pdf_print_ln(last_attr) - else begin - pdf_print_ln("/I << "); - thread_title(thread); - pdf_print_ln(">>"); - end; - pdf_indirect_ln("F", a); - pdf_end_dict; - repeat - pdf_begin_dict(a); - if a = b then - pdf_indirect_ln("T", thread); - pdf_indirect_ln("V", obj_bead_prev(a)); - pdf_indirect_ln("N", obj_bead_next(a)); - pdf_indirect_ln("P", obj_bead_page(a)); - pdf_indirect_ln("R", obj_bead_rect(a)); - pdf_end_dict; - a := obj_bead_next(a); - until a = b; -end; - -@ @ for whatsit node created by \pdfTeX@>= -print("("); -print_rule_dimen(pdf_height(p)); -print_char("+"); -print_rule_dimen(pdf_depth(p)); -print(")x"); -print_rule_dimen(pdf_width(p)) -@z - -@x [1356] -othercases print("whatsit?") -@y -pdf_literal_node: begin - print_esc("pdfliteral"); - case pdf_literal_mode(p) of - reset_origin: - do_nothing; - direct_page: - print(" page"); - direct_always: - print(" direct"); - othercases confusion("literal2") - endcases; - print_mark(pdf_literal_data(p)); -end; -pdf_refobj_node: begin - print_esc("pdfrefobj"); - if obj_obj_is_stream(pdf_obj_objnum(p)) > 0 then begin - if obj_obj_stream_attr(pdf_obj_objnum(p)) <> null then begin - print(" attr"); - print_mark(obj_obj_stream_attr(pdf_obj_objnum(p))); - end; - print(" stream"); - end; - if obj_obj_is_file(pdf_obj_objnum(p)) > 0 then - print(" file"); - print_mark(obj_obj_data(pdf_obj_objnum(p))); -end; -pdf_refxform_node: begin - print_esc("pdfrefxform"); - print("("); - print_scaled(obj_xform_height(pdf_xform_objnum(p))); - print_char("+"); - print_scaled(obj_xform_depth(pdf_xform_objnum(p))); - print(")x"); - print_scaled(obj_xform_width(pdf_xform_objnum(p))); -end; -pdf_refximage_node: begin - print_esc("pdfrefximage"); - print("("); - print_scaled(obj_ximage_height(pdf_ximage_objnum(p))); - print_char("+"); - print_scaled(obj_ximage_depth(pdf_ximage_objnum(p))); - print(")x"); - print_scaled(obj_ximage_width(pdf_ximage_objnum(p))); -end; -pdf_annot_node: begin - print_esc("pdfannot"); - @ for whatsit node created by \pdfTeX@>; - print_mark(pdf_annot_data(p)); -end; -pdf_start_link_node: begin - print_esc("pdflink"); - @ for whatsit node created by \pdfTeX@>; - if pdf_link_attr(p) <> null then begin - print(" attr"); - print_mark(pdf_link_attr(p)); - end; - print(" action"); - if pdf_action_type(pdf_link_action(p)) = pdf_action_user then begin - print(" user"); - print_mark(pdf_action_user_tokens(pdf_link_action(p))); - return; - end; - if pdf_action_file(pdf_link_action(p)) <> null then begin - print(" file"); - print_mark(pdf_action_file(pdf_link_action(p))); - end; - case pdf_action_type(pdf_link_action(p)) of - pdf_action_goto: begin - if pdf_action_named_id(pdf_link_action(p)) > 0 then begin - print(" goto name"); - print_mark(pdf_action_id(pdf_link_action(p))); - end - else begin - print(" goto num"); - print_int(pdf_action_id(pdf_link_action(p))) - end; - end; - pdf_action_page: begin - print(" page"); - print_int(pdf_action_id(pdf_link_action(p))); - print_mark(pdf_action_page_tokens(pdf_link_action(p))); - end; - pdf_action_thread: begin - if pdf_action_named_id(pdf_link_action(p)) > 0 then begin - print(" thread name"); - print_mark(pdf_action_id(pdf_link_action(p))); - end - else begin - print(" thread num"); - print_int(pdf_action_id(pdf_link_action(p))); - end; - end; - othercases pdf_error("displaying", "unknown action type"); - endcases; -end; -pdf_end_link_node: print_esc("pdfendlink"); -pdf_dest_node: begin - print_esc("pdfdest"); - if pdf_dest_named_id(p) > 0 then begin - print(" name"); - print_mark(pdf_dest_id(p)); - end - else begin - print(" num"); - print_int(pdf_dest_id(p)); - end; - print(" "); - case pdf_dest_type(p) of - pdf_dest_xyz: begin - print("xyz"); - if pdf_dest_xyz_zoom(p) <> null then begin - print(" zoom"); - print_int(pdf_dest_xyz_zoom(p)); - end; - end; - pdf_dest_fitbh: print("fitbh"); - pdf_dest_fitbv: print("fitbv"); - pdf_dest_fitb: print("fitb"); - pdf_dest_fith: print("fith"); - pdf_dest_fitv: print("fitv"); - pdf_dest_fitr: begin - print("fitr"); - @ for whatsit node created by \pdfTeX@>; - end; - pdf_dest_fit: print("fit"); - othercases print("unknown!"); - endcases; -end; -pdf_thread_node, -pdf_start_thread_node: begin - if subtype(p) = pdf_thread_node then - print_esc("pdfthread") - else - print_esc("pdfstartthread"); - print("("); print_rule_dimen(pdf_height(p)); print_char("+"); - print_rule_dimen(pdf_depth(p)); print(")x"); - print_rule_dimen(pdf_width(p)); - if pdf_thread_attr(p) <> null then begin - print(" attr"); - print_mark(pdf_thread_attr(p)); - end; - if pdf_thread_named_id(p) > 0 then begin - print(" name"); - print_mark(pdf_thread_id(p)); - end - else begin - print(" num"); - print_int(pdf_thread_id(p)); - end; -end; -pdf_end_thread_node: print_esc("pdfendthread"); -pdf_save_pos_node: print_esc("pdfsavepos"); -othercases print("whatsit?") -@z - -@x [1357] -othercases confusion("ext2") -@y -pdf_literal_node: begin - r := get_node(write_node_size); - add_token_ref(pdf_literal_data(p)); - words := write_node_size; -end; -pdf_refobj_node: begin - r := get_node(pdf_refobj_node_size); - words := pdf_refobj_node_size; -end; -pdf_refxform_node: begin - r := get_node(pdf_refxform_node_size); - words := pdf_refxform_node_size; -end; -pdf_refximage_node: begin - r := get_node(pdf_refximage_node_size); - words := pdf_refximage_node_size; -end; -pdf_annot_node: begin - r := get_node(pdf_annot_node_size); - add_token_ref(pdf_annot_data(p)); - words := pdf_annot_node_size; -end; -pdf_start_link_node: begin - r := get_node(pdf_annot_node_size); - pdf_height(r) := pdf_height(p); - pdf_depth(r) := pdf_depth(p); - pdf_width(r) := pdf_width(p); - pdf_link_attr(r) := pdf_link_attr(p); - if pdf_link_attr(r) <> null then - add_token_ref(pdf_link_attr(r)); - pdf_link_action(r) := pdf_link_action(p); - add_action_ref(pdf_link_action(r)); -end; -pdf_end_link_node: - r := get_node(small_node_size); -pdf_dest_node: begin - r := get_node(pdf_dest_node_size); - if pdf_dest_named_id(p) > 0 then - add_token_ref(pdf_dest_id(p)); - words := pdf_dest_node_size; -end; -pdf_thread_node, -pdf_start_thread_node: begin - r := get_node(pdf_thread_node_size); - if pdf_thread_named_id(p) > 0 then - add_token_ref(pdf_thread_id(p)); - if pdf_thread_attr(p) <> null then - add_token_ref(pdf_thread_attr(p)); - words := pdf_thread_node_size; -end; -pdf_end_thread_node: - r := get_node(small_node_size); -pdf_save_pos_node: - r := get_node(small_node_size); -othercases confusion("ext2") -@z - -@x [1358] -othercases confusion("ext3") -@y -pdf_literal_node: begin - delete_token_ref(pdf_literal_data(p)); - free_node(p, write_node_size); -end; -pdf_refobj_node: - free_node(p, pdf_refobj_node_size); -pdf_refxform_node: - free_node(p, pdf_refxform_node_size); -pdf_refximage_node: - free_node(p, pdf_refximage_node_size); -pdf_annot_node: begin - delete_token_ref(pdf_annot_data(p)); - free_node(p, pdf_annot_node_size); -end; -pdf_start_link_node: begin - if pdf_link_attr(p) <> null then - delete_token_ref(pdf_link_attr(p)); - delete_action_ref(pdf_link_action(p)); - free_node(p, pdf_annot_node_size); -end; -pdf_end_link_node: - free_node(p, small_node_size); -pdf_dest_node: begin - if pdf_dest_named_id(p) > 0 then - delete_token_ref(pdf_dest_id(p)); - free_node(p, pdf_dest_node_size); -end; -pdf_thread_node, -pdf_start_thread_node: begin - if pdf_thread_named_id(p) > 0 then - delete_token_ref(pdf_thread_id(p)); - if pdf_thread_attr(p) <> null then - delete_token_ref(pdf_thread_attr(p)); - free_node(p, pdf_thread_node_size); -end; -pdf_end_thread_node: - free_node(p, small_node_size); -pdf_save_pos_node: - free_node(p, small_node_size); -othercases confusion("ext3") -@z - -@x [1359] -@ @=do_nothing -@y -@ @= -if (subtype(p) = pdf_refxform_node) or (subtype(p) = pdf_refximage_node) then -begin x:=x+d+pdf_height(p); d:=pdf_depth(p); -s:=0; -if pdf_width(p)+s>w then w:=pdf_width(p)+s; -end -@z - -@x [1360] -@ @=do_nothing -@y -@ @= -if (subtype(p) = pdf_refxform_node) or (subtype(p) = pdf_refximage_node) then -begin x:=x+pdf_width(p); -s:=0; -if pdf_height(p)-s>h then h:=pdf_height(p)-s; -if pdf_depth(p)+s>d then d:=pdf_depth(p)+s; -end -@z - -@x [1361] -@ @=d:=0 -@y -@ @= -if (subtype(p) = pdf_refxform_node) or (subtype(p) = pdf_refximage_node) then - d := pdf_width(p) -else - d := 0 -@z - -@x [1362] -@=@+ -adv_past(cur_p) -@y -@=@+ -begin -adv_past(cur_p); -if (subtype(cur_p) = pdf_refxform_node) or (subtype(cur_p) = pdf_refximage_node) then - act_width:=act_width+pdf_width(cur_p); -end -@z - -@x [1364] -@ @= -goto contribute -@y -@ @= -begin - if (subtype(p) = pdf_refxform_node) or (subtype(p) = pdf_refximage_node) then - begin page_total:=page_total+page_depth+pdf_height(p); - page_depth:=pdf_depth(p); - end; - goto contribute; -end -@z - -@x [1365] -@ @= -goto not_found -@y -@ @= -begin - if (subtype(p) = pdf_refxform_node) or (subtype(p) = pdf_refximage_node) then - begin cur_height:=cur_height+prev_dp+pdf_height(p); prev_dp:=pdf_depth(p); - end; - goto not_found; -end -@z - -@x [1375] -@= -begin get_x_token; -if (cur_cmd=extension)and(cur_chr<=close_node) then - begin p:=tail; do_extension; {append a whatsit node} - out_what(tail); {do the action immediately} - flush_node_list(tail); tail:=p; link(p):=null; - end -else back_input; -end -@y -@= -begin get_x_token; -if cur_cmd=extension then begin - if cur_chr<=close_node then - begin p:=tail; do_extension; {append a whatsit node} - out_what(tail); {do the action immediately} - flush_node_list(tail); tail:=p; link(p):=null; - end - else case cur_chr of - pdf_obj_code: begin - do_extension; {scan object and set |pdf_last_obj|} - if obj_data_ptr(pdf_last_obj) = 0 then {this object has not been initialized yet} - pdf_error("ext1", "`\pdfobj reserveobjnum' cannot be used with \immediate"); - pdf_write_obj(pdf_last_obj); - end; - pdf_xform_code: begin - do_extension; {scan form and set |pdf_last_xform|} - pdf_cur_form := pdf_last_xform; - pdf_ship_out(obj_xform_box(pdf_last_xform), false); - end; - pdf_ximage_code: begin - do_extension; {scan image and set |pdf_last_ximage|} - pdf_write_image(pdf_last_ximage); - end; - othercases back_input - endcases; -end -else - back_input; -end -@z - -@x [1378] -@ @= -for k:=0 to 15 do if write_open[k] then a_close(write_file[k]) -@y -@ @= -for k:=0 to 15 do if write_open[k] then a_close(write_file[k]) - -@ Shipping out PDF marks. - -@= -dest_name_entry = record - objname: str_number; {destination name} - objnum: integer; {destination object number} -end; - -@ @= -@!cur_page_width: scaled; {width of page being shipped} -@!cur_page_height: scaled; {height of page being shipped} -@!cur_h_offset: scaled; {horizontal offset of page being shipped} -@!cur_v_offset: scaled; {vertical offset of page being shipped} -@!pdf_obj_list: pointer; {list of objects in the current page} -@!pdf_xform_list: pointer; {list of forms in the current page} -@!pdf_ximage_list: pointer; {list of images in the current page} -@!pdf_link_level: integer; {depth of nesting of box containing link annotation} -@!last_link: pointer; {pointer to the last link annotation} -@!pdf_link_ht, pdf_link_dp, pdf_link_wd: scaled; {dimensions of the last link -annotation} -@!last_thread: pointer; {pointer to the last thread} -@!pdf_thread_ht, pdf_thread_dp, pdf_thread_wd: scaled; {dimensions of the last -thread} -@!pdf_last_thread_id: halfword; {identifier of the last thread} -@!pdf_last_thread_named_id: boolean; {is identifier of the last thread named} -@!pdf_thread_level: integer; {depth of nesting of box containing the last thread} -@!pdf_annot_list: pointer; {list of annotations in the current page} -@!pdf_link_list: pointer; {list of link annotations in the current page} -@!pdf_dest_list: pointer; {list of destinations in the current page} -@!pdf_bead_list: pointer; {list of thread beads in the current page} -@!pdf_obj_count: integer; {counter of objects} -@!pdf_xform_count: integer; {counter of forms} -@!pdf_ximage_count: integer; {counter of images} -@!pdf_cur_form: integer; {the form being output} -@!pdf_first_outline, pdf_last_outline, pdf_parent_outline: integer; -@!pdf_xform_width, -@!pdf_xform_height, -@!pdf_xform_depth: scaled; {dimension of the current form} -@!pdf_info_toks: pointer; {additional keys of Info dictionary} -@!pdf_catalog_toks: pointer; {additional keys of Catalog dictionary} -@!pdf_catalog_openaction: integer; -@!pdf_names_toks: pointer; {additional keys of Names dictionary} -@!pdf_dest_names_ptr: integer; {first unused position in |dest_names|} -@!dest_names_size: integer; {maximum number of names in name tree of PDF output file} -@!dest_names: ^dest_name_entry; -@!image_orig_x, image_orig_y: integer; {origin of cropped pdf images} -@!link_level_stack: pointer; {stack to save |pdf_link_level|} -@!pdf_trailer_toks: pointer; {additional keys of Trailer dictionary} - -@ @= -pdf_link_level := -1; -link_level_stack := null; -pdf_first_outline:= 0; -pdf_last_outline:= 0; -pdf_parent_outline:= 0; -pdf_obj_count := 0; -pdf_xform_count := 0; -pdf_ximage_count := 0; -pdf_dest_names_ptr := 0; -pdf_info_toks := null; -pdf_catalog_toks := null; -pdf_names_toks := null; -pdf_catalog_openaction := 0; -pdf_trailer_toks := null; - -@ The following procedures are needed for outputing whatsit nodes for -pdfTeX. - -@= -procedure write_action(p: pointer); {write an action specification} -var s: str_number; - d: integer; -begin - if pdf_action_type(p) = pdf_action_user then begin - pdf_print_toks_ln(pdf_action_user_tokens(p)); - return; - end; - pdf_print("<< "); - if pdf_action_file(p) <> null then begin - pdf_print("/F "); - s := tokens_to_string(pdf_action_file(p)); - if (str_pool[str_start[s]] = 40) and - (str_pool[str_start[s] + length(s) - 1] = 41) then - pdf_print(s) - else begin - pdf_print_str(s); - end; - flush_str(s); - pdf_print(" "); - if pdf_action_new_window(p) > 0 then begin - pdf_print("/NewWindow "); - if pdf_action_new_window(p) = 1 then - pdf_print("true ") - else - pdf_print("false "); - end; - end; - case pdf_action_type(p) of - pdf_action_page: begin - if pdf_action_file(p) = null then begin - pdf_print("/S /GoTo /D ["); - pdf_print_int(get_obj(obj_type_page, pdf_action_id(p), false)); - pdf_print(" 0 R"); - end - else begin - pdf_print("/S /GoToR /D ["); - pdf_print_int(pdf_action_id(p) - 1); - end; - pdf_out(" "); - pdf_print(tokens_to_string(pdf_action_page_tokens(p))); - flush_str(last_tokens_string); - pdf_out("]"); - end; - pdf_action_goto: begin - if pdf_action_file(p) = null then begin - pdf_print("/S /GoTo "); - d := get_obj(obj_type_dest, pdf_action_id(p), - pdf_action_named_id(p)); - end - else - pdf_print("/S /GoToR "); - if pdf_action_named_id(p) > 0 then begin - pdf_str_entry("D", tokens_to_string(pdf_action_id(p))); - flush_str(last_tokens_string); - end - else if pdf_action_file(p) = null then - pdf_indirect("D", d) - else - pdf_error("ext4", "`goto' option cannot be used with both `file' and `num'"); - end; - pdf_action_thread: begin - pdf_print("/S /Thread "); - if pdf_action_file(p) = null then - d := get_obj(obj_type_thread, pdf_action_id(p), - pdf_action_named_id(p)); - if pdf_action_named_id(p) > 0 then begin - pdf_str_entry("D", tokens_to_string(pdf_action_id(p))); - flush_str(last_tokens_string); - end - else if pdf_action_file(p) = null then - pdf_indirect("D", d) - else - pdf_int_entry("D", pdf_action_id(p)); - end; - endcases; - pdf_print_ln(" >>"); -end; - -procedure set_rect_dimens(p, parent_box: pointer; x, y, w, h, d, margin: scaled); -begin - pdf_left(p) := cur_h; - if is_running(w) then - pdf_right(p) := x + width(parent_box) - else - pdf_right(p) := cur_h + w; - if is_running(h) then - pdf_top(p) := y - height(parent_box) - else - pdf_top(p) := cur_v - h; - if is_running(d) then - pdf_bottom(p) := y + depth(parent_box) - else - pdf_bottom(p) := cur_v + d; - pdf_left(p) := pdf_left(p) - margin; - pdf_top(p) := pdf_top(p) - margin; - pdf_right(p) := pdf_right(p) + margin; - pdf_bottom(p) := pdf_bottom(p) + margin; -end; - -procedure do_annot(p, parent_box: pointer; x, y: scaled); -begin - if not is_shipping_page then - pdf_error("ext4", "annotations cannot be inside an XForm"); - if doing_leaders then - return; - set_rect_dimens(p, parent_box, x, y, - pdf_width(p), pdf_height(p), pdf_depth(p), 0); - obj_annot_ptr(pdf_annot_objnum(p)) := p; - pdf_append_list(pdf_annot_objnum(p))(pdf_annot_list); -end; - -@ To implement nesting link annotations, we need a stack to save box testing -level of each link that has been broken. Each stack entry holds the -box nesting level and pointer the whatsit node created for -corresponding \.{\\pdfstartlink}. - -@d link_level(#) == info(#) -@d link_ptr(#) == info(# + 1) - -@= -procedure save_link_level(l: pointer); -var p, r: pointer; -begin - pdf_link_level := cur_s; - r := copy_node_list(l); - pdf_link_wd := pdf_width(r); - pdf_link_ht := pdf_height(r); - pdf_link_dp := pdf_depth(r); - p := get_node(small_node_size); - link_level(p) := pdf_link_level; - link_ptr(p) := r; - link(p) := link_level_stack; - link_level_stack := p; -end; - -procedure do_link(p, parent_box: pointer; x, y: scaled); -begin - if not is_shipping_page then - pdf_error("ext4", "link annotations cannot be inside an XForm"); - if type(parent_box) <> hlist_node then - pdf_error("ext4", "link annotations can be inside hbox only"); - save_link_level(p); - set_rect_dimens(p, parent_box, x, y, - pdf_link_wd, pdf_link_ht, pdf_link_dp, pdf_link_margin); - last_link := p; - pdf_create_obj(obj_type_others, 0); - obj_annot_ptr(obj_ptr) := p; - pdf_append_list(obj_ptr)(pdf_link_list); -end; - -procedure restore_link_level; -var p, r: pointer; -begin - if link_level_stack = null then - pdf_error("ext4", "invalid stack of link nesting level"); - p := link_level_stack; - link_level_stack := link(p); - r := link_ptr(p); - flush_node_list(r); - free_node(p, small_node_size); - p := link_level_stack; - if p = null then - pdf_link_level := -1 - else begin - pdf_link_level := link_level(p); - r := link_ptr(p); - pdf_link_wd := pdf_width(r); - pdf_link_ht := pdf_height(r); - pdf_link_dp := pdf_depth(r); - end; -end; - -procedure end_link; -begin - if pdf_link_level <> cur_s then - pdf_error("ext4", "\pdfendlink ended up in different nesting level than \pdfstartlink"); - if is_running(pdf_link_wd) and (last_link <> null) then - pdf_right(last_link) := cur_h + pdf_link_margin; - restore_link_level; - last_link := null; -end; - -@ For ``running'' annotations we must append a new node when the end of -annotation is in other box than its start. The new created node is identical to -corresponding whatsit node representing the start of annotation, but its -|info| field is |null|. We set |info| field just before destroying the node, in -order to use |flush_node_list| to do the job. - -@= -procedure append_link(parent_box: pointer; x, y: scaled); {append a new -|pdf_start_link_node| to |pdf_link_list| and update |last_link|} -var p, r: integer; -begin - if type(parent_box) <> hlist_node then - pdf_error("ext4", "link annotations can be inside hbox only"); - r := copy_node_list(link_ptr(link_level_stack)); {copy link node to |r|} - info(r) := max_halfword; {mark that this node is not a whatsit node} - link(r) := null; {this node will be destroyed separately} - set_rect_dimens(r, parent_box, x, y, - pdf_link_wd, pdf_link_ht, pdf_link_dp, pdf_link_margin); - pdf_create_obj(obj_type_others, 0); - obj_annot_ptr(obj_ptr) := r; - last_link := r; - pdf_append_list(obj_ptr)(pdf_link_list); -end; - -@ Threads are handled in similar way as link annotations. - -@= -procedure append_bead(p: pointer); -var a, b, c, t: integer; -begin - if not is_shipping_page then - pdf_error("ext4", "threads cannot be inside an XForm"); - t := get_obj(obj_type_thread, pdf_thread_id(p), pdf_thread_named_id(p)); - b := pdf_new_objnum; - obj_bead_ptr(b) := pdf_get_mem(pdfmem_bead_size); - obj_bead_page(b) := pdf_last_page; - obj_bead_data(b) := p; - if pdf_thread_attr(p) <> null then - obj_bead_attr(b) := tokens_to_string(pdf_thread_attr(p)) - else - obj_bead_attr(b) := 0; - if obj_thread_first(t) = 0 then begin - obj_thread_first(t) := b; - obj_bead_next(b) := b; - obj_bead_prev(b) := b; - end - else begin - a := obj_thread_first(t); - c := obj_bead_prev(a); - obj_bead_prev(b) := c; - obj_bead_next(b) := a; - obj_bead_prev(a) := b; - obj_bead_next(c) := b; - end; - pdf_append_list(b)(pdf_bead_list); -end; - -procedure do_thread(p, parent_box: pointer; x, y: scaled); -begin - if doing_leaders then - return; - if subtype(p) = pdf_start_thread_node then begin - pdf_thread_wd := pdf_width(p); - pdf_thread_ht := pdf_height(p); - pdf_thread_dp := pdf_depth(p); - pdf_last_thread_id := pdf_thread_id(p); - pdf_last_thread_named_id := (pdf_thread_named_id(p) > 0); - if pdf_last_thread_named_id then - add_token_ref(pdf_thread_id(p)); - pdf_thread_level := cur_s; - end; - set_rect_dimens(p, parent_box, x, y, - pdf_width(p), pdf_height(p), pdf_depth(p), - pdf_thread_margin); - append_bead(p); - last_thread := p; -end; - -procedure append_thread(parent_box: pointer; x, y: scaled); -var p: pointer; -begin - p := get_node(pdf_thread_node_size); - info(p) := max_halfword; {this is not a whatsit node} - link(p) := null; {this node will be destroyed separately} - pdf_width(p) := pdf_thread_wd; - pdf_height(p) := pdf_thread_ht; - pdf_depth(p) := pdf_thread_dp; - pdf_thread_attr(p) := null; - pdf_thread_id(p) := pdf_last_thread_id; - if pdf_last_thread_named_id then begin - add_token_ref(pdf_thread_id(p)); - pdf_thread_named_id(p) := 1; - end - else - pdf_thread_named_id(p) := 0; - set_rect_dimens(p, parent_box, x, y, - pdf_width(p), pdf_height(p), pdf_depth(p), - pdf_thread_margin); - append_bead(p); - last_thread := p; -end; - -procedure end_thread; -begin - if pdf_thread_level <> cur_s then - pdf_error("ext4", "\pdfendthread ended up in different nesting level than \pdfstartthread"); - if is_running(pdf_thread_dp) and (last_thread <> null) then - pdf_bottom(last_thread) := cur_v + pdf_thread_margin; - if pdf_last_thread_named_id then - delete_token_ref(pdf_last_thread_id); - last_thread := null; -end; - -function open_subentries(p: pointer): integer; -var k, c: integer; - l, r: integer; -begin - k := 0; - if obj_outline_first(p) <> 0 then begin - l := obj_outline_first(p); - repeat - incr(k); - c := open_subentries(l); - if obj_outline_count(l) > 0 then - k := k + c; - obj_outline_parent(l) := p; - r := obj_outline_next(l); - if r = 0 then - obj_outline_last(p) := l; - l := r; - until l = 0; - end; - if obj_outline_count(p) > 0 then - obj_outline_count(p) := k - else - obj_outline_count(p) := -k; - open_subentries := k; -end; - -procedure do_dest(p, parent_box: pointer; x, y: scaled); -var k: integer; -begin - if not is_shipping_page then - pdf_error("ext4", "destinations cannot be inside an XForm"); - if doing_leaders then - return; - k := get_obj(obj_type_dest, pdf_dest_id(p), pdf_dest_named_id(p)); - if obj_dest_ptr(k) <> null then begin - warn_dest_dup(pdf_dest_id(p), pdf_dest_named_id(p), - "ext4", "has been already used, duplicate ignored"); - return; - end; - obj_dest_ptr(k) := p; - pdf_append_list(k)(pdf_dest_list); - case pdf_dest_type(p) of - pdf_dest_xyz: begin - pdf_left(p) := cur_h; - pdf_top(p) := cur_v; - end; - pdf_dest_fith, - pdf_dest_fitbh: - pdf_top(p) := cur_v; - pdf_dest_fitv, - pdf_dest_fitbv: - pdf_left(p) := cur_h; - pdf_dest_fit, - pdf_dest_fitb: - do_nothing; - pdf_dest_fitr: - set_rect_dimens(p, parent_box, x, y, - pdf_width(p), pdf_height(p), pdf_depth(p), - pdf_dest_margin); - endcases; -end; - -procedure out_form(p: pointer); -begin - pdf_end_text; - pdf_print_ln("q"); - if pdf_lookup_list(pdf_xform_list, pdf_xform_objnum(p)) = null then - pdf_append_list(pdf_xform_objnum(p))(pdf_xform_list); - cur_v := cur_v + obj_xform_depth(pdf_xform_objnum(p)); - pdf_print("1 0 0 1 "); - pdf_print_bp(pdf_x(cur_h)); pdf_out(" "); - pdf_print_bp(pdf_y(cur_v)); - pdf_print_ln(" cm"); - pdf_print("/Fm"); - pdf_print_int(obj_info(pdf_xform_objnum(p))); - pdf_print_resname_prefix; - pdf_print_ln(" Do"); - pdf_print_ln("Q"); -end; - -procedure out_image(p: pointer); -var image: integer; -begin - image := obj_ximage_data(pdf_ximage_objnum(p)); - pdf_end_text; - pdf_print_ln("q"); - if pdf_lookup_list(pdf_ximage_list, pdf_ximage_objnum(p)) = null then - pdf_append_list(pdf_ximage_objnum(p))(pdf_ximage_list); - if not is_pdf_image(image) then begin - pdf_print_real(ext_xn_over_d(pdf_width(p), - ten_pow[6], one_hundred_bp), 4); - pdf_print(" 0 0 "); - pdf_print_real(ext_xn_over_d(pdf_height(p) + pdf_depth(p), - ten_pow[6], one_hundred_bp), 4); - pdf_out(" "); - pdf_print_bp(pdf_x(cur_h)); pdf_out(" "); - pdf_print_bp(pdf_y(cur_v)); - end - else begin - pdf_print_real(ext_xn_over_d(pdf_width(p), - ten_pow[6], image_width(image)), 6); - pdf_print(" 0 0 "); - pdf_print_real(ext_xn_over_d(pdf_height(p) + pdf_depth(p), - ten_pow[6], image_height(image)), 6); - pdf_out(" "); - pdf_print_bp(pdf_x(cur_h) - - ext_xn_over_d(pdf_width(p), epdf_orig_x(image), - image_width(image))); - pdf_out(" "); - pdf_print_bp(pdf_y(cur_v) - - ext_xn_over_d(pdf_height(p), epdf_orig_y(image), - image_height(image))); - end; - pdf_print_ln(" cm"); - pdf_print("/Im"); - pdf_print_int(obj_info(pdf_ximage_objnum(p))); - pdf_print_resname_prefix; - pdf_print_ln(" Do"); - pdf_print_ln("Q"); -end; - - -@ @= -case subtype(p) of -pdf_literal_node: - pdf_out_literal(p); -pdf_refobj_node: - pdf_append_list(pdf_obj_objnum(p))(pdf_obj_list); -pdf_refxform_node: - @; -pdf_refximage_node: - @; -pdf_annot_node: - do_annot(p, this_box, left_edge, top_edge + height(this_box)); -pdf_start_link_node: - pdf_error("ext4", "\pdfstartlink ended up in vlist"); -pdf_end_link_node: - pdf_error("ext4", "\pdfendlink ended up in vlist"); -pdf_dest_node: - do_dest(p, this_box, left_edge, top_edge + height(this_box)); -pdf_thread_node, -pdf_start_thread_node: - do_thread(p, this_box, left_edge, top_edge + height(this_box)); -pdf_end_thread_node: - end_thread; -pdf_save_pos_node: - @; -special_node: - pdf_special(p); -othercases out_what(p); -endcases - -@ @= -@!is_shipping_page: boolean; {set to |shipping_page| when |pdf_ship_out| starts} - -@ @= -begin - pdf_last_x_pos := cur_h; - if is_shipping_page then - pdf_last_y_pos := cur_page_height - cur_v - else - pdf_last_y_pos := pdf_xform_height + pdf_xform_depth - cur_v; -end - -@ @= -begin cur_v:=cur_v+pdf_height(p)+pdf_depth(p); save_v:=cur_v; - cur_h:=left_edge; - out_image(p); - cur_v:=save_v; cur_h:=left_edge; - end - -@ @= -begin cur_v:=cur_v+pdf_height(p); save_v:=cur_v; - cur_h:=left_edge; - out_form(p); - cur_v:=save_v+pdf_depth(p); cur_h:=left_edge; - end - -@ @= -case subtype(p) of -pdf_literal_node: - pdf_out_literal(p); -pdf_refobj_node: - pdf_append_list(pdf_obj_objnum(p))(pdf_obj_list); -pdf_refxform_node: - @; -pdf_refximage_node: - @; -pdf_annot_node: - do_annot(p, this_box, left_edge, base_line); -pdf_start_link_node: - do_link(p, this_box, left_edge, base_line); -pdf_end_link_node: begin - end_link; - @; -end; -pdf_dest_node: - do_dest(p, this_box, left_edge, base_line); -pdf_thread_node: - do_thread(p, this_box, left_edge, base_line); -pdf_start_thread_node: - pdf_error("ext4", "\pdfstartthread ended up in hlist"); -pdf_end_thread_node: - pdf_error("ext4", "\pdfendthread ended up in hlist"); -pdf_save_pos_node: - @; -special_node: - pdf_special(p); -othercases out_what(p); -endcases - -@ @= -begin - cur_v:=base_line+pdf_depth(p); - edge:=cur_h; - out_image(p); - cur_h:=edge+pdf_width(p); cur_v:=base_line; - end - -@ @= -begin - cur_v:=base_line; - edge:=cur_h; - out_form(p); - cur_h:=edge+pdf_width(p); cur_v:=base_line; - end -@z diff --git a/Build/source/texk/web2c/pdftexdir/pdftex.defines b/Build/source/texk/web2c/pdftexdir/pdftex.defines index 2bd9ef1fefa..977b472e168 100644 --- a/Build/source/texk/web2c/pdftexdir/pdftex.defines +++ b/Build/source/texk/web2c/pdftexdir/pdftex.defines @@ -1,5 +1,5 @@ { -Copyright (c) 1996-2005 Han The Thanh, +Copyright (c) 1996-2006 Han The Thanh, This file is part of pdfTeX. @@ -27,14 +27,19 @@ $Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/pdftex.defines#15 @define function getlpcode(); @define function getrpcode(); @define function getefcode(); +@define function getknbscode(); +@define function getstbscode(); +@define function getshbscode(); +@define function getknbccode(); +@define function getknaccode(); @define function texbopenin(); @define function vfbopenin (); @define procedure pdfmarkchar(); @define procedure writepdf(); +@define procedure pdfassert(); { functions from mapfile.c } @define function hasfmentry(); -@define function tfmoffm(); @define procedure checkextfm(); @define procedure pdfmapfile(); @define procedure pdfmapline(); @@ -59,11 +64,34 @@ $Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/pdftex.defines#15 @define procedure libpdffinish; @define procedure makepdftexbanner; @define procedure printcreationdate; +@define procedure printmoddate; @define procedure printID(); @define procedure setjobid(); @define procedure unescapehex(); @define procedure writestreamlength(); - +@define procedure removepdffile; +@define procedure garbagewarning; +@define function newcolorstack(); +@define function colorstackset(); +@define function colorstackpush(); +@define function colorstackpop(); +@define function colorstackcurrent(); +@define function colorstackused; +@define function colorstackskippagestart(); +@define procedure checkpdfsave(); +@define procedure checkpdfrestore(); +@define procedure pdfshipoutbegin(); +@define procedure pdfshipoutend(); +@define procedure pdfsetmatrix(); +@define procedure matrixtransformpoint(); +@define procedure matrixtransformrect(); +@define function matrixused; +@define procedure matrixrecalculate(); +@define function getllx; +@define function getlly; +@define function geturx; +@define function getury; +@define procedure allocvffnts; { functions from vfpacket.c } @define function newvfpacket(); @@ -73,6 +101,7 @@ $Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/pdftex.defines#15 { functions from writefont.c } @define procedure dopdffont(); +@define procedure writefontstuff; { functions from writeimg.c } @define function checkimageb(); @@ -102,4 +131,8 @@ $Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/pdftex.defines#15 @define procedure avlputobj(); @define function avlfindobj(); +{ functions from tounicode.c } +@define procedure deftounicode(); + { end of pdftex.defines } +{ vim: set syntax=web : } diff --git a/Build/source/texk/web2c/pdftexdir/pdftex.h b/Build/source/texk/web2c/pdftexdir/pdftex.h index 837d3f5a2e9..805301fc633 100644 --- a/Build/source/texk/web2c/pdftexdir/pdftex.h +++ b/Build/source/texk/web2c/pdftexdir/pdftex.h @@ -1,5 +1,5 @@ /* -Copyright (c) 1996-2002 Han The Thanh, +Copyright (c) 1996-2006 Han The Thanh, This file is part of pdfTeX. @@ -42,9 +42,26 @@ $Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/pdftex.h#4 $ #define getefcode(f, c) \ (pdffontefbase[f] == 0 ? 1000 : pdfmem[pdffontefbase[f] + c]) +#define getknbscode(f, c) \ + (pdffontknbsbase[f] == 0 ? 0 : pdfmem[pdffontknbsbase[f] + c]) + +#define getstbscode(f, c) \ + (pdffontstbsbase[f] == 0 ? 0 : pdfmem[pdffontstbsbase[f] + c]) + +#define getshbscode(f, c) \ + (pdffontshbsbase[f] == 0 ? 0 : pdfmem[pdffontshbsbase[f] + c]) + +#define getknbccode(f, c) \ + (pdffontknbcbase[f] == 0 ? 0 : pdfmem[pdffontknbcbase[f] + c]) + +#define getknaccode(f, c) \ + (pdffontknacbase[f] == 0 ? 0 : pdfmem[pdffontknacbase[f] + c]) + #define texbopenin(f) \ open_input (&(f), kpse_tex_format, FOPEN_RBIN_MODE) #define vfbopenin(f) \ open_input (&(f), kpse_vf_format, FOPEN_RBIN_MODE) +#define pdfassert assert + #include diff --git a/Build/source/texk/web2c/pdftexdir/pdftex.mk b/Build/source/texk/web2c/pdftexdir/pdftex.mk index 56a770546cf..2c485ed6874 100644 --- a/Build/source/texk/web2c/pdftexdir/pdftex.mk +++ b/Build/source/texk/web2c/pdftexdir/pdftex.mk @@ -1,112 +1,106 @@ -# Makefile fragment for pdfTeX and web2c. --infovore@xs4all.nl. Public domain. +# Makefile fragment for pdfeTeX and web2c. --infovore@xs4all.nl. Public domain. # This fragment contains the parts of the makefile that are most likely to -# differ between releases of pdfTeX. +# differ between releases of pdfeTeX. -# The libraries are not mentioned. As the matter stands, a change in their -# number or how they are configured requires changes to the main distribution -# anyway. +# We build pdftex +pdftex = @PETEX@ pdftex +pdftexdir = pdftexdir -# $Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/pdftex.mk#38 $ +LIBOBSDCOMPATDIR=../../libs/obsdcompat +LIBOBSDCOMPATFSRCDIR=$(srcdir)/$(LIBOBSDCOMPATDIR) +XCPPFLAGS=-I$(LIBOBSDCOMPATDIR) -I$(LIBOBSDCOMPATDIR)/.. -I$(LIBOBSDCOMPATFSRCDIR) -I$(LIBOBSDCOMPATFSRCDIR)/.. -Makefile: pdftexdir/pdftex.mk +Makefile: $(srcdir)/$(pdftexdir)/pdftex.mk -pdftex_bin = pdftex pdfetex ttf2afm pdftosrc +# pdftex_bin = pdftex ttf2afm pdftosrc +pdftex_bin = pdftex linux_build_dir = $(HOME)/pdftex/build/linux/texk/web2c -# We build pdftex -pdftex = @PTEX@ pdftex -pdftexdir = pdftexdir - # Extract pdftex version -pdftexdir/pdftex.version: pdftexdir/pdftex.ch - grep '^@d pdftex_version_string==' $(srcdir)/pdftexdir/pdftex.ch \ +$(pdftexdir)/pdftex.version: $(srcdir)/$(pdftexdir)/pdftex.web + test -d $(pdftexdir) || mkdir $(pdftexdir) + grep '^@d pdftex_version_string==' $(srcdir)/$(pdftexdir)/pdftex.web \ | sed "s/^.*'-//;s/'.*$$//" \ - >pdftexdir/pdftex.version + >$(pdftexdir)/pdftex.version # The C sources. pdftex_c = pdftexini.c pdftex0.c pdftex1.c pdftex2.c pdftex3.c pdftex_o = pdftexini.o pdftex0.o pdftex1.o pdftex2.o pdftex3.o pdftexextra.o # Making pdftex -pdftex: $(pdftex_o) $(pdftexextra_o) $(pdftexlibsdep) +pdftex: pdftexd.h $(pdftex_o) $(pdftexextra_o) $(pdftexlibsdep) @CXXHACKLINK@ $(pdftex_o) $(pdftexextra_o) $(pdftexlibs) $(socketlibs) @CXXHACKLDLIBS@ @CXXLDEXTRA@ # C file dependencies. -$(pdftex_c) pdftexcoerce.h pdftexd.h: pdftex.p $(web2c_texmf) +$(pdftex_c) pdftexcoerce.h pdftexd.h: pdftex.p $(web2c_texmf) $(srcdir)/$(pdftexdir)/pdftex.defines $(srcdir)/$(pdftexdir)/pdftex.h $(web2c) pdftex -pdftexextra.c: pdftexdir/pdftexextra.h lib/texmfmp.c +pdftexextra.c: $(pdftexdir)/pdftexextra.h lib/texmfmp.c + test -d $(pdftexdir) || mkdir $(pdftexdir) sed s/TEX-OR-MF-OR-MP/pdftex/ $(srcdir)/lib/texmfmp.c >$@ -pdftexdir/pdftexextra.h: pdftexdir/pdftexextra.in pdftexdir/pdftex.version - sed s/PDFTEX-VERSION/`cat pdftexdir/pdftex.version`/ \ - $(srcdir)/pdftexdir/pdftexextra.in >$@ - -# Tangling. -pdftex.p pdftex.pool: tangle pdftex.web pdftex.ch - $(TANGLE) pdftex.web pdftex.ch - -# Generation of the web and ch files. -# Sources for pdftex.web: -pdftex_web_srcs = $(srcdir)/tex.web \ - $(srcdir)/pdftexdir/pdftex.ch \ - $(srcdir)/pdftexdir/hz.ch \ - $(srcdir)/pdftexdir/misc.ch \ - $(srcdir)/pdftexdir/vadjust.ch \ - $(srcdir)/pdftexdir/pdftex2.ch -# Sources for etex.ch: -pdftex_ch_srcs = pdftex.web \ - $(srcdir)/pdftexdir/tex.ch0 \ +$(pdftexdir)/pdftexextra.h: $(pdftexdir)/pdftexextra.in $(pdftexdir)/pdftex.version etexdir/etex.version + test -d $(pdftexdir) || mkdir $(pdftexdir) + sed -e s/PDFTEX-VERSION/`cat $(pdftexdir)/pdftex.version`/ \ + -e s/ETEX-VERSION/`cat etexdir/etex.version`/ \ + $(srcdir)/$(pdftexdir)/pdftexextra.in >$@ + +# Tangling +pdftex.p pdftex.pool: tangle $(srcdir)/$(pdftexdir)/pdftex.web pdftex.ch + $(TANGLE) $(srcdir)/$(pdftexdir)/pdftex.web pdftex.ch + +# Sources for pdftex.ch: +pdftex_ch_srcs = $(srcdir)/$(pdftexdir)/pdftex.web \ + $(srcdir)/$(pdftexdir)/tex.ch0 \ $(srcdir)/tex.ch \ - $(srcdir)/pdftexdir/tex.ch1 \ - $(srcdir)/pdftexdir/tex.pch \ - $(srcdir)/pdftexdir/noligatures.ch \ - $(srcdir)/pdftexdir/pdfstrcmp.ch \ - $(srcdir)/pdftexdir/randoms.ch + $(srcdir)/$(pdftexdir)/pdftex.ch # Rules: -pdftex.web: tie pdftexdir/pdftex.mk $(pdftex_web_srcs) - $(TIE) -m pdftex.web $(pdftex_web_srcs) -pdftex.ch: $(pdftex_ch_srcs) +pdftex.ch: $(TIE) $(pdftex_ch_srcs) $(TIE) -c pdftex.ch $(pdftex_ch_srcs) +# pdfetex: (it's just a copy of pdftex) +pdfetex: pdftex + cp -p pdftex pdfetex +pdfetex.pool: pdftex.pool + cp -p pdftex.pool pdfetex.pool + # for developing only -pdftex-org.web: $(pdftex_ch_srcs_org) +pdftex-org.web: $(TIE) $(pdftex_ch_srcs_org) $(TIE) -m $@ $(pdftex_ch_srcs_org) -pdftex-all.web: pdftex.web pdftex.ch - $(TIE) -m $@ pdftex.web pdftex.ch - -# Tests... -check: @PTEX@ pdftex-check +pdftex-all.web: $(TIE) $(srcdir)/$(pdftexdir)/pdftex.web pdftex.ch + $(TIE) -m $@ $(srcdir)/$(pdftexdir)/pdftex.web pdftex.ch +pdftex-all.tex: pdftex-all.web + $(WEAVE) pdftex-all.web + echo -e '1s/ webmac/ pdfwebmac/\nw\nq' | ed $@ >/dev/null 2>&1 +pdftex-all.pdf: pdftex-all.tex + $(pdftex) pdftex-all.tex + +check: @PETEX@ pdftex-check pdftex-check: pdftex pdftex.fmt -# Cleaning up. clean:: pdftex-clean pdftex-clean: $(LIBTOOL) --mode=clean $(RM) pdftex rm -f $(pdftex_o) $(pdftex_c) pdftexextra.c pdftexcoerce.h - rm -f pdftexdir/pdftexextra.h pdftexdir/pdftex.version - rm -f pdftexd.h pdftex.p pdftex.pool pdftex.web pdftex.ch + rm -f $(pdftexdir)/pdftexextra.h + rm -f pdftexd.h pdftex.p pdftex.pool pdftex.ch rm -f pdftex.fmt pdftex.log -# Dumps. -all_pdffmts = @FMU@ pdftex.fmt $(pdffmts) +# Dumps +all_pdfefmts = @FMU@ pdfetex.fmt $(pdfefmts) -dumps: @PTEX@ pdffmts -pdffmts: $(all_pdffmts) +dumps: @PETEX@ pdfefmts +pdfefmts: $(all_pdfefmts) -pdffmtdir = $(web2cdir)/pdftex -$(pdffmtdir):: - $(SHELL) $(top_srcdir)/../mkinstalldirs $(pdffmtdir) +pdfefmtdir = $(web2cdir)/pdfetex +$(pdfefmtdir):: + $(SHELL) $(top_srcdir)/../mkinstalldirs $(pdfefmtdir) -pdftex.fmt: pdftex - $(dumpenv) $(MAKE) progname=pdftex files="plain.tex cmr10.tfm" prereq-check - $(dumpenv) ./pdftex --progname=pdftex --jobname=pdftex --ini \\pdfoutput=1 \\input plain \\dump + +% This file is part of pdfTeX. + +% pdfTeX is free software; you can redistribute it and/or modify it under the +% terms of the GNU General Public License as published by the Free Software +% Foundation; either version 2 of the License, or (at your option) any later +% version. + +% pdfTeX is distributed in the hope that it will be useful, but WITHOUT ANY +% WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +% A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +% You should have received a copy of the GNU General Public License along with +% pdfTeX; if not, write to the Free Software Foundation, Inc., 59 Temple Place, +% Suite 330, Boston, MA 02111-1307 USA + +% e-TeX is copyright (C) 1994,98 by Peter Breitenlohner; all rights are +% reserved. Copying of this file is authorized only if (1) you are Peter +% Breitenlohner, or if (2) you make absolutely no changes to your copy. +% (Programs such as PATCHWEB, TIE, or WEBMERGE allow the application of +% several change files to tex.web; the master files tex.web and etex.ch +% should stay intact.) + +% See etex_gen.tex for hints on how to install this program. +% And see etripman.tex for details about how to validate it. + +% This program is directly derived from Donald E. Knuth's TeX; +% the change history which follows and the reward offered for finders of +% bugs refer specifically to TeX; they should not be taken as referring +% to e-TeX, although the change history is relevant in that it +% demonstrates the evolutionary path followed. This program is not TeX; +% that name is reserved strictly for the program which is the creation +% and sole responsibility of Professor Knuth. + +% Version 0 was released in September 1982 after it passed a variety of tests. +% Version 1 was released in November 1983 after thorough testing. +% Version 1.1 fixed ``disappearing font identifiers'' et alia (July 1984). +% Version 1.2 allowed `0' in response to an error, et alia (October 1984). +% Version 1.3 made memory allocation more flexible and local (November 1984). +% Version 1.4 fixed accents right after line breaks, et alia (April 1985). +% Version 1.5 fixed \the\toks after other expansion in \edefs (August 1985). +% Version 2.0 (almost identical to 1.5) corresponds to "Volume B" (April 1986). +% Version 2.1 corrected anomalies in discretionary breaks (January 1987). +% Version 2.2 corrected "(Please type...)" with null \endlinechar (April 1987). +% Version 2.3 avoided incomplete page in premature termination (August 1987). +% Version 2.4 fixed \noaligned rules in indented displays (August 1987). +% Version 2.5 saved cur_order when expanding tokens (September 1987). +% Version 2.6 added 10sp slop when shipping leaders (November 1987). +% Version 2.7 improved rounding of negative-width characters (November 1987). +% Version 2.8 fixed weird bug if no \patterns are used (December 1987). +% Version 2.9 made \csname\endcsname's "relax" local (December 1987). +% Version 2.91 fixed \outer\def\a0{}\a\a bug (April 1988). +% Version 2.92 fixed \patterns, also file names with complex macros (May 1988). +% Version 2.93 fixed negative halving in allocator when mem_min<0 (June 1988). +% Version 2.94 kept open_log_file from calling fatal_error (November 1988). +% Version 2.95 solved that problem a better way (December 1988). +% Version 2.96 corrected bug in "Infinite shrinkage" recovery (January 1989). +% Version 2.97 corrected blunder in creating 2.95 (February 1989). +% Version 2.98 omitted save_for_after at outer level (March 1989). +% Version 2.99 caught $$\begingroup\halign..$$ (June 1989). +% Version 2.991 caught .5\ifdim.6... (June 1989). +% Version 2.992 introduced major changes for 8-bit extensions (September 1989). +% Version 2.993 fixed a save_stack synchronization bug et alia (December 1989). +% Version 3.0 fixed unusual displays; was more \output robust (March 1990). +% Version 3.1 fixed nullfont, disabled \write{\the\prevgraf} (September 1990). +% Version 3.14 fixed unprintable font names and corrected typos (March 1991). +% Version 3.141 more of same; reconstituted ligatures better (March 1992). +% Version 3.1415 preserved nonexplicit kerns, tidied up (February 1993). +% Version 3.14159 allowed fontmemsize to change; bulletproofing (March 1995). +% Version 3.141592 fixed \xleaders, glueset, weird alignments (December 2002). + +% A preliminary version of TeX--XeT was released in April 1992. +% TeX--XeT version 1.0 was released in June 1992, +% version 1.1 prevented arith overflow in glue computation (Oct 1992). +% A preliminary e-TeX version 0.95 was operational in March 1994. +% Version 1.0beta was released in May 1995. +% Version 1.01beta fixed bugs in just_copy and every_eof (December 1995). +% Version 1.02beta allowed 256 mark classes (March 1996). +% Version 1.1 changed \group{type,level} -> \currentgroup{type,level}, +% first public release (October 1996). +% Version 2.0 development was started in March 1997; +% fixed a ligature-\beginR bug in January 1998; +% was released in March 1998. +% Version 2.1 fixed a \marks bug (when min_halfword<>0) (January 1999). +% Version 2.2 development was started in Feb 2003; released in Oct 2004. +% fixed a bug in sparse array handling (0=>null), Jun 2002; +% fixed a bug in \lastnodetype (cur_val=>cur_val_level) +% reported by Hartmut Henkel , +% fix by Fabrice Popineau , +% Jan 2004; +% another bug in sparse array handling (cur_ptr=>cur_chr) +% reported by Taco Hoekwater , Jul 2004; +% fixed a sparse array reference count bug (\let,\futurelet), +% fix by Bernd Raichle , Aug 2004; +% reorganized handling of banner, additional token list and +% integer parameters, and similar in order to reduce the +% interference between eTeX, pdfTeX, and web2c change files. +% adapted to tex.web 3.141592, revised glue rounding for mixed +% direction typesetting; +% fixed a bug in the revised glue rounding code, detected by +% Tigran Aivazian , Oct 2004. + +% Although considerable effort has been expended to make the e-TeX program +% correct and reliable, no warranty is implied; the author disclaims any +% obligation or liability for damages, including but not limited to +% special, indirect, or consequential damages arising out of or in +% connection with the use or performance of this software. This work has +% been a ``labor of love'' and the author hopes that users enjoy it. + +% Here is TeX material that gets inserted after \input webmac +\def\hang{\hangindent 3em\noindent\ignorespaces} +\def\hangg#1 {\hang\hbox{#1 }} +\def\textindent#1{\hangindent2.5em\noindent\hbox to2.5em{\hss#1 }\ignorespaces} +\font\ninerm=cmr9 +\let\mc=\ninerm % medium caps for names like SAIL +\def\eTeX{$\varepsilon$-\TeX} +\font\revrm=xbmc10 % for right-to-left text +% to generate xbmc10 (i.e., reflected cmbx10) use a file +% xbmc10.mf containing: +%+++++++++++++++++++++++++++++++++++++++++++++++++ +% if unknown cmbase: input cmbase fi +% extra_endchar := extra_endchar & +% "currentpicture:=currentpicture " & +% "reflectedabout((.5[l,r],0),(.5[l,r],1));"; +% input cmbx10 +%+++++++++++++++++++++++++++++++++++++++++++++++++ +\ifx\beginL\undefined % this is TeX + \def\XeT{X\kern-.125em\lower.5ex\hbox{E}\kern-.1667emT} + \def\TeXeT{\TeX-\hbox{\revrm \XeT}} % for TeX-XeT + \def\TeXXeT{\TeX-\hbox{\revrm -\XeT}} % for TeX--XeT +\else + \ifx\eTeXversion\undefined % this is \TeXeT + \def\TeXeT{\TeX-{\revrm\beginR\TeX\endR}} % for TeX-XeT + \def\TeXXeT{\TeX-{\revrm\beginR\TeX-\endR}} % for TeX--XeT + \else % this is \eTeX + \def\TeXeT{\TeX-{\TeXXeTstate=1\revrm\beginR\TeX\endR}} % for TeX-XeT + \def\TeXXeT{\TeX-{\TeXXeTstate=1\revrm\beginR\TeX-\endR}} % for TeX--XeT + \fi +\fi +\def\PASCAL{Pascal} +\def\pdfTeX{pdf\TeX} +\def\pdfeTeX{pdf\eTeX} +\def\PDF{PDF} +\def\ph{\hbox{Pascal-H}} +\def\pct!{{\char`\%}} % percent sign in ordinary text +\def\grp{\.{\char'173...\char'175}} +\font\logo=logo10 % font used for the METAFONT logo +\def\MF{{\logo META}\-{\logo FONT}} +\def\<#1>{$\langle#1\rangle$} +\def\section{\mathhexbox278} + +\def\(#1){} % this is used to make section names sort themselves better +\def\9#1{} % this is used for sort keys in the index via @@:sort key}{entry@@> + +\outer\def\N#1. \[#2]#3.{\MN#1.\vfil\eject % begin starred section + \def\rhead{PART #2:\uppercase{#3}} % define running headline + \message{*\modno} % progress report + \edef\next{\write\cont{\Z{\?#2]#3}{\modno}{\the\pageno}}}\next + \ifon\startsection{\bf\ignorespaces#3.\quad}\ignorespaces} +\let\?=\relax % we want to be able to \write a \? + +\def\title{\pdfTeX} +% system dependent redefinitions of \title should come later +% and should use: +% \toks0=\expandafter{\title} +% \edef\title{...\the\toks0...} +\let\maybe=\iffalse % print only changed modules +\def\topofcontents{\hsize 5.5in + \vglue 0pt plus 1fil minus 1.5in + \def\?##1]{\hbox to 1in{\hfil##1.\ }} + } +\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in} +\pageno=3 +\def\glob{13} % this should be the section number of "" +\def\gglob{20, 26} % this should be the next two sections of "" + +@* \[1] Introduction. +This is \eTeX, a program derived from and extending the capabilities of +\TeX, a document compiler intended to produce typesetting of high +quality. +The \PASCAL\ program that follows is the definition of \TeX82, a standard +@:PASCAL}{\PASCAL@> +@!@:TeX82}{\TeX82@> +version of \TeX\ that is designed to be highly portable so that identical output +will be obtainable on a great variety of computers. + +The main purpose of the following program is to explain the algorithms of \TeX\ +as clearly as possible. As a result, the program will not necessarily be very +efficient when a particular \PASCAL\ compiler has translated it into a +particular machine language. However, the program has been written so that it +can be tuned to run efficiently in a wide variety of operating environments +by making comparatively few changes. Such flexibility is possible because +the documentation that follows is written in the \.{WEB} language, which is +at a higher level than \PASCAL; the preprocessing step that converts \.{WEB} +to \PASCAL\ is able to introduce most of the necessary refinements. +Semi-automatic translation to other languages is also feasible, because the +program below does not make extensive use of features that are peculiar to +\PASCAL. + +A large piece of software like \TeX\ has inherent complexity that cannot +be reduced below a certain level of difficulty, although each individual +part is fairly simple by itself. The \.{WEB} language is intended to make +the algorithms as readable as possible, by reflecting the way the +individual program pieces fit together and by providing the +cross-references that connect different parts. Detailed comments about +what is going on, and about why things were done in certain ways, have +been liberally sprinkled throughout the program. These comments explain +features of the implementation, but they rarely attempt to explain the +\TeX\ language itself, since the reader is supposed to be familiar with +{\sl The \TeX book}. +@.WEB@> +@:TeXbook}{\sl The \TeX book@> + +@ The present implementation has a long ancestry, beginning in the summer +of~1977, when Michael~F. Plass and Frank~M. Liang designed and coded +a prototype +@^Plass, Michael Frederick@> +@^Liang, Franklin Mark@> +@^Knuth, Donald Ervin@> +based on some specifications that the author had made in May of that year. +This original proto\TeX\ included macro definitions and elementary +manipulations on boxes and glue, but it did not have line-breaking, +page-breaking, mathematical formulas, alignment routines, error recovery, +or the present semantic nest; furthermore, +it used character lists instead of token lists, so that a control sequence +like \.{\\halign} was represented by a list of seven characters. A +complete version of \TeX\ was designed and coded by the author in late +1977 and early 1978; that program, like its prototype, was written in the +{\mc SAIL} language, for which an excellent debugging system was +available. Preliminary plans to convert the {\mc SAIL} code into a form +somewhat like the present ``web'' were developed by Luis Trabb~Pardo and +the author at the beginning of 1979, and a complete implementation was +created by Ignacio~A. Zabala in 1979 and 1980. The \TeX82 program, which +@^Zabala Salelles, Ignacio Andr\'es@> +was written by the author during the latter part of 1981 and the early +part of 1982, also incorporates ideas from the 1979 implementation of +@^Guibas, Leonidas Ioannis@> +@^Sedgewick, Robert@> +@^Wyatt, Douglas Kirk@> +\TeX\ in {\mc MESA} that was written by Leonidas Guibas, Robert Sedgewick, +and Douglas Wyatt at the Xerox Palo Alto Research Center. Several hundred +refinements were introduced into \TeX82 based on the experiences gained with +the original implementations, so that essentially every part of the system +has been substantially improved. After the appearance of ``Version 0'' in +September 1982, this program benefited greatly from the comments of +many other people, notably David~R. Fuchs and Howard~W. Trickey. +A final revision in September 1989 extended the input character set to +eight-bit codes and introduced the ability to hyphenate words from +different languages, based on some ideas of Michael~J. Ferguson. +@^Fuchs, David Raymond@> +@^Trickey, Howard Wellington@> +@^Ferguson, Michael John@> + +No doubt there still is plenty of room for improvement, but the author +is firmly committed to keeping \TeX82 ``frozen'' from now on; stability +and reliability are to be its main virtues. + +On the other hand, the \.{WEB} description can be extended without changing +the core of \TeX82 itself, and the program has been designed so that such +extensions are not extremely difficult to make. +The |banner| string defined here should be changed whenever \TeX\ +undergoes any modifications, so that it will be clear which version of +\TeX\ might be the guilty party when a problem arises. +@^extensions to \TeX@> +@^system dependencies@> + +This program contains code for various features extending \TeX, +therefore this program is called `\eTeX' and not +`\TeX'; the official name `\TeX' by itself is reserved +for software systems that are fully compatible with each other. +A special test suite called the ``\.{TRIP} test'' is available for +helping to determine whether a particular implementation deserves to be +known as `\TeX' [cf.~Stanford Computer Science report CS1027, +November 1984]. + +A similar test suite called the ``\.{e-TRIP} test'' is available for +helping to determine whether a particular implementation deserves to be +known as `\eTeX'. + +@d eTeX_version=2 { \.{\\eTeXversion} } +@d eTeX_revision==".2" { \.{\\eTeXrevision} } +@d eTeX_version_string=='-2.2' {current \eTeX\ version} +@# +@d eTeX_banner=='This is e-TeX, Version 3.141592',eTeX_version_string + {printed when \eTeX\ starts} +@# +@d pdftex_version==140 { \.{\\pdftexversion} } +@d pdftex_revision=="0" { \.{\\pdftexrevision} } +@d pdftex_version_string=='-1.40.0-rc4' {current \pdfTeX\ version} +@# +@d pdfeTeX_banner=='This is pdfeTeX, Version 3.141592',pdftex_version_string,eTeX_version_string + {printed when \pdfeTeX\ starts} +@# +@d pdfTeX_banner=='This is pdfTeX, Version 3.141592',pdftex_version_string + {printed when \pdfTeX\ starts} +@# +@d TeX_banner=='This is TeX, Version 3.141592' {printed when \TeX\ starts} +@# +@d banner==pdfeTeX_banner +@# +@d TEX==ETEX {change program name into |ETEX|} +@# +@d TeXXeT_code=0 {the \TeXXeT\ feature is optional} +@# +@d eTeX_states=1 {number of \eTeX\ state variables in |eqtb|} + +@ Different \PASCAL s have slightly different conventions, and the present +@!@:PASCAL H}{\ph@> +program expresses \TeX\ in terms of the \PASCAL\ that was +available to the author in 1982. Constructions that apply to +this particular compiler, which we shall call \ph, should help the +reader see how to make an appropriate interface for other systems +if necessary. (\ph\ is Charles Hedrick's modification of a compiler +@^Hedrick, Charles Locke@> +for the DECsystem-10 that was originally developed at the University of +Hamburg; cf.\ {\sl SOFTWARE---Practice \AM\ Experience \bf6} (1976), +29--42. The \TeX\ program below is intended to be adaptable, without +extensive changes, to most other versions of \PASCAL, so it does not fully +use the admirable features of \ph. Indeed, a conscious effort has been +made here to avoid using several idiosyncratic features of standard +\PASCAL\ itself, so that most of the code can be translated mechanically +into other high-level languages. For example, the `\&{with}' and `\\{new}' +features are not used, nor are pointer types, set types, or enumerated +scalar types; there are no `\&{var}' parameters, except in the case of files +--- \eTeX, however, does use `\&{var}' parameters for the |reverse| function; +there are no tag fields on variant records; there are no assignments +|real:=integer|; no procedures are declared local to other procedures.) + +The portions of this program that involve system-dependent code, where +changes might be necessary because of differences between \PASCAL\ compilers +and/or differences between +operating systems, can be identified by looking at the sections whose +numbers are listed under `system dependencies' in the index. Furthermore, +the index entries for `dirty \PASCAL' list all places where the restrictions +of \PASCAL\ have not been followed perfectly, for one reason or another. +@!@^system dependencies@> +@!@^dirty \PASCAL@> + +Incidentally, \PASCAL's standard |round| function can be problematical, +because it disagrees with the IEEE floating-point standard. +Many implementors have +therefore chosen to substitute their own home-grown rounding procedure. + +@ The program begins with a normal \PASCAL\ program heading, whose +components will be filled in later, using the conventions of \.{WEB}. +@.WEB@> +For example, the portion of the program called `\X\glob:Global +variables\X' below will be replaced by a sequence of variable declarations +that starts in $\section\glob$ of this documentation. In this way, we are able +to define each individual global variable when we are prepared to +understand what it means; we do not have to define all of the globals at +once. Cross references in $\section\glob$, where it says ``See also +sections \gglob, \dots,'' also make it possible to look at the set of +all global variables, if desired. Similar remarks apply to the other +portions of the program heading. + +Actually the heading shown here is not quite normal: The |program| line +does not mention any |output| file, because \ph\ would ask the \TeX\ user +to specify a file name if |output| were specified here. +@^system dependencies@> + +@d mtype==t@&y@&p@&e {this is a \.{WEB} coding trick:} +@f mtype==type {`\&{mtype}' will be equivalent to `\&{type}'} +@f type==true {but `|type|' will not be treated as a reserved word} + +@p @t\4@>@@/ +program TEX; {all file names are defined dynamically} +label @@/ +const @@/ +mtype @@/ +var @@/ +@# +procedure initialize; {this procedure gets things started properly} + var @@/ + begin @@; + end;@# +@t\4@>@@/ +@t\4@>@@/ + +@ The overall \TeX\ program begins with the heading just shown, after which +comes a bunch of procedure declarations and function declarations. +Finally we will get to the main program, which begins with the +comment `|start_here|'. If you want to skip down to the +main program now, you can look up `|start_here|' in the index. +But the author suggests that the best way to understand this program +is to follow pretty much the order of \TeX's components as they appear in the +\.{WEB} description you are now reading, since the present ordering is +intended to combine the advantages of the ``bottom up'' and ``top down'' +approaches to the problem of understanding a somewhat complicated system. + +@ Three labels must be declared in the main program, so we give them +symbolic names. + +@d start_of_TEX=1 {go here when \TeX's variables are initialized} +@d end_of_TEX=9998 {go here to close files and terminate gracefully} +@d final_end=9999 {this label marks the ending of the program} + +@= +start_of_TEX@t\hskip-2pt@>, end_of_TEX@t\hskip-2pt@>,@,final_end; + {key control points} + +@ Some of the code below is intended to be used only when diagnosing the +strange behavior that sometimes occurs when \TeX\ is being installed or +when system wizards are fooling around with \TeX\ without quite knowing +what they are doing. Such code will not normally be compiled; it is +delimited by the codewords `$|debug|\ldots|gubed|$', with apologies +to people who wish to preserve the purity of English. + +Similarly, there is some conditional code delimited by +`$|stat|\ldots|tats|$' that is intended for use when statistics are to be +kept about \TeX's memory usage. The |stat| $\ldots$ |tats| code also +implements diagnostic information for \.{\\tracingparagraphs} and +\.{\\tracingpages}. +@^debugging@> + +@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging} +@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging} +@f debug==begin +@f gubed==end +@# +@d stat==@{ {change this to `$\\{stat}\equiv\null$' when gathering + usage statistics} +@d tats==@t@>@} {change this to `$\\{tats}\equiv\null$' when gathering + usage statistics} +@f stat==begin +@f tats==end + +@ This program has two important variations: (1) There is a long and slow +version called \.{INITEX}, which does the extra calculations needed to +@.INITEX@> +initialize \TeX's internal tables; and (2)~there is a shorter and faster +production version, which cuts the initialization to a bare minimum. +Parts of the program that are needed in (1) but not in (2) are delimited by +the codewords `$|init|\ldots|tini|$'. + +@d init== {change this to `$\\{init}\equiv\.{@@\{}$' in the production version} +@d tini== {change this to `$\\{tini}\equiv\.{@@\}}$' in the production version} +@f init==begin +@f tini==end + +@= +@@/ +@!init @@;@+tini + +@ If the first character of a \PASCAL\ comment is a dollar sign, +\ph\ treats the comment as a list of ``compiler directives'' that will +affect the translation of this program into machine language. The +directives shown below specify full checking and inclusion of the \PASCAL\ +debugger when \TeX\ is being debugged, but they cause range checking and other +redundant code to be eliminated when the production system is being generated. +Arithmetic overflow will be detected in all cases. +@^system dependencies@> +@^Overflow in arithmetic@> + +@= +@{@&$C-,A+,D-@} {no range check, catch arithmetic overflow, no debug overhead} +@!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging} + +@ This \TeX\ implementation conforms to the rules of the {\sl Pascal User +@:PASCAL}{\PASCAL@> +@^system dependencies@> +Manual} published by Jensen and Wirth in 1975, except where system-dependent +@^Wirth, Niklaus@> +@^Jensen, Kathleen@> +code is necessary to make a useful system program, and except in another +respect where such conformity would unnecessarily obscure the meaning +and clutter up the code: We assume that |case| statements may include a +default case that applies if no matching label is found. Thus, we shall use +constructions like +$$\vbox{\halign{\ignorespaces#\hfil\cr +|case x of|\cr +1: $\langle\,$code for $x=1\,\rangle$;\cr +3: $\langle\,$code for $x=3\,\rangle$;\cr +|othercases| $\langle\,$code for |x<>1| and |x<>3|$\,\rangle$\cr +|endcases|\cr}}$$ +since most \PASCAL\ compilers have plugged this hole in the language by +incorporating some sort of default mechanism. For example, the \ph\ +compiler allows `|others|:' as a default label, and other \PASCAL s allow +syntaxes like `\&{else}' or `\&{otherwise}' or `\\{otherwise}:', etc. The +definitions of |othercases| and |endcases| should be changed to agree with +local conventions. Note that no semicolon appears before |endcases| in +this program, so the definition of |endcases| should include a semicolon +if the compiler wants one. (Of course, if no default mechanism is +available, the |case| statements of \TeX\ will have to be laboriously +extended by listing all remaining cases. People who are stuck with such +\PASCAL s have, in fact, done this, successfully but not happily!) + +@d othercases == others: {default for cases not listed explicitly} +@d endcases == @+end {follows the default case in an extended |case| statement} +@f othercases == else +@f endcases == end + +@ The following parameters can be changed at compile time to extend or +reduce \TeX's capacity. They may have different values in \.{INITEX} and +in production versions of \TeX. +@.INITEX@> +@^system dependencies@> + +@= +@!mem_max=30000; {greatest index in \TeX's internal |mem| array; + must be strictly less than |max_halfword|; + must be equal to |mem_top| in \.{INITEX}, otherwise |>=mem_top|} +@!mem_min=0; {smallest index in \TeX's internal |mem| array; + must be |min_halfword| or more; + must be equal to |mem_bot| in \.{INITEX}, otherwise |<=mem_bot|} +@!buf_size=500; {maximum number of characters simultaneously present in + current lines of open files and in control sequences between + \.{\\csname} and \.{\\endcsname}; must not exceed |max_halfword|} +@!error_line=72; {width of context lines on terminal error messages} +@!half_error_line=42; {width of first lines of contexts in terminal + error messages; should be between 30 and |error_line-15|} +@!max_print_line=79; {width of longest text lines output; should be at least 60} +@!stack_size=200; {maximum number of simultaneous input sources} +@!max_in_open=6; {maximum number of input files and error insertions that + can be going on simultaneously} +@!font_max=75; {maximum internal font number; must not exceed |max_quarterword| + and must be at most |font_base+256|} +@!font_mem_size=20000; {number of words of |font_info| for all fonts} +@!param_size=60; {maximum number of simultaneous macro parameters} +@!nest_size=40; {maximum number of semantic levels simultaneously active} +@!max_strings=3000; {maximum number of strings; must not exceed |max_halfword|} +@!string_vacancies=8000; {the minimum number of characters that should be + available for the user's control sequences and font names, + after \TeX's own error messages are stored} +@!pool_size=32000; {maximum number of characters in strings, including all + error messages and help texts, and the names of all fonts and + control sequences; must exceed |string_vacancies| by the total + length of \TeX's own strings, which is currently about 23000} +@!save_size=600; {space for saving values outside of current group; must be + at most |max_halfword|} +@!trie_size=8000; {space for hyphenation patterns; should be larger for + \.{INITEX} than it is in production versions of \TeX} +@!trie_op_size=500; {space for ``opcodes'' in the hyphenation patterns} +@!dvi_buf_size=800; {size of the output buffer; must be a multiple of 8} +@!file_name_size=40; {file names shouldn't be longer than this} +@!pool_name='TeXformats:TEX.POOL '; + {string of length |file_name_size|; tells where the string pool appears} +@.TeXformats@> + +@ Like the preceding parameters, the following quantities can be changed +at compile time to extend or reduce \TeX's capacity. But if they are changed, +it is necessary to rerun the initialization program \.{INITEX} +@.INITEX@> +to generate new tables for the production \TeX\ program. +One can't simply make helter-skelter changes to the following constants, +since certain rather complex initialization +numbers are computed from them. They are defined here using +\.{WEB} macros, instead of being put into \PASCAL's |const| list, in order to +emphasize this distinction. + +@d mem_bot=0 {smallest index in the |mem| array dumped by \.{INITEX}; + must not be less than |mem_min|} +@d mem_top==30000 {largest index in the |mem| array dumped by \.{INITEX}; + must be substantially larger than |mem_bot| + and not greater than |mem_max|} +@d font_base=0 {smallest internal font number; must not be less + than |min_quarterword|} +@d hash_size=2100 {maximum number of control sequences; it should be at most + about |(mem_max-mem_min)/10|} +@d hash_prime=1777 {a prime number equal to about 85\pct! of |hash_size|} +@d hyph_size=307 {another prime; the number of \.{\\hyphenation} exceptions} +@^system dependencies@> + +@ In case somebody has inadvertently made bad settings of the ``constants,'' +\TeX\ checks them using a global variable called |bad|. + +This is the first of many sections of \TeX\ where global variables are +defined. + +@= +@!bad:integer; {is some ``constant'' wrong?} + +@ Later on we will say `\ignorespaces|if mem_max>=max_halfword then bad:=14|', +or something similar. (We can't do that until |max_halfword| has been defined.) + +@= +bad:=0; +if (half_error_line<30)or(half_error_line>error_line-15) then bad:=1; +if max_print_line<60 then bad:=2; +if dvi_buf_size mod 8<>0 then bad:=3; +if mem_bot+1100>mem_top then bad:=4; +if hash_prime>hash_size then bad:=5; +if max_in_open>=128 then bad:=6; +if mem_top<256+11 then bad:=7; {we will want |null_list>255|} + +@ Labels are given symbolic names by the following definitions, so that +occasional |goto| statements will be meaningful. We insert the label +`|exit|' just before the `\ignorespaces|end|\unskip' of a procedure in +which we have used the `|return|' statement defined below; the label +`|restart|' is occasionally used at the very beginning of a procedure; and +the label `|reswitch|' is occasionally used just prior to a |case| +statement in which some cases change the conditions and we wish to branch +to the newly applicable case. Loops that are set up with the |loop| +construction defined below are commonly exited by going to `|done|' or to +`|found|' or to `|not_found|', and they are sometimes repeated by going to +`|continue|'. If two or more parts of a subroutine start differently but +end up the same, the shared code may be gathered together at +`|common_ending|'. + +Incidentally, this program never declares a label that isn't actually used, +because some fussy \PASCAL\ compilers will complain about redundant labels. + +@d exit=10 {go here to leave a procedure} +@d restart=20 {go here to start a procedure again} +@d reswitch=21 {go here to start a case statement again} +@d continue=22 {go here to resume a loop} +@d done=30 {go here to exit a loop} +@d done1=31 {like |done|, when there is more than one loop} +@d done2=32 {for exiting the second loop in a long block} +@d done3=33 {for exiting the third loop in a very long block} +@d done4=34 {for exiting the fourth loop in an extremely long block} +@d done5=35 {for exiting the fifth loop in an immense block} +@d done6=36 {for exiting the sixth loop in a block} +@d found=40 {go here when you've found it} +@d found1=41 {like |found|, when there's more than one per routine} +@d found2=42 {like |found|, when there's more than two per routine} +@d not_found=45 {go here when you've found nothing} +@d not_found1=46 {like |not_found|, when there's more than one} +@d not_found2=47 {like |not_found|, when there's more than two} +@d not_found3=48 {like |not_found|, when there's more than three} +@d not_found4=49 {like |not_found|, when there's more than four} +@d common_ending=50 {go here when you want to merge with another branch} + +@ Here are some macros for common programming idioms. + +@d incr(#) == #:=#+1 {increase a variable by unity} +@d decr(#) == #:=#-1 {decrease a variable by unity} +@d negate(#) == #:=-# {change the sign of a variable} +@d loop == @+ while true do@+ {repeat over and over until a |goto| happens} +@f loop == xclause + {\.{WEB}'s |xclause| acts like `\ignorespaces|while true do|\unskip'} +@d do_nothing == {empty statement} +@d return == goto exit {terminate a procedure call} +@f return == nil +@d empty=0 {symbolic name for a null constant} + +@* \[2] The character set. +In order to make \TeX\ readily portable to a wide variety of +computers, all of its input text is converted to an internal eight-bit +code that includes standard ASCII, the ``American Standard Code for +Information Interchange.'' This conversion is done immediately when each +character is read in. Conversely, characters are converted from ASCII to +the user's external representation just before they are output to a +text file. + +Such an internal code is relevant to users of \TeX\ primarily because it +governs the positions of characters in the fonts. For example, the +character `\.A' has ASCII code $65=@'101$, and when \TeX\ typesets +this letter it specifies character number 65 in the current font. +If that font actually has `\.A' in a different position, \TeX\ doesn't +know what the real position is; the program that does the actual printing from +\TeX's device-independent files is responsible for converting from ASCII to +a particular font encoding. +@^ASCII code@> + +\TeX's internal code also defines the value of constants +that begin with a reverse apostrophe; and it provides an index to the +\.{\\catcode}, \.{\\mathcode}, \.{\\uccode}, \.{\\lccode}, and \.{\\delcode} +tables. + +@ Characters of text that have been converted to \TeX's internal form +are said to be of type |ASCII_code|, which is a subrange of the integers. + +@= +@!ASCII_code=0..255; {eight-bit numbers} + +@ The original \PASCAL\ compiler was designed in the late 60s, when six-bit +character sets were common, so it did not make provision for lowercase +letters. Nowadays, of course, we need to deal with both capital and small +letters in a convenient way, especially in a program for typesetting; +so the present specification of \TeX\ has been written under the assumption +that the \PASCAL\ compiler and run-time system permit the use of text files +with more than 64 distinguishable characters. More precisely, we assume that +the character set contains at least the letters and symbols associated +with ASCII codes @'40 through @'176; all of these characters are now +available on most computer terminals. + +Since we are dealing with more characters than were present in the first +\PASCAL\ compilers, we have to decide what to call the associated data +type. Some \PASCAL s use the original name |char| for the +characters in text files, even though there now are more than 64 such +characters, while other \PASCAL s consider |char| to be a 64-element +subrange of a larger data type that has some other name. + +In order to accommodate this difference, we shall use the name |text_char| +to stand for the data type of the characters that are converted to and +from |ASCII_code| when they are input and output. We shall also assume +that |text_char| consists of the elements |chr(first_text_char)| through +|chr(last_text_char)|, inclusive. The following definitions should be +adjusted if necessary. +@^system dependencies@> + +@d text_char == char {the data type of characters in text files} +@d first_text_char=0 {ordinal number of the smallest element of |text_char|} +@d last_text_char=255 {ordinal number of the largest element of |text_char|} + +@= +@!i:integer; + +@ The \TeX\ processor converts between ASCII code and +the user's external character set by means of arrays |xord| and |xchr| +that are analogous to \PASCAL's |ord| and |chr| functions. + +@= +@!xord: array [text_char] of ASCII_code; + {specifies conversion of input characters} +@!xchr: array [ASCII_code] of text_char; + {specifies conversion of output characters} + +@ Since we are assuming that our \PASCAL\ system is able to read and +write the visible characters of standard ASCII (although not +necessarily using the ASCII codes to represent them), the following +assignment statements initialize the standard part of the |xchr| array +properly, without needing any system-dependent changes. On the other +hand, it is possible to implement \TeX\ with less complete character +sets, and in such cases it will be necessary to change something here. +@^system dependencies@> + +@= +xchr[@'40]:=' '; +xchr[@'41]:='!'; +xchr[@'42]:='"'; +xchr[@'43]:='#'; +xchr[@'44]:='$'; +xchr[@'45]:='%'; +xchr[@'46]:='&'; +xchr[@'47]:='''';@/ +xchr[@'50]:='('; +xchr[@'51]:=')'; +xchr[@'52]:='*'; +xchr[@'53]:='+'; +xchr[@'54]:=','; +xchr[@'55]:='-'; +xchr[@'56]:='.'; +xchr[@'57]:='/';@/ +xchr[@'60]:='0'; +xchr[@'61]:='1'; +xchr[@'62]:='2'; +xchr[@'63]:='3'; +xchr[@'64]:='4'; +xchr[@'65]:='5'; +xchr[@'66]:='6'; +xchr[@'67]:='7';@/ +xchr[@'70]:='8'; +xchr[@'71]:='9'; +xchr[@'72]:=':'; +xchr[@'73]:=';'; +xchr[@'74]:='<'; +xchr[@'75]:='='; +xchr[@'76]:='>'; +xchr[@'77]:='?';@/ +xchr[@'100]:='@@'; +xchr[@'101]:='A'; +xchr[@'102]:='B'; +xchr[@'103]:='C'; +xchr[@'104]:='D'; +xchr[@'105]:='E'; +xchr[@'106]:='F'; +xchr[@'107]:='G';@/ +xchr[@'110]:='H'; +xchr[@'111]:='I'; +xchr[@'112]:='J'; +xchr[@'113]:='K'; +xchr[@'114]:='L'; +xchr[@'115]:='M'; +xchr[@'116]:='N'; +xchr[@'117]:='O';@/ +xchr[@'120]:='P'; +xchr[@'121]:='Q'; +xchr[@'122]:='R'; +xchr[@'123]:='S'; +xchr[@'124]:='T'; +xchr[@'125]:='U'; +xchr[@'126]:='V'; +xchr[@'127]:='W';@/ +xchr[@'130]:='X'; +xchr[@'131]:='Y'; +xchr[@'132]:='Z'; +xchr[@'133]:='['; +xchr[@'134]:='\'; +xchr[@'135]:=']'; +xchr[@'136]:='^'; +xchr[@'137]:='_';@/ +xchr[@'140]:='`'; +xchr[@'141]:='a'; +xchr[@'142]:='b'; +xchr[@'143]:='c'; +xchr[@'144]:='d'; +xchr[@'145]:='e'; +xchr[@'146]:='f'; +xchr[@'147]:='g';@/ +xchr[@'150]:='h'; +xchr[@'151]:='i'; +xchr[@'152]:='j'; +xchr[@'153]:='k'; +xchr[@'154]:='l'; +xchr[@'155]:='m'; +xchr[@'156]:='n'; +xchr[@'157]:='o';@/ +xchr[@'160]:='p'; +xchr[@'161]:='q'; +xchr[@'162]:='r'; +xchr[@'163]:='s'; +xchr[@'164]:='t'; +xchr[@'165]:='u'; +xchr[@'166]:='v'; +xchr[@'167]:='w';@/ +xchr[@'170]:='x'; +xchr[@'171]:='y'; +xchr[@'172]:='z'; +xchr[@'173]:='{'; +xchr[@'174]:='|'; +xchr[@'175]:='}'; +xchr[@'176]:='~';@/ + +@ Some of the ASCII codes without visible characters have been given symbolic +names in this program because they are used with a special meaning. + +@d null_code=@'0 {ASCII code that might disappear} +@d carriage_return=@'15 {ASCII code used at end of line} +@d invalid_code=@'177 {ASCII code that many systems prohibit in text files} + +@ The ASCII code is ``standard'' only to a certain extent, since many +computer installations have found it advantageous to have ready access +to more than 94 printing characters. Appendix~C of {\sl The \TeX book\/} +gives a complete specification of the intended correspondence between +characters and \TeX's internal representation. +@:TeXbook}{\sl The \TeX book@> + +If \TeX\ is being used +on a garden-variety \PASCAL\ for which only standard ASCII +codes will appear in the input and output files, it doesn't really matter +what codes are specified in |xchr[0..@'37]|, but the safest policy is to +blank everything out by using the code shown below. + +However, other settings of |xchr| will make \TeX\ more friendly on +computers that have an extended character set, so that users can type things +like `\.^^Z' instead of `\.{\\ne}'. People with extended character sets can +assign codes arbitrarily, giving an |xchr| equivalent to whatever +characters the users of \TeX\ are allowed to have in their input files. +It is best to make the codes correspond to the intended interpretations as +shown in Appendix~C whenever possible; but this is not necessary. For +example, in countries with an alphabet of more than 26 letters, it is +usually best to map the additional letters into codes less than~@'40. +To get the most ``permissive'' character set, change |' '| on the +right of these assignment statements to |chr(i)|. +@^character set dependencies@> +@^system dependencies@> + +@= +for i:=0 to @'37 do xchr[i]:=' '; +for i:=@'177 to @'377 do xchr[i]:=' '; + +@ The following system-independent code makes the |xord| array contain a +suitable inverse to the information in |xchr|. Note that if |xchr[i]=xchr[j]| +where |i= +for i:=first_text_char to last_text_char do xord[chr(i)]:=invalid_code; +for i:=@'200 to @'377 do xord[xchr[i]]:=i; +for i:=0 to @'176 do xord[xchr[i]]:=i; + +@* \[3] Input and output. +The bane of portability is the fact that different operating systems treat +input and output quite differently, perhaps because computer scientists +have not given sufficient attention to this problem. People have felt somehow +that input and output are not part of ``real'' programming. Well, it is true +that some kinds of programming are more fun than others. With existing +input/output conventions being so diverse and so messy, the only sources of +joy in such parts of the code are the rare occasions when one can find a +way to make the program a little less bad than it might have been. We have +two choices, either to attack I/O now and get it over with, or to postpone +I/O until near the end. Neither prospect is very attractive, so let's +get it over with. + +The basic operations we need to do are (1)~inputting and outputting of +text, to or from a file or the user's terminal; (2)~inputting and +outputting of eight-bit bytes, to or from a file; (3)~instructing the +operating system to initiate (``open'') or to terminate (``close'') input or +output from a specified file; (4)~testing whether the end of an input +file has been reached. + +\TeX\ needs to deal with two kinds of files. +We shall use the term |alpha_file| for a file that contains textual data, +and the term |byte_file| for a file that contains eight-bit binary information. +These two types turn out to be the same on many computers, but +sometimes there is a significant distinction, so we shall be careful to +distinguish between them. Standard protocols for transferring +such files from computer to computer, via high-speed networks, are +now becoming available to more and more communities of users. + +The program actually makes use also of a third kind of file, called a +|word_file|, when dumping and reloading base information for its own +initialization. We shall define a word file later; but it will be possible +for us to specify simple operations on word files before they are defined. + +@= +@!eight_bits=0..255; {unsigned one-byte quantity} +@!alpha_file=packed file of text_char; {files that contain textual data} +@!byte_file=packed file of eight_bits; {files that contain binary data} + +@ Most of what we need to do with respect to input and output can be handled +by the I/O facilities that are standard in \PASCAL, i.e., the routines +called |get|, |put|, |eof|, and so on. But +standard \PASCAL\ does not allow file variables to be associated with file +names that are determined at run time, so it cannot be used to implement +\TeX; some sort of extension to \PASCAL's ordinary |reset| and |rewrite| +is crucial for our purposes. We shall assume that |name_of_file| is a variable +of an appropriate type such that the \PASCAL\ run-time system being used to +implement \TeX\ can open a file whose external name is specified by +|name_of_file|. +@^system dependencies@> + +@= +@!name_of_file:packed array[1..file_name_size] of char;@;@/ + {on some systems this may be a \&{record} variable} +@!name_length:0..file_name_size;@/{this many characters are actually + relevant in |name_of_file| (the rest are blank)} + +@ The \ph\ compiler with which the present version of \TeX\ was prepared has +extended the rules of \PASCAL\ in a very convenient way. To open file~|f|, +we can write +$$\vbox{\halign{#\hfil\qquad&#\hfil\cr +|reset(f,@t\\{name}@>,'/O')|&for input;\cr +|rewrite(f,@t\\{name}@>,'/O')|&for output.\cr}}$$ +The `\\{name}' parameter, which is of type `{\bf packed array +$[\langle\\{any}\rangle]$ of \\{char}}', stands for the name of +the external file that is being opened for input or output. +Blank spaces that might appear in \\{name} are ignored. + +The `\.{/O}' parameter tells the operating system not to issue its own +error messages if something goes wrong. If a file of the specified name +cannot be found, or if such a file cannot be opened for some other reason +(e.g., someone may already be trying to write the same file), we will have +|@!erstat(f)<>0| after an unsuccessful |reset| or |rewrite|. This allows +\TeX\ to undertake appropriate corrective action. +@:PASCAL H}{\ph@> +@^system dependencies@> + +\TeX's file-opening procedures return |false| if no file identified by +|name_of_file| could be opened. + +@d reset_OK(#)==erstat(#)=0 +@d rewrite_OK(#)==erstat(#)=0 + +@p function a_open_in(var f:alpha_file):boolean; + {open a text file for input} +begin reset(f,name_of_file,'/O'); a_open_in:=reset_OK(f); +end; +@# +function a_open_out(var f:alpha_file):boolean; + {open a text file for output} +begin rewrite(f,name_of_file,'/O'); a_open_out:=rewrite_OK(f); +end; +@# +function b_open_in(var f:byte_file):boolean; + {open a binary file for input} +begin reset(f,name_of_file,'/O'); b_open_in:=reset_OK(f); +end; +@# +function b_open_out(var f:byte_file):boolean; + {open a binary file for output} +begin rewrite(f,name_of_file,'/O'); b_open_out:=rewrite_OK(f); +end; +@# +function w_open_in(var f:word_file):boolean; + {open a word file for input} +begin reset(f,name_of_file,'/O'); w_open_in:=reset_OK(f); +end; +@# +function w_open_out(var f:word_file):boolean; + {open a word file for output} +begin rewrite(f,name_of_file,'/O'); w_open_out:=rewrite_OK(f); +end; + +@ Files can be closed with the \ph\ routine `|close(f)|', which +@^system dependencies@> +should be used when all input or output with respect to |f| has been completed. +This makes |f| available to be opened again, if desired; and if |f| was used for +output, the |close| operation makes the corresponding external file appear +on the user's area, ready to be read. + +These procedures should not generate error messages if a file is +being closed before it has been successfully opened. + +@p procedure a_close(var f:alpha_file); {close a text file} +begin close(f); +end; +@# +procedure b_close(var f:byte_file); {close a binary file} +begin close(f); +end; +@# +procedure w_close(var f:word_file); {close a word file} +begin close(f); +end; + +@ Binary input and output are done with \PASCAL's ordinary |get| and |put| +procedures, so we don't have to make any other special arrangements for +binary~I/O. Text output is also easy to do with standard \PASCAL\ routines. +The treatment of text input is more difficult, however, because +of the necessary translation to |ASCII_code| values. +\TeX's conventions should be efficient, and they should +blend nicely with the user's operating environment. + +@ Input from text files is read one line at a time, using a routine called +|input_ln|. This function is defined in terms of global variables called +|buffer|, |first|, and |last| that will be described in detail later; for +now, it suffices for us to know that |buffer| is an array of |ASCII_code| +values, and that |first| and |last| are indices into this array +representing the beginning and ending of a line of text. + +@= +@!buffer:array[0..buf_size] of ASCII_code; {lines of characters being read} +@!first:0..buf_size; {the first unused position in |buffer|} +@!last:0..buf_size; {end of the line just input to |buffer|} +@!max_buf_stack:0..buf_size; {largest index used in |buffer|} + +@ The |input_ln| function brings the next line of input from the specified +file into available positions of the buffer array and returns the value +|true|, unless the file has already been entirely read, in which case it +returns |false| and sets |last:=first|. In general, the |ASCII_code| +numbers that represent the next line of the file are input into +|buffer[first]|, |buffer[first+1]|, \dots, |buffer[last-1]|; and the +global variable |last| is set equal to |first| plus the length of the +line. Trailing blanks are removed from the line; thus, either |last=first| +(in which case the line was entirely blank) or |buffer[last-1]<>" "|. + +An overflow error is given, however, if the normal actions of |input_ln| +would make |last>=buf_size|; this is done so that other parts of \TeX\ +can safely look at the contents of |buffer[last+1]| without overstepping +the bounds of the |buffer| array. Upon entry to |input_ln|, the condition +|first + +@p function input_ln(var f:alpha_file;@!bypass_eoln:boolean):boolean; + {inputs the next line or returns |false|} +var last_nonblank:0..buf_size; {|last| with trailing blanks removed} +begin if bypass_eoln then if not eof(f) then get(f); + {input the first character of the line into |f^|} +last:=first; {cf.\ Matthew 19\thinspace:\thinspace30} +if eof(f) then input_ln:=false +else begin last_nonblank:=first; + while not eoln(f) do + begin if last>=max_buf_stack then + begin max_buf_stack:=last+1; + if max_buf_stack=buf_size then + @; + end; + buffer[last]:=xord[f^]; get(f); incr(last); + if buffer[last-1]<>" " then last_nonblank:=last; + end; + last:=last_nonblank; input_ln:=true; + end; +end; + +@ The user's terminal acts essentially like other files of text, except +that it is used both for input and for output. When the terminal is +considered an input file, the file variable is called |term_in|, and when it +is considered an output file the file variable is |term_out|. +@^system dependencies@> + +@= +@!term_in:alpha_file; {the terminal as an input file} +@!term_out:alpha_file; {the terminal as an output file} + +@ Here is how to open the terminal files +in \ph. The `\.{/I}' switch suppresses the first |get|. +@^system dependencies@> + +@d t_open_in==reset(term_in,'TTY:','/O/I') {open the terminal for text input} +@d t_open_out==rewrite(term_out,'TTY:','/O') {open the terminal for text output} + +@ Sometimes it is necessary to synchronize the input/output mixture that +happens on the user's terminal, and three system-dependent +procedures are used for this +purpose. The first of these, |update_terminal|, is called when we want +to make sure that everything we have output to the terminal so far has +actually left the computer's internal buffers and been sent. +The second, |clear_terminal|, is called when we wish to cancel any +input that the user may have typed ahead (since we are about to +issue an unexpected error message). The third, |wake_up_terminal|, +is supposed to revive the terminal if the user has disabled it by +some instruction to the operating system. The following macros show how +these operations can be specified in \ph: +@^system dependencies@> + +@d update_terminal == break(term_out) {empty the terminal output buffer} +@d clear_terminal == break_in(term_in,true) {clear the terminal input buffer} +@d wake_up_terminal == do_nothing {cancel the user's cancellation of output} + +@ We need a special routine to read the first line of \TeX\ input from +the user's terminal. This line is different because it is read before we +have opened the transcript file; there is sort of a ``chicken and +egg'' problem here. If the user types `\.{\\input paper}' on the first +line, or if some macro invoked by that line does such an \.{\\input}, +the transcript file will be named `\.{paper.log}'; but if no \.{\\input} +commands are performed during the first line of terminal input, the transcript +file will acquire its default name `\.{texput.log}'. (The transcript file +will not contain error messages generated by the first line before the +first \.{\\input} command.) +@.texput@> + +The first line is even more special if we are lucky enough to have an operating +system that treats \TeX\ differently from a run-of-the-mill \PASCAL\ object +program. It's nice to let the user start running a \TeX\ job by typing +a command line like `\.{tex paper}'; in such a case, \TeX\ will operate +as if the first line of input were `\.{paper}', i.e., the first line will +consist of the remainder of the command line, after the part that invoked +\TeX. + +The first line is special also because it may be read before \TeX\ has +input a format file. In such cases, normal error messages cannot yet +be given. The following code uses concepts that will be explained later. +(If the \PASCAL\ compiler does not support non-local |@!goto|\unskip, the +@^system dependencies@> +statement `|goto final_end|' should be replaced by something that +quietly terminates the program.) + +@= +if format_ident=0 then + begin write_ln(term_out,'Buffer size exceeded!'); goto final_end; +@.Buffer size exceeded@> + end +else begin cur_input.loc_field:=first; cur_input.limit_field:=last-1; + overflow("buffer size",buf_size); +@:TeX capacity exceeded buffer size}{\quad buffer size@> + end + +@ Different systems have different ways to get started. But regardless of +what conventions are adopted, the routine that initializes the terminal +should satisfy the following specifications: + +\yskip\textindent{1)}It should open file |term_in| for input from the + terminal. (The file |term_out| will already be open for output to the + terminal.) + +\textindent{2)}If the user has given a command line, this line should be + considered the first line of terminal input. Otherwise the + user should be prompted with `\.{**}', and the first line of input + should be whatever is typed in response. + +\textindent{3)}The first line of input, which might or might not be a + command line, should appear in locations |first| to |last-1| of the + |buffer| array. + +\textindent{4)}The global variable |loc| should be set so that the + character to be read next by \TeX\ is in |buffer[loc]|. This + character should not be blank, and we should have |loc + +@p function init_terminal:boolean; {gets the terminal input started} +label exit; +begin t_open_in; +loop@+begin wake_up_terminal; write(term_out,'**'); update_terminal; +@.**@> + if not input_ln(term_in,true) then {this shouldn't happen} + begin write_ln(term_out); + write(term_out,'! End of file on the terminal... why?'); +@.End of file on the terminal@> + init_terminal:=false; return; + end; + loc:=first; + while (loc +which converts single-character strings into the ASCII code number of the +single character involved, while it converts other strings into integers +and builds a string pool file. Thus, when the string constant \.{"."} appears +in the program below, \.{WEB} converts it into the integer 46, which is the +ASCII code for a period, while \.{WEB} will convert a string like \.{"hello"} +into some integer greater than~255. String number 46 will presumably be the +single character `\..'; but some ASCII codes have no standard visible +representation, and \TeX\ sometimes needs to be able to print an arbitrary +ASCII character, so the first 256 strings are used to specify exactly what +should be printed for each of the 256 possibilities. + +Elements of the |str_pool| array must be ASCII codes that can actually +be printed; i.e., they must have an |xchr| equivalent in the local +character set. (This restriction applies only to preloaded strings, +not to those generated dynamically by the user.) + +Some \PASCAL\ compilers won't pack integers into a single byte unless the +integers lie in the range |-128..127|. To accommodate such systems +we access the string pool only via macros that can easily be redefined. + +@d si(#) == # {convert from |ASCII_code| to |packed_ASCII_code|} +@d so(#) == # {convert from |packed_ASCII_code| to |ASCII_code|} + +@= +@!pool_pointer = 0..pool_size; {for variables that point into |str_pool|} +@!str_number = 0..max_strings; {for variables that point into |str_start|} +@!packed_ASCII_code = 0..255; {elements of |str_pool| array} + +@ @= +@!str_pool:packed array[pool_pointer] of packed_ASCII_code; {the characters} +@!str_start : array[str_number] of pool_pointer; {the starting pointers} +@!pool_ptr : pool_pointer; {first unused position in |str_pool|} +@!str_ptr : str_number; {number of the current string being created} +@!init_pool_ptr : pool_pointer; {the starting value of |pool_ptr|} +@!init_str_ptr : str_number; {the starting value of |str_ptr|} + +@ Several of the elementary string operations are performed using \.{WEB} +macros instead of \PASCAL\ procedures, because many of the +operations are done quite frequently and we want to avoid the +overhead of procedure calls. For example, here is +a simple macro that computes the length of a string. +@.WEB@> + +@d length(#)==(str_start[#+1]-str_start[#]) {the number of characters + in string number \#} + +@ The length of the current string is called |cur_length|: + +@d cur_length == (pool_ptr - str_start[str_ptr]) + +@ Strings are created by appending character codes to |str_pool|. +The |append_char| macro, defined here, does not check to see if the +value of |pool_ptr| has gotten too high; this test is supposed to be +made before |append_char| is used. There is also a |flush_char| +macro, which erases the last character appended. + +To test if there is room to append |l| more characters to |str_pool|, +we shall write |str_room(l)|, which aborts \TeX\ and gives an +apologetic error message if there isn't enough room. + +@d append_char(#) == {put |ASCII_code| \# at the end of |str_pool|} +begin str_pool[pool_ptr]:=si(#); incr(pool_ptr); +end +@d flush_char == decr(pool_ptr) {forget the last character in the pool} +@d str_room(#) == {make sure that the pool hasn't overflowed} + begin if pool_ptr+# > pool_size then + overflow("pool size",pool_size-init_pool_ptr); +@:TeX capacity exceeded pool size}{\quad pool size@> + end + +@ Once a sequence of characters has been appended to |str_pool|, it +officially becomes a string when the function |make_string| is called. +This function returns the identification number of the new string as its +value. + +@p function make_string : str_number; {current string enters the pool} +begin if str_ptr=max_strings then + overflow("number of strings",max_strings-init_str_ptr); +@:TeX capacity exceeded number of strings}{\quad number of strings@> +incr(str_ptr); str_start[str_ptr]:=pool_ptr; +make_string:=str_ptr-1; +end; + +@ To destroy the most recently made string, we say |flush_string|. + +@d flush_string==begin decr(str_ptr); pool_ptr:=str_start[str_ptr]; + end + +@ The following subroutine compares string |s| with another string of the +same length that appears in |buffer| starting at position |k|; +the result is |true| if and only if the strings are equal. +Empirical tests indicate that |str_eq_buf| is used in such a way that +it tends to return |true| about 80 percent of the time. + +@p function str_eq_buf(@!s:str_number;@!k:integer):boolean; + {test equality of strings} +label not_found; {loop exit} +var j: pool_pointer; {running index} +@!result: boolean; {result of comparison} +begin j:=str_start[s]; +while jbuffer[k] then + begin result:=false; goto not_found; + end; + incr(j); incr(k); + end; +result:=true; +not_found: str_eq_buf:=result; +end; + +@ Here is a similar routine, but it compares two strings in the string pool, +and it does not assume that they have the same length. + +@p function str_eq_str(@!s,@!t:str_number):boolean; + {test equality of strings} +label not_found; {loop exit} +var j,@!k: pool_pointer; {running indices} +@!result: boolean; {result of comparison} +begin result:=false; +if length(s)<>length(t) then goto not_found; +j:=str_start[s]; k:=str_start[t]; +while jstr_pool[k] then goto not_found; + incr(j); incr(k); + end; +result:=true; +not_found: str_eq_str:=result; +end; + +@ The initial values of |str_pool|, |str_start|, |pool_ptr|, +and |str_ptr| are computed by the \.{INITEX} program, based in part +on the information that \.{WEB} has output while processing \TeX. +@.INITEX@> +@^string pool@> + +@p @!init function get_strings_started:boolean; {initializes the string pool, + but returns |false| if something goes wrong} +label done,exit; +var k,@!l:0..255; {small indices or counters} +@!m,@!n:text_char; {characters input from |pool_file|} +@!g:str_number; {garbage} +@!a:integer; {accumulator for check sum} +@!c:boolean; {check sum has been checked} +begin pool_ptr:=0; str_ptr:=0; str_start[0]:=0; +@; +@; +exit:end; +tini + +@ @d app_lc_hex(#)==l:=#; + if l<10 then append_char(l+"0")@+else append_char(l-10+"a") + +@= +for k:=0 to 255 do + begin if (@) then + begin append_char("^"); append_char("^"); + if k<@'100 then append_char(k+@'100) + else if k<@'200 then append_char(k-@'100) + else begin app_lc_hex(k div 16); app_lc_hex(k mod 16); + end; + end + else append_char(k); + g:=make_string; + end + +@ The first 128 strings will contain 95 standard ASCII characters, and the +other 33 characters will be printed in three-symbol form like `\.{\^\^A}' +unless a system-dependent change is made here. Installations that have +an extended character set, where for example |xchr[@'32]=@t\.{\'^^Z\'}@>|, +would like string @'32 to be the single character @'32 instead of the +three characters @'136, @'136, @'132 (\.{\^\^Z}). On the other hand, +even people with an extended character set will want to represent string +@'15 by \.{\^\^M}, since @'15 is |carriage_return|; the idea is to +produce visible strings instead of tabs or line-feeds or carriage-returns +or bell-rings or characters that are treated anomalously in text files. + +Unprintable characters of codes 128--255 are, similarly, rendered +\.{\^\^80}--\.{\^\^ff}. + +The boolean expression defined here should be |true| unless \TeX\ +internal code number~|k| corresponds to a non-troublesome visible +symbol in the local character set. An appropriate formula for the +extended character set recommended in {\sl The \TeX book\/} would, for +example, be `|k in [0,@'10..@'12,@'14,@'15,@'33,@'177..@'377]|'. +If character |k| cannot be printed, and |k<@'200|, then character |k+@'100| or +|k-@'100| must be printable; moreover, ASCII codes |[@'41..@'46, +@'60..@'71, @'141..@'146, @'160..@'171]| must be printable. +Thus, at least 80 printable characters are needed. +@:TeXbook}{\sl The \TeX book@> +@^character set dependencies@> +@^system dependencies@> + +@= + (k<" ")or(k>"~") + +@ When the \.{WEB} system program called \.{TANGLE} processes the \.{TEX.WEB} +description that you are now reading, it outputs the \PASCAL\ program +\.{TEX.PAS} and also a string pool file called \.{TEX.POOL}. The \.{INITEX} +@.WEB@>@.INITEX@> +program reads the latter file, where each string appears as a two-digit decimal +length followed by the string itself, and the information is recorded in +\TeX's string memory. + +@= +@!init @!pool_file:alpha_file; {the string-pool file output by \.{TANGLE}} +tini + +@ @d bad_pool(#)==begin wake_up_terminal; write_ln(term_out,#); + a_close(pool_file); get_strings_started:=false; return; + end +@= +name_of_file:=pool_name; {we needn't set |name_length|} +if a_open_in(pool_file) then + begin c:=false; + repeat @; + until c; + a_close(pool_file); get_strings_started:=true; + end +else bad_pool('! I can''t read TEX.POOL.') +@.I can't read TEX.POOL@> + +@ @= +begin if eof(pool_file) then bad_pool('! TEX.POOL has no check sum.'); +@.TEX.POOL has no check sum@> +read(pool_file,m,n); {read two digits of string length} +if m='*' then @ +else begin if (xord[m]<"0")or(xord[m]>"9")or@| + (xord[n]<"0")or(xord[n]>"9") then + bad_pool('! TEX.POOL line doesn''t begin with two digits.'); +@.TEX.POOL line doesn't...@> + l:=xord[m]*10+xord[n]-"0"*11; {compute the length} + if pool_ptr+l+string_vacancies>pool_size then + bad_pool('! You have to increase POOLSIZE.'); +@.You have to increase POOLSIZE@> + for k:=1 to l do + begin if eoln(pool_file) then m:=' '@+else read(pool_file,m); + append_char(xord[m]); + end; + read_ln(pool_file); g:=make_string; + end; +end + +@ The \.{WEB} operation \.{@@\$} denotes the value that should be at the +end of this \.{TEX.POOL} file; any other value means that the wrong pool +file has been loaded. +@^check sum@> + +@= +begin a:=0; k:=1; +loop@+ begin if (xord[n]<"0")or(xord[n]>"9") then + bad_pool('! TEX.POOL check sum doesn''t have nine digits.'); +@.TEX.POOL check sum...@> + a:=10*a+xord[n]-"0"; + if k=9 then goto done; + incr(k); read(pool_file,n); + end; +done: if a<>@$ then bad_pool('! TEX.POOL doesn''t match; TANGLE me again.'); +@.TEX.POOL doesn't match@> +c:=true; +end + +@* \[5] On-line and off-line printing. +Messages that are sent to a user's terminal and to the transcript-log file +are produced by several `|print|' procedures. These procedures will +direct their output to a variety of places, based on the setting of +the global variable |selector|, which has the following possible +values: + +\yskip +\hang |term_and_log|, the normal setting, prints on the terminal and on the + transcript file. + +\hang |log_only|, prints only on the transcript file. + +\hang |term_only|, prints only on the terminal. + +\hang |no_print|, doesn't print at all. This is used only in rare cases + before the transcript file is open. + +\hang |pseudo|, puts output into a cyclic buffer that is used + by the |show_context| routine; when we get to that routine we shall discuss + the reasoning behind this curious mode. + +\hang |new_string|, appends the output to the current string in the + string pool. + +\hang 0 to 15, prints on one of the sixteen files for \.{\\write} output. + +\yskip +\noindent The symbolic names `|term_and_log|', etc., have been assigned +numeric codes that satisfy the convenient relations |no_print+1=term_only|, +|no_print+2=log_only|, |term_only+2=log_only+1=term_and_log|. + +Three additional global variables, |tally| and |term_offset| and +|file_offset|, record the number of characters that have been printed +since they were most recently cleared to zero. We use |tally| to record +the length of (possibly very long) stretches of printing; |term_offset| +and |file_offset|, on the other hand, keep track of how many characters +have appeared so far on the current line that has been output to the +terminal or to the transcript file, respectively. + +@d no_print=16 {|selector| setting that makes data disappear} +@d term_only=17 {printing is destined for the terminal only} +@d log_only=18 {printing is destined for the transcript file only} +@d term_and_log=19 {normal |selector| setting} +@d pseudo=20 {special |selector| setting for |show_context|} +@d new_string=21 {printing is deflected to the string pool} +@d max_selector=21 {highest selector setting} + +@= +@!log_file : alpha_file; {transcript of \TeX\ session} +@!selector : 0..max_selector; {where to print a message} +@!dig : array[0..22] of 0..15; {digits in a number being output} +@!tally : integer; {the number of characters recently printed} +@!term_offset : 0..max_print_line; + {the number of characters on the current terminal line} +@!file_offset : 0..max_print_line; + {the number of characters on the current file line} +@!trick_buf:array[0..error_line] of ASCII_code; {circular buffer for + pseudoprinting} +@!trick_count: integer; {threshold for pseudoprinting, explained later} +@!first_count: integer; {another variable for pseudoprinting} + +@ @= +selector:=term_only; tally:=0; term_offset:=0; file_offset:=0; + +@ Macro abbreviations for output to the terminal and to the log file are +defined here for convenience. Some systems need special conventions +for terminal output, and it is possible to adhere to those conventions +by changing |wterm|, |wterm_ln|, and |wterm_cr| in this section. +@^system dependencies@> + +@d wterm(#)==write(term_out,#) +@d wterm_ln(#)==write_ln(term_out,#) +@d wterm_cr==write_ln(term_out) +@d wlog(#)==write(log_file,#) +@d wlog_ln(#)==write_ln(log_file,#) +@d wlog_cr==write_ln(log_file) + +@ To end a line of text output, we call |print_ln|. + +@= +procedure print_ln; {prints an end-of-line} +begin case selector of +term_and_log: begin wterm_cr; wlog_cr; + term_offset:=0; file_offset:=0; + end; +log_only: begin wlog_cr; file_offset:=0; + end; +term_only: begin wterm_cr; term_offset:=0; + end; +no_print,pseudo,new_string: do_nothing; +othercases write_ln(write_file[selector]) +endcases;@/ +end; {|tally| is not affected} + +@ The |print_char| procedure sends one character to the desired destination, +using the |xchr| array to map it into an external character compatible with +|input_ln|. All printing comes through |print_ln| or |print_char|. + +@= +procedure print_char(@!s:ASCII_code); {prints a single character} +label exit; +begin if @ then + if selector + +@= +procedure print(@!s:integer); {prints string |s|} +label exit; +var j:pool_pointer; {current character code position} +@!nl:integer; {new-line character to restore} +begin if s>=str_ptr then s:="???" {this can't happen} +@.???@> +else if s<256 then + if s<0 then s:="???" {can't happen} + else begin if selector>pseudo then + begin print_char(s); return; {internal strings are not expanded} + end; + if (@) then + if selector= +procedure slow_print(@!s:integer); {prints string |s|} +var j:pool_pointer; {current character code position} +begin if (s>=str_ptr) or (s<256) then print(s) +else begin j:=str_start[s]; + while j= +wterm(banner); +if format_ident=0 then wterm_ln(' (no format preloaded)') +else begin slow_print(format_ident); print_ln; + end; +update_terminal; + +@ The procedure |print_nl| is like |print|, but it makes sure that the +string appears at the beginning of a new line. + +@= +procedure print_nl(@!s:str_number); {prints string |s| at beginning of line} +begin if ((term_offset>0)and(odd(selector)))or@| + ((file_offset>0)and(selector>=log_only)) then print_ln; +print(s); +end; + +@ The procedure |print_esc| prints a string that is preceded by +the user's escape character (which is usually a backslash). + +@= +procedure print_esc(@!s:str_number); {prints escape character, then |s|} +var c:integer; {the escape character code} +begin @; +if c>=0 then if c<256 then print(c); +slow_print(s); +end; + +@ An array of digits in the range |0..15| is printed by |print_the_digs|. + +@= +procedure print_the_digs(@!k:eight_bits); + {prints |dig[k-1]|$\,\ldots\,$|dig[0]|} +begin while k>0 do + begin decr(k); + if dig[k]<10 then print_char("0"+dig[k]) + else print_char("A"-10+dig[k]); + end; +end; + +@ The following procedure, which prints out the decimal representation of a +given integer |n|, has been written carefully so that it works properly +if |n=0| or if |(-n)| would cause overflow. It does not apply |mod| or |div| +to negative arguments, since such operations are not implemented consistently +by all \PASCAL\ compilers. + +@= +procedure print_int(@!n:integer); {prints an integer in decimal form} +var k:0..23; {index to current digit; we assume that $|n|<10^{23}$} +@!m:integer; {used to negate |n| in possibly dangerous cases} +begin k:=0; +if n<0 then + begin print_char("-"); + if n>-100000000 then negate(n) + else begin m:=-1-n; n:=m div 10; m:=(m mod 10)+1; k:=1; + if m<10 then dig[0]:=m + else begin dig[0]:=0; incr(n); + end; + end; + end; +repeat dig[k]:=n mod 10; n:=n div 10; incr(k); +until n=0; +print_the_digs(k); +end; + +@ Here is a trivial procedure to print two digits; it is usually called with +a parameter in the range |0<=n<=99|. + +@p procedure print_two(@!n:integer); {prints two least significant digits} +begin n:=abs(n) mod 100; print_char("0"+(n div 10)); +print_char("0"+(n mod 10)); +end; + +@ Hexadecimal printing of nonnegative integers is accomplished by |print_hex|. + +@p procedure print_hex(@!n:integer); + {prints a positive integer in hexadecimal form} +var k:0..22; {index to current digit; we assume that $0\L n<16^{22}$} +begin k:=0; print_char(""""); +repeat dig[k]:=n mod 16; n:=n div 16; incr(k); +until n=0; +print_the_digs(k); +end; + +@ Old versions of \TeX\ needed a procedure called |print_ASCII| whose function +is now subsumed by |print|. We retain the old name here as a possible aid to +future software arch\ae ologists. + +@d print_ASCII == print + +@ Roman numerals are produced by the |print_roman_int| routine. Readers +who like puzzles might enjoy trying to figure out how this tricky code +works; therefore no explanation will be given. Notice that 1990 yields +\.{mcmxc}, not \.{mxm}. + +@p procedure print_roman_int(@!n:integer); +label exit; +var j,@!k: pool_pointer; {mysterious indices into |str_pool|} +@!u,@!v: nonnegative_integer; {mysterious numbers} +begin j:=str_start["m2d5c2l5x2v5i"]; v:=1000; +loop@+ begin while n>=v do + begin print_char(so(str_pool[j])); n:=n-v; + end; + if n<=0 then return; {nonpositive input produces no output} + k:=j+2; u:=v div (so(str_pool[k-1])-"0"); + if str_pool[k-1]=si("2") then + begin k:=k+2; u:=u div (so(str_pool[k-1])-"0"); + end; + if n+u>=v then + begin print_char(so(str_pool[k])); n:=n+u; + end + else begin j:=j+2; v:=v div (so(str_pool[j-1])-"0"); + end; + end; +exit:end; + +@ The |print| subroutine will not print a string that is still being +created. The following procedure will. + +@p procedure print_current_string; {prints a yet-unmade string} +var j:pool_pointer; {points to current character code} +begin j:=str_start[str_ptr]; +while j +term_offset:=0; {the user's line ended with \<\rm return>} +decr(selector); {prepare to echo the input} +if last<>first then for k:=first to last-1 do print(buffer[k]); +print_ln; incr(selector); {restore previous status} +end; + +@* \[6] Reporting errors. +When something anomalous is detected, \TeX\ typically does something like this: +$$\vbox{\halign{#\hfil\cr +|print_err("Something anomalous has been detected");|\cr +|help3("This is the first line of my offer to help.")|\cr +|("This is the second line. I'm trying to")|\cr +|("explain the best way for you to proceed.");|\cr +|error;|\cr}}$$ +A two-line help message would be given using |help2|, etc.; these informal +helps should use simple vocabulary that complements the words used in the +official error message that was printed. (Outside the U.S.A., the help +messages should preferably be translated into the local vernacular. Each +line of help is at most 60 characters long, in the present implementation, +so that |max_print_line| will not be exceeded.) + +The |print_err| procedure supplies a `\.!' before the official message, +and makes sure that the terminal is awake if a stop is going to occur. +The |error| procedure supplies a `\..' after the official message, then it +shows the location of the error; and if |interaction=error_stop_mode|, +it also enters into a dialog with the user, during which time the help +message may be printed. +@^system dependencies@> + +@ The global variable |interaction| has four settings, representing increasing +amounts of user interaction: + +@d batch_mode=0 {omits all stops and omits terminal output} +@d nonstop_mode=1 {omits all stops} +@d scroll_mode=2 {omits error stops} +@d error_stop_mode=3 {stops at every opportunity to interact} +@d print_err(#)==begin if interaction=error_stop_mode then wake_up_terminal; + print_nl("! "); print(#); + end + +@= +@!interaction:batch_mode..error_stop_mode; {current level of interaction} + +@ @=interaction:=error_stop_mode; + +@ \TeX\ is careful not to call |error| when the print |selector| setting +might be unusual. The only possible values of |selector| at the time of +error messages are + +\yskip\hang|no_print| (when |interaction=batch_mode| + and |log_file| not yet open); + +\hang|term_only| (when |interaction>batch_mode| and |log_file| not yet open); + +\hang|log_only| (when |interaction=batch_mode| and |log_file| is open); + +\hang|term_and_log| (when |interaction>batch_mode| and |log_file| is open). + +@= +if interaction=batch_mode then selector:=no_print@+else selector:=term_only + +@ A global variable |deletions_allowed| is set |false| if the |get_next| +routine is active when |error| is called; this ensures that |get_next| +and related routines like |get_token| will never be called recursively. +A similar interlock is provided by |set_box_allowed|. +@^recursion@> + +The global variable |history| records the worst level of error that +has been detected. It has four possible values: |spotless|, |warning_issued|, +|error_message_issued|, and |fatal_error_stop|. + +Another global variable, |error_count|, is increased by one when an +|error| occurs without an interactive dialog, and it is reset to zero at +the end of every paragraph. If |error_count| reaches 100, \TeX\ decides +that there is no point in continuing further. + +@d spotless=0 {|history| value when nothing has been amiss yet} +@d warning_issued=1 {|history| value when |begin_diagnostic| has been called} +@d error_message_issued=2 {|history| value when |error| has been called} +@d fatal_error_stop=3 {|history| value when termination was premature} + +@= +@!deletions_allowed:boolean; {is it safe for |error| to call |get_token|?} +@!set_box_allowed:boolean; {is it safe to do a \.{\\setbox} assignment?} +@!history:spotless..fatal_error_stop; {has the source input been clean so far?} +@!error_count:-1..100; {the number of scrolled errors since the + last paragraph ended} + +@ The value of |history| is initially |fatal_error_stop|, but it will +be changed to |spotless| if \TeX\ survives the initialization process. + +@= +deletions_allowed:=true; set_box_allowed:=true; +error_count:=0; {|history| is initialized elsewhere} + +@ Since errors can be detected almost anywhere in \TeX, we want to declare the +error procedures near the beginning of the program. But the error procedures +in turn use some other procedures, which need to be declared |forward| +before we get to |error| itself. + +It is possible for |error| to be called recursively if some error arises +when |get_token| is being used to delete a token, and/or if some fatal error +occurs while \TeX\ is trying to fix a non-fatal one. But such recursion +@^recursion@> +is never more than two levels deep. + +@= +procedure@?normalize_selector; forward;@t\2@>@/ +procedure@?get_token; forward;@t\2@>@/ +procedure@?term_input; forward;@t\2@>@/ +procedure@?show_context; forward;@t\2@>@/ +procedure@?begin_file_reading; forward;@t\2@>@/ +procedure@?open_log_file; forward;@t\2@>@/ +procedure@?close_files_and_terminate; forward;@t\2@>@/ +procedure@?clear_for_error_prompt; forward;@t\2@>@/ +procedure@?give_err_help; forward;@t\2@>@/ +@t\4\hskip-\fontdimen2\font@>@;@+@!debug@+procedure@?debug_help; + forward;@;@+gubed + +@ Individual lines of help are recorded in the array |help_line|, which +contains entries in positions |0..(help_ptr-1)|. They should be printed +in reverse order, i.e., with |help_line[0]| appearing last. + +@d hlp1(#)==help_line[0]:=#;@+end +@d hlp2(#)==help_line[1]:=#; hlp1 +@d hlp3(#)==help_line[2]:=#; hlp2 +@d hlp4(#)==help_line[3]:=#; hlp3 +@d hlp5(#)==help_line[4]:=#; hlp4 +@d hlp6(#)==help_line[5]:=#; hlp5 +@d help0==help_ptr:=0 {sometimes there might be no help} +@d help1==@+begin help_ptr:=1; hlp1 {use this with one help line} +@d help2==@+begin help_ptr:=2; hlp2 {use this with two help lines} +@d help3==@+begin help_ptr:=3; hlp3 {use this with three help lines} +@d help4==@+begin help_ptr:=4; hlp4 {use this with four help lines} +@d help5==@+begin help_ptr:=5; hlp5 {use this with five help lines} +@d help6==@+begin help_ptr:=6; hlp6 {use this with six help lines} + +@= +@!help_line:array[0..5] of str_number; {helps for the next |error|} +@!help_ptr:0..6; {the number of help lines present} +@!use_err_help:boolean; {should the |err_help| list be shown?} + +@ @= +help_ptr:=0; use_err_help:=false; + +@ The |jump_out| procedure just cuts across all active procedure levels and +goes to |end_of_TEX|. This is the only nontrivial |@!goto| statement in the +whole program. It is used when there is no recovery from a particular error. + +Some \PASCAL\ compilers do not implement non-local |goto| statements. +@^system dependencies@> +In such cases the body of |jump_out| should simply be +`|close_files_and_terminate|;\thinspace' followed by a call on some system +procedure that quietly terminates the program. + +@= +procedure jump_out; +begin goto end_of_TEX; +end; + +@ Here now is the general |error| routine. + +@= +procedure error; {completes the job of error reporting} +label continue,exit; +var c:ASCII_code; {what the user types} +@!s1,@!s2,@!s3,@!s4:integer; + {used to save global variables when deleting tokens} +begin if history; +incr(error_count); +if error_count=100 then + begin print_nl("(That makes 100 errors; please try again.)"); +@.That makes 100 errors...@> + history:=fatal_error_stop; jump_out; + end; +@; +exit:end; + +@ @= +loop@+begin continue: clear_for_error_prompt; prompt_input("? "); +@.?\relax@> + if last=first then return; + c:=buffer[first]; + if c>="a" then c:=c+"A"-"a"; {convert to uppercase} + @; + end + +@ It is desirable to provide an `\.E' option here that gives the user +an easy way to return from \TeX\ to the system editor, with the offending +line ready to be edited. But such an extension requires some system +wizardry, so the present implementation simply types out the name of the +file that should be +edited and the relevant line number. +@^system dependencies@> + +There is a secret `\.D' option available when the debugging routines haven't +been commented~out. +@^debugging@> + +@= +case c of +"0","1","2","3","4","5","6","7","8","9": if deletions_allowed then + @; +@t\4\4@>@;@+@!debug "D": begin debug_help; goto continue;@+end;@+gubed@/ +"E": if base_ptr>0 then + begin print_nl("You want to edit file "); +@.You want to edit file x@> + slow_print(input_stack[base_ptr].name_field); + print(" at line "); print_int(line); + interaction:=scroll_mode; jump_out; + end; +"H": @; +"I":@; +"Q","R","S":@; +"X":begin interaction:=scroll_mode; jump_out; + end; +othercases do_nothing +endcases;@/ +@ + +@ @= +begin print("Type to proceed, S to scroll future error messages,");@/ +@.Type to proceed...@> +print_nl("R to run without stopping, Q to run quietly,");@/ +print_nl("I to insert something, "); +if base_ptr>0 then print("E to edit your file,"); +if deletions_allowed then + print_nl("1 or ... or 9 to ignore the next 1 to 9 tokens of input,"); +print_nl("H for help, X to quit."); +end + +@ Here the author of \TeX\ apologizes for making use of the numerical +relation between |"Q"|, |"R"|, |"S"|, and the desired interaction settings +|batch_mode|, |nonstop_mode|, |scroll_mode|. +@^Knuth, Donald Ervin@> + +@= +begin error_count:=0; interaction:=batch_mode+c-"Q"; +print("OK, entering "); +case c of +"Q":begin print_esc("batchmode"); decr(selector); + end; +"R":print_esc("nonstopmode"); +"S":print_esc("scrollmode"); +end; {there are no other cases} +print("..."); print_ln; update_terminal; return; +end + +@ When the following code is executed, |buffer[(first+1)..(last-1)]| may +contain the material inserted by the user; otherwise another prompt will +be given. In order to understand this part of the program fully, you need +to be familiar with \TeX's input stacks. + +@= +begin begin_file_reading; {enter a new syntactic level for terminal input} +{now |state=mid_line|, so an initial blank space will count as a blank} +if last>first+1 then + begin loc:=first+1; buffer[first]:=" "; + end +else begin prompt_input("insert>"); loc:=first; +@.insert>@> + end; +first:=last; +cur_input.limit_field:=last-1; {no |end_line_char| ends this line} +return; +end + +@ We allow deletion of up to 99 tokens at a time. + +@= +begin s1:=cur_tok; s2:=cur_cmd; s3:=cur_chr; s4:=align_state; +align_state:=1000000; OK_to_interrupt:=false; +if (last>first+1) and (buffer[first+1]>="0")and(buffer[first+1]<="9") then + c:=c*10+buffer[first+1]-"0"*11 +else c:=c-"0"; +while c>0 do + begin get_token; {one-level recursive call of |error| is possible} + decr(c); + end; +cur_tok:=s1; cur_cmd:=s2; cur_chr:=s3; align_state:=s4; OK_to_interrupt:=true; +help2("I have just deleted some text, as you asked.")@/ +("You can now delete more, or insert, or whatever."); +show_context; goto continue; +end + +@ @= +begin if use_err_help then + begin give_err_help; use_err_help:=false; + end +else begin if help_ptr=0 then + help2("Sorry, I don't know how to help in this situation.")@/ + @t\kern1em@>("Maybe you should try asking a human?"); + repeat decr(help_ptr); print(help_line[help_ptr]); print_ln; + until help_ptr=0; + end; +help4("Sorry, I already gave what help I could...")@/ + ("Maybe you should try asking a human?")@/ + ("An error might have occurred before I noticed any problems.")@/ + ("``If all else fails, read the instructions.''");@/ +goto continue; +end + +@ @= +if interaction>batch_mode then decr(selector); {avoid terminal output} +if use_err_help then + begin print_ln; give_err_help; + end +else while help_ptr>0 do + begin decr(help_ptr); print_nl(help_line[help_ptr]); + end; +print_ln; +if interaction>batch_mode then incr(selector); {re-enable terminal output} +print_ln + +@ A dozen or so error messages end with a parenthesized integer, so we +save a teeny bit of program space by declaring the following procedure: + +@p procedure int_error(@!n:integer); +begin print(" ("); print_int(n); print_char(")"); error; +end; + +@ In anomalous cases, the print selector might be in an unknown state; +the following subroutine is called to fix things just enough to keep +running a bit longer. + +@p procedure normalize_selector; +begin if log_opened then selector:=term_and_log +else selector:=term_only; +if job_name=0 then open_log_file; +if interaction=batch_mode then decr(selector); +end; + +@ The following procedure prints \TeX's last words before dying. + +@d succumb==begin if interaction=error_stop_mode then + interaction:=scroll_mode; {no more interaction} + if log_opened then error; + @!debug if interaction>batch_mode then debug_help;@+gubed@;@/ + history:=fatal_error_stop; jump_out; {irrecoverable error} + end + +@= +procedure fatal_error(@!s:str_number); {prints |s|, and that's it} +begin normalize_selector;@/ +print_err("Emergency stop"); help1(s); succumb; +@.Emergency stop@> +end; + +@ Here is the most dreaded error message. + +@= +procedure overflow(@!s:str_number;@!n:integer); {stop due to finiteness} +begin normalize_selector; +print_err("TeX capacity exceeded, sorry ["); +@.TeX capacity exceeded ...@> +print(s); print_char("="); print_int(n); print_char("]"); +help2("If you really absolutely need more capacity,")@/ + ("you can ask a wizard to enlarge me."); +succumb; +end; + +@ The program might sometime run completely amok, at which point there is +no choice but to stop. If no previous error has been detected, that's bad +news; a message is printed that is really intended for the \TeX\ +maintenance person instead of the user (unless the user has been +particularly diabolical). The index entries for `this can't happen' may +help to pinpoint the problem. +@^dry rot@> + +@= +procedure confusion(@!s:str_number); + {consistency check violated; |s| tells where} +begin normalize_selector; +if history + help1("I'm broken. Please show this to someone who can fix can fix"); + end +else begin print_err("I can't go on meeting you like this"); +@.I can't go on...@> + help2("One of your faux pas seems to have wounded me deeply...")@/ + ("in fact, I'm barely conscious. Please fix it and try again."); + end; +succumb; +end; + +@ Users occasionally want to interrupt \TeX\ while it's running. +If the \PASCAL\ runtime system allows this, one can implement +a routine that sets the global variable |interrupt| to some nonzero value +when such an interrupt is signalled. Otherwise there is probably at least +a way to make |interrupt| nonzero using the \PASCAL\ debugger. +@^system dependencies@> +@^debugging@> + +@d check_interrupt==begin if interrupt<>0 then pause_for_instructions; + end + +@= +@!interrupt:integer; {should \TeX\ pause for instructions?} +@!OK_to_interrupt:boolean; {should interrupts be observed?} + +@ @= +interrupt:=0; OK_to_interrupt:=true; + +@ When an interrupt has been detected, the program goes into its +highest interaction level and lets the user have nearly the full flexibility of +the |error| routine. \TeX\ checks for interrupts only at times when it is +safe to do this. + +@p procedure pause_for_instructions; +begin if OK_to_interrupt then + begin interaction:=error_stop_mode; + if (selector=log_only)or(selector=no_print) then + incr(selector); + print_err("Interruption"); +@.Interruption@> + help3("You rang?")@/ + ("Try to insert some instructions for me (e.g.,`I\showlists'),")@/ + ("unless you just want to quit by typing `X'."); + deletions_allowed:=false; error; deletions_allowed:=true; + interrupt:=0; + end; +end; + +@* \[7] Arithmetic with scaled dimensions. +The principal computations performed by \TeX\ are done entirely in terms of +integers less than $2^{31}$ in magnitude; and divisions are done only when both +dividend and divisor are nonnegative. Thus, the arithmetic specified in this +program can be carried out in exactly the same way on a wide variety of +computers, including some small ones. Why? Because the arithmetic +calculations need to be spelled out precisely in order to guarantee that +\TeX\ will produce identical output on different machines. If some +quantities were rounded differently in different implementations, we would +find that line breaks and even page breaks might occur in different places. +Hence the arithmetic of \TeX\ has been designed with care, and systems that +claim to be implementations of \TeX82 should follow precisely the +@:TeX82}{\TeX82@> +calculations as they appear in the present program. + +(Actually there are three places where \TeX\ uses |div| with a possibly negative +numerator. These are harmless; see |div| in the index. Also if the user +sets the \.{\\time} or the \.{\\year} to a negative value, some diagnostic +information will involve negative-numerator division. The same remarks +apply for |mod| as well as for |div|.) + +@ Here is a routine that calculates half of an integer, using an +unambiguous convention with respect to signed odd numbers. + +@p function half(@!x:integer):integer; +begin if odd(x) then half:=(x+1) div 2 +else half:=x @!div 2; +end; + +@ Fixed-point arithmetic is done on {\sl scaled integers\/} that are multiples +of $2^{-16}$. In other words, a binary point is assumed to be sixteen bit +positions from the right end of a binary computer word. + +@d unity == @'200000 {$2^{16}$, represents 1.00000} +@d two == @'400000 {$2^{17}$, represents 2.00000} + +@= +@!scaled = integer; {this type is used for scaled integers} +@!nonnegative_integer=0..@'17777777777; {$0\L x<2^{31}$} +@!small_number=0..63; {this type is self-explanatory} + +@ The following function is used to create a scaled integer from a given decimal +fraction $(.d_0d_1\ldots d_{k-1})$, where |0<=k<=17|. The digit $d_i$ is +given in |dig[i]|, and the calculation produces a correctly rounded result. + +@p function round_decimals(@!k:small_number) : scaled; + {converts a decimal fraction} +var a:integer; {the accumulator} +begin a:=0; +while k>0 do + begin decr(k); a:=(a+dig[k]*two) div 10; + end; +round_decimals:=(a+1) div 2; +end; + +@ Conversely, here is a procedure analogous to |print_int|. If the output +of this procedure is subsequently read by \TeX\ and converted by the +|round_decimals| routine above, it turns out that the original value will +be reproduced exactly; the ``simplest'' such decimal number is output, +but there is always at least one digit following the decimal point. + +The invariant relation in the \&{repeat} loop is that a sequence of +decimal digits yet to be printed will yield the original number if and only if +they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}funity then s:=s+@'100000-50000; {round the last digit} +print_char("0"+(s div unity)); s:=10*(s mod unity); delta:=delta*10; +until s<=delta; +end; + +@ Physical sizes that a \TeX\ user specifies for portions of documents are +represented internally as scaled points. Thus, if we define an `sp' (scaled +@^sp@> +point) as a unit equal to $2^{-16}$ printer's points, every dimension +inside of \TeX\ is an integer number of sp. There are exactly +4,736,286.72 sp per inch. Users are not allowed to specify dimensions +larger than $2^{30}-1$ sp, which is a distance of about 18.892 feet (5.7583 +meters); two such quantities can be added without overflow on a 32-bit +computer. + +The present implementation of \TeX\ does not check for overflow when +@^Overflow in arithmetic@> +dimensions are added or subtracted. This could be done by inserting a +few dozen tests of the form `\ignorespaces|if x>=@'10000000000 then +@t\\{report\_overflow}@>|', but the chance of overflow is so remote that +such tests do not seem worthwhile. + +\TeX\ needs to do only a few arithmetic operations on scaled quantities, +other than addition and subtraction, and the following subroutines do most of +the work. A single computation might use several subroutine calls, and it is +desirable to avoid producing multiple error messages in case of arithmetic +overflow; so the routines set the global variable |arith_error| to |true| +instead of reporting errors directly to the user. Another global variable, +|remainder|, holds the remainder after a division. + +@= +@!arith_error:boolean; {has arithmetic overflow occurred recently?} +@!remainder:scaled; {amount subtracted to get an exact division} + +@ The first arithmetical subroutine we need computes $nx+y$, where |x| +and~|y| are |scaled| and |n| is an integer. We will also use it to +multiply integers. + +@d nx_plus_y(#)==mult_and_add(#,@'7777777777) +@d mult_integers(#)==mult_and_add(#,0,@'17777777777) + +@p function mult_and_add(@!n:integer;@!x,@!y,@!max_answer:scaled):scaled; +begin if n<0 then + begin negate(x); negate(n); + end; +if n=0 then mult_and_add:=y +else if ((x<=(max_answer-y) div n)and(-x<=(max_answer+y) div n)) then + mult_and_add:=n*x+y +else begin arith_error:=true; mult_and_add:=0; + end; +end; + +@ We also need to divide scaled dimensions by integers. + +@p function x_over_n(@!x:scaled;@!n:integer):scaled; +var negative:boolean; {should |remainder| be negated?} +begin negative:=false; +if n=0 then + begin arith_error:=true; x_over_n:=0; remainder:=x; + end +else begin if n<0 then + begin negate(x); negate(n); negative:=true; + end; + if x>=0 then + begin x_over_n:=x div n; remainder:=x mod n; + end + else begin x_over_n:=-((-x) div n); remainder:=-((-x) mod n); + end; + end; +if negative then negate(remainder); +end; + +@ Then comes the multiplication of a scaled number by a fraction |n/d|, +where |n| and |d| are nonnegative integers |<=@t$2^{16}$@>| and |d| is +positive. It would be too dangerous to multiply by~|n| and then divide +by~|d|, in separate operations, since overflow might well occur; and it +would be too inaccurate to divide by |d| and then multiply by |n|. Hence +this subroutine simulates 1.5-precision arithmetic. + +@p function xn_over_d(@!x:scaled; @!n,@!d:integer):scaled; +var positive:boolean; {was |x>=0|?} +@!t,@!u,@!v:nonnegative_integer; {intermediate quantities} +begin if x>=0 then positive:=true +else begin negate(x); positive:=false; + end; +t:=(x mod @'100000)*n; +u:=(x div @'100000)*n+(t div @'100000); +v:=(u mod d)*@'100000 + (t mod @'100000); +if u div d>=@'100000 then arith_error:=true +else u:=@'100000*(u div d) + (v div d); +if positive then + begin xn_over_d:=u; remainder:=v mod d; + end +else begin xn_over_d:=-u; remainder:=-(v mod d); + end; +end; + +@ The next subroutine is used to compute the ``badness'' of glue, when a +total~|t| is supposed to be made from amounts that sum to~|s|. According +to {\sl The \TeX book}, the badness of this situation is $100(t/s)^3$; +however, badness is simply a heuristic, so we need not squeeze out the +last drop of accuracy when computing it. All we really want is an +approximation that has similar properties. +@:TeXbook}{\sl The \TeX book@> + +The actual method used to compute the badness is easier to read from the +program than to describe in words. It produces an integer value that is a +reasonably close approximation to $100(t/s)^3$, and all implementations +of \TeX\ should use precisely this method. Any badness of $2^{13}$ or more is +treated as infinitely bad, and represented by 10000. + +It is not difficult to prove that $$\hbox{|badness(t+1,s)>=badness(t,s) +>=badness(t,s+1)|}.$$ The badness function defined here is capable of +computing at most 1095 distinct values, but that is plenty. + +@d inf_bad = 10000 {infinitely bad value} + +@p function badness(@!t,@!s:scaled):halfword; {compute badness, given |t>=0|} +var r:integer; {approximation to $\alpha t/s$, where $\alpha^3\approx + 100\cdot2^{18}$} +begin if t=0 then badness:=0 +else if s<=0 then badness:=inf_bad +else begin if t<=7230584 then r:=(t*297) div s {$297^3=99.94\times2^{18}$} + else if s>=1663497 then r:=t div (s div 297) + else r:=t; + if r>1290 then badness:=inf_bad {$1290^3<2^{31}<1291^3$} + else badness:=(r*r*r+@'400000) div @'1000000; + end; {that was $r^3/2^{18}$, rounded to the nearest integer} +end; + +@ When \TeX\ ``packages'' a list into a box, it needs to calculate the +proportionality ratio by which the glue inside the box should stretch +or shrink. This calculation does not affect \TeX's decision making, +so the precise details of rounding, etc., in the glue calculation are not +of critical importance for the consistency of results on different computers. + +We shall use the type |glue_ratio| for such proportionality ratios. +A glue ratio should take the same amount of memory as an +|integer| (usually 32 bits) if it is to blend smoothly with \TeX's +other data structures. Thus |glue_ratio| should be equivalent to +|short_real| in some implementations of \PASCAL. Alternatively, +it is possible to deal with glue ratios using nothing but fixed-point +arithmetic; see {\sl TUGboat \bf3},1 (March 1982), 10--27. (But the +routines cited there must be modified to allow negative glue ratios.) +@^system dependencies@> + +@d set_glue_ratio_zero(#) == #:=0.0 {store the representation of zero ratio} +@d set_glue_ratio_one(#) == #:=1.0 {store the representation of unit ratio} +@d float(#) == # {convert from |glue_ratio| to type |real|} +@d unfloat(#) == # {convert from |real| to type |glue_ratio|} +@d float_constant(#) == #.0 {convert |integer| constant to |real|} + +@= +@!glue_ratio=real; {one-word representation of a glue expansion factor} + +@* \[7b] Random numbers. + +\font\tenlogo=logo10 % font used for the METAFONT logo +\def\MP{{\tenlogo META}\-{\tenlogo POST}} + +This section is (almost) straight from MetaPost. I had to change +the types (use |integer| instead of |fraction|), but that should +not have any influence on the actual calculations (the original +comments refer to quantities like |fraction_four| ($2^{30}$), and +that is the same as the numeric representation of |maxdimen|). + +I've copied the low-level variables and routines that are needed, but +only those (e.g. |m_log|), not the accompanying ones like |m_exp|. Most +of the following low-level numeric routines are only needed within the +calculation of |norm_rand|. I've been forced to rename |make_fraction| +to |make_frac| because TeX already has a routine by that name with +a wholly different function (it creates a |fraction_noad| for math +typesetting) -- Taco + +And now let's complete our collection of numeric utility routines +by considering random number generation. +\MP\ generates pseudo-random numbers with the additive scheme recommended +in Section 3.6 of {\sl The Art of Computer Programming}; however, the +results are random fractions between 0 and |fraction_one-1|, inclusive. + +There's an auxiliary array |randoms| that contains 55 pseudo-random +fractions. Using the recurrence $x_n=(x_{n-55}-x_{n-31})\bmod 2^{28}$, +we generate batches of 55 new $x_n$'s at a time by calling |new_randoms|. +The global variable |j_random| tells which element has most recently +been consumed. + +@= +@!randoms:array[0..54] of integer; {the last 55 random values generated} +@!j_random:0..54; {the number of unused |randoms|} +@!random_seed:scaled; {the default random seed} + +@ A small bit of metafont is needed. + +@d fraction_half==@'1000000000 {$2^{27}$, represents 0.50000000} +@d fraction_one==@'2000000000 {$2^{28}$, represents 1.00000000} +@d fraction_four==@'10000000000 {$2^{30}$, represents 4.00000000} +@d el_gordo == @'17777777777 {$2^{31}-1$, the largest value that \MP\ likes} +@d halfp(#)==(#) div 2 +@d double(#) == #:=#+# {multiply a variable by two} + +@ The |make_frac| routine produces the |fraction| equivalent of +|p/q|, given integers |p| and~|q|; it computes the integer +$f=\lfloor2^{28}p/q+{1\over2}\rfloor$, when $p$ and $q$ are +positive. If |p| and |q| are both of the same scaled type |t|, +the ``type relation'' |make_frac(t,t)=fraction| is valid; +and it's also possible to use the subroutine ``backwards,'' using +the relation |make_frac(t,fraction)=t| between scaled types. + +If the result would have magnitude $2^{31}$ or more, |make_frac| +sets |arith_error:=true|. Most of \MP's internal computations have +been designed to avoid this sort of error. + +If this subroutine were programmed in assembly language on a typical +machine, we could simply compute |(@t$2^{28}$@>*p)div q|, since a +double-precision product can often be input to a fixed-point division +instruction. But when we are restricted to \PASCAL\ arithmetic it +is necessary either to resort to multiple-precision maneuvering +or to use a simple but slow iteration. The multiple-precision technique +would be about three times faster than the code adopted here, but it +would be comparatively long and tricky, involving about sixteen +additional multiplications and divisions. + +This operation is part of \MP's ``inner loop''; indeed, it will +consume nearly 10\pct! of the running time (exclusive of input and output) +if the code below is left unchanged. A machine-dependent recoding +will therefore make \MP\ run faster. The present implementation +is highly portable, but slow; it avoids multiplication and division +except in the initial stage. System wizards should be careful to +replace it with a routine that is guaranteed to produce identical +results in all cases. +@^system dependencies@> + +As noted below, a few more routines should also be replaced by machine-dependent +code, for efficiency. But when a procedure is not part of the ``inner loop,'' +such changes aren't advisable; simplicity and robustness are +preferable to trickery, unless the cost is too high. +@^inner loop@> + +@p function make_frac(@!p,@!q:integer):integer; +var @!f:integer; {the fraction bits, with a leading 1 bit} +@!n:integer; {the integer part of $\vert p/q\vert$} +@!negative:boolean; {should the result be negated?} +@!be_careful:integer; {disables certain compiler optimizations} +begin if p>=0 then negative:=false +else begin negate(p); negative:=true; + end; +if q<=0 then + begin debug if q=0 then confusion("/");@;@+gubed@;@/ +@:this can't happen /}{\quad \./@> + negate(q); negative:=not negative; + end; +n:=p div q; p:=p mod q; +if n>=8 then + begin arith_error:=true; + if negative then make_frac:=-el_gordo@+else make_frac:=el_gordo; + end +else begin n:=(n-1)*fraction_one; + @; + if negative then make_frac:=-(f+n)@+else make_frac:=f+n; + end; +end; + +@ The |repeat| loop here preserves the following invariant relations +between |f|, |p|, and~|q|: +(i)~|0<=p + +@= +f:=1; +repeat be_careful:=p-q; p:=be_careful+p; +if p>=0 then f:=f+f+1 +else begin double(f); p:=p+q; + end; +until f>=fraction_one; +be_careful:=p-q; +if be_careful+p>=0 then incr(f) + +@ + +@p function take_frac(@!q:integer;@!f:integer):integer; +var @!p:integer; {the fraction so far} +@!negative:boolean; {should the result be negated?} +@!n:integer; {additional multiple of $q$} +@!be_careful:integer; {disables certain compiler optimizations} +begin @=0| and |q>0|@>; +if f; +be_careful:=n-el_gordo; +if be_careful+p>0 then + begin arith_error:=true; n:=el_gordo-p; + end; +if negative then take_frac:=-(n+p) +else take_frac:=n+p; +end; + +@ @=0| and |q>0|@>= +if f>=0 then negative:=false +else begin negate(f); negative:=true; + end; +if q<0 then + begin negate(q); negative:=not negative; + end; + +@ The invariant relations in this case are (i)~$\lfloor(qf+p)/2^k\rfloor +=\lfloor qf_0/2^{28}+{1\over2}\rfloor$, where $k$ is an integer and +$f_0$ is the original value of~$f$; (ii)~$2^k\L f<2^{k+1}$. +@^inner loop@> + +@= +p:=fraction_half; {that's $2^{27}$; the invariants hold now with $k=28$} +if q= +@!two_to_the:array[0..30] of integer; {powers of two} +@!spec_log:array[1..28] of integer; {special logarithms} + + +@ @= +two_to_the[0]:=1; +for k:=1 to 30 do two_to_the[k]:=2*two_to_the[k-1]; +spec_log[1]:=93032640; +spec_log[2]:=38612034; +spec_log[3]:=17922280; +spec_log[4]:=8662214; +spec_log[5]:=4261238; +spec_log[6]:=2113709; +spec_log[7]:=1052693; +spec_log[8]:=525315; +spec_log[9]:=262400; +spec_log[10]:=131136; +spec_log[11]:=65552; +spec_log[12]:=32772; +spec_log[13]:=16385; +for k:=14 to 27 do spec_log[k]:=two_to_the[27-k]; +spec_log[28]:=1; + +@ + +@p function m_log(@!x:integer):integer; +var @!y,@!z:integer; {auxiliary registers} +@!k:integer; {iteration counter} +begin if x<=0 then @ +else begin y:=1302456956+4-100; {$14\times2^{27}\ln2\approx1302456956.421063$} + z:=27595+6553600; {and $2^{16}\times .421063\approx 27595$} + while xfraction_four+4 do + @; + m_log:=y div 8; + end; +end; + +@ @= +begin z:=((x-1) div two_to_the[k])+1; {$z=\lceil x/2^k\rceil$} +while x= +begin print_err("Logarithm of "); +@.Logarithm...replaced by 0@> +print_scaled(x); print(" has been replaced by 0"); +help2("Since I don't take logs of non-positive numbers,")@/ + ("I'm zeroing this one. Proceed, with fingers crossed."); +error; m_log:=0; +end + +@ The following somewhat different subroutine tests rigorously if $ab$ is +greater than, equal to, or less than~$cd$, +given integers $(a,b,c,d)$. In most cases a quick decision is reached. +The result is $+1$, 0, or~$-1$ in the three respective cases. + +@d return_sign(#)==begin ab_vs_cd:=#; return; + end + +@p function ab_vs_cd(@!a,b,c,d:integer):integer; +label exit; +var @!q,@!r:integer; {temporary registers} +begin @=0|, |b,d>0|@>; +loop@+ begin q := a div d; r := c div b; + if q<>r then + if q>r then return_sign(1)@+else return_sign(-1); + q := a mod d; r := c mod b; + if r=0 then + if q=0 then return_sign(0)@+else return_sign(1); + if q=0 then return_sign(-1); + a:=b; b:=q; c:=d; d:=r; + end; {now |a>d>0| and |c>b>0|} +exit:end; + +@ @= +if a<0 then + begin negate(a); negate(b); + end; +if c<0 then + begin negate(c); negate(d); + end; +if d<=0 then + begin if b>=0 then + if ((a=0)or(b=0))and((c=0)or(d=0)) then return_sign(0) + else return_sign(1); + if d=0 then + if a=0 then return_sign(0)@+else return_sign(-1); + q:=a; a:=c; c:=q; q:=-b; b:=-d; d:=q; + end +else if b<=0 then + begin if b<0 then if a>0 then return_sign(-1); + if c=0 then return_sign(0) else return_sign(-1); + end + +@ To consume a random integer, the program below will say `|next_random|' +and then it will fetch |randoms[j_random]|. + +@d next_random==if j_random=0 then new_randoms + else decr(j_random) + +@p procedure new_randoms; +var @!k:0..54; {index into |randoms|} +@!x:integer; {accumulator} +begin for k:=0 to 23 do + begin x:=randoms[k]-randoms[k+31]; + if x<0 then x:=x+fraction_one; + randoms[k]:=x; + end; +for k:=24 to 54 do + begin x:=randoms[k]-randoms[k-24]; + if x<0 then x:=x+fraction_one; + randoms[k]:=x; + end; +j_random:=54; +end; + +@ To initialize the |randoms| table, we call the following routine. + +@p procedure init_randoms(@!seed:integer); +var @!j,@!jj,@!k:integer; {more or less random integers} +@!i:0..54; {index into |randoms|} +begin j:=abs(seed); +while j>=fraction_one do j:=halfp(j); +k:=1; +for i:=0 to 54 do + begin jj:=k; k:=j-k; j:=jj; + if k<0 then k:=k+fraction_one; + randoms[(i*21)mod 55]:=j; + end; +new_randoms; new_randoms; new_randoms; {``warm up'' the array} +end; + +@ To produce a uniform random number in the range |0<=u=u>x| +or |0=u=x|, given a |scaled| value~|x|, we proceed as shown here. + +Note that the call of |take_frac| will produce the values 0 and~|x| +with about half the probability that it will produce any other particular +values between 0 and~|x|, because it rounds its answers. + +@p function unif_rand(@!x:integer):integer; +var @!y:integer; {trial value} +begin next_random; y:=take_frac(abs(x),randoms[j_random]); +if y=abs(x) then unif_rand:=0 +else if x>0 then unif_rand:=y +else unif_rand:=-y; +end; + +@ Finally, a normal deviate with mean zero and unit standard deviation +can readily be obtained with the ratio method (Algorithm 3.4.1R in +{\sl The Art of Computer Programming\/}). + +@p function norm_rand:integer; +var @!x,@!u,@!l:integer; {what the book would call $2^{16}X$, $2^{28}U$, + and $-2^{24}\ln U$} +begin repeat + repeat next_random; + x:=take_frac(112429,randoms[j_random]-fraction_half); + {$2^{16}\sqrt{8/e}\approx 112428.82793$} + next_random; u:=randoms[j_random]; + until abs(x)=0; +norm_rand:=x; +end; + +@* \[8] Packed data. +In order to make efficient use of storage space, \TeX\ bases its major data +structures on a |memory_word|, which contains either a (signed) integer, +possibly scaled, or a (signed) |glue_ratio|, or a small number of +fields that are one half or one quarter of the size used for storing +integers. + +If |x| is a variable of type |memory_word|, it contains up to four +fields that can be referred to as follows: +$$\vbox{\halign{\hfil#&#\hfil&#\hfil\cr +|x|&.|int|&(an |integer|)\cr +|x|&.|sc|\qquad&(a |scaled| integer)\cr +|x|&.|gr|&(a |glue_ratio|)\cr +|x.hh.lh|, |x.hh|&.|rh|&(two halfword fields)\cr +|x.hh.b0|, |x.hh.b1|, |x.hh|&.|rh|&(two quarterword fields, one halfword + field)\cr +|x.qqqq.b0|, |x.qqqq.b1|, |x.qqqq|&.|b2|, |x.qqqq.b3|\hskip-100pt + &\qquad\qquad\qquad(four quarterword fields)\cr}}$$ +This is somewhat cumbersome to write, and not very readable either, but +macros will be used to make the notation shorter and more transparent. +The \PASCAL\ code below gives a formal definition of |memory_word| and +its subsidiary types, using packed variant records. \TeX\ makes no +assumptions about the relative positions of the fields within a word. + +Since we are assuming 32-bit integers, a halfword must contain at least +16 bits, and a quarterword must contain at least 8 bits. +@^system dependencies@> +But it doesn't hurt to have more bits; for example, with enough 36-bit +words you might be able to have |mem_max| as large as 262142, which is +eight times as much memory as anybody had during the first four years of +\TeX's existence. + +N.B.: Valuable memory space will be dreadfully wasted unless \TeX\ is compiled +by a \PASCAL\ that packs all of the |memory_word| variants into +the space of a single integer. This means, for example, that |glue_ratio| +words should be |short_real| instead of |real| on some computers. Some +\PASCAL\ compilers will pack an integer whose subrange is `|0..255|' into +an eight-bit field, but others insist on allocating space for an additional +sign bit; on such systems you can get 256 values into a quarterword only +if the subrange is `|-128..127|'. + +The present implementation tries to accommodate as many variations as possible, +so it makes few assumptions. If integers having the subrange +`|min_quarterword..max_quarterword|' can be packed into a quarterword, +and if integers having the subrange `|min_halfword..max_halfword|' +can be packed into a halfword, everything should work satisfactorily. + +It is usually most efficient to have |min_quarterword=min_halfword=0|, +so one should try to achieve this unless it causes a severe problem. +The values defined here are recommended for most 32-bit computers. + +@d min_quarterword=0 {smallest allowable value in a |quarterword|} +@d max_quarterword=255 {largest allowable value in a |quarterword|} +@d min_halfword==0 {smallest allowable value in a |halfword|} +@d max_halfword==65535 {largest allowable value in a |halfword|} + +@ Here are the inequalities that the quarterword and halfword values +must satisfy (or rather, the inequalities that they mustn't satisfy): + +@= +init if (mem_min<>mem_bot)or(mem_max<>mem_top) then bad:=10;@+tini@;@/ +if (mem_min>mem_bot)or(mem_max0)or(max_quarterword<127) then bad:=11; +if (min_halfword>0)or(max_halfword<32767) then bad:=12; +if (min_quarterwordmax_halfword) then bad:=13; +if (mem_min=max_halfword)or@| + (mem_bot-mem_min>max_halfword+1) then bad:=14; +if (font_basemax_quarterword) then bad:=15; +if font_max>font_base+256 then bad:=16; +if (save_size>max_halfword)or(max_strings>max_halfword) then bad:=17; +if buf_size>max_halfword then bad:=18; +if max_quarterword-min_quarterword<255 then bad:=19; + +@ The operation of adding or subtracting |min_quarterword| occurs quite +frequently in \TeX, so it is convenient to abbreviate this operation +by using the macros |qi| and |qo| for input and output to and from +quarterword format. + +The inner loop of \TeX\ will run faster with respect to compilers +that don't optimize expressions like `|x+0|' and `|x-0|', if these +macros are simplified in the obvious way when |min_quarterword=0|. +@^inner loop@>@^system dependencies@> + +@d qi(#)==#+min_quarterword + {to put an |eight_bits| item into a quarterword} +@d qo(#)==#-min_quarterword + {to take an |eight_bits| item out of a quarterword} +@d hi(#)==#+min_halfword + {to put a sixteen-bit item into a halfword} +@d ho(#)==#-min_halfword + {to take a sixteen-bit item from a halfword} + +@ The reader should study the following definitions closely: +@^system dependencies@> + +@d sc==int {|scaled| data is equivalent to |integer|} + +@= +@!quarterword = min_quarterword..max_quarterword; {1/4 of a word} +@!halfword=min_halfword..max_halfword; {1/2 of a word} +@!two_choices = 1..2; {used when there are two variants in a record} +@!four_choices = 1..4; {used when there are four variants in a record} +@!two_halves = packed record@;@/ + @!rh:halfword; + case two_choices of + 1: (@!lh:halfword); + 2: (@!b0:quarterword; @!b1:quarterword); + end; +@!four_quarters = packed record@;@/ + @!b0:quarterword; + @!b1:quarterword; + @!b2:quarterword; + @!b3:quarterword; + end; +@!memory_word = record@;@/ + case four_choices of + 1: (@!int:integer); + 2: (@!gr:glue_ratio); + 3: (@!hh:two_halves); + 4: (@!qqqq:four_quarters); + end; +@!word_file = file of memory_word; + +@ When debugging, we may want to print a |memory_word| without knowing +what type it is; so we print it in all modes. +@^dirty \PASCAL@>@^debugging@> + +@p @!debug procedure print_word(@!w:memory_word); + {prints |w| in all ways} +begin print_int(w.int); print_char(" ");@/ +print_scaled(w.sc); print_char(" ");@/ +print_scaled(round(unity*float(w.gr))); print_ln;@/ +@^real multiplication@> +print_int(w.hh.lh); print_char("="); print_int(w.hh.b0); print_char(":"); +print_int(w.hh.b1); print_char(";"); print_int(w.hh.rh); print_char(" ");@/ +print_int(w.qqqq.b0); print_char(":"); print_int(w.qqqq.b1); print_char(":"); +print_int(w.qqqq.b2); print_char(":"); print_int(w.qqqq.b3); +end; +gubed + +@* \[9] Dynamic memory allocation. +The \TeX\ system does nearly all of its own memory allocation, so that it +can readily be transported into environments that do not have automatic +facilities for strings, garbage collection, etc., and so that it can be in +control of what error messages the user receives. The dynamic storage +requirements of \TeX\ are handled by providing a large array |mem| in +which consecutive blocks of words are used as nodes by the \TeX\ routines. + +Pointer variables are indices into this array, or into another array +called |eqtb| that will be explained later. A pointer variable might +also be a special flag that lies outside the bounds of |mem|, so we +allow pointers to assume any |halfword| value. The minimum halfword +value represents a null pointer. \TeX\ does not assume that |mem[null]| exists. + +@d pointer==halfword {a flag or a location in |mem| or |eqtb|} +@d null==min_halfword {the null pointer} + +@= +@!temp_ptr:pointer; {a pointer variable for occasional emergency use} + +@ The |mem| array is divided into two regions that are allocated separately, +but the dividing line between these two regions is not fixed; they grow +together until finding their ``natural'' size in a particular job. +Locations less than or equal to |lo_mem_max| are used for storing +variable-length records consisting of two or more words each. This region +is maintained using an algorithm similar to the one described in exercise +2.5--19 of {\sl The Art of Computer Programming}. However, no size field +appears in the allocated nodes; the program is responsible for knowing the +relevant size when a node is freed. Locations greater than or equal to +|hi_mem_min| are used for storing one-word records; a conventional +\.{AVAIL} stack is used for allocation in this region. + +Locations of |mem| between |mem_bot| and |mem_top| may be dumped as part +of preloaded format files, by the \.{INITEX} preprocessor. +@.INITEX@> +Production versions of \TeX\ may extend the memory at both ends in order to +provide more space; locations between |mem_min| and |mem_bot| are always +used for variable-size nodes, and locations between |mem_top| and |mem_max| +are always used for single-word nodes. + +The key pointers that govern |mem| allocation have a prescribed order: +$$\advance\thickmuskip-2mu +\hbox{|null<=mem_min<=mem_bot= +@!mem : array[mem_min..mem_max] of memory_word; {the big dynamic storage area} +@!lo_mem_max : pointer; {the largest location of variable-size memory in use} +@!hi_mem_min : pointer; {the smallest location of one-word memory in use} + +@ In order to study the memory requirements of particular applications, it +is possible to prepare a version of \TeX\ that keeps track of current and +maximum memory usage. When code between the delimiters |@!stat| $\ldots$ +|tats| is not ``commented out,'' \TeX\ will run a bit slower but it will +report these statistics when |tracing_stats| is sufficiently large. + +@= +@!var_used, @!dyn_used : integer; {how much memory is in use} + +@ Let's consider the one-word memory region first, since it's the +simplest. The pointer variable |mem_end| holds the highest-numbered location +of |mem| that has ever been used. The free locations of |mem| that +occur between |hi_mem_min| and |mem_end|, inclusive, are of type +|two_halves|, and we write |info(p)| and |link(p)| for the |lh| +and |rh| fields of |mem[p]| when it is of this type. The single-word +free locations form a linked list +$$|avail|,\;\hbox{|link(avail)|},\;\hbox{|link(link(avail))|},\;\ldots$$ +terminated by |null|. + +@d link(#) == mem[#].hh.rh {the |link| field of a memory word} +@d info(#) == mem[#].hh.lh {the |info| field of a memory word} + +@= +@!avail : pointer; {head of the list of available one-word nodes} +@!mem_end : pointer; {the last one-word node used in |mem|} + +@ If memory is exhausted, it might mean that the user has forgotten +a right brace. We will define some procedures later that try to help +pinpoint the trouble. + +@p @@/ +@ + +@ The function |get_avail| returns a pointer to a new one-word node whose +|link| field is null. However, \TeX\ will halt if there is no more room left. +@^inner loop@> + +If the available-space list is empty, i.e., if |avail=null|, +we try first to increase |mem_end|. If that cannot be done, i.e., if +|mem_end=mem_max|, we try to decrease |hi_mem_min|. If that cannot be +done, i.e., if |hi_mem_min=lo_mem_max+1|, we have to quit. + +@p function get_avail : pointer; {single-word node allocation} +var p:pointer; {the new node being got} +begin p:=avail; {get top location in the |avail| stack} +if p<>null then avail:=link(avail) {and pop it off} +else if mem_end + end; + end; +link(p):=null; {provide an oft-desired initialization of the new node} +@!stat incr(dyn_used);@+tats@;{maintain statistics} +get_avail:=p; +end; + +@ Conversely, a one-word node is recycled by calling |free_avail|. +This routine is part of \TeX's ``inner loop,'' so we want it to be fast. +@^inner loop@> + +@d free_avail(#)== {single-word node liberation} + begin link(#):=avail; avail:=#; + @!stat decr(dyn_used);@+tats@/ + end + +@ There's also a |fast_get_avail| routine, which saves the procedure-call +overhead at the expense of extra programming. This routine is used in +the places that would otherwise account for the most calls of |get_avail|. +@^inner loop@> + +@d fast_get_avail(#)==@t@>@;@/ + begin #:=avail; {avoid |get_avail| if possible, to save time} + if #=null then #:=get_avail + else begin avail:=link(#); link(#):=null; + @!stat incr(dyn_used);@+tats@/ + end; + end + +@ The procedure |flush_list(p)| frees an entire linked list of +one-word nodes that starts at position |p|. +@^inner loop@> + +@p procedure flush_list(@!p:pointer); {makes list of single-word nodes + available} +var @!q,@!r:pointer; {list traversers} +begin if p<>null then + begin r:=p; + repeat q:=r; r:=link(r); @!stat decr(dyn_used);@+tats@/ + until r=null; {now |q| is the last node on the list} + link(q):=avail; avail:=p; + end; +end; + +@ The available-space list that keeps track of the variable-size portion +of |mem| is a nonempty, doubly-linked circular list of empty nodes, +pointed to by the roving pointer |rover|. + +Each empty node has size 2 or more; the first word contains the special +value |max_halfword| in its |link| field and the size in its |info| field; +the second word contains the two pointers for double linking. + +Each nonempty node also has size 2 or more. Its first word is of type +|two_halves|\kern-1pt, and its |link| field is never equal to |max_halfword|. +Otherwise there is complete flexibility with respect to the contents +of its other fields and its other words. + +(We require |mem_max= +@!rover : pointer; {points to some node in the list of empties} + +@ A call to |get_node| with argument |s| returns a pointer to a new node +of size~|s|, which must be 2~or more. The |link| field of the first word +of this new node is set to null. An overflow stop occurs if no suitable +space exists. + +If |get_node| is called with $s=2^{30}$, it simply merges adjacent free +areas and returns the value |max_halfword|. + +@p function get_node(@!s:integer):pointer; {variable-size node allocation} +label found,exit,restart; +var p:pointer; {the node currently under inspection} +@!q:pointer; {the node physically after node |p|} +@!r:integer; {the newly allocated node, or a candidate for this honor} +@!t:integer; {temporary register} +begin restart: p:=rover; {start at some free node in the ring} +repeat @; +@^inner loop@> +p:=rlink(p); {move to the next node in the ring} +until p=rover; {repeat until the whole list has been traversed} +if s=@'10000000000 then + begin get_node:=max_halfword; return; + end; +if lo_mem_max+2; +overflow("main memory size",mem_max+1-mem_min); + {sorry, nothing satisfactory is left} +@:TeX capacity exceeded main memory size}{\quad main memory size@> +found: link(r):=null; {this node is now nonempty} +@!stat var_used:=var_used+s; {maintain usage statistics} +tats@;@/ +get_node:=r; +exit:end; + +@ The lower part of |mem| grows by 1000 words at a time, unless +we are very close to going under. When it grows, we simply link +a new node into the available-space list. This method of controlled +growth helps to keep the |mem| usage consecutive when \TeX\ is +implemented on ``virtual memory'' systems. +@^virtual memory@> + +@= +begin if hi_mem_min-lo_mem_max>=1998 then t:=lo_mem_max+1000 +else t:=lo_mem_max+1+(hi_mem_min-lo_mem_max) div 2; + {|lo_mem_max+2<=tmem_bot+max_halfword then t:=mem_bot+max_halfword; +rlink(q):=rover; llink(q):=p; link(q):=empty_flag; node_size(q):=t-lo_mem_max;@/ +lo_mem_max:=t; link(lo_mem_max):=null; info(lo_mem_max):=null; +rover:=q; goto restart; +end + +@ Empirical tests show that the routine in this section performs a +node-merging operation about 0.75 times per allocation, on the average, +after which it finds that |r>p+1| about 95\pct! of the time. + +@= +q:=p+node_size(p); {find the physical successor} +@^inner loop@> +while is_empty(q) do {merge node |p| with node |q|} + begin t:=rlink(q); + if q=rover then rover:=t; + llink(t):=llink(q); rlink(llink(q)):=t;@/ + q:=q+node_size(q); + end; +r:=q-s; +if r>p+1 then @; +if r=p then if rlink(p)<>p then + @; +node_size(p):=q-p {reset the size in case it grew} + +@ @= +begin node_size(p):=r-p; {store the remaining size} +@^inner loop@> +rover:=p; {start searching here next time} +goto found; +end + +@ Here we delete node |p| from the ring, and let |rover| rove around. + +@= +begin rover:=rlink(p); t:=llink(p); +llink(rover):=t; rlink(t):=rover; +goto found; +end + +@ Conversely, when some variable-size node |p| of size |s| is no longer needed, +the operation |free_node(p,s)| will make its words available, by inserting +|p| as a new empty node just before where |rover| now points. +@^inner loop@> + +@p procedure free_node(@!p:pointer; @!s:halfword); {variable-size node + liberation} +var q:pointer; {|llink(rover)|} +begin node_size(p):=s; link(p):=empty_flag; +q:=llink(rover); llink(p):=q; rlink(p):=rover; {set both links} +llink(rover):=p; rlink(q):=p; {insert |p| into the ring} +@!stat var_used:=var_used-s;@+tats@;{maintain statistics} +end; + +@ Just before \.{INITEX} writes out the memory, it sorts the doubly linked +available space list. The list is probably very short at such times, so a +simple insertion sort is used. The smallest available location will be +pointed to by |rover|, the next-smallest by |rlink(rover)|, etc. + +@p @!init procedure sort_avail; {sorts the available variable-size nodes + by location} +var p,@!q,@!r: pointer; {indices into |mem|} +@!old_rover:pointer; {initial |rover| setting} +begin p:=get_node(@'10000000000); {merge adjacent free areas} +p:=rlink(rover); rlink(rover):=max_halfword; old_rover:=rover; +while p<>old_rover do @; +p:=rover; +while rlink(p)<>max_halfword do + begin llink(rlink(p)):=p; p:=rlink(p); + end; +rlink(p):=rover; llink(rover):=p; +end; +tini + +@ The following |while| loop is guaranteed to +terminate, since the list that starts at +|rover| ends with |max_halfword| during the sorting procedure. + +@= +if p@^Chinese characters@>@^Japanese characters@> +and styles of type. It is suggested that Chinese and Japanese fonts be +handled by representing such characters in two consecutive |char_node| +entries: The first of these has |font=font_base|, and its |link| points +to the second; +the second identifies the font and the character dimensions. +The saving feature about oriental characters is that most of them have +the same box dimensions. The |character| field of the first |char_node| +is a ``\\{charext}'' that distinguishes between graphic symbols whose +dimensions are identical for typesetting purposes. (See the \MF\ manual.) +Such an extension of \TeX\ would not be difficult; further details are +left to the reader. + +In order to make sure that the |character| code fits in a quarterword, +\TeX\ adds the quantity |min_quarterword| to the actual code. + +Character nodes appear only in horizontal lists, never in vertical lists. + +@d is_char_node(#) == (#>=hi_mem_min) + {does the argument point to a |char_node|?} +@d font == type {the font code in a |char_node|} +@d character == subtype {the character code in a |char_node|} + +@ An |hlist_node| stands for a box that was made from a horizontal list. +Each |hlist_node| is seven words long, and contains the following fields +(in addition to the mandatory |type| and |link|, which we shall not +mention explicitly when discussing the other node types): The |height| and +|width| and |depth| are scaled integers denoting the dimensions of the +box. There is also a |shift_amount| field, a scaled integer indicating +how much this box should be lowered (if it appears in a horizontal list), +or how much it should be moved to the right (if it appears in a vertical +list). There is a |list_ptr| field, which points to the beginning of the +list from which this box was fabricated; if |list_ptr| is |null|, the box +is empty. Finally, there are three fields that represent the setting of +the glue: |glue_set(p)| is a word of type |glue_ratio| that represents +the proportionality constant for glue setting; |glue_sign(p)| is +|stretching| or |shrinking| or |normal| depending on whether or not the +glue should stretch or shrink or remain rigid; and |glue_order(p)| +specifies the order of infinity to which glue setting applies (|normal|, +|fil|, |fill|, or |filll|). The |subtype| field is not used. + +@d hlist_node=0 {|type| of hlist nodes} +@d box_node_size=7 {number of words to allocate for a box node} +@d width_offset=1 {position of |width| field in a box node} +@d depth_offset=2 {position of |depth| field in a box node} +@d height_offset=3 {position of |height| field in a box node} +@d width(#) == mem[#+width_offset].sc {width of the box, in sp} +@d depth(#) == mem[#+depth_offset].sc {depth of the box, in sp} +@d height(#) == mem[#+height_offset].sc {height of the box, in sp} +@d shift_amount(#) == mem[#+4].sc {repositioning distance, in sp} +@d list_offset=5 {position of |list_ptr| field in a box node} +@d list_ptr(#) == link(#+list_offset) {beginning of the list inside the box} +@d glue_order(#) == subtype(#+list_offset) {applicable order of infinity} +@d glue_sign(#) == type(#+list_offset) {stretching or shrinking} +@d normal=0 {the most common case when several cases are named} +@d stretching = 1 {glue setting applies to the stretch components} +@d shrinking = 2 {glue setting applies to the shrink components} +@d glue_offset = 6 {position of |glue_set| in a box node} +@d glue_set(#) == mem[#+glue_offset].gr + {a word of type |glue_ratio| for glue setting} + +@ The |new_null_box| function returns a pointer to an |hlist_node| in +which all subfields have the values corresponding to `\.{\\hbox\{\}}'. +The |subtype| field is set to |min_quarterword|, since that's the desired +|span_count| value if this |hlist_node| is changed to an |unset_node|. + +@p function new_null_box:pointer; {creates a new box node} +var p:pointer; {the new node} +begin p:=get_node(box_node_size); type(p):=hlist_node; +subtype(p):=min_quarterword; +width(p):=0; depth(p):=0; height(p):=0; shift_amount(p):=0; list_ptr(p):=null; +glue_sign(p):=normal; glue_order(p):=normal; set_glue_ratio_zero(glue_set(p)); +new_null_box:=p; +end; + +@ A |vlist_node| is like an |hlist_node| in all respects except that it +contains a vertical list. + +@d vlist_node=1 {|type| of vlist nodes} + +@ A |rule_node| stands for a solid black rectangle; it has |width|, +|depth|, and |height| fields just as in an |hlist_node|. However, if +any of these dimensions is $-2^{30}$, the actual value will be determined +by running the rule up to the boundary of the innermost enclosing box. +This is called a ``running dimension.'' The |width| is never running in +an hlist; the |height| and |depth| are never running in a~vlist. + +@d rule_node=2 {|type| of rule nodes} +@d rule_node_size=4 {number of words to allocate for a rule node} +@d null_flag==-@'10000000000 {$-2^{30}$, signifies a missing item} +@d is_running(#) == (#=null_flag) {tests for a running dimension} + +@ A new rule node is delivered by the |new_rule| function. It +makes all the dimensions ``running,'' so you have to change the +ones that are not allowed to run. + +@p function new_rule:pointer; +var p:pointer; {the new node} +begin p:=get_node(rule_node_size); type(p):=rule_node; +subtype(p):=0; {the |subtype| is not used} +width(p):=null_flag; depth(p):=null_flag; height(p):=null_flag; +new_rule:=p; +end; + +@ Insertions are represented by |ins_node| records, where the |subtype| +indicates the corresponding box number. For example, `\.{\\insert 250}' +leads to an |ins_node| whose |subtype| is |250+min_quarterword|. +The |height| field of an |ins_node| is slightly misnamed; it actually holds +the natural height plus depth of the vertical list being inserted. +The |depth| field holds the |split_max_depth| to be used in case this +insertion is split, and the |split_top_ptr| points to the corresponding +|split_top_skip|. The |float_cost| field holds the |floating_penalty| that +will be used if this insertion floats to a subsequent page after a +split insertion of the same class. There is one more field, the +|ins_ptr|, which points to the beginning of the vlist for the insertion. + +@d ins_node=3 {|type| of insertion nodes} +@d ins_node_size=5 {number of words to allocate for an insertion} +@d float_cost(#)==mem[#+1].int {the |floating_penalty| to be used} +@d ins_ptr(#)==info(#+4) {the vertical list to be inserted} +@d split_top_ptr(#)==link(#+4) {the |split_top_skip| to be used} + +@ A |mark_node| has a |mark_ptr| field that points to the reference count +of a token list that contains the user's \.{\\mark} text. +In addition there is a |mark_class| field that contains the mark class. + +@d mark_node=4 {|type| of a mark node} +@d small_node_size=2 {number of words to allocate for most node types} +@d mark_ptr(#)==link(#+1) {head of the token list for a mark} +@d mark_class(#)==info(#+1) {the mark class} + +@ An |adjust_node|, which occurs only in horizontal lists, +specifies material that will be moved out into the surrounding +vertical list; i.e., it is used to implement \TeX's `\.{\\vadjust}' +operation. The |adjust_ptr| field points to the vlist containing this +material. + +@d adjust_node=5 {|type| of an adjust node} +@d adjust_pre == subtype {pre-adjustment?} +@#{|append_list| is used to append a list to |tail|} +@d append_list(#) == begin link(tail) := link(#); append_list_end +@d append_list_end(#) == tail := #; end + +@d adjust_ptr(#)==mem[#+1].int + {vertical list to be moved out of horizontal list} + +@ A |ligature_node|, which occurs only in horizontal lists, specifies +a character that was fabricated from the interaction of two or more +actual characters. The second word of the node, which is called the +|lig_char| word, contains |font| and |character| fields just as in a +|char_node|. The characters that generated the ligature have not been +forgotten, since they are needed for diagnostic messages and for +hyphenation; the |lig_ptr| field points to a linked list of character +nodes for all original characters that have been deleted. (This list +might be empty if the characters that generated the ligature were +retained in other nodes.) + +The |subtype| field is 0, plus 2 and/or 1 if the original source of the +ligature included implicit left and/or right boundaries. + +@d ligature_node=6 {|type| of a ligature node} +@d lig_char(#)==#+1 {the word where the ligature is to be found} +@d lig_ptr(#)==link(lig_char(#)) {the list of characters} + +@ The |new_ligature| function creates a ligature node having given +contents of the |font|, |character|, and |lig_ptr| fields. We also have +a |new_lig_item| function, which returns a two-word node having a given +|character| field. Such nodes are used for temporary processing as ligatures +are being created. + +@p function new_ligature(@!f,@!c:quarterword; @!q:pointer):pointer; +var p:pointer; {the new node} +begin p:=get_node(small_node_size); type(p):=ligature_node; +font(lig_char(p)):=f; character(lig_char(p)):=c; lig_ptr(p):=q; +subtype(p):=0; new_ligature:=p; +end; +@# +function new_lig_item(@!c:quarterword):pointer; +var p:pointer; {the new node} +begin p:=get_node(small_node_size); character(p):=c; lig_ptr(p):=null; +new_lig_item:=p; +end; + +@ A |disc_node|, which occurs only in horizontal lists, specifies a +``dis\-cretion\-ary'' line break. If such a break occurs at node |p|, the text +that starts at |pre_break(p)| will precede the break, the text that starts at +|post_break(p)| will follow the break, and text that appears in the next +|replace_count(p)| nodes will be ignored. For example, an ordinary +discretionary hyphen, indicated by `\.{\\-}', yields a |disc_node| with +|pre_break| pointing to a |char_node| containing a hyphen, |post_break=null|, +and |replace_count=0|. All three of the discretionary texts must be +lists that consist entirely of character, kern, box, rule, and ligature nodes. + +If |pre_break(p)=null|, the |ex_hyphen_penalty| will be charged for this +break. Otherwise the |hyphen_penalty| will be charged. The texts will +actually be substituted into the list by the line-breaking algorithm if it +decides to make the break, and the discretionary node will disappear at +that time; thus, the output routine sees only discretionaries that were +not chosen. + +@d disc_node=7 {|type| of a discretionary node} +@d replace_count==subtype {how many subsequent nodes to replace} +@d pre_break==llink {text that precedes a discretionary break} +@d post_break==rlink {text that follows a discretionary break} + +@p function new_disc:pointer; {creates an empty |disc_node|} +var p:pointer; {the new node} +begin p:=get_node(small_node_size); type(p):=disc_node; +replace_count(p):=0; pre_break(p):=null; post_break(p):=null; +new_disc:=p; +end; + +@ A |whatsit_node| is a wild card reserved for extensions to \TeX. The +|subtype| field in its first word says what `\\{whatsit}' it is, and +implicitly determines the node size (which must be 2 or more) and the +format of the remaining words. When a |whatsit_node| is encountered +in a list, special actions are invoked; knowledgeable people who are +careful not to mess up the rest of \TeX\ are able to make \TeX\ do new +things by adding code at the end of the program. For example, there +might be a `\TeX nicolor' extension to specify different colors of ink, +@^extensions to \TeX@> +and the whatsit node might contain the desired parameters. + +The present implementation of \TeX\ treats the features associated with +`\.{\\write}' and `\.{\\special}' as if they were extensions, in order to +illustrate how such routines might be coded. We shall defer further +discussion of extensions until the end of this program. + +@d whatsit_node=8 {|type| of special extension nodes} + +@ A |math_node|, which occurs only in horizontal lists, appears before and +after mathematical formulas. The |subtype| field is |before| before the +formula and |after| after it. There is a |width| field, which represents +the amount of surrounding space inserted by \.{\\mathsurround}. + +In addition a |math_node| with |subtype>after| and |width=0| will be +(ab)used to record a regular |math_node| reinserted after being +discarded at a line break or one of the text direction primitives ( +\.{\\beginL}, \.{\\endL}, \.{\\beginR}, and \.{\\endR} ). + +@d math_node=9 {|type| of a math node} +@d before=0 {|subtype| for math node that introduces a formula} +@d after=1 {|subtype| for math node that winds up a formula} +@# +@d M_code=2 +@d begin_M_code=M_code+before {|subtype| for \.{\\beginM} node} +@d end_M_code=M_code+after {|subtype| for \.{\\endM} node} +@d L_code=4 +@d begin_L_code=L_code+begin_M_code {|subtype| for \.{\\beginL} node} +@d end_L_code=L_code+end_M_code {|subtype| for \.{\\endL} node} +@d R_code=L_code+L_code +@d begin_R_code=R_code+begin_M_code {|subtype| for \.{\\beginR} node} +@d end_R_code=R_code+end_M_code {|subtype| for \.{\\endR} node} +@# +@d end_LR(#)==odd(subtype(#)) +@d end_LR_type(#)==(L_code*(subtype(#) div L_code)+end_M_code) +@d begin_LR_type(#)==(#-after+before) + +@p function new_math(@!w:scaled;@!s:small_number):pointer; +var p:pointer; {the new node} +begin p:=get_node(small_node_size); type(p):=math_node; +subtype(p):=s; width(p):=w; new_math:=p; +end; + +@ \TeX\ makes use of the fact that |hlist_node|, |vlist_node|, +|rule_node|, |ins_node|, |mark_node|, |adjust_node|, |ligature_node|, +|disc_node|, |whatsit_node|, and |math_node| are at the low end of the +type codes, by permitting a break at glue in a list if and only if the +|type| of the previous node is less than |math_node|. Furthermore, a +node is discarded after a break if its type is |math_node| or~more. + +@d precedes_break(#)==(type(#) +representing |null| plus the number of glue nodes that point to it (less one). +Note that the reference count appears in the same position as +the |link| field in list nodes; this is the field that is initialized +to |null| when a node is allocated, and it is also the field that is flagged +by |empty_flag| in empty nodes. + +Glue specifications also contain three |scaled| fields, for the |width|, +|stretch|, and |shrink| dimensions. Finally, there are two one-byte +fields called |stretch_order| and |shrink_order|; these contain the +orders of infinity (|normal|, |fil|, |fill|, or |filll|) +corresponding to the stretch and shrink values. + +@d glue_spec_size=4 {number of words to allocate for a glue specification} +@d glue_ref_count(#) == link(#) {reference count of a glue specification} +@d stretch(#) == mem[#+2].sc {the stretchability of this glob of glue} +@d shrink(#) == mem[#+3].sc {the shrinkability of this glob of glue} +@d stretch_order == type {order of infinity for stretching} +@d shrink_order == subtype {order of infinity for shrinking} +@d fil=1 {first-order infinity} +@d fill=2 {second-order infinity} +@d filll=3 {third-order infinity} + +@= +@!glue_ord=normal..filll; {infinity to the 0, 1, 2, or 3 power} + +@ Here is a function that returns a pointer to a copy of a glue spec. +The reference count in the copy is |null|, because there is assumed +to be exactly one reference to the new specification. + +@p function new_spec(@!p:pointer):pointer; {duplicates a glue specification} +var q:pointer; {the new spec} +begin q:=get_node(glue_spec_size);@/ +mem[q]:=mem[p]; glue_ref_count(q):=null;@/ +width(q):=width(p); stretch(q):=stretch(p); shrink(q):=shrink(p); +new_spec:=q; +end; + +@ And here's a function that creates a glue node for a given parameter +identified by its code number; for example, +|new_param_glue(line_skip_code)| returns a pointer to a glue node for the +current \.{\\lineskip}. + +@p function new_param_glue(@!n:small_number):pointer; +var p:pointer; {the new node} +@!q:pointer; {the glue specification} +begin p:=get_node(small_node_size); type(p):=glue_node; subtype(p):=n+1; +leader_ptr(p):=null;@/ +q:=@@t@>; +glue_ptr(p):=q; incr(glue_ref_count(q)); +new_param_glue:=p; +end; + +@ Glue nodes that are more or less anonymous are created by |new_glue|, +whose argument points to a glue specification. + +@p function new_glue(@!q:pointer):pointer; +var p:pointer; {the new node} +begin p:=get_node(small_node_size); type(p):=glue_node; subtype(p):=normal; +leader_ptr(p):=null; glue_ptr(p):=q; incr(glue_ref_count(q)); +new_glue:=p; +end; + +@ Still another subroutine is needed: this one is sort of a combination +of |new_param_glue| and |new_glue|. It creates a glue node for one of +the current glue parameters, but it makes a fresh copy of the glue +specification, since that specification will probably be subject to change, +while the parameter will stay put. The global variable |temp_ptr| is +set to the address of the new spec. + +@p function new_skip_param(@!n:small_number):pointer; +var p:pointer; {the new node} +begin temp_ptr:=new_spec(@); +p:=new_glue(temp_ptr); glue_ref_count(temp_ptr):=null; subtype(p):=n+1; +new_skip_param:=p; +end; + +@ A |kern_node| has a |width| field to specify a (normally negative) +amount of spacing. This spacing correction appears in horizontal lists +between letters like A and V when the font designer said that it looks +better to move them closer together or further apart. A kern node can +also appear in a vertical list, when its `|width|' denotes additional +spacing in the vertical direction. The |subtype| is either |normal| (for +kerns inserted from font information or math mode calculations) or |explicit| +(for kerns inserted from \.{\\kern} and \.{\\/} commands) or |acc_kern| +(for kerns inserted from non-math accents) or |mu_glue| (for kerns +inserted from \.{\\mkern} specifications in math formulas). + +@d kern_node=11 {|type| of a kern node} +@d explicit=1 {|subtype| of kern nodes from \.{\\kern} and \.{\\/}} +@d acc_kern=2 {|subtype| of kern nodes from accents} + +@# {memory structure for marginal kerns} +@d margin_kern_node = 40 +@d margin_kern_node_size = 3 +@d margin_char(#) == info(# + 2) + +@# {|subtype| of marginal kerns} +@d left_side == 0 +@d right_side == 1 + +@# {base for lp/rp/ef codes starts from 2: + 0 for |hyphen_char|, + 1 for |skew_char|} +@d lp_code_base == 2 +@d rp_code_base == 3 +@d ef_code_base == 4 +@d tag_code == 5 +@d kn_bs_code_base == 7 +@d st_bs_code_base == 8 +@d sh_bs_code_base == 9 +@d kn_bc_code_base == 10 +@d kn_ac_code_base == 11 + +@d auto_kern == explicit + +@d no_lig_code == 6 + +@d max_hlist_stack = 512 {maximum fill level for |hlist_stack|} +{maybe good if larger than |2 * max_quarterword|, so that box nesting level would overflow first} + +@ The |new_kern| function creates a kern node having a given width. + +@p function new_kern(@!w:scaled):pointer; +var p:pointer; {the new node} +begin p:=get_node(small_node_size); type(p):=kern_node; +subtype(p):=normal; +width(p):=w; +new_kern:=p; +end; + +@ A |penalty_node| specifies the penalty associated with line or page +breaking, in its |penalty| field. This field is a fullword integer, but +the full range of integer values is not used: Any penalty |>=10000| is +treated as infinity, and no break will be allowed for such high values. +Similarly, any penalty |<=-10000| is treated as negative infinity, and a +break will be forced. + +@d penalty_node=12 {|type| of a penalty node} +@d inf_penalty=inf_bad {``infinite'' penalty value} +@d eject_penalty=-inf_penalty {``negatively infinite'' penalty value} +@d penalty(#) == mem[#+1].int {the added cost of breaking a list here} + +@ Anyone who has been reading the last few sections of the program will +be able to guess what comes next. + +@p function new_penalty(@!m:integer):pointer; +var p:pointer; {the new node} +begin p:=get_node(small_node_size); type(p):=penalty_node; +subtype(p):=0; {the |subtype| is not used} +penalty(p):=m; new_penalty:=p; +end; + +@ You might think that we have introduced enough node types by now. Well, +almost, but there is one more: An |unset_node| has nearly the same format +as an |hlist_node| or |vlist_node|; it is used for entries in \.{\\halign} +or \.{\\valign} that are not yet in their final form, since the box +dimensions are their ``natural'' sizes before any glue adjustment has been +made. The |glue_set| word is not present; instead, we have a |glue_stretch| +field, which contains the total stretch of order |glue_order| that is +present in the hlist or vlist being boxed. +Similarly, the |shift_amount| field is replaced by a |glue_shrink| field, +containing the total shrink of order |glue_sign| that is present. +The |subtype| field is called |span_count|; an unset box typically +contains the data for |qo(span_count)+1| columns. +Unset nodes will be changed to box nodes when alignment is completed. + +@d unset_node=13 {|type| for an unset node} +@d glue_stretch(#)==mem[#+glue_offset].sc {total stretch in an unset node} +@d glue_shrink==shift_amount {total shrink in an unset node} +@d span_count==subtype {indicates the number of spanned columns} + +@ In fact, there are still more types coming. When we get to math formula +processing we will see that a |style_node| has |type=14|; and a number +of larger type codes will also be defined, for use in math mode only. + +@ Warning: If any changes are made to these data structure layouts, such as +changing any of the node sizes or even reordering the words of nodes, +the |copy_node_list| procedure and the memory initialization code +below may have to be changed. Such potentially dangerous parts of the +program are listed in the index under `data structure assumptions'. +@!@^data structure assumptions@> +However, other references to the nodes are made symbolically in terms of +the \.{WEB} macro definitions above, so that format changes will leave +\TeX's other algorithms intact. +@^system dependencies@> + +@* \[11] Memory layout. +Some areas of |mem| are dedicated to fixed usage, since static allocation is +more efficient than dynamic allocation when we can get away with it. For +example, locations |mem_bot| to |mem_bot+3| are always used to store the +specification for glue that is `\.{0pt plus 0pt minus 0pt}'. The +following macro definitions accomplish the static allocation by giving +symbolic names to the fixed positions. Static variable-size nodes appear +in locations |mem_bot| through |lo_mem_stat_max|, and static single-word nodes +appear in locations |hi_mem_stat_min| through |mem_top|, inclusive. It is +harmless to let |lig_trick| and |garbage| share the same location of |mem|. + +@d zero_glue==mem_bot {specification for \.{0pt plus 0pt minus 0pt}} +@d fil_glue==zero_glue+glue_spec_size {\.{0pt plus 1fil minus 0pt}} +@d fill_glue==fil_glue+glue_spec_size {\.{0pt plus 1fill minus 0pt}} +@d ss_glue==fill_glue+glue_spec_size {\.{0pt plus 1fil minus 1fil}} +@d fil_neg_glue==ss_glue+glue_spec_size {\.{0pt plus -1fil minus 0pt}} +@d lo_mem_stat_max==fil_neg_glue+glue_spec_size-1 {largest statically + allocated word in the variable-size |mem|} +@# +@d page_ins_head==mem_top {list of insertion data for current page} +@d contrib_head==mem_top-1 {vlist of items not yet on current page} +@d page_head==mem_top-2 {vlist for current page} +@d temp_head==mem_top-3 {head of a temporary list of some kind} +@d hold_head==mem_top-4 {head of a temporary list of another kind} +@d adjust_head==mem_top-5 {head of adjustment list returned by |hpack|} +@d active==mem_top-7 {head of active list in |line_break|, needs two words} +@d align_head==mem_top-8 {head of preamble list for alignments} +@d end_span==mem_top-9 {tail of spanned-width lists} +@d omit_template==mem_top-10 {a constant token list} +@d null_list==mem_top-11 {permanently empty list} +@d lig_trick==mem_top-12 {a ligature masquerading as a |char_node|} +@d garbage==mem_top-12 {used for scrap information} +@d backup_head==mem_top-13 {head of token list built by |scan_keyword|} +@d pre_adjust_head==mem_top-14 {head of pre-adjustment list returned by |hpack|} +@d hi_mem_stat_min==mem_top-14 {smallest statically allocated word in + the one-word |mem|} +@d hi_mem_stat_usage=15 {the number of one-word nodes always present} + +@ The following code gets |mem| off to a good start, when \TeX\ is +initializing itself the slow~way. + +@= +@!k:integer; {index into |mem|, |eqtb|, etc.} + +@ @= +for k:=mem_bot+1 to lo_mem_stat_max do mem[k].sc:=0; + {all glue dimensions are zeroed} +@^data structure assumptions@> +k:=mem_bot;@+while k<=lo_mem_stat_max do + {set first words of glue specifications} + begin glue_ref_count(k):=null+1; + stretch_order(k):=normal; shrink_order(k):=normal; + k:=k+glue_spec_size; + end; +stretch(fil_glue):=unity; stretch_order(fil_glue):=fil;@/ +stretch(fill_glue):=unity; stretch_order(fill_glue):=fill;@/ +stretch(ss_glue):=unity; stretch_order(ss_glue):=fil;@/ +shrink(ss_glue):=unity; shrink_order(ss_glue):=fil;@/ +stretch(fil_neg_glue):=-unity; stretch_order(fil_neg_glue):=fil;@/ +rover:=lo_mem_stat_max+1; +link(rover):=empty_flag; {now initialize the dynamic memory} +node_size(rover):=1000; {which is a 1000-word available node} +llink(rover):=rover; rlink(rover):=rover;@/ +lo_mem_max:=rover+1000; link(lo_mem_max):=null; info(lo_mem_max):=null;@/ +for k:=hi_mem_stat_min to mem_top do + mem[k]:=mem[lo_mem_max]; {clear list heads} +@; +avail:=null; mem_end:=mem_top; +hi_mem_min:=hi_mem_stat_min; {initialize the one-word memory} +var_used:=lo_mem_stat_max+1-mem_bot; dyn_used:=hi_mem_stat_usage; + {initialize statistics} + +@ If \TeX\ is extended improperly, the |mem| array might get screwed up. +For example, some pointers might be wrong, or some ``dead'' nodes might not +have been freed when the last reference to them disappeared. Procedures +|check_mem| and |search_mem| are available to help diagnose such +problems. These procedures make use of two arrays called |free| and +|was_free| that are present only if \TeX's debugging routines have +been included. (You may want to decrease the size of |mem| while you +@^debugging@> +are debugging.) + +@= +@!debug @!free: packed array [mem_min..mem_max] of boolean; {free cells} +@t\hskip10pt@>@!was_free: packed array [mem_min..mem_max] of boolean; + {previously free cells} +@t\hskip10pt@>@!was_mem_end,@!was_lo_max,@!was_hi_min: pointer; + {previous |mem_end|, |lo_mem_max|, and |hi_mem_min|} +@t\hskip10pt@>@!panicking:boolean; {do we want to check memory constantly?} +gubed + +@ @= +@!debug was_mem_end:=mem_min; {indicate that everything was previously free} +was_lo_max:=mem_min; was_hi_min:=mem_max; +panicking:=false; +gubed + +@ Procedure |check_mem| makes sure that the available space lists of +|mem| are well formed, and it optionally prints out all locations +that are reserved now but were free the last time this procedure was called. + +@p @!debug procedure check_mem(@!print_locs : boolean); +label done1,done2; {loop exits} +var p,@!q:pointer; {current locations of interest in |mem|} +@!clobbered:boolean; {is something amiss?} +begin for p:=mem_min to lo_mem_max do free[p]:=false; {you can probably + do this faster} +for p:=hi_mem_min to mem_end do free[p]:=false; {ditto} +@; +@; +@; +if print_locs then @; +for p:=mem_min to lo_mem_max do was_free[p]:=free[p]; +for p:=hi_mem_min to mem_end do was_free[p]:=free[p]; + {|was_free:=free| might be faster} +was_mem_end:=mem_end; was_lo_max:=lo_mem_max; was_hi_min:=hi_mem_min; +end; +gubed + +@ @= +p:=avail; q:=null; clobbered:=false; +while p<>null do + begin if (p>mem_end)or(p + print_int(q); goto done1; + end; + free[p]:=true; q:=p; p:=link(q); + end; +done1: + +@ @= +p:=rover; q:=null; clobbered:=false; +repeat if (p>=lo_mem_max)or(p=lo_mem_max)or(rlink(p)lo_mem_max)or@| (llink(rlink(p))<>p) then clobbered:=true; + if clobbered then + begin print_nl("Double-AVAIL list clobbered at "); + print_int(q); goto done2; + end; +for q:=p to p+node_size(p)-1 do {mark all locations free} + begin if free[q] then + begin print_nl("Doubly free location at "); +@.Doubly free location...@> + print_int(q); goto done2; + end; + free[q]:=true; + end; +q:=p; p:=rlink(p); +until p=rover; +done2: + +@ @= +p:=mem_min; +while p<=lo_mem_max do {node |p| should not be empty} + begin if is_empty(p) then + begin print_nl("Bad flag at "); print_int(p); +@.Bad flag...@> + end; + while (p<=lo_mem_max) and not free[p] do incr(p); + while (p<=lo_mem_max) and free[p] do incr(p); + end + +@ @= +begin print_nl("New busy locs:"); +for p:=mem_min to lo_mem_max do + if not free[p] and ((p>was_lo_max) or was_free[p]) then + begin print_char(" "); print_int(p); + end; +for p:=hi_mem_min to mem_end do + if not free[p] and + ((pwas_mem_end) or was_free[p]) then + begin print_char(" "); print_int(p); + end; +end + +@ The |search_mem| procedure attempts to answer the question ``Who points +to node~|p|?'' In doing so, it fetches |link| and |info| fields of |mem| +that might not be of type |two_halves|. Strictly speaking, this is +@^dirty \PASCAL@> +undefined in \PASCAL, and it can lead to ``false drops'' (words that seem to +point to |p| purely by coincidence). But for debugging purposes, we want +to rule out the places that do {\sl not\/} point to |p|, so a few false +drops are tolerable. + +@p @!debug procedure search_mem(@!p:pointer); {look for pointers to |p|} +var q:integer; {current position being searched} +begin for q:=mem_min to lo_mem_max do + begin if link(q)=p then + begin print_nl("LINK("); print_int(q); print_char(")"); + end; + if info(q)=p then + begin print_nl("INFO("); print_int(q); print_char(")"); + end; + end; +for q:=hi_mem_min to mem_end do + begin if link(q)=p then + begin print_nl("LINK("); print_int(q); print_char(")"); + end; + if info(q)=p then + begin print_nl("INFO("); print_int(q); print_char(")"); + end; + end; +@; +@; +@; +end; +gubed + +@@; + +@* \[12] Displaying boxes. +We can reinforce our knowledge of the data structures just introduced +by considering two procedures that display a list in symbolic form. +The first of these, called |short_display|, is used in ``overfull box'' +messages to give the top-level description of a list. The other one, +called |show_node_list|, prints a detailed description of exactly what +is in the data structure. + +The philosophy of |short_display| is to ignore the fine points about exactly +what is inside boxes, except that ligatures and discretionary breaks are +expanded. As a result, |short_display| is a recursive procedure, but the +recursion is never more than one level deep. +@^recursion@> + +A global variable |font_in_short_display| keeps track of the font code that +is assumed to be present when |short_display| begins; deviations from this +font will be printed. + +@= +@!font_in_short_display:integer; {an internal font number} + +@ Boxes, rules, inserts, whatsits, marks, and things in general that are +sort of ``complicated'' are indicated only by printing `\.{[]}'. + +@p procedure short_display(@!p:integer); {prints highlights of list |p|} +var n:integer; {for replacement counts} +begin while p>mem_min do + begin if is_char_node(p) then + begin if p<=mem_end then + begin if font(p)<>font_in_short_display then + begin if (font(p)font_max) then + print_char("*") +@.*\relax@> + else @; + print_char(" "); font_in_short_display:=font(p); + end; + print_ASCII(qo(character(p))); + end; + end + else @; + p:=link(p); + end; +end; + +@ @= +case type(p) of +hlist_node,vlist_node,ins_node,whatsit_node,mark_node,adjust_node, + unset_node: print("[]"); +rule_node: print_char("|"); +glue_node: if glue_ptr(p)<>zero_glue then print_char(" "); +math_node: if subtype(p)>=L_code then print("[]") + else print_char("$"); +ligature_node: short_display(lig_ptr(p)); +disc_node: begin short_display(pre_break(p)); + short_display(post_break(p));@/ + n:=replace_count(p); + while n>0 do + begin if link(p)<>null then p:=link(p); + decr(n); + end; + end; +othercases do_nothing +endcases + +@ The |show_node_list| routine requires some auxiliary subroutines: one to +print a font-and-character combination, one to print a token list without +its reference count, and one to print a rule dimension. + +@p procedure print_font_and_char(@!p:integer); {prints |char_node| data} +begin if p>mem_end then print_esc("CLOBBERED.") +else begin if (font(p)font_max) then print_char("*") +@.*\relax@> + else @; + print_char(" "); print_ASCII(qo(character(p))); + end; +end; +@# +procedure print_mark(@!p:integer); {prints token list data in braces} +begin print_char("{"); +if (pmem_end) then print_esc("CLOBBERED.") +else show_token_list(link(p),null,max_print_line-10); +print_char("}"); +end; +@# +procedure print_rule_dimen(@!d:scaled); {prints dimension in rule node} +begin if is_running(d) then print_char("*") else print_scaled(d); +@.*\relax@> +end; + +@ Then there is a subroutine that prints glue stretch and shrink, possibly +followed by the name of finite units: + +@p procedure print_glue(@!d:scaled;@!order:integer;@!s:str_number); + {prints a glue component} +begin print_scaled(d); +if (orderfilll) then print("foul") +else if order>normal then + begin print("fil"); + while order>fil do + begin print_char("l"); decr(order); + end; + end +else if s<>0 then print(s); +end; + +@ The next subroutine prints a whole glue specification. + +@p procedure print_spec(@!p:integer;@!s:str_number); + {prints a glue specification} +begin if (p=lo_mem_max) then print_char("*") +@.*\relax@> +else begin print_scaled(width(p)); + if s<>0 then print(s); + if stretch(p)<>0 then + begin print(" plus "); print_glue(stretch(p),stretch_order(p),s); + end; + if shrink(p)<>0 then + begin print(" minus "); print_glue(shrink(p),shrink_order(p),s); + end; + end; +end; + +@ We also need to declare some procedures that appear later in this +documentation. + +@p @@; +@ + +@ Since boxes can be inside of boxes, |show_node_list| is inherently recursive, +@^recursion@> +up to a given maximum number of levels. The history of nesting is indicated +by the current string, which will be printed at the beginning of each line; +the length of this string, namely |cur_length|, is the depth of nesting. + +Recursive calls on |show_node_list| therefore use the following pattern: + +@d node_list_display(#)== + begin append_char("."); show_node_list(#); flush_char; + end {|str_room| need not be checked; see |show_box| below} + +@ A global variable called |depth_threshold| is used to record the maximum +depth of nesting for which |show_node_list| will show information. If we +have |depth_threshold=0|, for example, only the top level information will +be given and no sublists will be traversed. Another global variable, called +|breadth_max|, tells the maximum number of items to show at each level; +|breadth_max| had better be positive, or you won't see anything. + +@= +@!depth_threshold : integer; {maximum nesting depth in box displays} +@!breadth_max : integer; {maximum number of items shown at the same list level} + +@ Now we are ready for |show_node_list| itself. This procedure has been +written to be ``extra robust'' in the sense that it should not crash or get +into a loop even if the data structures have been messed up by bugs in +the rest of the program. You can safely call its parent routine +|show_box(p)| for arbitrary values of |p| when you are debugging \TeX. +However, in the presence of bad data, the procedure may +@^dirty \PASCAL@>@^debugging@> +fetch a |memory_word| whose variant is different from the way it was stored; +for example, it might try to read |mem[p].hh| when |mem[p]| +contains a scaled integer, if |p| is a pointer that has been +clobbered or chosen at random. + +@p procedure show_node_list(@!p:integer); {prints a node list symbolically} +label exit; +var n:integer; {the number of items already printed at this level} +@!g:real; {a glue ratio, as a floating point number} +begin if cur_length>depth_threshold then + begin if p>null then print(" []"); + {indicate that there's been some truncation} + return; + end; +n:=0; +while p>mem_min do + begin print_ln; print_current_string; {display the nesting history} + if p>mem_end then {pointer out of range} + begin print("Bad link, display aborted."); return; +@.Bad link...@> + end; + incr(n); if n>breadth_max then {time to stop} + begin print("etc."); return; +@.etc@> + end; + @; + p:=link(p); + end; +exit: +end; + +@ @= +if is_char_node(p) then print_font_and_char(p) +else case type(p) of + hlist_node,vlist_node,unset_node: @; + rule_node: @; + ins_node: @; + whatsit_node: @; + glue_node: @; + margin_kern_node: begin + print_esc("kern"); + print_scaled(width(p)); + if subtype(p) = left_side then + print(" (left margin)") + else + print(" (right margin)"); + end; + kern_node: @; + math_node: @; + ligature_node: @; + penalty_node: @; + disc_node: @; + mark_node: @; + adjust_node: @; + @t\4@>@@; + othercases print("Unknown node type!") + endcases + +@ @= +begin if type(p)=hlist_node then print_esc("h") +else if type(p)=vlist_node then print_esc("v") +else print_esc("unset"); +print("box("); print_scaled(height(p)); print_char("+"); +print_scaled(depth(p)); print(")x"); print_scaled(width(p)); +if type(p)=unset_node then + @ +else begin @; + if shift_amount(p)<>0 then + begin print(", shifted "); print_scaled(shift_amount(p)); + end; + if eTeX_ex then @; + end; +node_list_display(list_ptr(p)); {recursive call} +end + +@ @= +begin if span_count(p)<>min_quarterword then + begin print(" ("); print_int(qo(span_count(p))+1); + print(" columns)"); + end; +if glue_stretch(p)<>0 then + begin print(", stretch "); print_glue(glue_stretch(p),glue_order(p),0); + end; +if glue_shrink(p)<>0 then + begin print(", shrink "); print_glue(glue_shrink(p),glue_sign(p),0); + end; +end + +@ The code will have to change in this place if |glue_ratio| is +a structured type instead of an ordinary |real|. Note that this routine +should avoid arithmetic errors even if the |glue_set| field holds an +arbitrary random value. The following code assumes that a properly +formed nonzero |real| number has absolute value $2^{20}$ or more when +it is regarded as an integer; this precaution was adequate to prevent +floating point underflow on the author's computer. +@^system dependencies@> +@^dirty \PASCAL@> + +@= +g:=float(glue_set(p)); +if (g<>float_constant(0))and(glue_sign(p)<>normal) then + begin print(", glue set "); + if glue_sign(p)=shrinking then print("- "); + if abs(mem[p+glue_offset].int)<@'4000000 then print("?.?") + else if abs(g)>float_constant(20000) then + begin if g>float_constant(0) then print_char(">") + else print("< -"); + print_glue(20000*unity,glue_order(p),0); + end + else print_glue(round(unity*g),glue_order(p),0); +@^real multiplication@> + end + +@ @= +begin print_esc("rule("); print_rule_dimen(height(p)); print_char("+"); +print_rule_dimen(depth(p)); print(")x"); print_rule_dimen(width(p)); +end + +@ @= +begin print_esc("insert"); print_int(qo(subtype(p))); +print(", natural size "); print_scaled(height(p)); +print("; split("); print_spec(split_top_ptr(p),0); +print_char(","); print_scaled(depth(p)); +print("); float cost "); print_int(float_cost(p)); +node_list_display(ins_ptr(p)); {recursive call} +end + +@ @= +if subtype(p)>=a_leaders then @ +else begin print_esc("glue"); + if subtype(p)<>normal then + begin print_char("("); + if subtype(p)cond_math_glue then + begin print_char(" "); + if subtype(p)= +begin print_esc(""); +if subtype(p)=c_leaders then print_char("c") +else if subtype(p)=x_leaders then print_char("x"); +print("leaders "); print_spec(glue_ptr(p),0); +node_list_display(leader_ptr(p)); {recursive call} +end + +@ An ``explicit'' kern value is indicated implicitly by an explicit space. + +@= +if subtype(p)<>mu_glue then + begin print_esc("kern"); + if subtype(p)<>normal then print_char(" "); + print_scaled(width(p)); + if subtype(p)=acc_kern then print(" (for accent)"); +@.for accent@> + end +else begin print_esc("mkern"); print_scaled(width(p)); print("mu"); + end + +@ @= +if subtype(p)>after then + begin if end_LR(p) then print_esc("end") + else print_esc("begin"); + if subtype(p)>R_code then print_char("R") + else if subtype(p)>L_code then print_char("L") + else print_char("M"); + end else +begin print_esc("math"); +if subtype(p)=before then print("on") +else print("off"); +if width(p)<>0 then + begin print(", surrounded "); print_scaled(width(p)); + end; +end + +@ @= +begin print_font_and_char(lig_char(p)); print(" (ligature "); +if subtype(p)>1 then print_char("|"); +font_in_short_display:=font(lig_char(p)); short_display(lig_ptr(p)); +if odd(subtype(p)) then print_char("|"); +print_char(")"); +end + +@ @= +begin print_esc("penalty "); print_int(penalty(p)); +end + +@ The |post_break| list of a discretionary node is indicated by a prefixed +`\.{\char'174}' instead of the `\..' before the |pre_break| list. + +@= +begin print_esc("discretionary"); +if replace_count(p)>0 then + begin print(" replacing "); print_int(replace_count(p)); + end; +node_list_display(pre_break(p)); {recursive call} +append_char("|"); show_node_list(post_break(p)); flush_char; {recursive call} +end + +@ @= +begin print_esc("mark"); +if mark_class(p)<>0 then + begin print_char("s"); print_int(mark_class(p)); + end; +print_mark(mark_ptr(p)); +end + +@ @= +begin print_esc("vadjust"); if adjust_pre(p) <> 0 then print(" pre "); +node_list_display(adjust_ptr(p)); {recursive call} +end + +@ The recursive machinery is started by calling |show_box|. +@^recursion@> + +@p procedure show_box(@!p:pointer); +begin @; +if breadth_max<=0 then breadth_max:=5; +if pool_ptr+depth_threshold>=pool_size then + depth_threshold:=pool_size-pool_ptr-1; + {now there's enough room for prefix string} +show_node_list(p); {the show starts at |p|} +print_ln; +end; + +@* \[13] Destroying boxes. +When we are done with a node list, we are obliged to return it to free +storage, including all of its sublists. The recursive procedure +|flush_node_list| does this for us. + +@ First, however, we shall consider two non-recursive procedures that do +simpler tasks. The first of these, |delete_token_ref|, is called when +a pointer to a token list's reference count is being removed. This means +that the token list should disappear if the reference count was |null|, +otherwise the count should be decreased by one. +@^reference counts@> + +@d token_ref_count(#) == info(#) {reference count preceding a token list} + +@p procedure delete_token_ref(@!p:pointer); {|p| points to the reference count + of a token list that is losing one reference} +begin if token_ref_count(p)=null then flush_list(p) +else decr(token_ref_count(p)); +end; + +@ Similarly, |delete_glue_ref| is called when a pointer to a glue +specification is being withdrawn. +@^reference counts@> +@d fast_delete_glue_ref(#)==@t@>@;@/ + begin if glue_ref_count(#)=null then free_node(#,glue_spec_size) + else decr(glue_ref_count(#)); + end + +@p procedure delete_glue_ref(@!p:pointer); {|p| points to a glue specification} +fast_delete_glue_ref(p); + +@ Now we are ready to delete any node list, recursively. +In practice, the nodes deleted are usually charnodes (about 2/3 of the time), +and they are glue nodes in about half of the remaining cases. +@^recursion@> + +@p procedure flush_node_list(@!p:pointer); {erase list of nodes starting at |p|} +label done; {go here when node |p| has been freed} +var q:pointer; {successor to node |p|} +begin while p<>null do +@^inner loop@> + begin q:=link(p); + if is_char_node(p) then free_avail(p) + else begin case type(p) of + hlist_node,vlist_node,unset_node: begin flush_node_list(list_ptr(p)); + free_node(p,box_node_size); goto done; + end; + rule_node: begin free_node(p,rule_node_size); goto done; + end; + ins_node: begin flush_node_list(ins_ptr(p)); + delete_glue_ref(split_top_ptr(p)); + free_node(p,ins_node_size); goto done; + end; + whatsit_node: @; + glue_node: begin fast_delete_glue_ref(glue_ptr(p)); + if leader_ptr(p)<>null then flush_node_list(leader_ptr(p)); + end; + kern_node,math_node,penalty_node: do_nothing; + margin_kern_node: begin + free_avail(margin_char(p)); + free_node(p, margin_kern_node_size); + goto done; + end; + ligature_node: flush_node_list(lig_ptr(p)); + mark_node: delete_token_ref(mark_ptr(p)); + disc_node: begin flush_node_list(pre_break(p)); + flush_node_list(post_break(p)); + end; + adjust_node: flush_node_list(adjust_ptr(p)); + @t\4@>@@; + othercases confusion("flushing") +@:this can't happen flushing}{\quad flushing@> + endcases;@/ + free_node(p,small_node_size); + done:end; + p:=q; + end; +end; + +@* \[14] Copying boxes. +Another recursive operation that acts on boxes is sometimes needed: The +procedure |copy_node_list| returns a pointer to another node list that has +the same structure and meaning as the original. Note that since glue +specifications and token lists have reference counts, we need not make +copies of them. Reference counts can never get too large to fit in a +halfword, since each pointer to a node is in a different memory address, +and the total number of memory addresses fits in a halfword. +@^recursion@> +@^reference counts@> + +(Well, there actually are also references from outside |mem|; if the +|save_stack| is made arbitrarily large, it would theoretically be possible +to break \TeX\ by overflowing a reference count. But who would want to do that?) + +@d add_token_ref(#)==incr(token_ref_count(#)) {new reference to a token list} +@d add_glue_ref(#)==incr(glue_ref_count(#)) {new reference to a glue spec} + +@ The copying procedure copies words en masse without bothering +to look at their individual fields. If the node format changes---for +example, if the size is altered, or if some link field is moved to another +relative position---then this code may need to be changed too. +@^data structure assumptions@> + +@p function copy_node_list(@!p:pointer):pointer; {makes a duplicate of the + node list that starts at |p| and returns a pointer to the new list} +var h:pointer; {temporary head of copied list} +@!q:pointer; {previous position in new list} +@!r:pointer; {current node being fabricated for new list} +@!words:0..5; {number of words remaining to be copied} +begin h:=get_avail; q:=h; +while p<>null do + begin @; + link(q):=r; q:=r; p:=link(p); + end; +link(q):=null; q:=link(h); free_avail(h); +copy_node_list:=q; +end; + +@ @= +words:=1; {this setting occurs in more branches than any other} +if is_char_node(p) then r:=get_avail +else @; +while words>0 do + begin decr(words); mem[r+words]:=mem[p+words]; + end + +@ @= +case type(p) of +hlist_node,vlist_node,unset_node: begin r:=get_node(box_node_size); + mem[r+6]:=mem[p+6]; mem[r+5]:=mem[p+5]; {copy the last two words} + list_ptr(r):=copy_node_list(list_ptr(p)); {this affects |mem[r+5]|} + words:=5; + end; +rule_node: begin r:=get_node(rule_node_size); words:=rule_node_size; + end; +ins_node: begin r:=get_node(ins_node_size); mem[r+4]:=mem[p+4]; + add_glue_ref(split_top_ptr(p)); + ins_ptr(r):=copy_node_list(ins_ptr(p)); {this affects |mem[r+4]|} + words:=ins_node_size-1; + end; +whatsit_node:@; +glue_node: begin r:=get_node(small_node_size); add_glue_ref(glue_ptr(p)); + glue_ptr(r):=glue_ptr(p); leader_ptr(r):=copy_node_list(leader_ptr(p)); + end; +kern_node,math_node,penalty_node: begin r:=get_node(small_node_size); + words:=small_node_size; + end; +margin_kern_node: begin + r := get_node(margin_kern_node_size); + fast_get_avail(margin_char(r)); + font(margin_char(r)) := font(margin_char(p)); + character(margin_char(r)) := character(margin_char(p)); + words := small_node_size; + end; +ligature_node: begin r:=get_node(small_node_size); + mem[lig_char(r)]:=mem[lig_char(p)]; {copy |font| and |character|} + lig_ptr(r):=copy_node_list(lig_ptr(p)); + end; +disc_node: begin r:=get_node(small_node_size); + pre_break(r):=copy_node_list(pre_break(p)); + post_break(r):=copy_node_list(post_break(p)); + end; +mark_node: begin r:=get_node(small_node_size); add_token_ref(mark_ptr(p)); + words:=small_node_size; + end; +adjust_node: begin r:=get_node(small_node_size); + adjust_ptr(r):=copy_node_list(adjust_ptr(p)); + end; {|words=1=small_node_size-1|} +othercases confusion("copying") +@:this can't happen copying}{\quad copying@> +endcases + +@* \[15] The command codes. +Before we can go any further, we need to define symbolic names for the internal +code numbers that represent the various commands obeyed by \TeX. These codes +are somewhat arbitrary, but not completely so. For example, the command +codes for character types are fixed by the language, since a user says, +e.g., `\.{\\catcode \`\\\${} = 3}' to make \.{\char'44} a math delimiter, +and the command code |math_shift| is equal to~3. Some other codes have +been made adjacent so that |case| statements in the program need not consider +cases that are widely spaced, or so that |case| statements can be replaced +by |if| statements. + +At any rate, here is the list, for future reference. First come the +``catcode'' commands, several of which share their numeric codes with +ordinary commands when the catcode cannot emerge from \TeX's scanning routine. + +@d escape=0 {escape delimiter (called \.\\ in {\sl The \TeX book\/})} +@:TeXbook}{\sl The \TeX book@> +@d relax=0 {do nothing ( \.{\\relax} )} +@d left_brace=1 {beginning of a group ( \.\{ )} +@d right_brace=2 {ending of a group ( \.\} )} +@d math_shift=3 {mathematics shift character ( \.\$ )} +@d tab_mark=4 {alignment delimiter ( \.\&, \.{\\span} )} +@d car_ret=5 {end of line ( |carriage_return|, \.{\\cr}, \.{\\crcr} )} +@d out_param=5 {output a macro parameter} +@d mac_param=6 {macro parameter symbol ( \.\# )} +@d sup_mark=7 {superscript ( \.{\char'136} )} +@d sub_mark=8 {subscript ( \.{\char'137} )} +@d ignore=9 {characters to ignore ( \.{\^\^@@} )} +@d endv=9 {end of \ list in alignment template} +@d spacer=10 {characters equivalent to blank space ( \.{\ } )} +@d letter=11 {characters regarded as letters ( \.{A..Z}, \.{a..z} )} +@d other_char=12 {none of the special character types} +@d active_char=13 {characters that invoke macros ( \.{\char`\~} )} +@d par_end=13 {end of paragraph ( \.{\\par} )} +@d match=13 {match a macro parameter} +@d comment=14 {characters that introduce comments ( \.\% )} +@d end_match=14 {end of parameters to macro} +@d stop=14 {end of job ( \.{\\end}, \.{\\dump} )} +@d invalid_char=15 {characters that shouldn't appear ( \.{\^\^?} )} +@d delim_num=15 {specify delimiter numerically ( \.{\\delimiter} )} +@d max_char_code=15 {largest catcode for individual characters} + +@ Next are the ordinary run-of-the-mill command codes. Codes that are +|min_internal| or more represent internal quantities that might be +expanded by `\.{\\the}'. + +@d char_num=16 {character specified numerically ( \.{\\char} )} +@d math_char_num=17 {explicit math code ( \.{\\mathchar} )} +@d mark=18 {mark definition ( \.{\\mark} )} +@d xray=19 {peek inside of \TeX\ ( \.{\\show}, \.{\\showbox}, etc.~)} +@d make_box=20 {make a box ( \.{\\box}, \.{\\copy}, \.{\\hbox}, etc.~)} +@d hmove=21 {horizontal motion ( \.{\\moveleft}, \.{\\moveright} )} +@d vmove=22 {vertical motion ( \.{\\raise}, \.{\\lower} )} +@d un_hbox=23 {unglue a box ( \.{\\unhbox}, \.{\\unhcopy} )} +@d un_vbox=24 {unglue a box ( \.{\\unvbox}, \.{\\unvcopy} )} + {( or \.{\\pagediscards}, \.{\\splitdiscards} )} +@d remove_item=25 {nullify last item ( \.{\\unpenalty}, + \.{\\unkern}, \.{\\unskip} )} +@d hskip=26 {horizontal glue ( \.{\\hskip}, \.{\\hfil}, etc.~)} +@d vskip=27 {vertical glue ( \.{\\vskip}, \.{\\vfil}, etc.~)} +@d mskip=28 {math glue ( \.{\\mskip} )} +@d kern=29 {fixed space ( \.{\\kern})} +@d mkern=30 {math kern ( \.{\\mkern} )} +@d leader_ship=31 {use a box ( \.{\\shipout}, \.{\\leaders}, etc.~)} +@d halign=32 {horizontal table alignment ( \.{\\halign} )} +@d valign=33 {vertical table alignment ( \.{\\valign} )} + {or text direction directives ( \.{\\beginL}, etc.~)} +@d no_align=34 {temporary escape from alignment ( \.{\\noalign} )} +@d vrule=35 {vertical rule ( \.{\\vrule} )} +@d hrule=36 {horizontal rule ( \.{\\hrule} )} +@d insert=37 {vlist inserted in box ( \.{\\insert} )} +@d vadjust=38 {vlist inserted in enclosing paragraph ( \.{\\vadjust} )} +@d ignore_spaces=39 {gobble |spacer| tokens ( \.{\\ignorespaces} )} +@d after_assignment=40 {save till assignment is done ( \.{\\afterassignment} )} +@d after_group=41 {save till group is done ( \.{\\aftergroup} )} +@d break_penalty=42 {additional badness ( \.{\\penalty} )} +@d start_par=43 {begin paragraph ( \.{\\indent}, \.{\\noindent} )} +@d ital_corr=44 {italic correction ( \.{\\/} )} +@d accent=45 {attach accent in text ( \.{\\accent} )} +@d math_accent=46 {attach accent in math ( \.{\\mathaccent} )} +@d discretionary=47 {discretionary texts ( \.{\\-}, \.{\\discretionary} )} +@d eq_no=48 {equation number ( \.{\\eqno}, \.{\\leqno} )} +@d left_right=49 {variable delimiter ( \.{\\left}, \.{\\right} )} + {( or \.{\\middle} )} +@d math_comp=50 {component of formula ( \.{\\mathbin}, etc.~)} +@d limit_switch=51 {diddle limit conventions ( \.{\\displaylimits}, etc.~)} +@d above=52 {generalized fraction ( \.{\\above}, \.{\\atop}, etc.~)} +@d math_style=53 {style specification ( \.{\\displaystyle}, etc.~)} +@d math_choice=54 {choice specification ( \.{\\mathchoice} )} +@d non_script=55 {conditional math glue ( \.{\\nonscript} )} +@d vcenter=56 {vertically center a vbox ( \.{\\vcenter} )} +@d case_shift=57 {force specific case ( \.{\\lowercase}, \.{\\uppercase}~)} +@d message=58 {send to user ( \.{\\message}, \.{\\errmessage} )} +@d extension=59 {extensions to \TeX\ ( \.{\\write}, \.{\\special}, etc.~)} +@d in_stream=60 {files for reading ( \.{\\openin}, \.{\\closein} )} +@d begin_group=61 {begin local grouping ( \.{\\begingroup} )} +@d end_group=62 {end local grouping ( \.{\\endgroup} )} +@d omit=63 {omit alignment template ( \.{\\omit} )} +@d ex_space=64 {explicit space ( \.{\\\ } )} +@d no_boundary=65 {suppress boundary ligatures ( \.{\\noboundary} )} +@d radical=66 {square root and similar signs ( \.{\\radical} )} +@d end_cs_name=67 {end control sequence ( \.{\\endcsname} )} +@d min_internal=68 {the smallest code that can follow \.{\\the}} +@d char_given=68 {character code defined by \.{\\chardef}} +@d math_given=69 {math code defined by \.{\\mathchardef}} +@d last_item=70 {most recent item ( \.{\\lastpenalty}, + \.{\\lastkern}, \.{\\lastskip} )} +@d max_non_prefixed_command=70 {largest command code that can't be \.{\\global}} + +@ The next codes are special; they all relate to mode-independent +assignment of values to \TeX's internal registers or tables. +Codes that are |max_internal| or less represent internal quantities +that might be expanded by `\.{\\the}'. + +@d toks_register=71 {token list register ( \.{\\toks} )} +@d assign_toks=72 {special token list ( \.{\\output}, \.{\\everypar}, etc.~)} +@d assign_int=73 {user-defined integer ( \.{\\tolerance}, \.{\\day}, etc.~)} +@d assign_dimen=74 {user-defined length ( \.{\\hsize}, etc.~)} +@d assign_glue=75 {user-defined glue ( \.{\\baselineskip}, etc.~)} +@d assign_mu_glue=76 {user-defined muglue ( \.{\\thinmuskip}, etc.~)} +@d assign_font_dimen=77 {user-defined font dimension ( \.{\\fontdimen} )} +@d assign_font_int=78 {user-defined font integer ( \.{\\hyphenchar}, + \.{\\skewchar} )} +@d set_aux=79 {specify state info ( \.{\\spacefactor}, \.{\\prevdepth} )} +@d set_prev_graf=80 {specify state info ( \.{\\prevgraf} )} +@d set_page_dimen=81 {specify state info ( \.{\\pagegoal}, etc.~)} +@d set_page_int=82 {specify state info ( \.{\\deadcycles}, + \.{\\insertpenalties} )} + {( or \.{\\interactionmode} )} +@d set_box_dimen=83 {change dimension of box ( \.{\\wd}, \.{\\ht}, \.{\\dp} )} +@d set_shape=84 {specify fancy paragraph shape ( \.{\\parshape} )} + {(or \.{\\interlinepenalties}, etc.~)} +@d def_code=85 {define a character code ( \.{\\catcode}, etc.~)} +@d def_family=86 {declare math fonts ( \.{\\textfont}, etc.~)} +@d set_font=87 {set current font ( font identifiers )} +@d def_font=88 {define a font file ( \.{\\font} )} +@d register=89 {internal register ( \.{\\count}, \.{\\dimen}, etc.~)} +@d max_internal=89 {the largest code that can follow \.{\\the}} +@d advance=90 {advance a register or parameter ( \.{\\advance} )} +@d multiply=91 {multiply a register or parameter ( \.{\\multiply} )} +@d divide=92 {divide a register or parameter ( \.{\\divide} )} +@d prefix=93 {qualify a definition ( \.{\\global}, \.{\\long}, \.{\\outer} )} + {( or \.{\\protected} )} +@d let=94 {assign a command code ( \.{\\let}, \.{\\futurelet} )} +@d shorthand_def=95 {code definition ( \.{\\chardef}, \.{\\countdef}, etc.~)} +@d read_to_cs=96 {read into a control sequence ( \.{\\read} )} + {( or \.{\\readline} )} +@d def=97 {macro definition ( \.{\\def}, \.{\\gdef}, \.{\\xdef}, \.{\\edef} )} +@d set_box=98 {set a box ( \.{\\setbox} )} +@d hyph_data=99 {hyphenation data ( \.{\\hyphenation}, \.{\\patterns} )} +@d set_interaction=100 {define level of interaction ( \.{\\batchmode}, etc.~)} +@d letterspace_font=101 {letterspace a font ( \.{\\letterspacefont} )} +@d max_command=101 {the largest command code seen at |big_switch|} + +@ The remaining command codes are extra special, since they cannot get through +\TeX's scanner to the main control routine. They have been given values higher +than |max_command| so that their special nature is easily discernible. +The ``expandable'' commands come first. + +@d undefined_cs=max_command+1 {initial state of most |eq_type| fields} +@d expand_after=max_command+2 {special expansion ( \.{\\expandafter} )} +@d no_expand=max_command+3 {special nonexpansion ( \.{\\noexpand} )} +@d input=max_command+4 {input a source file ( \.{\\input}, \.{\\endinput} )} + {( or \.{\\scantokens} )} +@d if_test=max_command+5 {conditional text ( \.{\\if}, \.{\\ifcase}, etc.~)} +@d fi_or_else=max_command+6 {delimiters for conditionals ( \.{\\else}, etc.~)} +@d cs_name=max_command+7 {make a control sequence from tokens ( \.{\\csname} )} +@d convert=max_command+8 {convert to text ( \.{\\number}, \.{\\string}, etc.~)} +@d the=max_command+9 {expand an internal quantity ( \.{\\the} )} + {( or \.{\\unexpanded}, \.{\\detokenize} )} +@d top_bot_mark=max_command+10 {inserted mark ( \.{\\topmark}, etc.~)} +@d call=max_command+11 {non-long, non-outer control sequence} +@d long_call=max_command+12 {long, non-outer control sequence} +@d outer_call=max_command+13 {non-long, outer control sequence} +@d long_outer_call=max_command+14 {long, outer control sequence} +@d end_template=max_command+15 {end of an alignment template} +@d dont_expand=max_command+16 {the following token was marked by \.{\\noexpand}} +@d glue_ref=max_command+17 {the equivalent points to a glue specification} +@d shape_ref=max_command+18 {the equivalent points to a parshape specification} +@d box_ref=max_command+19 {the equivalent points to a box node, or is |null|} +@d data=max_command+20 {the equivalent is simply a halfword number} + +@* \[16] The semantic nest. +\TeX\ is typically in the midst of building many lists at once. For example, +when a math formula is being processed, \TeX\ is in math mode and +working on an mlist; this formula has temporarily interrupted \TeX\ from +being in horizontal mode and building the hlist of a paragraph; and this +paragraph has temporarily interrupted \TeX\ from being in vertical mode +and building the vlist for the next page of a document. Similarly, when a +\.{\\vbox} occurs inside of an \.{\\hbox}, \TeX\ is temporarily +interrupted from working in restricted horizontal mode, and it enters +internal vertical mode. The ``semantic nest'' is a stack that +keeps track of what lists and modes are currently suspended. + +At each level of processing we are in one of six modes: + +\yskip\hang|vmode| stands for vertical mode (the page builder); + +\hang|hmode| stands for horizontal mode (the paragraph builder); + +\hang|mmode| stands for displayed formula mode; + +\hang|-vmode| stands for internal vertical mode (e.g., in a \.{\\vbox}); + +\hang|-hmode| stands for restricted horizontal mode (e.g., in an \.{\\hbox}); + +\hang|-mmode| stands for math formula mode (not displayed). + +\yskip\noindent The mode is temporarily set to zero while processing \.{\\write} +texts in the |ship_out| routine. + +Numeric values are assigned to |vmode|, |hmode|, and |mmode| so that +\TeX's ``big semantic switch'' can select the appropriate thing to +do by computing the value |abs(mode)+cur_cmd|, where |mode| is the current +mode and |cur_cmd| is the current command code. + +@d vmode=1 {vertical mode} +@d hmode=vmode+max_command+1 {horizontal mode} +@d mmode=hmode+max_command+1 {math mode} + +@p procedure print_mode(@!m:integer); {prints the mode represented by |m|} +begin if m>0 then + case m div (max_command+1) of + 0:print("vertical"); + 1:print("horizontal"); + 2:print("display math"); + end +else if m=0 then print("no") +else case (-m) div (max_command+1) of + 0:print("internal vertical"); + 1:print("restricted horizontal"); + 2:print("math"); + end; +print(" mode"); +end; + +@ The state of affairs at any semantic level can be represented by +five values: + +\yskip\hang|mode| is the number representing the semantic mode, as +just explained. + +\yskip\hang|head| is a |pointer| to a list head for the list being built; +|link(head)| therefore points to the first element of the list, or +to |null| if the list is empty. + +\yskip\hang|tail| is a |pointer| to the final node of the list being +built; thus, |tail=head| if and only if the list is empty. + +\yskip\hang|prev_graf| is the number of lines of the current paragraph that +have already been put into the present vertical list. + +\yskip\hang|aux| is an auxiliary |memory_word| that gives further information +that is needed to characterize the situation. + +\yskip\noindent +In vertical mode, |aux| is also known as |prev_depth|; it is the scaled +value representing the depth of the previous box, for use in baseline +calculations, or it is |<=-1000|pt if the next box on the vertical list is to +be exempt from baseline calculations. In horizontal mode, |aux| is also +known as |space_factor| and |clang|; it holds the current space factor used in +spacing calculations, and the current language used for hyphenation. +(The value of |clang| is undefined in restricted horizontal mode.) +In math mode, |aux| is also known as |incompleat_noad|; if +not |null|, it points to a record that represents the numerator of a +generalized fraction for which the denominator is currently being formed +in the current list. + +There is also a sixth quantity, |mode_line|, which correlates +the semantic nest with the user's input; |mode_line| contains the source +line number at which the current level of nesting was entered. The negative +of this line number is the |mode_line| at the level of the +user's output routine. + +A seventh quantity, |eTeX_aux|, is used by the extended features \eTeX. +In vertical modes it is known as |LR_save| and holds the LR stack when a +paragraph is interrupted by a displayed formula. In display math mode +it is known as |LR_box| and holds a pointer to a prototype box for the +display. In math mode it is known as |delim_ptr| and points to the most +recent |left_noad| or |middle_noad| of a |math_left_group|. + +In horizontal mode, the |prev_graf| field is used for initial language data. + +The semantic nest is an array called |nest| that holds the |mode|, |head|, +|tail|, |prev_graf|, |aux|, and |mode_line| values for all semantic levels +below the currently active one. Information about the currently active +level is kept in the global quantities |mode|, |head|, |tail|, |prev_graf|, +|aux|, and |mode_line|, which live in a \PASCAL\ record that is ready to +be pushed onto |nest| if necessary. + +@d ignore_depth==-65536000 {|prev_depth| value that is ignored} + +@= +@!list_state_record=record@!mode_field:-mmode..mmode;@+ + @!head_field,@!tail_field: pointer; + @!eTeX_aux_field: pointer; + @!pg_field,@!ml_field: integer;@+ + @!aux_field: memory_word; + end; + +@ @d mode==cur_list.mode_field {current mode} +@d head==cur_list.head_field {header node of current list} +@d tail==cur_list.tail_field {final node on current list} +@d eTeX_aux==cur_list.eTeX_aux_field {auxiliary data for \eTeX} +@d LR_save==eTeX_aux {LR stack when a paragraph is interrupted} +@d LR_box==eTeX_aux {prototype box for display} +@d delim_ptr==eTeX_aux {most recent left or right noad of a math left group} +@d prev_graf==cur_list.pg_field {number of paragraph lines accumulated} +@d aux==cur_list.aux_field {auxiliary data about the current list} +@d prev_depth==aux.sc {the name of |aux| in vertical mode} +@d space_factor==aux.hh.lh {part of |aux| in horizontal mode} +@d clang==aux.hh.rh {the other part of |aux| in horizontal mode} +@d incompleat_noad==aux.int {the name of |aux| in math mode} +@d mode_line==cur_list.ml_field {source file line number at beginning of list} + +@= +@!nest:array[0..nest_size] of list_state_record; +@!nest_ptr:0..nest_size; {first unused location of |nest|} +@!max_nest_stack:0..nest_size; {maximum of |nest_ptr| when pushing} +@!cur_list:list_state_record; {the ``top'' semantic state} +@!shown_mode:-mmode..mmode; {most recent mode shown by \.{\\tracingcommands}} +@!save_tail: pointer; {save |tail| so we can examine whether we have an auto + kern before a glue} + +@ Here is a common way to make the current list grow: + +@d tail_append(#)==begin link(tail):=#; tail:=link(tail); + end + +@ We will see later that the vertical list at the bottom semantic level is split +into two parts; the ``current page'' runs from |page_head| to |page_tail|, +and the ``contribution list'' runs from |contrib_head| to |tail| of +semantic level zero. The idea is that contributions are first formed in +vertical mode, then ``contributed'' to the current page (during which time +the page-breaking decisions are made). For now, we don't need to know +any more details about the page-building process. + +@= +nest_ptr:=0; max_nest_stack:=0; +mode:=vmode; head:=contrib_head; tail:=contrib_head; +eTeX_aux:=null; +prev_depth:=ignore_depth; mode_line:=0; +prev_graf:=0; shown_mode:=0; +@; + +@ When \TeX's work on one level is interrupted, the state is saved by +calling |push_nest|. This routine changes |head| and |tail| so that +a new (empty) list is begun; it does not change |mode| or |aux|. + +@p procedure push_nest; {enter a new semantic level, save the old} +begin if nest_ptr>max_nest_stack then + begin max_nest_stack:=nest_ptr; + if nest_ptr=nest_size then overflow("semantic nest size",nest_size); +@:TeX capacity exceeded semantic nest size}{\quad semantic nest size@> + end; +nest[nest_ptr]:=cur_list; {stack the record} +incr(nest_ptr); head:=get_avail; tail:=head; prev_graf:=0; mode_line:=line; +eTeX_aux:=null; +end; + +@ Conversely, when \TeX\ is finished on the current level, the former +state is restored by calling |pop_nest|. This routine will never be +called at the lowest semantic level, nor will it be called unless |head| +is a node that should be returned to free memory. + +@p procedure pop_nest; {leave a semantic level, re-enter the old} +begin free_avail(head); decr(nest_ptr); cur_list:=nest[nest_ptr]; +end; + +@ Here is a procedure that displays what \TeX\ is working on, at all levels. + +@p procedure@?print_totals; forward;@t\2@> +procedure show_activities; +var p:0..nest_size; {index into |nest|} +@!m:-mmode..mmode; {mode} +@!a:memory_word; {auxiliary} +@!q,@!r:pointer; {for showing the current page} +@!t:integer; {ditto} +begin nest[nest_ptr]:=cur_list; {put the top level into the array} +print_nl(""); print_ln; +for p:=nest_ptr downto 0 do + begin m:=nest[p].mode_field; a:=nest[p].aux_field; + print_nl("### "); print_mode(m); + print(" entered at line "); print_int(abs(nest[p].ml_field)); + if m=hmode then if nest[p].pg_field <> @'40600000 then + begin print(" (language"); print_int(nest[p].pg_field mod @'200000); + print(":hyphenmin"); print_int(nest[p].pg_field div @'20000000); + print_char(","); print_int((nest[p].pg_field div @'200000) mod @'100); + print_char(")"); + end; + if nest[p].ml_field<0 then print(" (\output routine)"); + if p=0 then + begin @; + if link(contrib_head)<>null then + print_nl("### recent contributions:"); + end; + show_box(link(nest[p].head_field)); + @; + end; +end; + +@ @= +case abs(m) div (max_command+1) of +0: begin print_nl("prevdepth "); + if a.sc<=ignore_depth then print("ignored") + else print_scaled(a.sc); + if nest[p].pg_field<>0 then + begin print(", prevgraf "); + print_int(nest[p].pg_field); print(" line"); + if nest[p].pg_field<>1 then print_char("s"); + end; + end; +1: begin print_nl("spacefactor "); print_int(a.hh.lh); + if m>0 then@+ if a.hh.rh>0 then + begin print(", current language "); print_int(a.hh.rh);@+ + end; + end; +2: if a.int<>null then + begin print("this will be denominator of:"); show_box(a.int);@+ + end; +end {there are no other cases} + +@* \[17] The table of equivalents. +Now that we have studied the data structures for \TeX's semantic routines, +we ought to consider the data structures used by its syntactic routines. In +other words, our next concern will be +the tables that \TeX\ looks at when it is scanning +what the user has written. + +The biggest and most important such table is called |eqtb|. It holds the +current ``equivalents'' of things; i.e., it explains what things mean +or what their current values are, for all quantities that are subject to +the nesting structure provided by \TeX's grouping mechanism. There are six +parts to |eqtb|: + +\yskip\hangg 1) |eqtb[active_base..(hash_base-1)]| holds the current +equivalents of single-character control sequences. + +\yskip\hangg 2) |eqtb[hash_base..(glue_base-1)]| holds the current +equivalents of multiletter control sequences. + +\yskip\hangg 3) |eqtb[glue_base..(local_base-1)]| holds the current +equivalents of glue parameters like the current baselineskip. + +\yskip\hangg 4) |eqtb[local_base..(int_base-1)]| holds the current +equivalents of local halfword quantities like the current box registers, +the current ``catcodes,'' the current font, and a pointer to the current +paragraph shape. + +\yskip\hangg 5) |eqtb[int_base..(dimen_base-1)]| holds the current +equivalents of fullword integer parameters like the current hyphenation +penalty. + +\yskip\hangg 6) |eqtb[dimen_base..eqtb_size]| holds the current equivalents +of fullword dimension parameters like the current hsize or amount of +hanging indentation. + +\yskip\noindent Note that, for example, the current amount of +baselineskip glue is determined by the setting of a particular location +in region~3 of |eqtb|, while the current meaning of the control sequence +`\.{\\baselineskip}' (which might have been changed by \.{\\def} or +\.{\\let}) appears in region~2. + +@ Each entry in |eqtb| is a |memory_word|. Most of these words are of type +|two_halves|, and subdivided into three fields: + +\yskip\hangg 1) The |eq_level| (a quarterword) is the level of grouping at +which this equivalent was defined. If the level is |level_zero|, the +equivalent has never been defined; |level_one| refers to the outer level +(outside of all groups), and this level is also used for global +definitions that never go away. Higher levels are for equivalents that +will disappear at the end of their group. @^global definitions@> + +\yskip\hangg 2) The |eq_type| (another quarterword) specifies what kind of +entry this is. There are many types, since each \TeX\ primitive like +\.{\\hbox}, \.{\\def}, etc., has its own special code. The list of +command codes above includes all possible settings of the |eq_type| field. + +\yskip\hangg 3) The |equiv| (a halfword) is the current equivalent value. +This may be a font number, a pointer into |mem|, or a variety of other +things. + +@d eq_level_field(#)==#.hh.b1 +@d eq_type_field(#)==#.hh.b0 +@d equiv_field(#)==#.hh.rh +@d eq_level(#)==eq_level_field(eqtb[#]) {level of definition} +@d eq_type(#)==eq_type_field(eqtb[#]) {command code for equivalent} +@d equiv(#)==equiv_field(eqtb[#]) {equivalent value} +@d level_zero=min_quarterword {level for undefined quantities} +@d level_one=level_zero+1 {outermost level for defined quantities} + +@ Many locations in |eqtb| have symbolic names. The purpose of the next +paragraphs is to define these names, and to set up the initial values of the +equivalents. + +In the first region we have 256 equivalents for ``active characters'' that +act as control sequences, followed by 256 equivalents for single-character +control sequences. + +Then comes region~2, which corresponds to the hash table that we will +define later. The maximum address in this region is used for a dummy +control sequence that is perpetually undefined. There also are several +locations for control sequences that are perpetually defined +(since they are used in error recovery). + +@d active_base=1 {beginning of region 1, for active character equivalents} +@d single_base=active_base+256 {equivalents of one-character control sequences} +@d null_cs=single_base+256 {equivalent of \.{\\csname\\endcsname}} +@d hash_base=null_cs+1 {beginning of region 2, for the hash table} +@d frozen_control_sequence=hash_base+hash_size {for error recovery} +@d frozen_protection=frozen_control_sequence {inaccessible but definable} +@d frozen_cr=frozen_control_sequence+1 {permanent `\.{\\cr}'} +@d frozen_end_group=frozen_control_sequence+2 {permanent `\.{\\endgroup}'} +@d frozen_right=frozen_control_sequence+3 {permanent `\.{\\right}'} +@d frozen_fi=frozen_control_sequence+4 {permanent `\.{\\fi}'} +@d frozen_end_template=frozen_control_sequence+5 {permanent `\.{\\endtemplate}'} +@d frozen_endv=frozen_control_sequence+6 {second permanent `\.{\\endtemplate}'} +@d frozen_relax=frozen_control_sequence+7 {permanent `\.{\\relax}'} +@d end_write=frozen_control_sequence+8 {permanent `\.{\\endwrite}'} +@d frozen_dont_expand=frozen_control_sequence+9 + {permanent `\.{\\notexpanded:}'} +@d frozen_null_font=frozen_control_sequence+10 + {permanent `\.{\\nullfont}'} +@d frozen_primitive=frozen_control_sequence+11 + {permanent `\.{\\pdfprimitive}'} +@d font_id_base=frozen_null_font-font_base + {begins table of 257 permanent font identifiers} +@d undefined_control_sequence=frozen_null_font+257 {dummy location} +@d glue_base=undefined_control_sequence+1 {beginning of region 3} + +@= +eq_type(undefined_control_sequence):=undefined_cs; +equiv(undefined_control_sequence):=null; +eq_level(undefined_control_sequence):=level_zero; +for k:=active_base to undefined_control_sequence-1 do + eqtb[k]:=eqtb[undefined_control_sequence]; + +@ Here is a routine that displays the current meaning of an |eqtb| entry +in region 1 or~2. (Similar routines for the other regions will appear +below.) + +@= +begin sprint_cs(n); print_char("="); print_cmd_chr(eq_type(n),equiv(n)); +if eq_type(n)>=call then + begin print_char(":"); show_token_list(link(equiv(n)),null,32); + end; +end + +@ Region 3 of |eqtb| contains the 256 \.{\\skip} registers, as well as the +glue parameters defined here. It is important that the ``muskip'' +parameters have larger numbers than the others. + +@d line_skip_code=0 {interline glue if |baseline_skip| is infeasible} +@d baseline_skip_code=1 {desired glue between baselines} +@d par_skip_code=2 {extra glue just above a paragraph} +@d above_display_skip_code=3 {extra glue just above displayed math} +@d below_display_skip_code=4 {extra glue just below displayed math} +@d above_display_short_skip_code=5 + {glue above displayed math following short lines} +@d below_display_short_skip_code=6 + {glue below displayed math following short lines} +@d left_skip_code=7 {glue at left of justified lines} +@d right_skip_code=8 {glue at right of justified lines} +@d top_skip_code=9 {glue at top of main pages} +@d split_top_skip_code=10 {glue at top of split pages} +@d tab_skip_code=11 {glue between aligned entries} +@d space_skip_code=12 {glue between words (if not |zero_glue|)} +@d xspace_skip_code=13 {glue after sentences (if not |zero_glue|)} +@d par_fill_skip_code=14 {glue on last line of paragraph} +@d thin_mu_skip_code=15 {thin space in math formula} +@d med_mu_skip_code=16 {medium space in math formula} +@d thick_mu_skip_code=17 {thick space in math formula} +@d glue_pars=18 {total number of glue parameters} +@d skip_base=glue_base+glue_pars {table of 256 ``skip'' registers} +@d mu_skip_base=skip_base+256 {table of 256 ``muskip'' registers} +@d local_base=mu_skip_base+256 {beginning of region 4} +@# +@d skip(#)==equiv(skip_base+#) {|mem| location of glue specification} +@d mu_skip(#)==equiv(mu_skip_base+#) {|mem| location of math glue spec} +@d glue_par(#)==equiv(glue_base+#) {|mem| location of glue specification} +@d line_skip==glue_par(line_skip_code) +@d baseline_skip==glue_par(baseline_skip_code) +@d par_skip==glue_par(par_skip_code) +@d above_display_skip==glue_par(above_display_skip_code) +@d below_display_skip==glue_par(below_display_skip_code) +@d above_display_short_skip==glue_par(above_display_short_skip_code) +@d below_display_short_skip==glue_par(below_display_short_skip_code) +@d left_skip==glue_par(left_skip_code) +@d right_skip==glue_par(right_skip_code) +@d top_skip==glue_par(top_skip_code) +@d split_top_skip==glue_par(split_top_skip_code) +@d tab_skip==glue_par(tab_skip_code) +@d space_skip==glue_par(space_skip_code) +@d xspace_skip==glue_par(xspace_skip_code) +@d par_fill_skip==glue_par(par_fill_skip_code) +@d thin_mu_skip==glue_par(thin_mu_skip_code) +@d med_mu_skip==glue_par(med_mu_skip_code) +@d thick_mu_skip==glue_par(thick_mu_skip_code) + +@=glue_par(n) + +@ Sometimes we need to convert \TeX's internal code numbers into symbolic +form. The |print_skip_param| routine gives the symbolic name of a glue +parameter. + +@= +procedure print_skip_param(@!n:integer); +begin case n of +line_skip_code: print_esc("lineskip"); +baseline_skip_code: print_esc("baselineskip"); +par_skip_code: print_esc("parskip"); +above_display_skip_code: print_esc("abovedisplayskip"); +below_display_skip_code: print_esc("belowdisplayskip"); +above_display_short_skip_code: print_esc("abovedisplayshortskip"); +below_display_short_skip_code: print_esc("belowdisplayshortskip"); +left_skip_code: print_esc("leftskip"); +right_skip_code: print_esc("rightskip"); +top_skip_code: print_esc("topskip"); +split_top_skip_code: print_esc("splittopskip"); +tab_skip_code: print_esc("tabskip"); +space_skip_code: print_esc("spaceskip"); +xspace_skip_code: print_esc("xspaceskip"); +par_fill_skip_code: print_esc("parfillskip"); +thin_mu_skip_code: print_esc("thinmuskip"); +med_mu_skip_code: print_esc("medmuskip"); +thick_mu_skip_code: print_esc("thickmuskip"); +othercases print("[unknown glue parameter!]") +endcases; +end; + +@ The symbolic names for glue parameters are put into \TeX's hash table +by using the routine called |primitive|, defined below. Let us enter them +now, so that we don't have to list all those parameter names anywhere else. + +@= +primitive("lineskip",assign_glue,glue_base+line_skip_code);@/ +@!@:line_skip_}{\.{\\lineskip} primitive@> +primitive("baselineskip",assign_glue,glue_base+baseline_skip_code);@/ +@!@:baseline_skip_}{\.{\\baselineskip} primitive@> +primitive("parskip",assign_glue,glue_base+par_skip_code);@/ +@!@:par_skip_}{\.{\\parskip} primitive@> +primitive("abovedisplayskip",assign_glue,glue_base+above_display_skip_code);@/ +@!@:above_display_skip_}{\.{\\abovedisplayskip} primitive@> +primitive("belowdisplayskip",assign_glue,glue_base+below_display_skip_code);@/ +@!@:below_display_skip_}{\.{\\belowdisplayskip} primitive@> +primitive("abovedisplayshortskip", + assign_glue,glue_base+above_display_short_skip_code);@/ +@!@:above_display_short_skip_}{\.{\\abovedisplayshortskip} primitive@> +primitive("belowdisplayshortskip", + assign_glue,glue_base+below_display_short_skip_code);@/ +@!@:below_display_short_skip_}{\.{\\belowdisplayshortskip} primitive@> +primitive("leftskip",assign_glue,glue_base+left_skip_code);@/ +@!@:left_skip_}{\.{\\leftskip} primitive@> +primitive("rightskip",assign_glue,glue_base+right_skip_code);@/ +@!@:right_skip_}{\.{\\rightskip} primitive@> +primitive("topskip",assign_glue,glue_base+top_skip_code);@/ +@!@:top_skip_}{\.{\\topskip} primitive@> +primitive("splittopskip",assign_glue,glue_base+split_top_skip_code);@/ +@!@:split_top_skip_}{\.{\\splittopskip} primitive@> +primitive("tabskip",assign_glue,glue_base+tab_skip_code);@/ +@!@:tab_skip_}{\.{\\tabskip} primitive@> +primitive("spaceskip",assign_glue,glue_base+space_skip_code);@/ +@!@:space_skip_}{\.{\\spaceskip} primitive@> +primitive("xspaceskip",assign_glue,glue_base+xspace_skip_code);@/ +@!@:xspace_skip_}{\.{\\xspaceskip} primitive@> +primitive("parfillskip",assign_glue,glue_base+par_fill_skip_code);@/ +@!@:par_fill_skip_}{\.{\\parfillskip} primitive@> +primitive("thinmuskip",assign_mu_glue,glue_base+thin_mu_skip_code);@/ +@!@:thin_mu_skip_}{\.{\\thinmuskip} primitive@> +primitive("medmuskip",assign_mu_glue,glue_base+med_mu_skip_code);@/ +@!@:med_mu_skip_}{\.{\\medmuskip} primitive@> +primitive("thickmuskip",assign_mu_glue,glue_base+thick_mu_skip_code);@/ +@!@:thick_mu_skip_}{\.{\\thickmuskip} primitive@> + +@ @= +assign_glue,assign_mu_glue: if chr_code= +equiv(glue_base):=zero_glue; eq_level(glue_base):=level_one; +eq_type(glue_base):=glue_ref; +for k:=glue_base+1 to local_base-1 do eqtb[k]:=eqtb[glue_base]; +glue_ref_count(zero_glue):=glue_ref_count(zero_glue)+local_base-glue_base; + +@ @= +if n= +primitive("output",assign_toks,output_routine_loc); +@!@:output_}{\.{\\output} primitive@> +primitive("everypar",assign_toks,every_par_loc); +@!@:every_par_}{\.{\\everypar} primitive@> +primitive("everymath",assign_toks,every_math_loc); +@!@:every_math_}{\.{\\everymath} primitive@> +primitive("everydisplay",assign_toks,every_display_loc); +@!@:every_display_}{\.{\\everydisplay} primitive@> +primitive("everyhbox",assign_toks,every_hbox_loc); +@!@:every_hbox_}{\.{\\everyhbox} primitive@> +primitive("everyvbox",assign_toks,every_vbox_loc); +@!@:every_vbox_}{\.{\\everyvbox} primitive@> +primitive("everyjob",assign_toks,every_job_loc); +@!@:every_job_}{\.{\\everyjob} primitive@> +primitive("everycr",assign_toks,every_cr_loc); +@!@:every_cr_}{\.{\\everycr} primitive@> +primitive("errhelp",assign_toks,err_help_loc); +@!@:err_help_}{\.{\\errhelp} primitive@> +primitive("pdfpagesattr",assign_toks,pdf_pages_attr_loc); +@!@:pdf_pages_attr_}{\.{\\pdfpagesattr} primitive@> +primitive("pdfpageattr",assign_toks,pdf_page_attr_loc); +@!@:pdf_page_attr_}{\.{\\pdfpageattr} primitive@> +primitive("pdfpageresources",assign_toks,pdf_page_resources_loc); +@!@:pdf_page_resources_}{\.{\\pdfpageresources} primitive@> +primitive("pdfpkmode",assign_toks,pdf_pk_mode_loc); +@!@:pdf_pk_mode_}{\.{\\pdfpkmode} primitive@> + +@ @= +assign_toks: if chr_code>=toks_base then + begin print_esc("toks"); print_int(chr_code-toks_base); + end +else case chr_code of + output_routine_loc: print_esc("output"); + every_par_loc: print_esc("everypar"); + every_math_loc: print_esc("everymath"); + every_display_loc: print_esc("everydisplay"); + every_hbox_loc: print_esc("everyhbox"); + every_vbox_loc: print_esc("everyvbox"); + every_job_loc: print_esc("everyjob"); + every_cr_loc: print_esc("everycr"); + @/@@/ + pdf_pages_attr_loc: print_esc("pdfpagesattr"); + pdf_page_attr_loc: print_esc("pdfpageattr"); + pdf_page_resources_loc: print_esc("pdfpageresources"); + pdf_pk_mode_loc: print_esc("pdfpkmode"); + othercases print_esc("errhelp") + endcases; + +@ We initialize most things to null or undefined values. An undefined font +is represented by the internal code |font_base|. + +However, the character code tables are given initial values based on the +conventional interpretation of ASCII code. These initial values should +not be changed when \TeX\ is adapted for use with non-English languages; +all changes to the initialization conventions should be made in format +packages, not in \TeX\ itself, so that global interchange of formats is +possible. + +@d null_font==font_base +@d var_code==@'70000 {math code meaning ``use the current family''} + +@= +par_shape_ptr:=null; eq_type(par_shape_loc):=shape_ref; +eq_level(par_shape_loc):=level_one;@/ +for k:=etex_pen_base to etex_pens-1 do + eqtb[k]:=eqtb[par_shape_loc]; +for k:=output_routine_loc to toks_base+255 do + eqtb[k]:=eqtb[undefined_control_sequence]; +box(0):=null; eq_type(box_base):=box_ref; eq_level(box_base):=level_one; +for k:=box_base+1 to box_base+255 do eqtb[k]:=eqtb[box_base]; +cur_font:=null_font; eq_type(cur_font_loc):=data; +eq_level(cur_font_loc):=level_one;@/ +for k:=math_font_base to math_font_base+47 do eqtb[k]:=eqtb[cur_font_loc]; +equiv(cat_code_base):=0; eq_type(cat_code_base):=data; +eq_level(cat_code_base):=level_one;@/ +for k:=cat_code_base+1 to int_base-1 do eqtb[k]:=eqtb[cat_code_base]; +for k:=0 to 255 do + begin cat_code(k):=other_char; math_code(k):=hi(k); sf_code(k):=1000; + end; +cat_code(carriage_return):=car_ret; cat_code(" "):=spacer; +cat_code("\"):=escape; cat_code("%"):=comment; +cat_code(invalid_code):=invalid_char; cat_code(null_code):=ignore; +for k:="0" to "9" do math_code(k):=hi(k+var_code); +for k:="A" to "Z" do + begin cat_code(k):=letter; cat_code(k+"a"-"A"):=letter;@/ + math_code(k):=hi(k+var_code+@"100); + math_code(k+"a"-"A"):=hi(k+"a"-"A"+var_code+@"100);@/ + lc_code(k):=k+"a"-"A"; lc_code(k+"a"-"A"):=k+"a"-"A";@/ + uc_code(k):=k; uc_code(k+"a"-"A"):=k;@/ + sf_code(k):=999; + end; + +@ @= +if (n=par_shape_loc) or ((n>=etex_pen_base) and (npar_shape_loc then + begin print_int(penalty(equiv(n))); print_char(" "); + print_int(penalty(equiv(n)+1)); + if penalty(equiv(n))>1 then print_esc("ETC."); + end + else print_int(info(par_shape_ptr)); + end +else if n +else @ + +@ @= +begin if n=cur_font_loc then print("current font") +else if n@/ +othercases print("[unknown integer parameter!]") +endcases; +end; + +@ The integer parameter names must be entered into the hash table. + +@= +primitive("pretolerance",assign_int,int_base+pretolerance_code);@/ +@!@:pretolerance_}{\.{\\pretolerance} primitive@> +primitive("tolerance",assign_int,int_base+tolerance_code);@/ +@!@:tolerance_}{\.{\\tolerance} primitive@> +primitive("linepenalty",assign_int,int_base+line_penalty_code);@/ +@!@:line_penalty_}{\.{\\linepenalty} primitive@> +primitive("hyphenpenalty",assign_int,int_base+hyphen_penalty_code);@/ +@!@:hyphen_penalty_}{\.{\\hyphenpenalty} primitive@> +primitive("exhyphenpenalty",assign_int,int_base+ex_hyphen_penalty_code);@/ +@!@:ex_hyphen_penalty_}{\.{\\exhyphenpenalty} primitive@> +primitive("clubpenalty",assign_int,int_base+club_penalty_code);@/ +@!@:club_penalty_}{\.{\\clubpenalty} primitive@> +primitive("widowpenalty",assign_int,int_base+widow_penalty_code);@/ +@!@:widow_penalty_}{\.{\\widowpenalty} primitive@> +primitive("displaywidowpenalty", + assign_int,int_base+display_widow_penalty_code);@/ +@!@:display_widow_penalty_}{\.{\\displaywidowpenalty} primitive@> +primitive("brokenpenalty",assign_int,int_base+broken_penalty_code);@/ +@!@:broken_penalty_}{\.{\\brokenpenalty} primitive@> +primitive("binoppenalty",assign_int,int_base+bin_op_penalty_code);@/ +@!@:bin_op_penalty_}{\.{\\binoppenalty} primitive@> +primitive("relpenalty",assign_int,int_base+rel_penalty_code);@/ +@!@:rel_penalty_}{\.{\\relpenalty} primitive@> +primitive("predisplaypenalty",assign_int,int_base+pre_display_penalty_code);@/ +@!@:pre_display_penalty_}{\.{\\predisplaypenalty} primitive@> +primitive("postdisplaypenalty",assign_int,int_base+post_display_penalty_code);@/ +@!@:post_display_penalty_}{\.{\\postdisplaypenalty} primitive@> +primitive("interlinepenalty",assign_int,int_base+inter_line_penalty_code);@/ +@!@:inter_line_penalty_}{\.{\\interlinepenalty} primitive@> +primitive("doublehyphendemerits", + assign_int,int_base+double_hyphen_demerits_code);@/ +@!@:double_hyphen_demerits_}{\.{\\doublehyphendemerits} primitive@> +primitive("finalhyphendemerits", + assign_int,int_base+final_hyphen_demerits_code);@/ +@!@:final_hyphen_demerits_}{\.{\\finalhyphendemerits} primitive@> +primitive("adjdemerits",assign_int,int_base+adj_demerits_code);@/ +@!@:adj_demerits_}{\.{\\adjdemerits} primitive@> +primitive("mag",assign_int,int_base+mag_code);@/ +@!@:mag_}{\.{\\mag} primitive@> +primitive("delimiterfactor",assign_int,int_base+delimiter_factor_code);@/ +@!@:delimiter_factor_}{\.{\\delimiterfactor} primitive@> +primitive("looseness",assign_int,int_base+looseness_code);@/ +@!@:looseness_}{\.{\\looseness} primitive@> +primitive("time",assign_int,int_base+time_code);@/ +@!@:time_}{\.{\\time} primitive@> +primitive("day",assign_int,int_base+day_code);@/ +@!@:day_}{\.{\\day} primitive@> +primitive("month",assign_int,int_base+month_code);@/ +@!@:month_}{\.{\\month} primitive@> +primitive("year",assign_int,int_base+year_code);@/ +@!@:year_}{\.{\\year} primitive@> +primitive("showboxbreadth",assign_int,int_base+show_box_breadth_code);@/ +@!@:show_box_breadth_}{\.{\\showboxbreadth} primitive@> +primitive("showboxdepth",assign_int,int_base+show_box_depth_code);@/ +@!@:show_box_depth_}{\.{\\showboxdepth} primitive@> +primitive("hbadness",assign_int,int_base+hbadness_code);@/ +@!@:hbadness_}{\.{\\hbadness} primitive@> +primitive("vbadness",assign_int,int_base+vbadness_code);@/ +@!@:vbadness_}{\.{\\vbadness} primitive@> +primitive("pausing",assign_int,int_base+pausing_code);@/ +@!@:pausing_}{\.{\\pausing} primitive@> +primitive("tracingonline",assign_int,int_base+tracing_online_code);@/ +@!@:tracing_online_}{\.{\\tracingonline} primitive@> +primitive("tracingmacros",assign_int,int_base+tracing_macros_code);@/ +@!@:tracing_macros_}{\.{\\tracingmacros} primitive@> +primitive("tracingstats",assign_int,int_base+tracing_stats_code);@/ +@!@:tracing_stats_}{\.{\\tracingstats} primitive@> +primitive("tracingparagraphs",assign_int,int_base+tracing_paragraphs_code);@/ +@!@:tracing_paragraphs_}{\.{\\tracingparagraphs} primitive@> +primitive("tracingpages",assign_int,int_base+tracing_pages_code);@/ +@!@:tracing_pages_}{\.{\\tracingpages} primitive@> +primitive("tracingoutput",assign_int,int_base+tracing_output_code);@/ +@!@:tracing_output_}{\.{\\tracingoutput} primitive@> +primitive("tracinglostchars",assign_int,int_base+tracing_lost_chars_code);@/ +@!@:tracing_lost_chars_}{\.{\\tracinglostchars} primitive@> +primitive("tracingcommands",assign_int,int_base+tracing_commands_code);@/ +@!@:tracing_commands_}{\.{\\tracingcommands} primitive@> +primitive("tracingrestores",assign_int,int_base+tracing_restores_code);@/ +@!@:tracing_restores_}{\.{\\tracingrestores} primitive@> +primitive("uchyph",assign_int,int_base+uc_hyph_code);@/ +@!@:uc_hyph_}{\.{\\uchyph} primitive@> +primitive("outputpenalty",assign_int,int_base+output_penalty_code);@/ +@!@:output_penalty_}{\.{\\outputpenalty} primitive@> +primitive("maxdeadcycles",assign_int,int_base+max_dead_cycles_code);@/ +@!@:max_dead_cycles_}{\.{\\maxdeadcycles} primitive@> +primitive("hangafter",assign_int,int_base+hang_after_code);@/ +@!@:hang_after_}{\.{\\hangafter} primitive@> +primitive("floatingpenalty",assign_int,int_base+floating_penalty_code);@/ +@!@:floating_penalty_}{\.{\\floatingpenalty} primitive@> +primitive("globaldefs",assign_int,int_base+global_defs_code);@/ +@!@:global_defs_}{\.{\\globaldefs} primitive@> +primitive("fam",assign_int,int_base+cur_fam_code);@/ +@!@:fam_}{\.{\\fam} primitive@> +primitive("escapechar",assign_int,int_base+escape_char_code);@/ +@!@:escape_char_}{\.{\\escapechar} primitive@> +primitive("defaulthyphenchar",assign_int,int_base+default_hyphen_char_code);@/ +@!@:default_hyphen_char_}{\.{\\defaulthyphenchar} primitive@> +primitive("defaultskewchar",assign_int,int_base+default_skew_char_code);@/ +@!@:default_skew_char_}{\.{\\defaultskewchar} primitive@> +primitive("endlinechar",assign_int,int_base+end_line_char_code);@/ +@!@:end_line_char_}{\.{\\endlinechar} primitive@> +primitive("newlinechar",assign_int,int_base+new_line_char_code);@/ +@!@:new_line_char_}{\.{\\newlinechar} primitive@> +primitive("language",assign_int,int_base+language_code);@/ +@!@:language_}{\.{\\language} primitive@> +primitive("lefthyphenmin",assign_int,int_base+left_hyphen_min_code);@/ +@!@:left_hyphen_min_}{\.{\\lefthyphenmin} primitive@> +primitive("righthyphenmin",assign_int,int_base+right_hyphen_min_code);@/ +@!@:right_hyphen_min_}{\.{\\righthyphenmin} primitive@> +primitive("holdinginserts",assign_int,int_base+holding_inserts_code);@/ +@!@:holding_inserts_}{\.{\\holdinginserts} primitive@> +primitive("errorcontextlines",assign_int,int_base+error_context_lines_code);@/ +@!@:error_context_lines_}{\.{\\errorcontextlines} primitive@> +primitive("pdfoutput",assign_int,int_base+pdf_output_code);@/ +@!@:pdf_output_}{\.{\\pdfoutput} primitive@> +primitive("pdfcompresslevel",assign_int,int_base+pdf_compress_level_code);@/ +@!@:pdf_compress_level_}{\.{\\pdfcompresslevel} primitive@> +primitive("pdfobjcompresslevel",assign_int,int_base+pdf_objcompresslevel_code);@/ +@!@:pdf_objcompresslevel_}{\.{\\pdfobjcompresslevel} primitive@> +primitive("pdfdecimaldigits",assign_int,int_base+pdf_decimal_digits_code);@/ +@!@:pdf_decimal_digits_}{\.{\\pdfdecimaldigits} primitive@> +primitive("pdfmovechars",assign_int,int_base+pdf_move_chars_code);@/ +@!@:pdf_move_chars_}{\.{\\pdfmovechars} primitive@> +primitive("pdfimageresolution",assign_int,int_base+pdf_image_resolution_code);@/ +@!@:pdf_image_resolution_}{\.{\\pdfimageresolution} primitive@> +primitive("pdfpkresolution",assign_int,int_base+pdf_pk_resolution_code);@/ +@!@:pdf_pk_resolution_}{\.{\\pdfpkresolution} primitive@> +primitive("pdfuniqueresname",assign_int,int_base+pdf_unique_resname_code);@/ +@!@:pdf_unique_resname_}{\.{\\pdfuniqueresname} primitive@> +primitive("pdfoptionpdfminorversion",assign_int,int_base+pdf_minor_version_code);@/ +@!@:pdf_minor_version_}{\.{\\pdfoptionpdfminorversion} primitive@> +primitive("pdfoptionalwaysusepdfpagebox",assign_int,int_base+pdf_option_always_use_pdfpagebox_code);@/ +@!@:pdf_option_always_use_pdfpagebox_}{\.{\\pdfoptionalwaysusepdfpagebox} primitive@> +primitive("pdfoptionpdfinclusionerrorlevel",assign_int,int_base+pdf_option_pdf_inclusion_errorlevel_code);@/ +@!@:pdf_option_pdf_inclusion_errorlevel_}{\.{\\pdfoptionpdfinclusionerrorlevel} primitive@> +primitive("pdfminorversion",assign_int,int_base+pdf_minor_version_code);@/ +@!@:pdf_minor_version_}{\.{\\pdfminorversion} primitive@> +primitive("pdfforcepagebox",assign_int,int_base+pdf_force_pagebox_code);@/ +@!@:pdf_force_pagebox_}{\.{\\pdfforcepagebox} primitive@> +primitive("pdfpagebox",assign_int,int_base+pdf_pagebox_code);@/ +@!@:pdf_pagebox_}{\.{\\pdfpagebox} primitive@> +primitive("pdfinclusionerrorlevel",assign_int,int_base+pdf_inclusion_errorlevel_code);@/ +@!@:pdf_inclusion_errorlevel_}{\.{\\pdfinclusionerrorlevel} primitive@> +primitive("pdfgamma",assign_int,int_base+pdf_gamma_code);@/ +@!@:pdf_gamma_}{\.{\\pdfgamma} primitive@> +primitive("pdfimagegamma",assign_int,int_base+pdf_image_gamma_code);@/ +@!@:pdf_image_gamma_}{\.{\\pdfimagegamma} primitive@> +primitive("pdfimagehicolor",assign_int,int_base+pdf_image_hicolor_code);@/ +@!@:pdf_image_hicolor_}{\.{\\pdfimagehicolor} primitive@> +primitive("pdfimageapplygamma",assign_int,int_base+pdf_image_apply_gamma_code);@/ +@!@:pdf_image_apply_gamma_}{\.{\\pdfimageapplygamma} primitive@> +primitive("pdfadjustspacing",assign_int,int_base+pdf_adjust_spacing_code);@/ +@!@:pdf_adjust_spacing_}{\.{\\pdfadjustspacing} primitive@> +primitive("pdfprotrudechars",assign_int,int_base+pdf_protrude_chars_code);@/ +@!@:pdf_protrude_chars_}{\.{\\pdfprotrudechars} primitive@> +primitive("pdftracingfonts",assign_int,int_base+pdf_tracing_fonts_code);@/ +@!@:pdf_tracing_fonts_}{\.{\\pdftracingfonts} primitive@> +primitive("pdfadjustinterwordglue",assign_int,int_base+pdf_adjust_interword_glue_code);@/ +@!@:pdf_adjust_interword_glue_}{\.{\\pdfadjustinterwordglue} primitive@> +primitive("pdfprependkern",assign_int,int_base+pdf_prepend_kern_code);@/ +@!@:pdf_prepend_kern_}{\.{\\pdfprependkern} primitive@> +primitive("pdfappendkern",assign_int,int_base+pdf_append_kern_code);@/ +@!@:pdf_append_kern_}{\.{\\pdfappendkern} primitive@> +primitive("pdfgentounicode",assign_int,int_base+pdf_gen_tounicode_code);@/ +@!@:pdf_gen_tounicode_}{\.{\\pdfgentounicode} primitive@> +primitive("pdfdraftmode",assign_int,int_base+pdf_draftmode_code);@/ +@!@:pdf_draftmode_}{\.{\\pdfdraftmode} primitive@> + +@ @= +assign_int: if chr_code + +@= +for k:=int_base to del_code_base-1 do eqtb[k].int:=0; +mag:=1000; tolerance:=10000; hang_after:=1; max_dead_cycles:=25; +escape_char:="\"; end_line_char:=carriage_return; +for k:=0 to 255 do del_code(k):=-1; +del_code("."):=0; {this null delimiter is used in error recovery} + +@ The following procedure, which is called just before \TeX\ initializes its +input and output, establishes the initial values of the date and time. +@^system dependencies@> +Since standard \PASCAL\ cannot provide such information, something special +is needed. The program here simply specifies July 4, 1776, at noon; but +users probably want a better approximation to the truth. + +@p procedure fix_date_and_time; +begin time:=12*60; {minutes since midnight} +day:=4; {fourth day of the month} +month:=7; {seventh month of the year} +year:=1776; {Anno Domini} +end; + +@ @= +begin if n= +@!old_setting:0..max_selector; + +@ The final region of |eqtb| contains the dimension parameters defined +here, and the 256 \.{\\dimen} registers. + +@d par_indent_code=0 {indentation of paragraphs} +@d math_surround_code=1 {space around math in text} +@d line_skip_limit_code=2 {threshold for |line_skip| instead of |baseline_skip|} +@d hsize_code=3 {line width in horizontal mode} +@d vsize_code=4 {page height in vertical mode} +@d max_depth_code=5 {maximum depth of boxes on main pages} +@d split_max_depth_code=6 {maximum depth of boxes on split pages} +@d box_max_depth_code=7 {maximum depth of explicit vboxes} +@d hfuzz_code=8 {tolerance for overfull hbox messages} +@d vfuzz_code=9 {tolerance for overfull vbox messages} +@d delimiter_shortfall_code=10 {maximum amount uncovered by variable delimiters} +@d null_delimiter_space_code=11 {blank space in null delimiters} +@d script_space_code=12 {extra space after subscript or superscript} +@d pre_display_size_code=13 {length of text preceding a display} +@d display_width_code=14 {length of line for displayed equation} +@d display_indent_code=15 {indentation of line for displayed equation} +@d overfull_rule_code=16 {width of rule that identifies overfull hboxes} +@d hang_indent_code=17 {amount of hanging indentation} +@d h_offset_code=18 {amount of horizontal offset when shipping pages out} +@d v_offset_code=19 {amount of vertical offset when shipping pages out} +@d emergency_stretch_code=20 {reduces badnesses on final pass of line-breaking} +@d pdftex_first_dimen_code = 21 {first number defined in this section} +@d pdf_h_origin_code = pdftex_first_dimen_code + 0 {horigin of the PDF output} +@d pdf_v_origin_code = pdftex_first_dimen_code + 1 {vorigin of the PDF output} +@d pdf_page_width_code = pdftex_first_dimen_code + 2 {page width of the PDF output} +@d pdf_page_height_code = pdftex_first_dimen_code + 3 {page height of the PDF output} +@d pdf_link_margin_code = pdftex_first_dimen_code + 4 {link margin in the PDF output} +@d pdf_dest_margin_code = pdftex_first_dimen_code + 5 {dest margin in the PDF output} +@d pdf_thread_margin_code = pdftex_first_dimen_code + 6 {thread margin in the PDF output} +@d pdf_first_line_height_code = pdftex_first_dimen_code + 7 +@d pdf_last_line_depth_code = pdftex_first_dimen_code + 8 +@d pdf_each_line_height_code = pdftex_first_dimen_code + 9 +@d pdf_each_line_depth_code = pdftex_first_dimen_code + 10 +@d pdf_px_dimen_code = pdftex_first_dimen_code + 11 +@d pdftex_last_dimen_code = pdftex_first_dimen_code + 11 {last number defined in this section} +@d dimen_pars = pdftex_last_dimen_code + 1 {total number of dimension parameters} +@d scaled_base=dimen_base+dimen_pars + {table of 256 user-defined \.{\\dimen} registers} +@d eqtb_size=scaled_base+255 {largest subscript of |eqtb|} +@# +@d dimen(#)==eqtb[scaled_base+#].sc +@d dimen_par(#)==eqtb[dimen_base+#].sc {a scaled quantity} +@d par_indent==dimen_par(par_indent_code) +@d math_surround==dimen_par(math_surround_code) +@d line_skip_limit==dimen_par(line_skip_limit_code) +@d hsize==dimen_par(hsize_code) +@d vsize==dimen_par(vsize_code) +@d max_depth==dimen_par(max_depth_code) +@d split_max_depth==dimen_par(split_max_depth_code) +@d box_max_depth==dimen_par(box_max_depth_code) +@d hfuzz==dimen_par(hfuzz_code) +@d vfuzz==dimen_par(vfuzz_code) +@d delimiter_shortfall==dimen_par(delimiter_shortfall_code) +@d null_delimiter_space==dimen_par(null_delimiter_space_code) +@d script_space==dimen_par(script_space_code) +@d pre_display_size==dimen_par(pre_display_size_code) +@d display_width==dimen_par(display_width_code) +@d display_indent==dimen_par(display_indent_code) +@d overfull_rule==dimen_par(overfull_rule_code) +@d hang_indent==dimen_par(hang_indent_code) +@d h_offset==dimen_par(h_offset_code) +@d v_offset==dimen_par(v_offset_code) +@d emergency_stretch==dimen_par(emergency_stretch_code) +@d pdf_h_origin == dimen_par(pdf_h_origin_code) +@d pdf_v_origin == dimen_par(pdf_v_origin_code) +@d pdf_page_width == dimen_par(pdf_page_width_code) +@d pdf_page_height == dimen_par(pdf_page_height_code) +@d pdf_link_margin == dimen_par(pdf_link_margin_code) +@d pdf_dest_margin == dimen_par(pdf_dest_margin_code) +@d pdf_thread_margin == dimen_par(pdf_thread_margin_code) +@d pdf_first_line_height == dimen_par(pdf_first_line_height_code) +@d pdf_last_line_depth == dimen_par(pdf_last_line_depth_code) +@d pdf_each_line_height == dimen_par(pdf_each_line_height_code) +@d pdf_each_line_depth == dimen_par(pdf_each_line_depth_code) +@d pdf_px_dimen == dimen_par(pdf_px_dimen_code) + +@p procedure print_length_param(@!n:integer); +begin case n of +par_indent_code:print_esc("parindent"); +math_surround_code:print_esc("mathsurround"); +line_skip_limit_code:print_esc("lineskiplimit"); +hsize_code:print_esc("hsize"); +vsize_code:print_esc("vsize"); +max_depth_code:print_esc("maxdepth"); +split_max_depth_code:print_esc("splitmaxdepth"); +box_max_depth_code:print_esc("boxmaxdepth"); +hfuzz_code:print_esc("hfuzz"); +vfuzz_code:print_esc("vfuzz"); +delimiter_shortfall_code:print_esc("delimitershortfall"); +null_delimiter_space_code:print_esc("nulldelimiterspace"); +script_space_code:print_esc("scriptspace"); +pre_display_size_code:print_esc("predisplaysize"); +display_width_code:print_esc("displaywidth"); +display_indent_code:print_esc("displayindent"); +overfull_rule_code:print_esc("overfullrule"); +hang_indent_code:print_esc("hangindent"); +h_offset_code:print_esc("hoffset"); +v_offset_code:print_esc("voffset"); +emergency_stretch_code:print_esc("emergencystretch"); +pdf_h_origin_code: print_esc("pdfhorigin"); +pdf_v_origin_code: print_esc("pdfvorigin"); +pdf_page_width_code: print_esc("pdfpagewidth"); +pdf_page_height_code: print_esc("pdfpageheight"); +pdf_link_margin_code: print_esc("pdflinkmargin"); +pdf_dest_margin_code: print_esc("pdfdestmargin"); +pdf_thread_margin_code: print_esc("pdfthreadmargin"); +pdf_first_line_height_code: print_esc("pdffirstlineheight"); +pdf_last_line_depth_code: print_esc("pdflastlinedepth"); +pdf_each_line_height_code: print_esc("pdfeachlineheight"); +pdf_each_line_depth_code: print_esc("pdfeachlinedepth"); +pdf_px_dimen_code: print_esc("pdfpxdimen"); +othercases print("[unknown dimen parameter!]") +endcases; +end; + +@ @= +primitive("parindent",assign_dimen,dimen_base+par_indent_code);@/ +@!@:par_indent_}{\.{\\parindent} primitive@> +primitive("mathsurround",assign_dimen,dimen_base+math_surround_code);@/ +@!@:math_surround_}{\.{\\mathsurround} primitive@> +primitive("lineskiplimit",assign_dimen,dimen_base+line_skip_limit_code);@/ +@!@:line_skip_limit_}{\.{\\lineskiplimit} primitive@> +primitive("hsize",assign_dimen,dimen_base+hsize_code);@/ +@!@:hsize_}{\.{\\hsize} primitive@> +primitive("vsize",assign_dimen,dimen_base+vsize_code);@/ +@!@:vsize_}{\.{\\vsize} primitive@> +primitive("maxdepth",assign_dimen,dimen_base+max_depth_code);@/ +@!@:max_depth_}{\.{\\maxdepth} primitive@> +primitive("splitmaxdepth",assign_dimen,dimen_base+split_max_depth_code);@/ +@!@:split_max_depth_}{\.{\\splitmaxdepth} primitive@> +primitive("boxmaxdepth",assign_dimen,dimen_base+box_max_depth_code);@/ +@!@:box_max_depth_}{\.{\\boxmaxdepth} primitive@> +primitive("hfuzz",assign_dimen,dimen_base+hfuzz_code);@/ +@!@:hfuzz_}{\.{\\hfuzz} primitive@> +primitive("vfuzz",assign_dimen,dimen_base+vfuzz_code);@/ +@!@:vfuzz_}{\.{\\vfuzz} primitive@> +primitive("delimitershortfall", + assign_dimen,dimen_base+delimiter_shortfall_code);@/ +@!@:delimiter_shortfall_}{\.{\\delimitershortfall} primitive@> +primitive("nulldelimiterspace", + assign_dimen,dimen_base+null_delimiter_space_code);@/ +@!@:null_delimiter_space_}{\.{\\nulldelimiterspace} primitive@> +primitive("scriptspace",assign_dimen,dimen_base+script_space_code);@/ +@!@:script_space_}{\.{\\scriptspace} primitive@> +primitive("predisplaysize",assign_dimen,dimen_base+pre_display_size_code);@/ +@!@:pre_display_size_}{\.{\\predisplaysize} primitive@> +primitive("displaywidth",assign_dimen,dimen_base+display_width_code);@/ +@!@:display_width_}{\.{\\displaywidth} primitive@> +primitive("displayindent",assign_dimen,dimen_base+display_indent_code);@/ +@!@:display_indent_}{\.{\\displayindent} primitive@> +primitive("overfullrule",assign_dimen,dimen_base+overfull_rule_code);@/ +@!@:overfull_rule_}{\.{\\overfullrule} primitive@> +primitive("hangindent",assign_dimen,dimen_base+hang_indent_code);@/ +@!@:hang_indent_}{\.{\\hangindent} primitive@> +primitive("hoffset",assign_dimen,dimen_base+h_offset_code);@/ +@!@:h_offset_}{\.{\\hoffset} primitive@> +primitive("voffset",assign_dimen,dimen_base+v_offset_code);@/ +@!@:v_offset_}{\.{\\voffset} primitive@> +primitive("emergencystretch",assign_dimen,dimen_base+emergency_stretch_code);@/ +@!@:emergency_stretch_}{\.{\\emergencystretch} primitive@> +primitive("pdfhorigin",assign_dimen,dimen_base+pdf_h_origin_code);@/ +@!@:pdf_h_origin_}{\.{\\pdfhorigin} primitive@> +primitive("pdfvorigin",assign_dimen,dimen_base+pdf_v_origin_code);@/ +@!@:pdf_v_origin_}{\.{\\pdfvorigin} primitive@> +primitive("pdfpagewidth",assign_dimen,dimen_base+pdf_page_width_code);@/ +@!@:pdf_page_width_}{\.{\\pdfpagewidth} primitive@> +primitive("pdfpageheight",assign_dimen,dimen_base+pdf_page_height_code);@/ +@!@:pdf_page_height_}{\.{\\pdfpageheight} primitive@> +primitive("pdflinkmargin",assign_dimen,dimen_base+pdf_link_margin_code);@/ +@!@:pdf_link_margin_}{\.{\\pdflinkmargin} primitive@> +primitive("pdfdestmargin",assign_dimen,dimen_base+pdf_dest_margin_code);@/ +@!@:pdf_dest_margin_}{\.{\\pdfdestmargin} primitive@> +primitive("pdfthreadmargin",assign_dimen,dimen_base+pdf_thread_margin_code);@/ +@!@:pdf_thread_margin_}{\.{\\pdfthreadmargin} primitive@> +primitive("pdffirstlineheight",assign_dimen,dimen_base+pdf_first_line_height_code);@/ +@!@:pdf_first_line_height_}{\.{\\pdffirstlineheight} primitive@> +primitive("pdflastlinedepth",assign_dimen,dimen_base+pdf_last_line_depth_code);@/ +@!@:pdf_last_line_depth_}{\.{\\pdflastlinedepth} primitive@> +primitive("pdfeachlineheight",assign_dimen,dimen_base+pdf_each_line_height_code);@/ +@!@:pdf_each_line_height_}{\.{\\pdfeachlineheight} primitive@> +primitive("pdfeachlinedepth",assign_dimen,dimen_base+pdf_each_line_depth_code);@/ +@!@:pdf_each_line_depth_}{\.{\\pdfeachlinedepth} primitive@> +primitive("pdfpxdimen",assign_dimen,dimen_base+pdf_px_dimen_code);@/ +@!@:pdf_px_dimen_}{\.{\\pdfpxdimen} primitive@> + +@ @= +assign_dimen: if chr_code= +for k:=dimen_base to eqtb_size do eqtb[k].sc:=0; + +@ @= +begin if n@@;@/ +@!stat procedure show_eqtb(@!n:pointer); +begin if n +else if n +else if n +else if n +else if n<=eqtb_size then @ +else print_char("?"); {this can't happen either} +end; +tats + +@ The last two regions of |eqtb| have fullword values instead of the +three fields |eq_level|, |eq_type|, and |equiv|. An |eq_type| is unnecessary, +but \TeX\ needs to store the |eq_level| information in another array +called |xeq_level|. + +@= +@!eqtb:array[active_base..eqtb_size] of memory_word; +@!xeq_level:array[int_base..eqtb_size] of quarterword; + +@ @= +for k:=int_base to eqtb_size do xeq_level[k]:=level_one; + +@ When the debugging routine |search_mem| is looking for pointers having a +given value, it is interested only in regions 1 to~3 of~|eqtb|, and in the +first part of region~4. + +@= +for q:=active_base to box_base+255 do + begin if equiv(q)=p then + begin print_nl("EQUIV("); print_int(q); print_char(")"); + end; + end + +@* \[18] The hash table. +Control sequences are stored and retrieved by means of a fairly standard hash +table algorithm called the method of ``coalescing lists'' (cf.\ Algorithm 6.4C +in {\sl The Art of Computer Programming\/}). Once a control sequence enters the +table, it is never removed, because there are complicated situations +involving \.{\\gdef} where the removal of a control sequence at the end of +a group would be a mistake preventable only by the introduction of a +complicated reference-count mechanism. + +The actual sequence of letters forming a control sequence identifier is +stored in the |str_pool| array together with all the other strings. An +auxiliary array |hash| consists of items with two halfword fields per +word. The first of these, called |next(p)|, points to the next identifier +belonging to the same coalesced list as the identifier corresponding to~|p|; +and the other, called |text(p)|, points to the |str_start| entry for +|p|'s identifier. If position~|p| of the hash table is empty, we have +|text(p)=0|; if position |p| is either empty or the end of a coalesced +hash list, we have |next(p)=0|. An auxiliary pointer variable called +|hash_used| is maintained in such a way that all locations |p>=hash_used| +are nonempty. The global variable |cs_count| tells how many multiletter +control sequences have been defined, if statistics are being kept. + +A global boolean variable called |no_new_control_sequence| is set to +|true| during the time that new hash table entries are forbidden. + +@d next(#) == hash[#].lh {link for coalesced lists} +@d text(#) == hash[#].rh {string number for control sequence name} +@d hash_is_full == (hash_used=hash_base) {test if all positions are occupied} +@d font_id_text(#) == text(font_id_base+#) {a frozen font identifier's name} + +@= +@!hash: array[hash_base..undefined_control_sequence-1] of two_halves; + {the hash table} +@!hash_used:pointer; {allocation pointer for |hash|} +@!no_new_control_sequence:boolean; {are new identifiers legal?} +@!cs_count:integer; {total number of known identifiers} + +@ Primitive support needs a few extra variables and definitions + +@d prim_size=2100 {maximum number of primitives } +@d prim_prime=1777 {about 85\pct! of |primitive_size|} +@d prim_base=1 +@d prim_next(#) == prim[#].lh {link for coalesced lists} +@d prim_text(#) == prim[#].rh {string number for control sequence name} +@d prim_is_full == (prim_used=prim_base) {test if all positions are occupied} +@d prim_eq_level_field(#)==#.hh.b1 +@d prim_eq_type_field(#)==#.hh.b0 +@d prim_equiv_field(#)==#.hh.rh +@d prim_eq_level(#)==prim_eq_level_field(prim_eqtb[#]) {level of definition} +@d prim_eq_type(#)==prim_eq_type_field(prim_eqtb[#]) {command code for equivalent} +@d prim_equiv(#)==prim_equiv_field(prim_eqtb[#]) {equivalent value} +@d undefined_primitive=0 + +@= +@!prim: array [0..prim_size] of two_halves; {the primitives table} +@!prim_used:pointer; {allocation pointer for |prim|} +@!prim_eqtb:array[0..prim_size] of memory_word; + +@ @= +no_new_control_sequence:=true; {new identifiers are usually forbidden} +prim_next(0):=0; prim_text(0):=0; +for k:=1 to prim_size do prim[k]:=prim[0]; +prim_eq_level(0) := level_zero; +prim_eq_type(0) := undefined_cs; +prim_equiv(0) := null; +for k:=1 to prim_size do prim_eqtb[k]:=prim_eqtb[0]; +next(hash_base):=0; text(hash_base):=0; +for k:=hash_base+1 to undefined_control_sequence-1 do hash[k]:=hash[hash_base]; + +@ @= +prim_used:=prim_size; {nothing is used} +hash_used:=frozen_control_sequence; {nothing is used} +cs_count:=0; +eq_type(frozen_dont_expand):=dont_expand; +text(frozen_dont_expand):="notexpanded:"; +@.notexpanded:@> + +eq_type(frozen_primitive):=ignore_spaces; +equiv(frozen_primitive):=1; +eq_level(frozen_primitive):=level_one; +text(frozen_primitive):="pdfprimitive"; +@ Here is the subroutine that searches the hash table for an identifier +that matches a given string of length |l>1| appearing in |buffer[j.. +(j+l-1)]|. If the identifier is found, the corresponding hash table address +is returned. Otherwise, if the global variable |no_new_control_sequence| +is |true|, the dummy address |undefined_control_sequence| is returned. +Otherwise the identifier is inserted into the hash table and its location +is returned. + +@p function id_lookup(@!j,@!l:integer):pointer; {search the hash table} +label found; {go here if you found it} +var h:integer; {hash code} +@!d:integer; {number of characters in incomplete current string} +@!p:pointer; {index in |hash| array} +@!k:pointer; {index in |buffer| array} +begin @; +p:=h+hash_base; {we start searching here; note that |0<=h0 then if length(text(p))=l then + if str_eq_buf(text(p),j) then goto found; + if next(p)=0 then + begin if no_new_control_sequence then + p:=undefined_control_sequence + else @; + goto found; + end; + p:=next(p); + end; +found: id_lookup:=p; +end; + +@ @= +begin if text(p)>0 then + begin repeat if hash_is_full then overflow("hash size",hash_size); +@:TeX capacity exceeded hash size}{\quad hash size@> + decr(hash_used); + until text(hash_used)=0; {search for an empty location in |hash|} + next(p):=hash_used; p:=hash_used; + end; +str_room(l); d:=cur_length; +while pool_ptr>str_start[str_ptr] do + begin decr(pool_ptr); str_pool[pool_ptr+l]:=str_pool[pool_ptr]; + end; {move current string up to make room for another} +for k:=j to j+l-1 do append_char(buffer[k]); +text(p):=make_string; pool_ptr:=pool_ptr+d; +@!stat incr(cs_count);@+tats@;@/ +end + +@ The value of |hash_prime| should be roughly 85\pct! of |hash_size|, and it +should be a prime number. The theory of hashing tells us to expect fewer +than two table probes, on the average, when the search is successful. +[See J.~S. Vitter, {\sl Journal of the ACM\/ \bf30} (1983), 231--258.] +@^Vitter, Jeffrey Scott@> + +@= +h:=buffer[j]; +for k:=j+1 to j+l-1 do + begin h:=h+h+buffer[k]; + while h>=hash_prime do h:=h-hash_prime; + end + +@ Here is the subroutine that searches the primitive table for an identifier + +@p function prim_lookup(@!s:str_number):pointer; {search the primitives table} +label found; {go here if you found it} +var h:integer; {hash code} +@!p:pointer; {index in |hash| array} +@!k:pointer; {index in string pool} +@!j,@!l:integer; +begin +if s<256 then begin + p := s; + if (p<0) or (prim_eq_level(p)<>level_one) then + p := undefined_primitive; +end +else begin + j:=str_start[s]; + if s = str_ptr then l := cur_length else l := length(s); + @; + p:=h+prim_base; {we start searching here; note that |0<=h0 then if length(prim_text(p))=l then + if str_eq_str(prim_text(p),s) then goto found; + if prim_next(p)=0 then + begin if no_new_control_sequence then + p:=undefined_primitive + else @; + goto found; + end; + p:=prim_next(p); + end; + end; +found: prim_lookup:=p; +end; + +@ @= +begin if prim_text(p)>0 then + begin repeat if prim_is_full then overflow("primitive size",prim_size); +@:TeX capacity exceeded primitive size}{\quad primitive size@> + decr(prim_used); + until prim_text(prim_used)=0; {search for an empty location in |prim|} + prim_next(p):=prim_used; p:=prim_used; + end; +prim_text(p):=s; +end + +@ The value of |prim_prime| should be roughly 85\pct! of +|prim_size|, and it should be a prime number. + +@= +h:=str_pool[j]; +for k:=j+1 to j+l-1 do + begin h:=h+h+str_pool[k]; + while h>=prim_prime do h:=h-prim_prime; + end + + +@ Single-character control sequences do not need to be looked up in a hash +table, since we can use the character code itself as a direct address. +The procedure |print_cs| prints the name of a control sequence, given +a pointer to its address in |eqtb|. A space is printed after the name +unless it is a single nonletter or an active character. This procedure +might be invoked with invalid data, so it is ``extra robust.'' The +individual characters must be printed one at a time using |print|, since +they may be unprintable. + +@= +procedure print_cs(@!p:integer); {prints a purported control sequence} +begin if p=single_base then + if p=null_cs then + begin print_esc("csname"); print_esc("endcsname"); + end + else begin print_esc(p-single_base); + if cat_code(p-single_base)=letter then print_char(" "); + end + else if p + else print(p-active_base) +else if p>=undefined_control_sequence then print_esc("IMPOSSIBLE.") +else if (text(p)<0)or(text(p)>=str_ptr) then print_esc("NONEXISTENT.") +@.NONEXISTENT@> +else begin print_esc(text(p)); + print_char(" "); + end; +end; + +@ Here is a similar procedure; it avoids the error checks, and it never +prints a space after the control sequence. + +@= +procedure sprint_cs(@!p:pointer); {prints a control sequence} +begin if pbuf_size+1 then + overflow("buffer size",buf_size); +@:TeX capacity exceeded buffer size}{\quad buffer size@> + for j:=0 to l-1 do buffer[first+j]:=so(str_pool[k+j]); + cur_val:=id_lookup(first,l); {|no_new_control_sequence| is |false|} + flush_string; text(cur_val):=s; {we don't want to have the string twice} + prim_val:=prim_lookup(s); + end; +eq_level(cur_val):=level_one; eq_type(cur_val):=c; equiv(cur_val):=o; +prim_eq_level(prim_val):=level_one; +prim_eq_type(prim_val):=c; +prim_equiv(prim_val):=o; +end; +tini + +@ Many of \TeX's primitives need no |equiv|, since they are identifiable +by their |eq_type| alone. These primitives are loaded into the hash table +as follows: + +@= +primitive(" ",ex_space,0);@/ +@!@:Single-character primitives /}{\quad\.{\\\ }@> +primitive("/",ital_corr,0);@/ +@!@:Single-character primitives /}{\quad\.{\\/}@> +primitive("accent",accent,0);@/ +@!@:accent_}{\.{\\accent} primitive@> +primitive("advance",advance,0);@/ +@!@:advance_}{\.{\\advance} primitive@> +primitive("afterassignment",after_assignment,0);@/ +@!@:after_assignment_}{\.{\\afterassignment} primitive@> +primitive("aftergroup",after_group,0);@/ +@!@:after_group_}{\.{\\aftergroup} primitive@> +primitive("begingroup",begin_group,0);@/ +@!@:begin_group_}{\.{\\begingroup} primitive@> +primitive("char",char_num,0);@/ +@!@:char_}{\.{\\char} primitive@> +primitive("csname",cs_name,0);@/ +@!@:cs_name_}{\.{\\csname} primitive@> +primitive("delimiter",delim_num,0);@/ +@!@:delimiter_}{\.{\\delimiter} primitive@> +primitive("divide",divide,0);@/ +@!@:divide_}{\.{\\divide} primitive@> +primitive("endcsname",end_cs_name,0);@/ +@!@:end_cs_name_}{\.{\\endcsname} primitive@> +primitive("endgroup",end_group,0); +@!@:end_group_}{\.{\\endgroup} primitive@> +text(frozen_end_group):="endgroup"; eqtb[frozen_end_group]:=eqtb[cur_val];@/ +primitive("expandafter",expand_after,0);@/ +@!@:expand_after_}{\.{\\expandafter} primitive@> +primitive("font",def_font,0);@/ +@!@:font_}{\.{\\font} primitive@> +primitive("letterspacefont",letterspace_font,0);@/ +@!@:letterspace_font_}{\.{\\letterspacefont} primitive@> +primitive("fontdimen",assign_font_dimen,0);@/ +@!@:font_dimen_}{\.{\\fontdimen} primitive@> +primitive("halign",halign,0);@/ +@!@:halign_}{\.{\\halign} primitive@> +primitive("hrule",hrule,0);@/ +@!@:hrule_}{\.{\\hrule} primitive@> +primitive("ignorespaces",ignore_spaces,0);@/ +@!@:ignore_spaces_}{\.{\\ignorespaces} primitive@> +primitive("insert",insert,0);@/ +@!@:insert_}{\.{\\insert} primitive@> +primitive("mark",mark,0);@/ +@!@:mark_}{\.{\\mark} primitive@> +primitive("mathaccent",math_accent,0);@/ +@!@:math_accent_}{\.{\\mathaccent} primitive@> +primitive("mathchar",math_char_num,0);@/ +@!@:math_char_}{\.{\\mathchar} primitive@> +primitive("mathchoice",math_choice,0);@/ +@!@:math_choice_}{\.{\\mathchoice} primitive@> +primitive("multiply",multiply,0);@/ +@!@:multiply_}{\.{\\multiply} primitive@> +primitive("noalign",no_align,0);@/ +@!@:no_align_}{\.{\\noalign} primitive@> +primitive("noboundary",no_boundary,0);@/ +@!@:no_boundary_}{\.{\\noboundary} primitive@> +primitive("noexpand",no_expand,0);@/ +@!@:no_expand_}{\.{\\noexpand} primitive@> +primitive("pdfprimitive",no_expand,1);@/ +@!@:pdfprimitive_}{\.{\\pdfprimitive} primitive@> +primitive("nonscript",non_script,0);@/ +@!@:non_script_}{\.{\\nonscript} primitive@> +primitive("omit",omit,0);@/ +@!@:omit_}{\.{\\omit} primitive@> +primitive("parshape",set_shape,par_shape_loc);@/ +@!@:par_shape_}{\.{\\parshape} primitive@> +primitive("penalty",break_penalty,0);@/ +@!@:penalty_}{\.{\\penalty} primitive@> +primitive("prevgraf",set_prev_graf,0);@/ +@!@:prev_graf_}{\.{\\prevgraf} primitive@> +primitive("radical",radical,0);@/ +@!@:radical_}{\.{\\radical} primitive@> +primitive("read",read_to_cs,0);@/ +@!@:read_}{\.{\\read} primitive@> +primitive("relax",relax,256); {cf.\ |scan_file_name|} +@!@:relax_}{\.{\\relax} primitive@> +text(frozen_relax):="relax"; eqtb[frozen_relax]:=eqtb[cur_val];@/ +primitive("setbox",set_box,0);@/ +@!@:set_box_}{\.{\\setbox} primitive@> +primitive("the",the,0);@/ +@!@:the_}{\.{\\the} primitive@> +primitive("toks",toks_register,mem_bot);@/ +@!@:toks_}{\.{\\toks} primitive@> +primitive("vadjust",vadjust,0);@/ +@!@:vadjust_}{\.{\\vadjust} primitive@> +primitive("valign",valign,0);@/ +@!@:valign_}{\.{\\valign} primitive@> +primitive("vcenter",vcenter,0);@/ +@!@:vcenter_}{\.{\\vcenter} primitive@> +primitive("vrule",vrule,0);@/ +@!@:vrule_}{\.{\\vrule} primitive@> + +@ Each primitive has a corresponding inverse, so that it is possible to +display the cryptic numeric contents of |eqtb| in symbolic form. +Every call of |primitive| in this program is therefore accompanied by some +straightforward code that forms part of the |print_cmd_chr| routine +below. + +@= +accent: print_esc("accent"); +advance: print_esc("advance"); +after_assignment: print_esc("afterassignment"); +after_group: print_esc("aftergroup"); +assign_font_dimen: print_esc("fontdimen"); +begin_group: print_esc("begingroup"); +break_penalty: print_esc("penalty"); +char_num: print_esc("char"); +cs_name: print_esc("csname"); +def_font: print_esc("font"); +letterspace_font: print_esc("letterspacefont"); +delim_num: print_esc("delimiter"); +divide: print_esc("divide"); +end_cs_name: print_esc("endcsname"); +end_group: print_esc("endgroup"); +ex_space: print_esc(" "); +expand_after: if chr_code=0 then print_esc("expandafter") + @; +halign: print_esc("halign"); +hrule: print_esc("hrule"); +ignore_spaces: if chr_code=0 then print_esc("ignorespaces") else print_esc("pdfprimitive"); +insert: print_esc("insert"); +ital_corr: print_esc("/"); +mark: begin print_esc("mark"); + if chr_code>0 then print_char("s"); + end; +math_accent: print_esc("mathaccent"); +math_char_num: print_esc("mathchar"); +math_choice: print_esc("mathchoice"); +multiply: print_esc("multiply"); +no_align: print_esc("noalign"); +no_boundary:print_esc("noboundary"); +no_expand: if chr_code=0 then print_esc("noexpand") + else print_esc("pdfprimitive"); +non_script: print_esc("nonscript"); +omit: print_esc("omit"); +radical: print_esc("radical"); +read_to_cs: if chr_code=0 then print_esc("read") + @; +relax: print_esc("relax"); +set_box: print_esc("setbox"); +set_prev_graf: print_esc("prevgraf"); +set_shape: case chr_code of + par_shape_loc: print_esc("parshape"); + @@;@/ + end; {there are no other cases} +the: if chr_code=0 then print_esc("the") + @; +toks_register: @; +vadjust: print_esc("vadjust"); +valign: if chr_code=0 then print_esc("valign")@/ + @; +vcenter: print_esc("vcenter"); +vrule: print_esc("vrule"); + +@ We will deal with the other primitives later, at some point in the program +where their |eq_type| and |equiv| values are more meaningful. For example, +the primitives for math mode will be loaded when we consider the routines +that deal with formulas. It is easy to find where each particular +primitive was treated by looking in the index at the end; for example, the +section where |"radical"| entered |eqtb| is listed under `\.{\\radical} +primitive'. (Primitives consisting of a single nonalphabetic character, +@!like `\.{\\/}', are listed under `Single-character primitives'.) +@!@^Single-character primitives@> + +Meanwhile, this is a convenient place to catch up on something we were unable +to do before the hash table was defined: + +@= +begin + if pdf_font_blink[font(p)] = null_font then + print_esc(font_id_text(font(p))) + else + print_esc(font_id_text(pdf_font_blink[font(p)])); + if pdf_tracing_fonts > 0 then begin + print(" ("); + print(font_name[font(p)]); + if font_size[font(p)] <> font_dsize[font(p)] then begin + print("@@"); + print_scaled(font_size[font(p)]); + print("pt"); + end; + print(")"); + end else + if pdf_font_expand_ratio[font(p)] <> 0 then begin + print(" ("); + if pdf_font_expand_ratio[font(p)] > 0 then + print("+"); + print_int(pdf_font_expand_ratio[font(p)]); + print(")"); + end; +end + +@* \[19] Saving and restoring equivalents. +The nested structure provided by `$\.{\char'173}\ldots\.{\char'175}$' groups +in \TeX\ means that |eqtb| entries valid in outer groups should be saved +and restored later if they are overridden inside the braces. When a new |eqtb| +value is being assigned, the program therefore checks to see if the previous +entry belongs to an outer level. In such a case, the old value is placed +on the |save_stack| just before the new value enters |eqtb|. At the +end of a grouping level, i.e., when the right brace is sensed, the +|save_stack| is used to restore the outer values, and the inner ones are +destroyed. + +Entries on the |save_stack| are of type |memory_word|. The top item on +this stack is |save_stack[p]|, where |p=save_ptr-1|; it contains three +fields called |save_type|, |save_level|, and |save_index|, and it is +interpreted in one of five ways: + +\yskip\hangg 1) If |save_type(p)=restore_old_value|, then +|save_index(p)| is a location in |eqtb| whose current value should +be destroyed at the end of the current group and replaced by |save_stack[p-1]|. +Furthermore if |save_index(p)>=int_base|, then |save_level(p)| +should replace the corresponding entry in |xeq_level|. + +\yskip\hangg 2) If |save_type(p)=restore_zero|, then |save_index(p)| +is a location in |eqtb| whose current value should be destroyed at the end +of the current group, when it should be +replaced by the current value of |eqtb[undefined_control_sequence]|. + +\yskip\hangg 3) If |save_type(p)=insert_token|, then |save_index(p)| +is a token that should be inserted into \TeX's input when the current +group ends. + +\yskip\hangg 4) If |save_type(p)=level_boundary|, then |save_level(p)| +is a code explaining what kind of group we were previously in, and +|save_index(p)| points to the level boundary word at the bottom of +the entries for that group. +Furthermore, in extended \eTeX\ mode, |save_stack[p-1]| contains the +source line number at which the current level of grouping was entered. + +\yskip\hang 5) If |save_type(p)=restore_sa|, then |sa_chain| points to a +chain of sparse array entries to be restored at the end of the current +group. Furthermore |save_index(p)| and |save_level(p)| should replace +the values of |sa_chain| and |sa_level| respectively. + +@d save_type(#)==save_stack[#].hh.b0 {classifies a |save_stack| entry} +@d save_level(#)==save_stack[#].hh.b1 + {saved level for regions 5 and 6, or group code} +@d save_index(#)==save_stack[#].hh.rh + {|eqtb| location or |save_stack| location} +@d restore_old_value=0 {|save_type| when a value should be restored later} +@d restore_zero=1 {|save_type| when an undefined entry should be restored} +@d insert_token=2 {|save_type| when a token is being saved for later use} +@d level_boundary=3 {|save_type| corresponding to beginning of group} +@d restore_sa=4 {|save_type| when sparse array entries should be restored} + +@p@t\4@>@ + +@ Here are the group codes that are used to discriminate between different +kinds of groups. They allow \TeX\ to decide what special actions, if any, +should be performed when a group ends. +\def\grp{\.{\char'173...\char'175}} + +Some groups are not supposed to be ended by right braces. For example, +the `\.\$' that begins a math formula causes a |math_shift_group| to +be started, and this should be terminated by a matching `\.\$'. Similarly, +a group that starts with \.{\\left} should end with \.{\\right}, and +one that starts with \.{\\begingroup} should end with \.{\\endgroup}. + +@d bottom_level=0 {group code for the outside world} +@d simple_group=1 {group code for local structure only} +@d hbox_group=2 {code for `\.{\\hbox}\grp'} +@d adjusted_hbox_group=3 {code for `\.{\\hbox}\grp' in vertical mode} +@d vbox_group=4 {code for `\.{\\vbox}\grp'} +@d vtop_group=5 {code for `\.{\\vtop}\grp'} +@d align_group=6 {code for `\.{\\halign}\grp', `\.{\\valign}\grp'} +@d no_align_group=7 {code for `\.{\\noalign}\grp'} +@d output_group=8 {code for output routine} +@d math_group=9 {code for, e.g, `\.{\char'136}\grp'} +@d disc_group=10 {code for `\.{\\discretionary}\grp\grp\grp'} +@d insert_group=11 {code for `\.{\\insert}\grp', `\.{\\vadjust}\grp'} +@d vcenter_group=12 {code for `\.{\\vcenter}\grp'} +@d math_choice_group=13 {code for `\.{\\mathchoice}\grp\grp\grp\grp'} +@d semi_simple_group=14 {code for `\.{\\begingroup...\\endgroup}'} +@d math_shift_group=15 {code for `\.{\$...\$}'} +@d math_left_group=16 {code for `\.{\\left...\\right}'} +@d max_group_code=16 + +@= +@!group_code=0..max_group_code; {|save_level| for a level boundary} + +@ The global variable |cur_group| keeps track of what sort of group we are +currently in. Another global variable, |cur_boundary|, points to the +topmost |level_boundary| word. And |cur_level| is the current depth of +nesting. The routines are designed to preserve the condition that no entry +in the |save_stack| or in |eqtb| ever has a level greater than |cur_level|. + +@ @= +@!save_stack : array[0..save_size] of memory_word; +@!save_ptr : 0..save_size; {first unused entry on |save_stack|} +@!max_save_stack:0..save_size; {maximum usage of save stack} +@!cur_level: quarterword; {current nesting level for groups} +@!cur_group: group_code; {current group type} +@!cur_boundary: 0..save_size; {where the current level begins} + +@ At this time it might be a good idea for the reader to review the introduction +to |eqtb| that was given above just before the long lists of parameter names. +Recall that the ``outer level'' of the program is |level_one|, since +undefined control sequences are assumed to be ``defined'' at |level_zero|. + +@= +save_ptr:=0; cur_level:=level_one; cur_group:=bottom_level; cur_boundary:=0; +max_save_stack:=0; + +@ The following macro is used to test if there is room for up to seven more +entries on |save_stack|. By making a conservative test like this, we can +get by with testing for overflow in only a few places. + +@d check_full_save_stack==if save_ptr>max_save_stack then + begin max_save_stack:=save_ptr; + if max_save_stack>save_size-7 then overflow("save size",save_size); +@:TeX capacity exceeded save size}{\quad save size@> + end + +@ Procedure |new_save_level| is called when a group begins. The +argument is a group identification code like `|hbox_group|'. After +calling this routine, it is safe to put five more entries on |save_stack|. + +In some cases integer-valued items are placed onto the +|save_stack| just below a |level_boundary| word, because this is a +convenient place to keep information that is supposed to ``pop up'' just +when the group has finished. +For example, when `\.{\\hbox to 100pt}\grp' is being treated, the 100pt +dimension is stored on |save_stack| just before |new_save_level| is +called. + +We use the notation |saved(k)| to stand for an integer item that +appears in location |save_ptr+k| of the save stack. + +@d saved(#)==save_stack[save_ptr+#].int + +@p procedure new_save_level(@!c:group_code); {begin a new level of grouping} +begin check_full_save_stack; +if eTeX_ex then + begin saved(0):=line; incr(save_ptr); + end; +save_type(save_ptr):=level_boundary; save_level(save_ptr):=cur_group; +save_index(save_ptr):=cur_boundary; +if cur_level=max_quarterword then overflow("grouping levels", +@:TeX capacity exceeded grouping levels}{\quad grouping levels@> + max_quarterword-min_quarterword); + {quit if |(cur_level+1)| is too big to be stored in |eqtb|} +cur_boundary:=save_ptr; cur_group:=c; +@!stat if tracing_groups>0 then group_trace(false);@+tats@;@/ +incr(cur_level); incr(save_ptr); +end; + +@ Just before an entry of |eqtb| is changed, the following procedure should +be called to update the other data structures properly. It is important +to keep in mind that reference counts in |mem| include references from +within |save_stack|, so these counts must be handled carefully. +@^reference counts@> + +@p procedure eq_destroy(@!w:memory_word); {gets ready to forget |w|} +var q:pointer; {|equiv| field of |w|} +begin case eq_type_field(w) of +call,long_call,outer_call,long_outer_call: delete_token_ref(equiv_field(w)); +glue_ref: delete_glue_ref(equiv_field(w)); +shape_ref: begin q:=equiv_field(w); {we need to free a \.{\\parshape} block} + if q<>null then free_node(q,info(q)+info(q)+1); + end; {such a block is |2n+1| words long, where |n=info(q)|} +box_ref: flush_node_list(equiv_field(w)); +@/@@/ +othercases do_nothing +endcases; +end; + +@ To save a value of |eqtb[p]| that was established at level |l|, we +can use the following subroutine. + +@p procedure eq_save(@!p:pointer;@!l:quarterword); {saves |eqtb[p]|} +begin check_full_save_stack; +if l=level_zero then save_type(save_ptr):=restore_zero +else begin save_stack[save_ptr]:=eqtb[p]; incr(save_ptr); + save_type(save_ptr):=restore_old_value; + end; +save_level(save_ptr):=l; save_index(save_ptr):=p; incr(save_ptr); +end; + +@ The procedure |eq_define| defines an |eqtb| entry having specified +|eq_type| and |equiv| fields, and saves the former value if appropriate. +This procedure is used only for entries in the first four regions of |eqtb|, +i.e., only for entries that have |eq_type| and |equiv| fields. +After calling this routine, it is safe to put four more entries on +|save_stack|, provided that there was room for four more entries before +the call, since |eq_save| makes the necessary test. + +@d assign_trace(#)==@!stat if tracing_assigns>0 then restore_trace(#); + tats + +@p procedure eq_define(@!p:pointer;@!t:quarterword;@!e:halfword); + {new data for |eqtb|} +label exit; +begin if eTeX_ex and(eq_type(p)=t)and(equiv(p)=e) then + begin assign_trace(p,"reassigning")@;@/ + eq_destroy(eqtb[p]); return; + end; +assign_trace(p,"changing")@;@/ +if eq_level(p)=cur_level then eq_destroy(eqtb[p]) +else if cur_level>level_one then eq_save(p,eq_level(p)); +eq_level(p):=cur_level; eq_type(p):=t; equiv(p):=e; +assign_trace(p,"into")@;@/ +exit:end; + +@ The counterpart of |eq_define| for the remaining (fullword) positions in +|eqtb| is called |eq_word_define|. Since |xeq_level[p]>=level_one| for all +|p|, a `|restore_zero|' will never be used in this case. + +@p procedure eq_word_define(@!p:pointer;@!w:integer); +label exit; +begin if eTeX_ex and(eqtb[p].int=w) then + begin assign_trace(p,"reassigning")@;@/ + return; + end; +assign_trace(p,"changing")@;@/ +if xeq_level[p]<>cur_level then + begin eq_save(p,xeq_level[p]); xeq_level[p]:=cur_level; + end; +eqtb[p].int:=w; +assign_trace(p,"into")@;@/ +exit:end; + +@ The |eq_define| and |eq_word_define| routines take care of local definitions. +@^global definitions@> +Global definitions are done in almost the same way, but there is no need +to save old values, and the new value is associated with |level_one|. + +@p procedure geq_define(@!p:pointer;@!t:quarterword;@!e:halfword); + {global |eq_define|} +begin assign_trace(p,"globally changing")@;@/ +begin eq_destroy(eqtb[p]); +eq_level(p):=level_one; eq_type(p):=t; equiv(p):=e; +end; +assign_trace(p,"into")@;@/ +end; +@# +procedure geq_word_define(@!p:pointer;@!w:integer); {global |eq_word_define|} +begin assign_trace(p,"globally changing")@;@/ +begin eqtb[p].int:=w; xeq_level[p]:=level_one; +end; +assign_trace(p,"into")@;@/ +end; + +@ Subroutine |save_for_after| puts a token on the stack for save-keeping. + +@p procedure save_for_after(@!t:halfword); +begin if cur_level>level_one then + begin check_full_save_stack; + save_type(save_ptr):=insert_token; save_level(save_ptr):=level_zero; + save_index(save_ptr):=t; incr(save_ptr); + end; +end; + +@ The |unsave| routine goes the other way, taking items off of |save_stack|. +This routine takes care of restoration when a level ends; everything +belonging to the topmost group is cleared off of the save stack. + +@p +procedure@?back_input; forward; @t\2@> +procedure unsave; {pops the top level off the save stack} +label done; +var p:pointer; {position to be restored} +@!l:quarterword; {saved level, if in fullword regions of |eqtb|} +@!t:halfword; {saved value of |cur_tok|} +@!a:boolean; {have we already processed an \.{\\aftergroup} ?} +begin a:=false; +if cur_level>level_one then + begin decr(cur_level); + @; + end +else confusion("curlevel"); {|unsave| is not used when |cur_group=bottom_level|} +@:this can't happen curlevel}{\quad curlevel@> +end; + +@ @= +loop@+begin decr(save_ptr); + if save_type(save_ptr)=level_boundary then goto done; + p:=save_index(save_ptr); + if save_type(save_ptr)=insert_token then + @ + else if save_type(save_ptr)=restore_sa then + begin sa_restore; sa_chain:=p; sa_level:=save_level(save_ptr); + end + else begin if save_type(save_ptr)=restore_old_value then + begin l:=save_level(save_ptr); decr(save_ptr); + end + else save_stack[save_ptr]:=eqtb[undefined_control_sequence]; + @; + end; + end; +done: @!stat if tracing_groups>0 then group_trace(true);@+tats@;@/ +if grp_stack[in_open]=cur_boundary then group_warning; + {groups possibly not properly nested with files} +cur_group:=save_level(save_ptr); cur_boundary:=save_index(save_ptr); +if eTeX_ex then decr(save_ptr) + +@ A global definition, which sets the level to |level_one|, +@^global definitions@> +will not be undone by |unsave|. If at least one global definition of +|eqtb[p]| has been carried out within the group that just ended, the +last such definition will therefore survive. + +@= +if p0 then restore_trace(p,"retaining");@+tats@;@/ + end + else begin eq_destroy(eqtb[p]); {destroy the current value} + eqtb[p]:=save_stack[save_ptr]; {restore the saved value} + @!stat if tracing_restores>0 then restore_trace(p,"restoring");@+tats@;@/ + end +else if xeq_level[p]<>level_one then + begin eqtb[p]:=save_stack[save_ptr]; xeq_level[p]:=l; + @!stat if tracing_restores>0 then restore_trace(p,"restoring");@+tats@;@/ + end +else begin + @!stat if tracing_restores>0 then restore_trace(p,"retaining");@+tats@;@/ + end + +@ @= +@!stat procedure restore_trace(@!p:pointer;@!s:str_number); + {|eqtb[p]| has just been restored or retained} +begin begin_diagnostic; print_char("{"); print(s); print_char(" "); +show_eqtb(p); print_char("}"); +end_diagnostic(false); +end; +tats + +@ When looking for possible pointers to a memory location, it is helpful +to look for references from |eqtb| that might be waiting on the +save stack. Of course, we might find spurious pointers too; but this +routine is merely an aid when debugging, and at such times we are +grateful for any scraps of information, even if they prove to be irrelevant. +@^dirty \PASCAL@> + +@= +if save_ptr>0 then for q:=0 to save_ptr-1 do + begin if equiv_field(save_stack[q])=p then + begin print_nl("SAVE("); print_int(q); print_char(")"); + end; + end + +@ Most of the parameters kept in |eqtb| can be changed freely, but there's +an exception: The magnification should not be used with two different +values during any \TeX\ job, since a single magnification is applied to an +entire run. The global variable |mag_set| is set to the current magnification +whenever it becomes necessary to ``freeze'' it at a particular value. + +@= +@!mag_set:integer; {if nonzero, this magnification should be used henceforth} + +@ @= +mag_set:=0; + +@ The |prepare_mag| subroutine is called whenever \TeX\ wants to use |mag| +for magnification. + +@p procedure prepare_mag; +begin if (mag_set>0)and(mag<>mag_set) then + begin print_err("Incompatible magnification ("); print_int(mag); +@.Incompatible magnification@> + print(");"); print_nl(" the previous value will be retained"); + help2("I can handle only one magnification ratio per job. So I've")@/ + ("reverted to the magnification you used earlier on this run.");@/ + int_error(mag_set); + geq_word_define(int_base+mag_code,mag_set); {|mag:=mag_set|} + end; +if (mag<=0)or(mag>32768) then + begin print_err("Illegal magnification has been changed to 1000");@/ +@.Illegal magnification...@> + help1("The magnification ratio must be between 1 and 32768."); + int_error(mag); geq_word_define(int_base+mag_code,1000); + end; +mag_set:=mag; +end; + +@* \[20] Token lists. +A \TeX\ token is either a character or a control sequence, and it is +@^token@> +represented internally in one of two ways: (1)~A character whose ASCII +code number is |c| and whose command code is |m| is represented as the +number $2^8m+c$; the command code is in the range |1<=m<=14|. (2)~A control +sequence whose |eqtb| address is |p| is represented as the number +|cs_token_flag+p|. Here |cs_token_flag=@t$2^{12}-1$@>| is larger than +$2^8m+c$, yet it is small enough that |cs_token_flag+p< max_halfword|; +thus, a token fits comfortably in a halfword. + +A token |t| represents a |left_brace| command if and only if +|t= +if cs_token_flag+undefined_control_sequence>max_halfword then bad:=21; + +@ A token list is a singly linked list of one-word nodes in |mem|, where +each word contains a token and a link. Macro definitions, output-routine +definitions, marks, \.{\\write} texts, and a few other things +are remembered by \TeX\ in the form +of token lists, usually preceded by a node with a reference count in its +|token_ref_count| field. The token stored in location |p| is called +|info(p)|. + +Three special commands appear in the token lists of macro definitions. +When |m=match|, it means that \TeX\ should scan a parameter +for the current macro; when |m=end_match|, it means that parameter +matching should end and \TeX\ should start reading the macro text; and +when |m=out_param|, it means that \TeX\ should insert parameter +number |c| into the text at this point. + +The enclosing \.{\char'173} and \.{\char'175} characters of a macro +definition are omitted, but the final right brace of an output routine +is included at the end of its token list. + +Here is an example macro definition that illustrates these conventions. +After \TeX\ processes the text +$$\.{\\def\\mac a\#1\#2 \\b \{\#1\\-a \#\#1\#2 \#2\}}$$ +the definition of \.{\\mac} is represented as a token list containing +$$\def\,{\hskip2pt} +\vbox{\halign{\hfil#\hfil\cr +(reference count), |letter|\,\.a, |match|\,\#, |match|\,\#, |spacer|\,\.\ , +\.{\\b}, |end_match|,\cr +|out_param|\,1, \.{\\-}, |letter|\,\.a, |spacer|\,\.\ , |mac_param|\,\#, +|other_char|\,\.1,\cr +|out_param|\,2, |spacer|\,\.\ , |out_param|\,2.\cr}}$$ +The procedure |scan_toks| builds such token lists, and |macro_call| +does the parameter matching. +@^reference counts@> + +Examples such as +$$\.{\\def\\m\{\\def\\m\{a\}\ b\}}$$ +explain why reference counts would be needed even if \TeX\ had no \.{\\let} +operation: When the token list for \.{\\m} is being read, the redefinition of +\.{\\m} changes the |eqtb| entry before the token list has been fully +consumed, so we dare not simply destroy a token list when its +control sequence is being redefined. + +If the parameter-matching part of a definition ends with `\.{\#\{}', +the corresponding token list will have `\.\{' just before the `|end_match|' +and also at the very end. The first `\.\{' is used to delimit the parameter; the +second one keeps the first from disappearing. + +@ The procedure |show_token_list|, which prints a symbolic form of +the token list that starts at a given node |p|, illustrates these +conventions. The token list being displayed should not begin with a reference +count. However, the procedure is intended to be robust, so that if the +memory links are awry or if |p| is not really a pointer to a token list, +nothing catastrophic will happen. + +An additional parameter |q| is also given; this parameter is either null +or it points to a node in the token list where a certain magic computation +takes place that will be explained later. (Basically, |q| is non-null when +we are printing the two-line context information at the time of an error +message; |q| marks the place corresponding to where the second line +should begin.) + +For example, if |p| points to the node containing the first \.a in the +token list above, then |show_token_list| will print the string +$$\hbox{`\.{a\#1\#2\ \\b\ ->\#1\\-a\ \#\#1\#2\ \#2}';}$$ +and if |q| points to the node containing the second \.a, +the magic computation will be performed just before the second \.a is printed. + +The generation will stop, and `\.{\\ETC.}' will be printed, if the length +of printing exceeds a given limit~|l|. Anomalous entries are printed in the +form of control sequences that are not followed by a blank space, e.g., +`\.{\\BAD.}'; this cannot be confused with actual control sequences because +a real control sequence named \.{BAD} would come out `\.{\\BAD\ }'. + +@= +procedure show_token_list(@!p,@!q:integer;@!l:integer); +label exit; +var m,@!c:integer; {pieces of a token} +@!match_chr:ASCII_code; {character used in a `|match|'} +@!n:ASCII_code; {the highest parameter number, as an ASCII digit} +begin match_chr:="#"; n:="0"; tally:=0; +while (p<>null) and (tally; + @; + p:=link(p); + end; +if p<>null then print_esc("ETC."); +@.ETC@> +exit: +end; + +@ @= +if (pmem_end) then + begin print_esc("CLOBBERED."); return; +@.CLOBBERED@> + end; +if info(p)>=cs_token_flag then print_cs(info(p)-cs_token_flag) +else begin m:=info(p) div @'400; c:=info(p) mod @'400; + if info(p)<0 then print_esc("BAD.") +@.BAD@> + else @; + end + +@ The procedure usually ``learns'' the character code used for macro +parameters by seeing one in a |match| command before it runs into any +|out_param| commands. + +@= +case m of +left_brace,right_brace,math_shift,tab_mark,sup_mark,sub_mark,spacer, + letter,other_char: print(c); +mac_param: begin print(c); print(c); + end; +out_param: begin print(match_chr); + if c<=9 then print_char(c+"0") + else begin print_char("!"); return; + end; + end; +match: begin match_chr:=c; print(c); incr(n); print_char(n); + if n>"9" then return; + end; +end_match: if c=0 then print("->"); +@.->@> +othercases print_esc("BAD.") +@.BAD@> +endcases + +@ Here's the way we sometimes want to display a token list, given a pointer +to its reference count; the pointer may be null. + +@p procedure token_show(@!p:pointer); +begin if p<>null then show_token_list(link(p),null,10000000); +end; + +@ The |print_meaning| subroutine displays |cur_cmd| and |cur_chr| in +symbolic form, including the expansion of a macro or mark. + +@p procedure print_meaning; +begin print_cmd_chr(cur_cmd,cur_chr); +if cur_cmd>=call then + begin print_char(":"); print_ln; token_show(cur_chr); + end +else if (cur_cmd=top_bot_mark)and(cur_chr= +@!cur_cmd: eight_bits; {current command set by |get_next|} +@!cur_chr: halfword; {operand of current command} +@!cur_cs: pointer; {control sequence found here, zero if none found} +@!cur_tok: halfword; {packed representative of |cur_cmd| and |cur_chr|} + +@ The |print_cmd_chr| routine prints a symbolic interpretation of a +command code and its modifier. This is used in certain `\.{You can\'t}' +error messages, and in the implementation of diagnostic routines like +\.{\\show}. + +The body of |print_cmd_chr| is a rather tedious listing of print +commands, and most of it is essentially an inverse to the |primitive| +routine that enters a \TeX\ primitive into |eqtb|. Therefore much of +this procedure appears elsewhere in the program, +together with the corresponding |primitive| calls. + +@d chr_cmd(#)==begin print(#); print_ASCII(chr_code); + end + +@= +procedure print_cmd_chr(@!cmd:quarterword;@!chr_code:halfword); +var n:integer; {temp variable} +begin case cmd of +left_brace: chr_cmd("begin-group character "); +right_brace: chr_cmd("end-group character "); +math_shift: chr_cmd("math shift character "); +mac_param: chr_cmd("macro parameter character "); +sup_mark: chr_cmd("superscript character "); +sub_mark: chr_cmd("subscript character "); +endv: print("end of alignment template"); +spacer: chr_cmd("blank space "); +letter: chr_cmd("the letter "); +other_char: chr_cmd("the character "); +@t\4@>@@/ +othercases print("[unknown command code!]") +endcases; +end; + +@ Here is a procedure that displays the current command. + +@p procedure show_cur_cmd_chr; +var n:integer; {level of \.{\\if...\\fi} nesting} +@!l:integer; {line where \.{\\if} started} +@!p:pointer; +begin begin_diagnostic; print_nl("{"); +if mode<>shown_mode then + begin print_mode(mode); print(": "); shown_mode:=mode; + end; +print_cmd_chr(cur_cmd,cur_chr); +if tracing_ifs>0 then + if cur_cmd>=if_test then if cur_cmd<=fi_or_else then + begin print(": "); + if cur_cmd=fi_or_else then + begin print_cmd_chr(if_test,cur_if); print_char(" "); + n:=0; l:=if_line; + end + else begin n:=1; l:=line; + end; + p:=cond_ptr; + while p<>null do + begin incr(n); p:=link(p); + end; + print("(level "); print_int(n); print_char(")"); print_if_line(l); + end; +print_char("}"); +end_diagnostic(false); +end; + +@* \[22] Input stacks and states. +This implementation of +\TeX\ uses two different conventions for representing sequential stacks. +@^stack conventions@>@^conventions for representing stacks@> + +\yskip\hangg 1) If there is frequent access to the top entry, and if the +stack is essentially never empty, then the top entry is kept in a global +variable (even better would be a machine register), and the other entries +appear in the array $\\{stack}[0\to(\\{ptr}-1)]$. For example, the +semantic stack described above is handled this way, and so is the input +stack that we are about to study. + +\yskip\hangg 2) If there is infrequent top access, the entire stack contents +are in the array $\\{stack}[0\to(\\{ptr}-1)]$. For example, the |save_stack| +is treated this way, as we have seen. + +\yskip\noindent +The state of \TeX's input mechanism appears in the input stack, whose +entries are records with six fields, called |state|, |index|, |start|, |loc|, +|limit|, and |name|. This stack is maintained with +convention~(1), so it is declared in the following way: + +@= +@!in_state_record = record + @!state_field, @!index_field: quarterword; + @!start_field,@!loc_field, @!limit_field, @!name_field: halfword; + end; + +@ @= +@!input_stack : array[0..stack_size] of in_state_record; +@!input_ptr : 0..stack_size; {first unused location of |input_stack|} +@!max_in_stack: 0..stack_size; {largest value of |input_ptr| when pushing} +@!cur_input : in_state_record; + {the ``top'' input state, according to convention (1)} + +@ We've already defined the special variable |loc==cur_input.loc_field| +in our discussion of basic input-output routines. The other components of +|cur_input| are defined in the same way: + +@d state==cur_input.state_field {current scanner state} +@d index==cur_input.index_field {reference for buffer information} +@d start==cur_input.start_field {starting position in |buffer|} +@d limit==cur_input.limit_field {end of current line in |buffer|} +@d name==cur_input.name_field {name of the current file} + +@ Let's look more closely now at the control variables +(|state|,~|index|,~|start|,~|loc|,~|limit|,~|name|), +assuming that \TeX\ is reading a line of characters that have been input +from some file or from the user's terminal. There is an array called +|buffer| that acts as a stack of all lines of characters that are +currently being read from files, including all lines on subsidiary +levels of the input stack that are not yet completed. \TeX\ will return to +the other lines when it is finished with the present input file. + +(Incidentally, on a machine with byte-oriented addressing, it might be +appropriate to combine |buffer| with the |str_pool| array, +letting the buffer entries grow downward from the top of the string pool +and checking that these two tables don't bump into each other.) + +The line we are currently working on begins in position |start| of the +buffer; the next character we are about to read is |buffer[loc]|; and +|limit| is the location of the last character present. If |loc>limit|, +the line has been completely read. Usually |buffer[limit]| is the +|end_line_char|, denoting the end of a line, but this is not +true if the current line is an insertion that was entered on the user's +terminal in response to an error message. + +The |name| variable is a string number that designates the name of +the current file, if we are reading a text file. It is zero if we +are reading from the terminal; it is |n+1| if we are reading from +input stream |n|, where |0<=n<=16|. (Input stream 16 stands for +an invalid stream number; in such cases the input is actually from +the terminal, under control of the procedure |read_toks|.) +Finally |18<=name<=19| indicates that we are reading a pseudo file +created by the \.{\\scantokens} command. + +The |state| variable has one of three values, when we are scanning such +files: +$$\baselineskip 15pt\vbox{\halign{#\hfil\cr +1) |state=mid_line| is the normal state.\cr +2) |state=skip_blanks| is like |mid_line|, but blanks are ignored.\cr +3) |state=new_line| is the state at the beginning of a line.\cr}}$$ +These state values are assigned numeric codes so that if we add the state +code to the next character's command code, we get distinct values. For +example, `|mid_line+spacer|' stands for the case that a blank +space character occurs in the middle of a line when it is not being +ignored; after this case is processed, the next value of |state| will +be |skip_blanks|. + +@d mid_line=1 {|state| code when scanning a line of characters} +@d skip_blanks=2+max_char_code {|state| code when ignoring blanks} +@d new_line=3+max_char_code+max_char_code {|state| code at start of line} + +@ Additional information about the current line is available via the +|index| variable, which counts how many lines of characters are present +in the buffer below the current level. We have |index=0| when reading +from the terminal and prompting the user for each line; then if the user types, +e.g., `\.{\\input paper}', we will have |index=1| while reading +the file \.{paper.tex}. However, it does not follow that |index| is the +same as the input stack pointer, since many of the levels on the input +stack may come from token lists. For example, the instruction `\.{\\input +paper}' might occur in a token list. + +The global variable |in_open| is equal to the |index| +value of the highest non-token-list level. Thus, the number of partially read +lines in the buffer is |in_open+1|, and we have |in_open=index| +when we are not reading a token list. + +If we are not currently reading from the terminal, or from an input +stream, we are reading from the file variable |input_file[index]|. We use +the notation |terminal_input| as a convenient abbreviation for |name=0|, +and |cur_file| as an abbreviation for |input_file[index]|. + +The global variable |line| contains the line number in the topmost +open file, for use in error messages. If we are not reading from +the terminal, |line_stack[index]| holds the line number for the +enclosing level, so that |line| can be restored when the current +file has been read. Line numbers should never be negative, since the +negative of the current line number is used to identify the user's output +routine in the |mode_line| field of the semantic nest entries. + +If more information about the input state is needed, it can be +included in small arrays like those shown here. For example, +the current page or segment number in the input file might be +put into a variable |@!page|, maintained for enclosing levels in +`\ignorespaces|@!page_stack:array[1..max_in_open] of integer|\unskip' +by analogy with |line_stack|. +@^system dependencies@> + +@d terminal_input==(name=0) {are we reading from the terminal?} +@d cur_file==input_file[index] {the current |alpha_file| variable} + +@= +@!in_open : 0..max_in_open; {the number of lines in the buffer, less one} +@!open_parens : 0..max_in_open; {the number of open text files} +@!input_file : array[1..max_in_open] of alpha_file; +@!line : integer; {current line number in the current source file} +@!line_stack : array[1..max_in_open] of integer; + +@ Users of \TeX\ sometimes forget to balance left and right braces properly, +and one of the ways \TeX\ tries to spot such errors is by considering an +input file as broken into subfiles by control sequences that +are declared to be \.{\\outer}. + +A variable called |scanner_status| tells \TeX\ whether or not to complain +when a subfile ends. This variable has six possible values: + +\yskip\hang|normal|, means that a subfile can safely end here without incident. + +\yskip\hang|skipping|, means that a subfile can safely end here, but not a file, +because we're reading past some conditional text that was not selected. + +\yskip\hang|defining|, means that a subfile shouldn't end now because a +macro is being defined. + +\yskip\hang|matching|, means that a subfile shouldn't end now because a +macro is being used and we are searching for the end of its arguments. + +\yskip\hang|aligning|, means that a subfile shouldn't end now because we are +not finished with the preamble of an \.{\\halign} or \.{\\valign}. + +\yskip\hang|absorbing|, means that a subfile shouldn't end now because we are +reading a balanced token list for \.{\\message}, \.{\\write}, etc. + +\yskip\noindent +If the |scanner_status| is not |normal|, the variable |warning_index| points +to the |eqtb| location for the relevant control sequence name to print +in an error message. + +@d skipping=1 {|scanner_status| when passing conditional text} +@d defining=2 {|scanner_status| when reading a macro definition} +@d matching=3 {|scanner_status| when reading macro arguments} +@d aligning=4 {|scanner_status| when reading an alignment preamble} +@d absorbing=5 {|scanner_status| when reading a balanced text} + +@= +@!scanner_status : normal..absorbing; {can a subfile end now?} +@!warning_index : pointer; {identifier relevant to non-|normal| scanner status} +@!def_ref : pointer; {reference count of token list being defined} + +@ Here is a procedure that uses |scanner_status| to print a warning message +when a subfile has ended, and at certain other crucial times: + +@= +procedure runaway; +var p:pointer; {head of runaway list} +begin if scanner_status>skipping then + begin print_nl("Runaway "); +@.Runaway...@> + case scanner_status of + defining: begin print("definition"); p:=def_ref; + end; + matching: begin print("argument"); p:=temp_head; + end; + aligning: begin print("preamble"); p:=hold_head; + end; + absorbing: begin print("text"); p:=def_ref; + end; + end; {there are no other cases} + print_char("?");print_ln; show_token_list(link(p),null,error_line-10); + end; +end; + +@ However, all this discussion about input state really applies only to the +case that we are inputting from a file. There is another important case, +namely when we are currently getting input from a token list. In this case +|state=token_list|, and the conventions about the other state variables +are different: + +\yskip\hang|loc| is a pointer to the current node in the token list, i.e., +the node that will be read next. If |loc=null|, the token list has been +fully read. + +\yskip\hang|start| points to the first node of the token list; this node +may or may not contain a reference count, depending on the type of token +list involved. + +\yskip\hang|token_type|, which takes the place of |index| in the +discussion above, is a code number that explains what kind of token list +is being scanned. + +\yskip\hang|name| points to the |eqtb| address of the control sequence +being expanded, if the current token list is a macro. + +\yskip\hang|param_start|, which takes the place of |limit|, tells where +the parameters of the current macro begin in the |param_stack|, if the +current token list is a macro. + +\yskip\noindent The |token_type| can take several values, depending on +where the current token list came from: + +\yskip\hang|parameter|, if a parameter is being scanned; + +\hang|u_template|, if the \ part of an alignment +template is being scanned; + +\hang|v_template|, if the \ part of an alignment +template is being scanned; + +\hang|backed_up|, if the token list being scanned has been inserted as +`to be read again'. + +\hang|inserted|, if the token list being scanned has been inserted as +the text expansion of a \.{\\count} or similar variable; + +\hang|macro|, if a user-defined control sequence is being scanned; + +\hang|output_text|, if an \.{\\output} routine is being scanned; + +\hang|every_par_text|, if the text of \.{\\everypar} is being scanned; + +\hang|every_math_text|, if the text of \.{\\everymath} is being scanned; + +\hang|every_display_text|, if the text of \.{\\everydisplay} is being scanned; + +\hang|every_hbox_text|, if the text of \.{\\everyhbox} is being scanned; + +\hang|every_vbox_text|, if the text of \.{\\everyvbox} is being scanned; + +\hang|every_job_text|, if the text of \.{\\everyjob} is being scanned; + +\hang|every_cr_text|, if the text of \.{\\everycr} is being scanned; + +\hang|mark_text|, if the text of a \.{\\mark} is being scanned; + +\hang|write_text|, if the text of a \.{\\write} is being scanned. + +\yskip\noindent +The codes for |output_text|, |every_par_text|, etc., are equal to a constant +plus the corresponding codes for token list parameters |output_routine_loc|, +|every_par_loc|, etc. The token list begins with a reference count if and +only if |token_type>=macro|. +@^reference counts@> + +Since \eTeX's additional token list parameters precede |toks_base|, the +corresponding token types must precede |write_text|. + +@d token_list=0 {|state| code when scanning a token list} +@d token_type==index {type of current token list} +@d param_start==limit {base of macro parameters in |param_stack|} +@d parameter=0 {|token_type| code for parameter} +@d u_template=1 {|token_type| code for \ template} +@d v_template=2 {|token_type| code for \ template} +@d backed_up=3 {|token_type| code for text to be reread} +@d inserted=4 {|token_type| code for inserted texts} +@d macro=5 {|token_type| code for defined control sequences} +@d output_text=6 {|token_type| code for output routines} +@d every_par_text=7 {|token_type| code for \.{\\everypar}} +@d every_math_text=8 {|token_type| code for \.{\\everymath}} +@d every_display_text=9 {|token_type| code for \.{\\everydisplay}} +@d every_hbox_text=10 {|token_type| code for \.{\\everyhbox}} +@d every_vbox_text=11 {|token_type| code for \.{\\everyvbox}} +@d every_job_text=12 {|token_type| code for \.{\\everyjob}} +@d every_cr_text=13 {|token_type| code for \.{\\everycr}} +@d mark_text=14 {|token_type| code for \.{\\topmark}, etc.} +@# +@d eTeX_text_offset=output_routine_loc-output_text +@d every_eof_text=every_eof_loc-eTeX_text_offset + {|token_type| code for \.{\\everyeof}} +@# +@d write_text=toks_base-eTeX_text_offset {|token_type| code for \.{\\write}} + +@ The |param_stack| is an auxiliary array used to hold pointers to the token +lists for parameters at the current level and subsidiary levels of input. +This stack is maintained with convention (2), and it grows at a different +rate from the others. + +@= +@!param_stack:array [0..param_size] of pointer; + {token list pointers for parameters} +@!param_ptr:0..param_size; {first unused entry in |param_stack|} +@!max_param_stack:integer; + {largest value of |param_ptr|, will be |<=param_size+9|} + +@ The input routines must also interact with the processing of +\.{\\halign} and \.{\\valign}, since the appearance of tab marks and +\.{\\cr} in certain places is supposed to trigger the beginning of special +\ template text in the scanner. This magic is accomplished by an +|align_state| variable that is increased by~1 when a `\.{\char'173}' is +scanned and decreased by~1 when a `\.{\char'175}' is scanned. The |align_state| +is nonzero during the \ template, after which it is set to zero; the +\ template begins when a tab mark or \.{\\cr} occurs at a time that +|align_state=0|. + +@= +@!align_state:integer; {group level with respect to current alignment} + +@ Thus, the ``current input state'' can be very complicated indeed; there +can be many levels and each level can arise in a variety of ways. The +|show_context| procedure, which is used by \TeX's error-reporting routine to +print out the current input state on all levels down to the most recent +line of characters from an input file, illustrates most of these conventions. +The global variable |base_ptr| contains the lowest level that was +displayed by this procedure. + +@= +@!base_ptr:0..stack_size; {shallowest level shown by |show_context|} + +@ The status at each level is indicated by printing two lines, where the first +line indicates what was read so far and the second line shows what remains +to be read. The context is cropped, if necessary, so that the first line +contains at most |half_error_line| characters, and the second contains +at most |error_line|. Non-current input levels whose |token_type| is +`|backed_up|' are shown only if they have not been fully read. + +@p procedure show_context; {prints where the scanner is} +label done; +var old_setting:0..max_selector; {saved |selector| setting} +@!nn:integer; {number of contexts shown so far, less one} +@!bottom_line:boolean; {have we reached the final context to be shown?} +@@/ +begin base_ptr:=input_ptr; input_stack[base_ptr]:=cur_input; + {store current state} +nn:=-1; bottom_line:=false; +loop@+begin cur_input:=input_stack[base_ptr]; {enter into the context} + if (state<>token_list) then + if (name>19) or (base_ptr=0) then bottom_line:=true; + if (base_ptr=input_ptr)or bottom_line or(nn + else if nn=error_context_lines then + begin print_nl("..."); incr(nn); {omitted if |error_context_lines<0|} + end; + if bottom_line then goto done; + decr(base_ptr); + end; +done: cur_input:=input_stack[input_ptr]; {restore original state} +end; + +@ @= +begin if (base_ptr=input_ptr) or (state<>token_list) or + (token_type<>backed_up) or (loc<>null) then + {we omit backed-up token lists that have already been read} + begin tally:=0; {get ready to count characters} + old_setting:=selector; + if state<>token_list then + begin @; + @; + end + else begin @; + @; + end; + selector:=old_setting; {stop pseudoprinting} + @; + incr(nn); + end; +end + +@ This routine should be changed, if necessary, to give the best possible +indication of where the current line resides in the input file. +For example, on some systems it is best to print both a page and line number. +@^system dependencies@> + +@= +if name<=17 then + if terminal_input then + if base_ptr=0 then print_nl("<*>") else print_nl(" ") + else begin print_nl(" + print_char(">"); + end +else if index<>in_open then {input from a pseudo file} + begin print_nl("l."); print_int(line_stack[index+1]); + end +else begin print_nl("l."); print_int(line); + end; +print_char(" ") + +@ @= +case token_type of +parameter: print_nl(" "); +u_template,v_template: print_nl("