% $Id: tex4ht-c.tex 1391 2023-10-13 22:34:24Z karl $
% tex tex4ht-c
% Copyright 2009-2023 TeX Users Group
% Copyright 1996-2009 Eitan M. Gurari
% Released under LPPL 1.3c+.
% See tex4ht-cpright.tex for license text.
\def\MSVCI{}
\def\MSVCI{NO}
\def\export{ }
\input common
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input DraTex.sty \input AlDraTex.sty
\input tex4ht.sty
\Preamble{html,th4,family,sections+,xhtml}
\EndPreamble
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\expandafter \ifx \csname append:def\endcsname \relax
\expandafter\def\csname append:def\endcsname#1#2{%
\def\Xtemp{\def#1}%
\expandafter\expandafter\expandafter\Xtemp\expandafter{#1#2}}
\fi
\expandafter \ifx \csname Verbatim\endcsname \relax
\def\Verbatim{\bgroup
\catcode`\^=10 \catcode`\\=10 \catcode`\%=10
\catcode`\{=10 \catcode`\}=10 \catcode`\#=10 \catcode`\#=10
\XVerbatim}
\long\def\XVerbatim#1EndVerbatim{\egroup}
\fi
\expandafter \ifx \csname Verb\endcsname \relax
\def\Verb{\bgroup \catcode`\^=10 \catcode`\\=10 \catcode`\%=10
\catcode`\{=10 \catcode`\}=10 \catcode`\#=10 \catcode`\#=10
\leavevmode \Xctgs}
\def\Xctgs#1{\def\Xeat##1#1{\egroup}\Xeat }
\fi
\ifx \HAssign\UnDef
\csname newcount\endcsname \tmpXxXcnt
\def\advXxX#1{\def\XxXvar{#1}\futurelet\XxXtemp\AdvancXxX}
\def\AdvancXxX{\ifx [\XxXtemp \expandafter\AdvancXxXe
\else \expandXxXafter{\expandafter\advXxXc\XxXvar}\fi}
\def\AdvancXxXe[#1]{\expandafter\advXxXc\csname
\expandafter\string\XxXvar[#1]\endcsname}
\def\gXxXAdvance#1{\bgroup \def\XxXtemp{#1}%
\tmpXxXcnt#1\afterassignment\XxXgplus \mthXxXop\tmpXxXcnt}
\def\XxXgplus{\expandafter\xdef\XxXtemp{\the\tmpXxXcnt}\egroup}
\def\XxXAdvance#1{\bgroup \def\XxXtemp{#1}%
\tmpXxXcnt#1\afterassignment\XxXaplus \mthXxXop\tmpXxXcnt}
\def\XxXaplus{\xdef\XxXtemp{\def\expandafter\noexpand\XxXtemp{\the\tmpXxXcnt}}%
\egroup \XxXtemp}
\def\HAssign{\XxXssg\edef}
\def\gHAssign{\XxXssg\xdef}
\def\XxXssg#1#2{\let\dXxXfn#1\def\XxXvar{#2}\futurelet\XxXtemp\XxXAssgn}
\def\XxXAssgn{%
\ifx [\XxXtemp
\expandafter\dXxXfn\XxXvar[##1]{%
\noexpand\csname \expandafter
\string\XxXvar[##1]\noexpand\endcsname}%
\expandafter\assgXxXm
\else \afterassignment\assgXxXv \expandafter \tmpXxXcnt \fi }
\def\assgXxXv{\expandafter\dXxXfn\XxXvar{\the\tmpXxXcnt}}
\def\assgXxXm[#1]{%
\def\XxXtemp{\expandafter\dXxXfn
\csname \expandafter\string\XxXvar[#1]\endcsname{\the\tmpXxXcnt}}%
\afterassignment\XxXtemp \tmpXxXcnt}
\fi
\catcode`\:=11
\csname newcount\endcsname\tmp:cnt
\expandafter\ifx \csname no:catcodes\endcsname\relax
\def\no:catcodes#1#2#3{%
\tmp:cnt=#1
\def\no::catcodes{%
\catcode\tmp:cnt=#3
\ifnum\tmp:cnt<#2
\advance\tmp:cnt by 1 \expandafter\no::catcodes
\fi }%
\no::catcodes }
\fi
\let\:oldlq=\`
\let\:oldrq=\'
\def\'{\leavevmode \:cats \::cats}
\def\::cats#1{\if #1"\expandafter\:oldrq
\else \def\:eat##1#1{\tt ##1\egroup}\expandafter\:eat\fi}
\def\`{\leavevmode \:cats \:::cats}
\def\:::cats#1{\if #1"\expandafter\:oldlq
\else \def\:eat##1#1{`{\tt ##1}'\egroup}\expandafter\:eat\fi}
\bgroup
\catcode`\^=7
\gdef\:cats{\bgroup \no:catcodes0{255}{12}\catcode`\ = 10
\catcode`\^^M = 10 \catcode`\^^I = 10
}
\egroup
\catcode`\:=12
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input ProTex.sty
\ifHtml \AlProTex{c,<<<>>>,`,title,list,ClearCode,_^,CodeLineNo}
\let\coDE=\<
\def\<{\vfil\par\coDE}
\else \AlProTex{c,<<<>>>,`,title,list,[],ClearCode,_^}
\fi
\font\CommentFont=cmitt10
\def\?{\CommentFont}
\ifHtml
\TocAt{Chapter,Section,SubSection,LikeSection,/Appendix,/Part}
\TocAt{Appendix,Section,SubSection,LikeSection,/Chapter,/Part}
%\CutAt{+Part}
\CutAt{Chapter,Part,Appendix}
\CutAt{Appendix,Part,Chapter}
\EveryChapter{\TocChapter % [\Link[\jobname toc.html]{}{}Expanded toc\EndLink]
}
\EveryAppendix{\TocAppendix %[\Link[\jobname toc.html]{}{}Expanded toc\EndLink]
}
\expandafter\let\expandafter\oldbye\expandafter=\csname bye\endcsname
\expandafter\gdef\csname bye\endcsname{%
\FileStream{\jobname toc.html}%
\TableOfContents
\EndFileStream{\jobname toc.html}%
\csname oldbye\endcsname}
\else
\hbadness=10000 \vbadness=10000 \hfuzz=99in \vfuzz=99in
\catcode`\:=11
\def\ChildOf{}
\def\ro:nd#1{}
\def\HRefPort\<#1\>{}
\catcode`\:=12
\fi
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% \ChildOf[ChapterCounter]{tex4ht}
\ifx \export\:UnDef \else \def\CodeId#1#2{} \fi
\def\UnderLine#1{\ifHtml
% \IgnorePar\EndP
\HCode{<strong>}#1\HCode{</strong>}\else\underbar{#1}\fi}
\let\CModifyShowCode=\ModifyShowCode
\let\CModifyOutputCode=\ModifyOutputCode
\def\ModifyShowCode{\def\[##1({##1\UnderLine{(}}\def\;{\UnderLine{;}}\CModifyShowCode}
\catcode`\^=7
\catcode`\@=6 \catcode`\#=12 \catcode`\^^M=13\relax%
\def\ModifyOutputCode{%
\def\;{ SEP }%
\def\[@@1(@@2)@@3;{@@1
#ifdef ANSI
#define SEP ,
(@@3)
#undef SEP
#else
#define SEP ;
(@@2)@@3;
#undef SEP
#endif
}%
\CModifyOutputCode}%
\catcode`\^^M=5
\catcode`\@=12
\catcode`\#=6
\catcode`\^=13
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Part{tex4ht.c}
\TableOfContents[Chapter,Appendix,Section,LikeSection]
\Chapter{Flow of Program}
\input tex4ht-cpright
\Section{Preamble}
A command-line program running in the console environment of the
operating system (MS-DOS, MS-DOS window of Windows 3.1, Unix shell,
etc). The input is a heavily specialized DVI file. The main output is
the HTML/XML and \`'.css', also \`'.lg', \`'.idv', and
\`'.tmp'.
\<tex4ht.c\><<<
/* tex4ht.c (`version), generated from `jobname.tex
Copyright 2009-2023 TeX Users Group
Copyright `CopyYear.1996. Eitan M. Gurari
`<TeX4ht copyright`> */
`<tex4ht.h`>
>>>
\<tex4ht.h\><<<
/* Compiler options (uncommented | command line), as comments:
Classic C (CC) default
#define ANSI ansi-c, c++
#define DOS_C
#define HAVE_STRING_H <string.h>
#define HAVE_DIRENT_H <dirent.h>
#define HAVE_SYS_NDIR_H <sys/ndir.h>
#define HAVE_SYS_DIR_H <sys/dir.h>
#define HAVE_NDIR_H <ndir.h>
#define HAVE_IO_H <io.h>
#define HAVE_UNISTD_H <unistd.h>
#define WIN32
#define KPATHSEA
#define CDECL .......
#define BCC32 bordland c++
*************************************************
Tex4ht variables *
(uncommented | command line) *
----------------------------------------------- */
`<h-defines`>
/* ******************************************** */
`<define MSVC-1-52`>
`<bordland: bcc32 -DBCC32 tex4ht`>
`<bordland: bcc -DBCC -mh tex4ht`>
`<DJGPP`>
#ifdef DOS_C
#define DOS
#endif
#ifdef DOS
#define DOS_WIN32
#define HAVE_STRING_H <string.h>
#endif
#ifdef WIN32
#define DOS_WIN32
#ifndef KPATHSEA
#define HAVE_STRING_H <string.h>
#endif
#endif
`<h-defines DOS-WIN32`>
`<h-defines KWIN32`>
`<h-include`>
`<h-defines 2`>
>>>
% lint tex4ht.c -DHAVE_DIRENT_H -DHAVE_STRING_H
\<bordland: bcc32 -DBCC32 tex4ht\><<<
#ifdef BCC32
#define DOS_WIN32
#define ANSI
#define HAVE_DIRENT_H
#define PLATFORM "ms-win32"
#endif
>>>
The \`'-mh' below calls for `Huge Memory Model'.
\<bordland: bcc -DBCC -mh tex4ht\><<<
#ifdef BCC
#define DOS_C
#define ANSI
#define HAVE_DIRENT_H
#define PLATFORM "ms-dos"
#ifndef PATH_MAX
#define PATH_MAX 256
#endif
#endif
>>>
\ifHtml[\HPage{more}\Verbatim
> 1) I think there is a bug in Tex4ht (perhaps in the Dos'port), because
> it doesn't find the .tfm files when they are in different directories
> (with the ! at the end of the "tc:d:\share\texmf\fonts\tfm!"). I am
> obliged to put all the files in a common directory.
The directory search has been disabled for the DOS port with
the following code.
#define DOS
#define NOSUBDIR
#endif
I'm not sure the decision to disable recursive searches was a good one,
and I'll probably reinstate recursive searches in the next version.
If you compiled the source code, instead of downloading an executable
application, try recompiling the source, after removing the above
segment of code.
\EndVerbatim\EndHPage{}]\fi
microsoft visual c options: msdos application (.exe), large, ss=sd
\Verbatim
#define MSVC_1_52_DOS
#define MSVC_1_52_DOS_LIB not fully implemented
------------------------------------------------------------
End options --- Start comments *
------------------------------------------------------------
MSVC_1_52_DOS: *
MS-DOS appl (project type), release (buld mode), *
huge, ss==ds, 80286, no debug *
no sub directories (missing opendir,closedir, readdir) *
MSVC_1_52_DOS_LIB: *
as above, but opendir,closedir, readdir compiled *
with tex4ht *
------------------------------------------------------------
End comments
\EndVerbatim
\<h-include\><<<
#ifdef KPATHSEA
#include <kpathsea/config.h>
#endif
#include <stdio.h> `% EOF, FILE `%
#include <stdlib.h> `% EXIT_FAILURE `%
>>>
\<defines\><<<
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif
>>>
\<h-include\><<<
#ifdef HAVE_STRING_H
#include <string.h>
#endif
>>>
Under ANSI C, all malloc stuff is declared in \`'<stdlib.h>' (which you
also include), hence this non-standard header is redundant.
\SubSection{MSVC-1-52-DOS}
\<define MSVC-1-52\MSVCI\><<<
#ifdef MSVC_1_52_DOS_LIB
#define MSVC_1_52_DOS
#endif
#ifdef MSVC_1_52_DOS
#define DOS
#define NOSUBDIR
#endif
#ifdef MSVC_1_52_DOS_LIB
#undef NOSUBDIR
#endif
>>>
\<if not-def MSVC-1-52\MSVCI\><<<
#ifndef MSVC_1_52_DOS
>>>
\<endif not-def MSVC-1-52\MSVCI\><<<
#endif
>>>
\SubSection{KPATHSEA Windows}
% #define NOSUBDIR
% MSVC_1_52_DOS -> DOS, NOSUBDIR, not <dirent.h>
% ANSI --> PROTOTYPE
% DOS -> <string.h>
\<h-defines KWIN32\><<<
#ifdef KPATHSEA
#ifdef WIN32
#define KWIN32
#endif
#endif
>>>
Here are the promised patch. However I have not yet tested it very
much. It is unlikely that I test it with non-kpathsea setting. At
least the patch compiles in both kpathsea and non-kpathsea setting.
The following patch is very similar
to some code in kpathsea / web2c.
\<h-include\><<<
#ifdef WIN32
#ifdef KPATHSEA
#undef CDECL
#define CDECL __cdecl
#else
#include <windows.h>
#endif
#else
#ifdef KPATHSEA
#define CDECL
#endif
#endif
>>>
\<if not-WIN32\><<<
#ifndef WIN32
>>>
\<endif not-WIN32\><<<
#endif
>>>
\<header functions\><<<
#ifdef WIN32
static BOOL sigint_handler(ARG_I(DWORD));
#endif
>>>
\<functions\><<<
#ifdef WIN32
`[
static BOOL sigint_handler(dwCtrlType) DWORD dwCtrlType
;{
`<avoid warning of unused dwCtrlType`>
err_i(32);
return FALSE; `% return value obligatory `%
}
#endif
>>>
\<avoid warning of unused dwCtrlType\><<<
if( dwCtrlType ){ (IGNORED) printf(" "); }
>>>
\<CDECL\><<<
#ifdef CDECL
CDECL
#endif
>>>
Forgetting \`'_pascal' and \`'_cdecl' modifiers: Each C function may
be either \`'_pascal' or \`'_cdecl'. This modifier defines how
parameters are passed to it. Default for Smalltalk definition is
\`'_cdecl'. Default for C functions depends on compiler settings, and
you can use other types uncompatible with Smalltalk. In Windows API
16-bit functions are \`'_pascal' and 32-bit \`'_cdecl'.
The following Shared across recursive calls, it acts like a stack.
\<vars\><<<
#ifdef WIN32
static U_CHAR dirname[PATH_MAX];
#endif
>>>
Recursive directory search relies on some Windows API similar to
opendir/readdir
\<file = search WIN32 subdir (name,str)\><<<
{
WIN32_FIND_DATA find_file_data;
HANDLE hnd;
int proceed;
(IGNORED) strcpy((char *) dirname, (char *) str);
strct(dirname, "/*.*"); `% "*.*" or "*" -- seems equivalent. `%
hnd = FindFirstFile(dirname, &find_file_data);
if (hnd != INVALID_HANDLE_VALUE) {
`<valid WIN32 handle`>
}
}
>>>
\<valid WIN32 handle\><<<
proceed = 1;
while (proceed) {
if( !eq_str(find_file_data.cFileName, ".") &&
!eq_str(find_file_data.cFileName, "..") )
{
(IGNORED) strcpy((char *) str+n, (char *) find_file_data.cFileName );
str[n-1] = dir_path_slash(str);
if (find_file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if( (file = search_file_ext(name, str, flags)) != NULL ){
FindClose(hnd);
return file; }
}
}
proceed = FindNextFile(hnd, &find_file_data);
}
FindClose(hnd);
>>>
SIGFPE is handled by the C library, SIGSEGV too but not generated by
the system, so it does nothing but is harmless
SetConsoleCtrlHandler is need to catch Ctrl+C and Ctrl+Break under
Windows, SIGINT is not generated by the system.
\<KWIN32 signals\><<<
SetConsoleCtrlHandler((PHANDLER_ROUTINE)sigint_handler, TRUE);
>>>
\SubSection{DJGPP}
\Verbatim
* tex4ht.c [__DJGPP__] (ANSI, DOS_GIF_FILE): Define.
(LSTAT): Define to stat for every system that doesn't define
S_ISLNK.
(WRITE_BIN_FLAGS, READ_BIN_FLAGS): Use binary mode with DJGPP.
(dos_file_names): New variable.
[WIN32] (sigint_handler): Add missing semi-colon in prototype
declaration.
(sig_err): If got fatal signal other than SIGINT or SIGQUIT, don't
return to caller, since the program will be aborted otherwise.
(search_file_ext): Allow use of both forward- and back-slashes on
DOS and Windows platforms.
(insert_ch) [DOS_GIF_FILE]: Only use butchered file name if long
file names aren't supported (dos_file_names is TRUE).
(main) [KPATHSEA]: If input is from stdin which is not a console
device, switch it to binary mode. Set dos_file_names to TRUE for
all DOS platforms, FALSE on Unix and WIN32, and compute at runtime
for DJGPP. Simplify logic of finding the init file by pushing
HOME=C: into environment if $HOME isn't defined on DOSISH systems.
Only use butchered file names for GIF files if long file names
aren't supported (dos_file_names is TRUE).
\EndVerbatim
\<DJGPP\><<<
#ifdef __DJGPP__
#define DOS_WIN
#define ANSI
#ifndef HAVE_STRING_H
#define HAVE_STRING_H 1
#endif
#endif
>>>
\<vars\><<<
static BOOL dos_file_names =
#ifdef DOS_GIF_FILE
TRUE
#else
FALSE
#endif
;
>>>
Set dos\_file\_names to TRUE for
all DOS platforms, FALSE on Unix and WIN32, and compute at
runtime
for DJGPP.
\<DJGPP gif names for pictorial symbols\><<<
#if !defined(DOS_GIF_FILE) && !defined(WIN32) && defined(__DJGPP__)
dos_file_names = !_use_lfn(NULL);
#endif
>>>
\<extra options\><<<
switch( *(p+2) ){
case 's':{ dos_file_names = TRUE; break; }
default:{ bad_arg; }
}
>>>
\Verbatim
> The diffs are attached below. The first hunk is because HAVE_STRING_H is
> already defined by c-auto.h that's generated by configure, and GCC
> complained about redefinition. The second hunk just moves the call to
> _use_lfn to a place where the variable "name" is defined. _use_lfn needs
> an actual file name as argument, because the support for long file names
> is determined dynamically for each filesystem. (There can be cases where
> you run on Windows 9X, where DJGPP supports long file names, but one or
> more of the file names passed to tex4ht reside on some networked drive
> mounted with a network client which doesn't support long file names.)
> Now, do we want tex4ht to propose a long and a short name for each
> output file, and determine in each case which of the two names to
> assign according to whether `use_lfn(name)' accepts the long name?
That's what I had in mind, yes.
The philosophy behind that is that DJGPP executables should use long
file names whenever they are available. However, given that there are
several different filesystems accessible from the machine (via a
network), long names might be supported by some of these filesystems,
but not by others. So the DJGPP version needs to decide whether or
not to use the long name for every command-line argument it processes,
since each such argument can belong to a different filesystem.
For example, suppose tex4ht.exe is invoked like this:
tex4ht c:/foo/bar/baz.dvi d:/dvi/xyz.dvi
If the C: drive supports long file names, but D: does not (because it
is mounted by a network software which doesn't support long file
names), my intent was that tex4ht will behave differently with each
file.
This feature turns out to be too expensive to utilize in tex4ht.c.
The problem is that tex4ht first creates the html files from the dvi
source, and during this phase it determines which pictorial symbols
are needed. The gif files of the pictorial symbols get their names
during this phase. However, the gif files themselves are created only
at the end of the compilation, after all the names have been already
determined and used.
Anyway, the fallback should be to determine the value of dos_file_names
(sp?) once, in the beginning of `main', by calling _use_lfn(NULL). This
checks long file name support by the default filesystem, which is usually
a good indication of other filesystems as well.
Corrections to my previous email:
In the first phase, tex4ht.c extracts the html code from the dvi file
and, along this process, determines the names of the gif files for the
pictorial symbols in use. The following code fragement is responsible
for getting the names.
if( !dos_file_names ){
print_f(font_tbl[cur_fnt].name);
if( mag == 10 ) (IGNORED) sprintf(str, GIF_I, design_ch, gif);
else (IGNORED) sprintf(str, GIF_II, mag, design_ch,
gif);
}
In the second phase, tex4ht.c creates a dvi page for each of the
pictorial symbols in use, places the code in the idv file, and puts in
the lg file requests for translating the dvi pages into gif files of
the specified names. The file names are provided by the following
code.
if( !dos_file_names ){
if( mag == 10 ) (IGNORED) sprintf(str, ``%s-%x%s'',
font_tbl[cur_fnt].name, ch, gif);
else (IGNORED) sprintf(str, ``%s-%x-%x%s'',
font_tbl[cur_fnt].name, mag, ch, gif);
}
tex4ht.c never gets to create the gif files. Instead, t4ht.c scans
the lg file, and use that information to invoke auxiliary tools for
translating the dvi pages into gif files.
Added also a command line switch -xs to allow forcing of short names
for the gif files.
\EndVerbatim
\<DJGPP signals\><<<
#ifdef __DJGPP__
if (s != SIGINT && s != SIGQUIT)
exit(EXIT_FAILURE);
#endif
>>>
\SubSection{Compatibity}
\Verbatim
> The style file provides a set of commands for requesting html code.
> The user may insert these commands to the source code, and tex4ht will
> use them to create the desired output. I placed a more detailed
> overview with examples in
>
http://www.cse.ohio-state.edu/~gurari/temp/XXX/
I've downloaded the Essential Tex example, and managed to convert it
(sort of). There are a few problems with file name lengths - I had to
rename the file to es.tex to get it to be processed properly. According
to the .lg file I should have a lot of es.idv[xx] files; since DOS only
supports 3 character extentions, they had all overwritten each other
(truncating the extention to .idv), so I only had one file. Trying to
view this file made my driver complain that it was corrupt (it lacked a
postamble).
Basically, most of the functionality seems to work under DOS, but the
file naming conventions appear to be a problem. If you want a more
detailed report of the problems, let me know.
> I have no problems dealing with C code, but I'm not familiar with the
> different standards (including ANSI C) and libraries that exist
> around. I'll need to overcome this deficiency to make tex4ht.c a
> portable utility.
I've been following comp.lang.c for quite a while now, which focuses
strongly on portable (ANSI) C, so I have some insight in that. If you
solely use the ANSI C library functions (which should be possible, I
think), you don't need to worry about portability - any conforming
compiler can generate the code. For example, I noticed that your code
includes:
Under ANSI C, all malloc stuff is declared in <stdlib.h> (which you
also include), hence this non-standard header is redundant.
Looking a bit better at the source code, I found that I could rid
myself of all the prototype problems by defining ARG. For ANSI, this
should be the default. If you really want to go for ANSI, you can get
rid of all these ARG_I macro's and fully and unconditionally prototype
everything.
All of the "const is long" warnings can be fixed by appending a "L"
after all constants bigger than 32767. The "conversion may loose
significant digits" warnings appear to be related with indexing arrays
with longs - I haven't figured this one out yet, as far as I know ANSI
allows this. (DOS compilers generally don't support it, though - I can
just turn off this warning).
A few warnings result from using an assignment inside an if-clause,
mostly with file opening code (e.g. "if(name = fopen(stuff)){file is
open}"). Changing this to an explicit comparison to NULL ("if((name =
fopen(stuff)) != NULL)") kills the warnings while allowing the compiler
to warn you when you typed "=" where you ment "==".
One last thing I noticed: after other attempts at opening "tex4ht.env"
have failed, you try to open "C:\.tex4ht". Since DOS doesn't allow files
with no name (only an extention), and the maximum size of the extention
is 3 chars anyway, this will always fail - so this code can be deleted.
> I'll get an ANSI C compiler for my PC, and will fix tex4ht.c to deal
> also with that standard. I'll might ask your advice regarding
> searching directories and files on pc's, but if this will be the case
> I'll prepare a small toy example capturing the features I need.
You may be used to stuff like opendir/readdir/closedir; I believe
there is code in the snippets maintained by Bob Stout that emulates
these calls under DOS. If you place this stuff in a separate module,
your main code can remain portable (it is a good idea to separate the
non-portable stuff from the portable code).
Note that a port of gcc to DOS exists (djgpp), which eliminates many
of the porting problems. It still would be nice if it compiled
error-free on my native DOS compiler, though.
\EndVerbatim
\Verbatim
Good - I'm glad that I'm of some help. The code snippet you sent me
compiled without a single warning here, so you're on the right track
(although stuff like stat() isn't ANSI, it appears supported by a lot of
compilers).
I compiled the source with the defines DOS and ANSI - and most of it
worked. You forgot the terminating quote for "tex4ht.env on line 1715. I
also get warnings that denominator and numerator are not being used
(only assigned to) and, from the source, the warning appears to be
correct.
If I turn on my compilers ANSI flag, I have a few problems. First,
your function declarations are still in K&R style C. Although ANSI
allows that, it prohibits type checking, and gives some problems with
argument promotion. Specifically, the function insert_ch_map() gives
problems. Your declaration looks like this (I reformatted it a bit for
readability ;-):
void insert_ch_map(ch, tag)
char ch;
BOOL tag;
{ /* etc */
Changing this to the normal ANSI definition shuts up my compiler:
void insert_ch_map(char ch, BOOL tag) { /* etc */
I suggest that you change your other function definitions to ANSI
style too (if you still want to go for ANSI, that is).
Another problem I ran into is that _fileno is unavailable to both
compiler and linker if I turn on ANSI compilation. I checked where you
use it, and it is in only one place, to find the file length of the DVI
file. Your code:
struct STSTAT temp;
(IGNORED) fstat (fileno(dvi_file), &temp);
file_len = (long) temp.st_size;
if( (file_len % 4) != 0 ) bad_dvi;
If you change this to use ftell and fseek, you'll have gained ANSI
compatibility (and therewith portability). I tried the following code
instead of yours:
long curr_pos;
curr_pos = ftell(dvi_file);
fseek(dvi_file, 0, SEEK_END);
file_len = ftell(dvi_file);
fseek(dvi_file, curr_pos, SEEK_SET);
if( (file_len % 4) != 0 ) bad_dvi;
To the best of my nowledge, this works exactly like your code (I
tested it). My executable is compiled with this code.
I've found other non-ANSI stuff that you may want to remove; if you'd
like me to check for it (and, if I can, suggest an ANSI alternative),
let me know. I've found at least access() - I haven't looked into how to
do that portably.
> With the new versions of the tools, and the parameter `htm' (instead of
> `html') in \Preamble, the full name gentle.tex can be used.
Yes, this appears to be working now.
> A line of the form
> -+- needs -+- es.idv[xx] ==> abc.gif -+-
> in the .lg file asks that the xx'th page of es.idv will be translated
> into a gif picture and be stored in file abc.gif.
OK, I misunderstood that. I will hold off looking into the picture
translation for a bit - but this seems like a job for perl. There is a
DOS port of perl, and I'm just familiarizing myself with it. I don't
know if the port is able to launch sub-processes. If not, I could
probably come up with an awk or perl script that creates a batch file to
do the job. (Requiring awk may be nicer than requiring perl, since the
DOS port of awk is only a little over 90 KB, while perl is seven times
as large). Alternatively, you could perhaps change the format of the lg
file a bit, to contain lines like:
convert es.idv xx abc.gif
i.e., provide the required information as dvi file, page number, and
output filename on the commandline of "convert" (or however you want to
call it). This could that be used almost without effort as shell script
or batch file.
> After getting your last email, I bought Turbo C++ 4.5 for windows (I
> couldn't find one for dos without reaching to a very fat system). I
> tried the attached program in different modes, including ANSI C, and
> to my surprise opendir/readdir/closedir worked fine.
Here too. Strange. Opendir etc is not ANSI, but it is Posix - and
apparently Borland added some posix stuff to their libraries. I'm not
sure if you should stick with them or not. I haven't studied that part
of your source code yet, so I can't say if it can be easily ANSIfied. If
we stick with this, and later you want to port to Apple, Amiga,
whatever, you're probably in trouble.
> That left me even more puzzled to why my version of MSVC doesn't
> support these functions.
They're Posix - a Unix standard. Apparently they felt is wasn't
appropriate for DOS.
A few notes on the working of tex4ht.sty: Some of my code doesn't
compile because I have \label{}s inside sectioning commands. LaTeX
itself does allow this. Also, all headings are links to themselves -
why?
Below, I included a LaTeX source that both your code and LaTeX2HTML
can't seem to translate properly - but then, I hardly know anything of
the possibilities of tex4ht.sty. Have you started any form of
documentation yet? (I don't read TeX, only LaTeX, so looking at the sty
file myself didn't help much).
\documentclass[11pt]{article}
\usepackage{alltt}
%% Environment to typeset screen text, menus etc. in: verbatim-style
%% and boxed.
\newsavebox{\scrntxt}
\newenvironment{boxedfig}[2]%
% First argument is width of box, second is caption text
{ \newcommand{\capt}{#2}% To transport caption to end of environment
\begin{minipage}[b]{#1}%
\begin{lrbox}{\scrntxt}\begin{minipage}{\textwidth}%
\fontsize{10}{11}\selectfont\begin{alltt}%
} %
{ \end{alltt}\end{minipage}\end{lrbox}\setlength{\fboxsep}{3pt}%
\framebox[\textwidth][l]{\usebox{\scrntxt}}\caption{\capt}%
\end{minipage}%
}
\begin{document}
Below you will find two figures. They are boxed neatly, and typeset in a
fixed-width font of a size independent of the main font size.
\begin{figure}[htb]
\begin{boxedfig}{0.495\textwidth}{The MBR 1 boot menu\label{bootmenu:1}}
Please select partition too boot.
Choose from:
1. 123456789AB
2. 123456789AB
3. 123456789AB
4. 123456789AB
?
\end{boxedfig}\hfill
\begin{boxedfig}{0.495\textwidth}{The MBR 2 boot menu\label{bootmenu:2}}
Please select partition too boot.
Choose from:
1. 12345678
2. 12345678
3. 12345678
4. 12345678
Boot?
\end{boxedfig}
\end{figure}
I want to be able to reference both
figure~\ref{bootmenu:1} and figure~\ref{bootmenu:2} from my HTML file.
How to achieve this?
\end{document}
\EndVerbatim
\<defines\><<<
#if INT_MAX < 2147483647L `%2^31`%
#define LONG L
#endif
>>>
Lint does not like non-null pointers to be cast.
\`'free(void *)' instead of \`'free()' will make lint to stop
complaining, but is this really the problem. For this function we have a
definition \`'void free(void *)'.
\`'1<<20' should be \`'1L<<20' for short integer.
Integers on Dos they are just 16 bits!!! Also, ordering of bytes is
inconsistence.
\<h-include\><<<
#include <limits.h> `% INT_MAX `%
>>>
\<defines\><<<
#ifdef LONG
#define INTEGER long
#else
#define INTEGER int
#endif
#define U_CHAR char
>>>
\Verbatim
string.h: extern int strlen( );
string.h: extern size_t strlen(char *);
string.h: extern size_t strlen(const char *);
string.h: extern size_t strlen( );
void * realloc( void *, size_t)
\EndVerbatim
\<resplit argv for windows\><<<
#ifdef WIN32
/* The idea here is to split options apart at spaces: a single argument
"-foo -bar" becomes the two options "-foo" and "-bar". We need to
do this for Windows because mk4ht passes this sort of combined
option in one string to scripts like htlatex.{unix,bat}. In the
Unix case, the shell resplits words when calling t4ht and tex4ht,
so the program see two options. But this does not happen with the
.bat; %4, for instance, remains "-foo -bar". So we fix it here. */
if (argc > 2) {
int i, nargc;
char **nargv, **pnargv, **pargv;
nargv = (char **) xmalloc (2 * argc * sizeof (char *));
pnargv = nargv;
pargv = argv;
*pnargv++ = xstrdup (*pargv++);
*pnargv++ = xstrdup (*pargv++);
nargc = 2;
for (i=2; i < argc; i++) {
char *p, *q, *r;
p = q = *pargv++;
while (*p == ' ' || *p == '\t') {
p++;
q++;
}
while (*p != ' ' && *p != '\t' && *p) {
p++;
if (*p == '\0') {
*pnargv++ = xstrdup(q);
nargc++;
} else if (*p == ' ' || *p == '\t') {
r = p;
while (*p == ' ' || *p == '\t')
p++;
if (*p == '-' || *p == '\0') {
*r = '\0';
*pnargv++ = xstrdup(q);
nargc++;
q = p;
}
}
}
}
nargv[nargc] = NULL;
argv = nargv;
argc = nargc;
}
#endif
>>>
\SubSection{Signals}
\<h-include\><<<
#include <signal.h>
>>>
\<header functions\><<<
static void
`<CDECL`>
sig_err(ARG_I(int));
>>>
\<functions\><<<
`[
static void
`<CDECL`>
sig_err(s) int s
;{
(void) signal(s,SIG_IGN); `%ignore the signal`%
switch( s ){
#ifdef SIGSEGV
case SIGSEGV: err_i(30);
#endif
case SIGFPE : err_i(31);
#if defined(SIGINT) && !defined(WIN32)
case SIGINT : err_i(32);
#endif
}
`<DJGPP signals`>
}
>>>
DJGPP:(sig\_err): If got fatal signal other than SIGINT or SIGQUIT, don't
return to caller, since the program will be aborted otherwise.
\<set signals\><<<
#ifdef SIGSEGV
(void) signal(SIGSEGV,sig_err);
#endif
(void) signal(SIGFPE,sig_err);
#ifdef WIN32
`<KWIN32 signals`>
#else
#ifdef SIGINT
(void) signal(SIGINT,sig_err); `%Control-c, user interrupt`%
#endif
#endif
>>>
SIGSEGV, SIGILL, SIGTERM not implemented in MS-DOS. They are supplied just for
compatibility. Looks like that SIGINT is defined for windows but not for dos.
\ifHtml[\HPage{more}\Verbatim
Eitan> I also wonder whether the WIN32 really needs to discard the
Eitan> following code fragment.
signal() is supported through the standard libc, but is mostly
ineffective. An example of the kind of code needed is attached.
/* Interrupt handler. mt_exit() is a cleanup_and_exit function */
#ifdef _WIN32
BOOL sigint_handler(DWORD dwCtrlType)
{
mt_exit(3);
return FALSE; /* return value obligatory */
}
#else
void sigint_handler (int sig)
{
mt_exit(3);
}
#endif
/* Catch signals, so we clean up if the child is interrupted.
This emulates "trap 'whatever' 1 2 15". */
#ifdef _WIN32
SetConsoleCtrlHandler((PHANDLER_ROUTINE)sigint_handler, TRUE);
#else
# ifdef SIGINT
signal (SIGINT, sigint_handler);
# endif
# ifdef SIGHUP
signal (SIGHUP, sigint_handler);
# endif
# ifdef SIGTERM
signal (SIGTERM, sigint_handler);
# endif
#endif
\EndVerbatim\EndHPage{}]\fi
Msvc recommends not using printf, but we ignoring this recommendation
here with the assumption that the recommendation relates to I/O
interrupts that are not considered here.
\<signals messages: 30-32\><<<
"Illegal storage address\n", `%30 segmentation`%
"Floating-point\n", `%31 `%
"Interrupt with Cntr-C\n", `%32 `%
>>>
\Section{Kpathsea}
\<KPATHSEA h-include\><<<
#ifdef KPATHSEA
#include <kpathsea/c-errno.h>
#include <kpathsea/c-ctype.h>
#include <kpathsea/c-fopen.h>
#include <kpathsea/c-pathmx.h>
#include <kpathsea/proginit.h>
#include <kpathsea/tex-file.h>
#include <kpathsea/tex-make.h>
#include <kpathsea/variable.h>
#include <signal.h>
#if !defined(_AMIGA) && !defined(WIN32)
#include <sys/time.h>
#endif
#include <fcntl.h>
#include <setjmp.h>
#endif `%/* KPATHSEA */`%
>>>
\List{*}
\item
The \`'__cdecl' is defined in KPATHSEA: kpathsea/c-protos.h
is defined to nothing for compilers
other than MS.
\item
Compiling for kpathsea:
\List{*}
\item Download \Link[
http://www.xs4all.nl/\string
~infovore/tex4htk.tgz]{}{}{
http://www.xs4all.nl/\string
~infovore/tex4htk.tgz}\EndLink.
\item At texk, issue the commands \`'./configure' and \`'make'.
\EndList
\item
In the presence of kpathsea, there is no reason for the explicit
t-records in tex4ht.env. All tex4ht needs is just the information
available to latex about the tfm files, and kpathsea already embeds
it.
\item
To my understanding, the value of @texmf@ is known to kpathsea within
some variable (TEXMF?). The value of that variable can be accessed by
tex4ht through \`'kpse_var_value( "TEXMF" )'. So i-records similar to the
following ones may provide to tex4ht the information needed to locate
the files.
\Verbatim
ikpatsea{TEXMF}\tex4ht\ht-fonts\
ikpatsea{.....}\tex4ht\ht-fonts\
\EndVerbatim
Of course, I rather have texht request kpathsea to perform the search,
but I don't know how to request it through a command similar to the
following one.
\Verbatim
extended_kpse_find_file(filename, var, path, ...)
^^^
"TEXMF"
^^^^^
"\tex4ht\ht-fonts\"
\EndVerbatim
I don't yet follow the details of \`'client_path' but I suspect it might
provide support for instructions similar to the above.
\EndList
%%%%%%%%%%%%%%%%%%
\Section{Outline}
%%%%%%%%%%%%%%%%%%
\<tex4ht.c\><<<
`<defines`>
`<types`>
`<vars`>
`<header functions`>
`<functions`>
`[
int `<CDECL`> main(argc, argv)
int argc`;
U_CHAR **argv
;{ `<main's vars`>
`<resplit argv for windows`>
`<set signals`>
`<program signature`>
`<init traces`>
`<main's init`>
`<main's body`>
return 0;
}
>>>
The operating system claims that
`Compilation exited abnormally with code 255' even on cases that
come back through return. why? Was this corrected?
\<program signature\><<<
(IGNORED) printf("----------------------------\n");
#ifndef KPATHSEA
#ifdef PLATFORM
(IGNORED) printf("tex4ht.c (`version %s)\n",PLATFORM);
#else
(IGNORED) printf("tex4ht.c (`version)\n");
#endif
#else
#ifdef PLATFORM
(IGNORED) printf("tex4ht.c (`version %s kpathsea)\n",PLATFORM);
#else
(IGNORED) printf("tex4ht.c (`version kpathsea)\n");
#endif
#endif
for(i=0; i<argc; i++){
(IGNORED) printf("%s%s ", (i>1)?"\n " : "", argv[i]); }
(IGNORED) printf("\n");
>>>
\SubSection{Main's Body}
\<main's body\><<<
{ long file_len;
`<scan job args`>
`<find tex4ht.env`>
`<get from tex4ht.env file`>
`<file-len = size of file`>
`<open log file`>
`<export TEX4HTFONTSET`>
`<scan postamble`>
(IGNORED) fclose(dot_file);
}
`<scan preamble`>
`<scan pages`>
put_char('\n');put_char('\n');
`<close active output files`>
{ `<idv vars`>
`<extract dvi chunks`> }
>>>
\<vars\><<<
static FILE* dot_file;
>>>
\<main's vars\><<<
register INTEGER i;
int ch;
>>>
\SubSection{Utilities}
\<defines\><<<
#define m_alloc(typ,n) (typ *) malloc_chk((int) ((n) * sizeof(typ)))
>>>
\<header functions\><<<
static void* malloc_chk(ARG_I(int));
>>>
\<functions\><<<
`[
static void* malloc_chk( n ) int n
;{ void* p;
if((p = (void *) malloc( (size_t) n)) == NULL ) bad_mem;
return p;
}
>>>
\<header functions\><<<
static void* r_alloc(ARG_II(void *, size_t));
>>>
\<functions\><<<
`[
static void* r_alloc( q, n )
void *q`;
size_t n
;{ void* p;
if((p = (void *) realloc( q, (size_t) n)) == NULL) bad_mem;
return p;
}
>>>
\Section{Installers and Makefiles}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Command Line Switches}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% q, *
\<scan job args\><<<
{ int i;
U_CHAR *p;
const U_CHAR *in_name = "", *out_name = "";
`<kpathsea arg 0`>
`<DJGPP gif names for pictorial symbols`>
if( argc == 1 ){ bad_arg; }
for(i=1; i<argc; i++) {
if( *( p=argv[i] ) == '-' ){ `<scan flaged args`> }
else in_name = argv[i];
}
`<post scan comm ln args`>
`<open input file`>
{ `<pre open output file`> }
}
>>>
If \`'strlen() argv[i] ) == 2' the switch is followed by a
space to be deleted when more input is awaited.
\<scan flaged args\><<<
if( (int) strlen((char *) argv[i] ) == 2 ){
if( ++i == argc ) bad_arg;
}
switch( *(p+1) ){
case 'b':{ `<get start char-gif from com ln`> break; }
case 'c':{ `<env blocks selector`> break;}
case 'e':{ `<get .env directory from com ln`>break; }
case 'f':{ `<get file name wo dir`>break; }
case 'F':{ `<get ignored chars`> break; }
case 'g':{ `<get gif-type from com ln`> break; }
case 'h':{ `<trace context`> break; }
case 'i':{ `<get htf font dir from com ln`> break; }
case 'l':{ `<get tex4ht.fls from com ln`> break; }
case 'P':{ `<permission for system calls`> break; }
case 'S':{ `<get gif script from com ln`> break; }
case 's':{ `<get cascade style sheet data`> break; }
case 't':{ `<get tfm font dir from com ln`> break; }
case 'u':{ `<get unicode entity representations`> break; }
case 'v':{ `<idv version replacement num`> break; }
case 'x':{ `<extra options`> break; }
case '.':{ `<default extension`> break; }
default:{ bad_arg; }
}
>>>
The following is to allow for commands \`'htlatex
-f/../source/foo.tex' which draw files from other directories for
latex, but use the current directory for tex4ht.c (and t4ht.c). The
character immediately after \`'-f' is a directory indicator character.
The \`'-f/' requests a search for the indicated file to take place in
the current directory, not in a remote one.
\<get file name wo dir\><<<
p = p + 2;
in_name = p + (int) strlen((char *) p );
while( *in_name != *p ){ in_name--; }
in_name++;
>>>
Suboptions for \`'-h'.
\<trace context\><<<
{
char trace = *(p+2);
if (trace == 'A' || trace == 'e') { `<context for warn and errs`> }
if (trace == 'A' || trace == 'f') { `<dump htf files`> }
if (trace == 'A' || trace == 'F') { `<trace htf search`> }
if (trace == 'A' || trace == 's') { `<trace specials in dvi`> }
if (trace == 'A' || trace == 'g') { `<cmd line trace groups`> }
if (trace == 'A' || trace == 'v') { `<dump env file`> }
if (trace == 'A' || trace == 'V') { `<trace env search`> }
else { bad_arg; }
}
>>>
The following is incomplete!!!
\<command line options\><<<
"improper command line\ntex4ht [-f<path-separator-ch>]in-file[.dvi]\n"
" [-.<ext>] replacement to default file extension name .dvi\n"
" [-c<tag name>] choose named segment in env file\n"
" [-e<env-file>]\n"
" [-f<path-separator-ch>] remove path from the file name\n"
" [-F<ch-code>] replacement for missing font characters; 0--255; default 0\n"
" [-g<bitmap-file-ext>]\n"
" [-h[efFgsvVA]] trace: e-errors/warnings, f-htf, F-htf search\n"
" g-groups, s-specials, v-env, V-env search, A-all\n"
" [-i<htf-font-dir>]\n"
" [-l<bookkeeping-file>]\n"
" [-P(*|<filter>)] permission for system calls: *-always, filter\n"
" [-S<image-script>]\n"
" [-s<css-file-ext>] default: -s4cs; multiple entries allowed\n"
" [-t<tfm-font-dir>]\n"
" [-u10] base 10 for unicode characters\n"
" [-utf8] utf-8 encoding for unicode characters\n"
" [-v<idv version>] replacement for the given dvi version\n"
" [-xs] ms-dos file names for automatically generated gifs\n"
>>>
Line breaks within command lines should be physically given.
\Section{I/O Files}
\<defines\><<<
#ifndef PATH_MAX
#define PATH_MAX 512
#endif
>>>
\Verbatim
../html.dir/a.out a -s"--- needs --- %%1.idv[%%2] ==> %%3 ---
" -b"--- characters ---
\EndVerbatim
\SubSection{Open Input}
\<vars\><<<
static FILE* dvi_file;
>>>
\<main's init\><<<
dvi_file = stdin;
>>>
Below: If input is from stdin which is not a console
device, switch it to binary mode.
Warning `statement with no effect' under \`'gcc -Wall' on non-Windows.
This is not a problem; SET_BINARY is a no-op.
\<open input file\><<<
if( *in_name != '\0' ){ `<file = non-piped input file`> }
#ifdef KWIN32
else if (!isatty(fileno(stdin))) SET_BINARY(fileno(stdin));
#endif
>>>
\<file = non-piped input file\><<<
BOOL tag;
job_name_n = (int) strlen( in_name );
job_name = m_alloc(char, job_name_n+6);
(IGNORED) strcpy((char *) job_name, in_name);
tag = job_name_n < 3;
if( !tag ){
tag = !eq_str( job_name+job_name_n-`<dvi ext name length`>,`<dvi ext name`>);
}
if( tag ){
job_name_n+=`<dvi ext name length`>; (IGNORED) strct(job_name, `<dvi ext name`>);
}
if( (dvi_file = fopen(job_name, READ_BIN_FLAGS)) == NULL )
{ `<try jobname with extension`> }
`<open idv file`>
>>>
\<dvi ext name length\><<<
(
(ext==NULL)? 4 : (int) strlen((char *) ext)
)
>>>
\<dvi ext name\><<<
(
(ext==NULL)? ".dvi" : ext
)
>>>
\<vars\><<<
static U_CHAR *ext = NULL;
>>>
\<default extension\><<<
ext = p+1;
>>>
The following tries to remove an extension from the filename,
before adding the `.dvi' extension.
This can help with ht scripts of the form
\Verbatim
$1 $2
$1 $2
$1 $2
tex4ht $2
t4ht $2
\EndVerbatim
in cases that the users insert for \`'$2' file names with extension.
\<try jobname with extension\><<<
{ int i;
for(i=job_name_n-5; i; i--){
if( job_name[i] == '.' ){
job_name[i] = '\0';
job_name_n = i + `<dvi ext name length`>;
(IGNORED) strct(job_name, `<dvi ext name`>);
break;
} }
if( (dvi_file = fopen(job_name, READ_BIN_FLAGS)) == NULL ){
warn_i_str(1, job_name); bad_in_file(job_name);
} }
>>>
\<vars\><<<
static char* job_name;
static int job_name_n;
>>>
strcat should be in string.h, but c++ doesn't find it there.
We use it just for concatenating an extension of
file name. Should have the interface
\`'char *strcat( (char *, const U_CHAR *) );'.
\<header functions\><<<
static void strct( ARG_II(char *, const U_CHAR *) );
>>>
\<functions\><<<
`[
static void strct( str1, str2 )
U_CHAR * str1`;
const U_CHAR * str2
;{ U_CHAR * ch;
ch = str1 + (int) strlen((char *) str1);
(IGNORED) strcpy((char *) ch, str2 );
}
>>>
\SubSection{Open Root Html File}
\<pre open output file\><<<
U_CHAR *name=0;
if( *out_name == '\0' )
{ if( *in_name == '\0' ){ `<name = "tex4htput.html"`> }
else { `<name = derived from input file name`> }
}
else{ `<name = output file from arg list`> }
no_root_file = name;
>>>
We want to wait with actually opening the file output file until the
last minute, to allow a special to change the extension name from the
tex file. That last minute is taken to be when a cll to openning
ort closing a file occurs, or when output is to be written into the file.
\<open output file\><<<
if( no_root_file ){ open_o_file(); }
>>>
\<header functions\><<<
static void open_o_file( ARG_I(void) );
>>>
\<functions\><<<
static void open_o_file(MYVOID)
{
`<open root tex4ht file`>
cur_o_file = root_file = open_html_file(no_root_file);
no_root_file = (char *) 0;
}
>>>
% free((void *) no_root_file);
\<open root tex4ht file\><<<
struct files_rec* p;
p = m_alloc(struct files_rec, 1);
if( opened_files != (struct files_rec*) 0 ) opened_files->prev = p;
p->prev = (struct files_rec *) 0;
p->next = opened_files; opened_files = p;
p->name = no_root_file;
p->file =
>>>
\<cond insert new line\><<<
if( !no_root_file ){
`<end text accent`>
if( !`<radical-line-off`> ){ put_char('\n'); }
}
>>>
\<change ext of root file\><<<
if( no_root_file ){
U_CHAR *name;
name = m_alloc(char, 256);
(IGNORED) strcpy((char *) name, (char *) no_root_file );
free((void *) no_root_file);
no_root_file = name;
name += (size_t) strlen((char *) name); while( *(--name) != '.' ); name++;
while( special_n-- ){
if( (no_root_file+253) == name ) name--;
*name++ = get_char();
}
*name = '\0';
} else {
U_CHAR str[256], *p;
p = str; while( special_n-- ){ *p++ = get_char(); } *p = '\0';
warn_i_str(43,str);
}
>>>
\<vars\><<<
static U_CHAR *no_root_file;
>>>
\<name = "tex4htput.html"\><<<
bad_arg;
>>>
\<name = derived from input file name\><<<
int n = (int) strlen((char *) job_name );
name = m_alloc(char, 6 + n);
(IGNORED) strcpy((char *) name, (char *) job_name);
n -= 4; *(name + n) = '\0';
(IGNORED) strct(name,".html");
#ifdef HTM
name[n+4] ='\0';
#endif
>>>
\<name = output file from arg list\><<<
int tag = 1;
int n = (int) strlen( out_name );
name = m_alloc(char, 6 + n);
(IGNORED) strcpy((char *) name, out_name);
while( n-- ) tag = tag && (*(name+n) != '.') ;
if( tag ) (IGNORED) strct(name,".html");
#ifdef HTM
name[n+4] = '\0';
#endif
>>>
\<header functions\><<<
static FILE* open_html_file( ARG_I(char*) );
>>>
\<functions\><<<
`[
static FILE* open_html_file(name)
char* name
;{ FILE* file;
char* str;
str = m_alloc(char, (int) strlen((char *) name) + 1);
(IGNORED) strcpy((char *) str, (char *) name);
(IGNORED) printf(" file %s\n", str);
(IGNORED) fprintf(log_file, "File: %s\n", str);
if( (file = fopen(str, WRITE_TEXT_FLAGS)) == NULL ) bad_in_file(str);
free((void *) str);
return file;
}
>>>
\<vars\><<<
static FILE *out_file = (FILE *) 0,
*root_file = (FILE *) 0,
*cur_o_file = (FILE *) 0;
>>>
\SubSection{Close Output}
\<close active output filesNO\><<<
if( root_file != (FILE *) 0 ) (IGNORED) fclose(root_file);
>>>
\SubSection{Get Input-File Length}
Files must have length divisible by four and have 4 to 7 \`'trail' characters.
Another problem I ran into is that fileno is unavailable to both
compiler and linker if I turn on ANSI compilation. I checked where you
use it, and it is in only one place, to find the file length of the DVI
file. Your code:
\Verbatim
{ struct STSTAT temp;
(IGNORED) fstat (fileno(dvi_file), &temp);
file_len = (long) temp.st_size;
if( (file_len % 4) != 0 ) bad_dvi;
}
\EndVerbatim
If you change this to use ftell and fseek, you'll have gained ANSI
compatibility (and therewith portability). I tried the following code
instead of yours:
\<file-len = size of file\><<<
{ long curr_pos;
curr_pos = ftell(dvi_file);
(IGNORED) fseek(dvi_file, 0, SEEK_END);
file_len = ftell(dvi_file);
(IGNORED) fseek(dvi_file, curr_pos, `<abs file addr`>);
if( (file_len % 4) != 0 ) bad_dvi;
}
>>>
% SEEK_SET);
To the best of my nowledge, this works exactly like your code (I
tested it). My executable is compiled with this code.
see also fgetpos and fsetpos.
\Section{Coding Hints}
\ifHtml[\HPage{ini (initialization) files in windows}\Verbatim
\Verbatim
/*************** foo.c file *******************/
#include<stdio.h>
#include<string.h>
#include<conio.h>
#include<windows.h>
// Windows-specific functions used:
// _fullpath : make filename into full path
// _splitpath : split a _fullpath into its component parts
// GetPrivateProfileString: handle the .ini file
_fullpath(path,argv[0],250);
_splitpath(path,drive,dir,filname,ext);
char TEXINAME[100], NOBATCHNAME[MV2];
GetPrivateProfileString("Files", "TEXINAME", NOBATCHNAME, TEXINAME, 100, "foo.ini");
GetPrivateProfileString("Strings", "SWPPACK", dSWPPACK, SWPPACK, 100,"foo.ini");
DEBFLAG=GetPrivateProfileInt("integers", "DEBFLAG",0, "foo.ini");
/*************** foo.ini file ********************/
[integers]
; DEBFLAG = 1 prints additional details
DEBFLAG = 0
[files]
; texinfo
TexiName = texis.bat
[strings]
; swp package
SWPPACK = {swpht}
\EndVerbatim\EndHPage{}]\fi
\ifHtml[\HPage{detecting tex tree}\Verbatim
> > on a Windows machine? (How can the installer know where to put files
> > without actually searching the entire hard disc.) Any ideas?
>
> Unfortunately, I have no idea on how to avoid a global search.
a) create a file foo.tex containing
hello
\bye
b) run
tex foo.tex
c) open foo.log and analyze the results. you should be able to get all
the clues you need about their setup
\EndVerbatim\EndHPage{}]\fi
\ifHtml[\HPage{kpathsea}\Verbatim
My key discovery was the library `kpathsea' which does file name look up.
And in particular `kpsewhich --show-path TEXFILETYPE'. So, for example,
`kpsewhich --show-path tfm' returns a colon separated list of paths to tfm
files.
\EndVerbatim\EndHPage{}]\fi
\ifHtml[\HPage{more}\Verbatim
with some help from my neighbour, i got this working at last....
you had
if( *(p = gif_open[gif_flag]) ) IGNORED) strct(str,p);
which we changed to
p = gif_open[gif_flag];
if( p && *p) (IGNORED) strct(str,p);
because if gif_open[fig_flag] is NULL (which you initialize it to
be!), then the *p points to a dead end.....
this applied in some other places too, see appended diff.
{ struct group_info *p, *last;
p = stack[ stack_n ].begin;
\EndVerbatim\EndHPage{}]\fi
\Chapter{Structure of Dvi Files}
\Section{Background}
\Link[
http://www.rpi.edu/\string~sofkam/DVI/dvi.html]{}{}DVI driver standards \EndLink
Dvi files are consisted of three parts.
\bgroup
\def\.#1.#2.{%
\ifcase #1
\Link{#2}{dvistrct}\or
\Link{#2}{}\or
\Link{#2}{}\fi
#2\EndLink}
\Verbatim-\
+-----------+
| \.0.preamble. |
+-----------+
| \.1.pages. |
+-----------+
| \.2.postamble. |
+-----------+
\EndVerbatim
\egroup
\SubSection{Example}
\Link{}{dvcd} \EndLink{}
\Verbatim
000000: PRE version : 2
000002: numerator : 25400000
000006: denominator : 473628672
000010: magnification : 1000
000014: job name ( 27) : TeX output 1995.02.22:0104
000042: BOP page number : 1
000047: 0 0 0
000059: 0 0 0
000071: 0 0 0
000083: prev page offset : -00001
000087: PUSH
000088: DOWN3: -917504
000092: POP
000093: DOWN4: 42152922
000098: PUSH
000099: DOWN4: -41497562
000104: PUSH
000105: FNT_DEF1: 29
000107: checksum : -538297224
000111: scale : 655360
000115: design : 655360
000119: name : cmtt10
000127: FONT_29
000128: Char: <HTML><TITLE>try.htex</TITLE><BODY>
000163: POP
000164: Y3: 786432
000168: PUSH
000169: PUSH
000170: RIGHT3: 1310720
000174: Char: <P>
000177: POP
000178: RIGHT3: 2342903
000182: FNT_DEF1: 12
000184: checksum : 555887770
000188: scale : 655360
000192: design : 655360
000196: name : cmsy10
000204: FONT_12
000205: Char: ff /* ligature (non-printing) */
000206: W3: 145632
000210: Char: 0x 0
000211: W0
000212: Char: A
000213: PUSH
000214: DOWN3: -237825
000218: Char: ffi /* ligature (non-printing) */
000219: POP
000220: RIGHT3: 704510
000224: FONT_29
000225: Char: A
000226: RIGHT3: 344061
000230: Char: B
000231: POP
000232: Y0
000233: PUSH
000234: Char: </BODY></HTML>
000248: POP
000249: POP
000250: DOWN3: 1572864
000254: EOP
000255: POST last page offset : 000042
000260: numerator : 25400000
000264: denominator : 473628672
000268: magnification : 1000
000272: max page height : 43725786
000276: max page width : 30785863
000280: stack size needed: 3
000282: number of pages : 1
000284: FNT_DEF1: 29
000286: checksum : -538297224
000290: scale : 655360
000294: design : 655360
000298: name : cmtt10
000306: FNT_DEF1: 12
000308: checksum : 555887770
000312: scale : 655360
000316: design : 655360
000320: name : cmsy10
000328: POSTPOST postamble offset : 000255
000333: version : 2
000334: TRAILER
000335: TRAILER
000336: TRAILER
000337: TRAILER
000338: TRAILER
000339: TRAILER
\EndVerbatim
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Scan Postamble}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
The postamble of a \Link{dvistrct}{postamble}dvi file\EndLink{} has the
following structure. It should be consistent with the preamble.
\bgroup
\def\.#1.{\Link{fntdef}{}#1\EndLink}
\Verbatim-\
no_ops ??? >= 0 bytes NOPS, I think they are allowed here...
postamble_marker 1 ubyte POST
last_page_offset 4 sbytes
numerator 4 ubytes
denominator 4 ubytes
magnification 4 ubytes
max_page_height 4 ubytes
max_page_width 4 ubytes
max_stack 2 ubytes
total_pages 2 ubytes number of pages in file
.. \.FONT DEFINITIONS. ...
POST_POST 1 byte
postamble_offset 4 sbytes offset in file where postamble starts
version_id 1 ubyte
trailer >= 4 ubytes TRAILER
<EOF>
\EndVerbatim
\egroup
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{No Op's at the End of File}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Verify \LinkPort\<eof op\>{}eof op\EndLink{} and version number
at postamble. There should be at least four trailing characters, and
normally there are no more than seven. They act as signatyre and
provide for file lengths that are divisible by four, aimed at
machines that pack four bytes into a word.
\<scan postamble\><<<
i=0;
do{
i++; file_len -= 1;
(IGNORED) fseek(dvi_file, file_len, `<abs file addr`>);
} while( (ch=get_char()) == `<eof op`> );
eof_op_n = file_len;
if( (i<4)
||
((ch != `<version dvi`>) && (ch > `<version xdv`>))
) bad_dvi;
version_id = ch;
>>>
\<vars\><<<
static int version_id;
>>>
The function \`'(IGNORED) fseek' enables random access to file.
\<abs file addr\><<<
0>>>
\<relative file addr\><<<
1>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Find Start of Postamble}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<scan postamble\><<<
file_len -= 5;
(IGNORED) fseek(dvi_file, file_len, `<abs file addr`>);
if( get_char() != `<end-postamble op`> ) bad_dvi;
eof_op_n -= begin_postamble = get_unt(4);
(IGNORED) fseek(dvi_file, begin_postamble, `<abs file addr`>);
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Find Stack Size and Number of Pages}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<scan postamble\><<<
if( get_char() != `<begin-postamble op`> ) bad_dvi;
(IGNORED) fseek(dvi_file, 16L, `<relative file addr`>);
`<max page dimensions`>
if( (stack_len = (int) get_unt(2)) < 1) bad_dvi;
`<stack = m-alloc...`>
unread_pages = (int) get_unt(2);
>>>
\<main's vars\><<<
int unread_pages;
>>>
\<vars\><<<
static int stack_len;
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Font Definitions}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<scan postamble\><<<
{ `<scan fonts vars`>
BOOL missing_fonts;
#ifndef KPATHSEA
`<fls var`>
`<get dir of tex4ht.fls`>
`<open old cache file`>
#endif
`<init scan fonts vars`>
missing_fonts = FALSE;
`<check env font variables`>
`<start loading fonts`>
`<load unicode.4hf`>
while( (ch = get_char()) != `<end-postamble op`> ){
`<scan font entry`>
}
`<end loading fonts`>
if( missing_fonts ) err_i(14);
#ifndef KPATHSEA
`<close old cache file`>
`<set new cache file`>
#endif
`<free html font memory`>
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Scan Preamble}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
The preamble of a \Link{dvistrct}{preamble}dvi file\EndLink{} has the
following structure.
\Verbatim
no_ops >= 0 bytes NOP, nops before the preamble
preamble_marker 1 ubyte PRE
version_id 1 ubyte
numerator 4 ubytes
denominator 4 ubytes
magnification 4 ubytes
id_len 1 ubyte lenght of identification string
id_string id_len ubytes identification string
\EndVerbatim
\<scan preamble\><<<
(IGNORED) fseek(dvi_file, 0L, `<abs file addr`>);
ch = get_noop();
if( ch != `<start of preamble op`> ) bad_dvi;
if( ((int) get_char()) != version_id ) bad_dvi;
(void) get_unt(4); `%numerator = (INTEGER) get_unt(4);`%
(void) get_unt(4); `%denominator = (INTEGER) get_unt(4);`%
`%magnification =`% (void) get_unt(4);
for( i= get_char(); i>0; i-- ) ch = get_char();
>>>
\<version dvi\><<<
2 >>>
The following is from xetex. As of XeTeX 3.14159265-2.6-0.99996 (TeX
Live 2016), the id value is 7, but it seems prudent and harmless to
allow for a few future updates without recompiling. Or perhaps we
should not check this value at all with xdv.
\<version xdv\><<<
10
>>>
%%%%%%%%%%%%%%%%%%%%
\Section{Scan Pages}
%%%%%%%%%%%%%%%%%%
The pages in a \Link{dvistrct}{pages}dvi file\EndLink{} have the
following structure.
\Verbatim
no_ops >= 0 bytes NOP
begin_of_page 1 ubyte BOP
page_nr 4 sbytes page number
36 bytes
prev_page_offset 4 sbytes offset in file where previous
page starts, -1 for none
.. PAGE DATA ...
end_of_page 1 ubyte EOP
\EndVerbatim
\<scan pages\><<<
{
dis_pages = unread_pages;
while( unread_pages-- ){
(IGNORED) printf("[%d", dis_pages - unread_pages);
`<initial values for new page`>
if( get_noop() != `<start page op`> ) bad_dvi;
for( i = 1; i<45; i++ )
if( get_char() == EOF ) bad_dvi;
while( (ch = get_char()) != `<end page op`> ){
`<process ch according to the case`>
}
`<dump ch-map at end of page`>
(IGNORED) printf("]%c",unread_pages % 10 == 0? '\n' : ' ');
put_char('\n');
} }
>>>
The new line at end of pages is importnant and problematic for
verbatim environment that are expressed by \`'<pre>' elements
\<initial values for new page\><<<
x_val = dx_1 = dx_2 = 0; max_x_val = -10000; `%temp`%
y_val = max_y_val = prev_y_val = dy_1 = dy_2 = 0;
>>>
[\Link{dvcd}{}example of a dvi file\EndLink]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Dvi Trace}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Good for post processing by plug-ins of `tex4ht.c'. That is, we set
\ifHtml[\HPage{hints}\Verbatim
Date: Fri, 22 Aug 1997 20:50:03 -0400 (EDT)
From: Eitan Gurari <
[email protected]>
To: ...
Subject: LaTeX (TeX?) to MathML converter
The way that I understand it, the current proposal is to build a new
engine for math formulas, modeled after the one used by TeX. It seems
to me that, with a little change of a direction, we might have here a
golden opportunity to get much more than just a converter to MathML
from a subset of (La)TeX.
The alternative approach I have in mind consists of a minor
modification to the TeX engine, for optionally seeding dvi files with
\special-hints. Specifically, in standard mode, TeX will output the
standard dvi output, and ignore the modifications made to its
engine. On the other hand, in `special' mode (activated, e.g., by a
switch in a command line), TeX will output self-created \special
instructions into the dvi code.
The specials will mark strategic points in the dvi code, with the
objective of acting as hints to drivers which process the dvi code.
In the case of formulas, the hints will identify the boundaries of the
formulas, the subscripts and superscripts, and other entities of
significance. In the case of halign and valign tables, the specials
will be placed around the tables, around the rows, around the entries,
and at omitted boundaries. Other locations of possible interest might
be page, paragraph, and line breaks. Hence, creating sgml-oriented
tagging, with desirable interpretations provided by dvi drivers.
The following are the main advantages that I can see for the
alternative approach.
1. Compatibility. The marking by the special hints will work with
all TeX-based source documents, no matter what instructions and
style files they employ (including the rich collection of amstex
and amslatex documents).
2. Flexibility. Alternative sgml tags to MathML will be possible,
including tags customized by users with realizations in
style sheets. Non-hypertext applications might also find uses for
structural hints emitted by TeX.
3. Usability. The original proposal is oriented toward
latex2html like systems that try to emulate, only with partial
success, the behavior of TeX. The alternative proposal will serve
such systems, as well as more recent systems that rely on the
native engine of TeX to do the job (e.g., vtex, tex4ht--my system).
In fact, latex2html will need to do only a minor adjustment
to its current implementation. Instead of placing <IMG> pointers to
gif's created externally for the figures, it will import
the MathML code created externally.
4. Portability. The same as for TeX.
5. Effort. It seems to me that the effort needed to modify the
TeX program should be minimal, for someone who is familiar with
the program (not me). I guess the following statement
from the TeX program is applicable here.
1340. Extensions. The program above includes a bunch of "hooks" that
allow further capabilities to be added without upsetting TeX's basic
structure. Most of these hooks are concerned with "whatsit" nodes,
which are intended to be used for special purposes; whenever a new
extension to TeX involves a new kind of whatsit node, a corresponding
change needs to be made to the routines below that deal with such
nodes, but it will usually be unnecessary to make many changes to the
other parts of this program.
In order to demonstrate how extensions can be made, we shall treat
`\write', `\openout', `\closeout', `\immediate', `\special', and
Extracting the code from the dvi files should be a straightforward
task for a driver, when the special hints are present.
On the other hand, the following quote from the TeX program might
also be of interest, if the original proposal intends to include tables.
768. Alignment. It's sort of a miracle whenever \halign and \valign
work, because they cut across so many of the control structures of
TeX.
6. Stability. The core of the TeX engine is a frozen component, probably
untouched even by its recent extensions (etex, pdftex, ...). Hence, a
well thought set of \special-hints will provide a stable base for
different drivers to play with.
Thanks for your attention, -eitan
> Consultant/Programmer sought to develop an LaTeX to MathML converter:
>
<snip>
>
> The MathML specificaion was written with an eye toward the ability to
> convert other mathematical data formats into MathML. Translators that
> can convert mathematical documents utilizing TeX markup will be
> especially useful due to the large quantity of TeX documents that the
> research mathematics community has authored over the past decade.
>
> The AMS, the Geometry Center, and SIAM intend to collaborate to
> support the development of a general purpose LaTeX to MathML
> translator, using the popular public-domain LaTeX2HTML utility as
> model. LaTeX2HTML converts a LaTeX document into an HTML document in
> two general passes. First, LaTeX document structures are converted
> into HTML (e.g. \section to <H1>) and second, each piece of LaTeX math
> is converted into a GIF graphic and inserted into the HTML document
> with an <IMG> command.
>
> We are seeking a consultant/programmer skilled in both TeX and Perl
> (LaTeX2HTML is written in Perl) to adapt LaTeX2HTML to create MathML
> code for each piece of LaTeX math rather than producing GIF graphics.
> Since the end result must be completely compatible with LaTeX, we are
> envisioning the job in terms of modifying TeX to convert its internal
> math list structures into MathML. However, the exact methodology
> employed would be at the discretion of the consultant.
>
<snip>
\EndVerbatim\EndHPage{}]\fi{}
for the postprocessors.
\List{disc}
\item \`'@%P' --- On-off modes for traceing of POP, PUSH in DVI
\item \`'@%C' --- On-off modes for traceing CHAR...RAHC in DVI
\item \`'@%H' --- On-off modes for traceing h spaces
\item \`'@%V' --- On-off modes for traceing v spaces
\item \`'@%R' --- On-off modes for traceing x rulers
In all the above, uppercase for plus, lowercase for minus 1
\item \`'@%%Zx...pre...x...post...' --- Group tracing.
Default \`'<!-- DVI ' and \`'\n-->', where \''\n' represents
new line char (i.e., \`'\EolnCh').
\`'x' can be any character. if it is not there,
the postfix is assumed to be empty.
Z: P, p, D, d,....
\item \`'@%...' --- Not used, if not of the above format
\EndList
\<dvi trace\><<<
if( special_n>1 ) {
special_n--;
if ( get_char() == '%' ) {
if( special_n>2 ) { `<delimiters for trace dvi`> }
else { `<consume unused specials`> }
} else { `<consume unused specials`> }
} else if( special_n ) {
special_n--;
switch ( get_char() ){
case 'P': { trace_dvi_P++; break; }
case 'C': { trace_dvi_C++; break; }
case 'V': { trace_dvi_V++; break; }
case 'H': { trace_dvi_H++; break; }
case 'R': { trace_dvi_R++; break; }
case 'p': { trace_dvi_P--; break; }
case 'c': { trace_dvi_C--; break; }
case 'v': { trace_dvi_V--; break; }
case 'h': { trace_dvi_H--; break; }
case 'r': { trace_dvi_R--; break; }
default: { ; }
} }
>>>
Options 'h' and 'v' are not in use.
\<css for chars\><<<
if( span_on && (default_font != font_tbl[cur_fnt].num) ){
if( !ch_map_flag && start_span ){
if( span_name_on ){
`<open output file`>
if( span_open[0] ) if( *span_open[0] )
(IGNORED) fprintf(cur_o_file, "%s", span_open[0]);
if( span_name[0] ) if( *span_name[0] )
(IGNORED) fprintf(cur_o_file,
span_name[0], font_tbl[cur_fnt].family_name);
if( span_size[0] ) if( *span_size[0] )
(IGNORED) fprintf(cur_o_file,
span_size[0], font_tbl[cur_fnt].font_size);
if( span_mag[0] )
if( *span_mag[0] && (font_tbl[cur_fnt].mag != 100))
(IGNORED) fprintf(cur_o_file,
span_mag[0], font_tbl[cur_fnt].mag);
if( span_ch[0] ) if( *span_ch[0] )
(IGNORED) fprintf(cur_o_file, "%s", span_ch[0]);
}
start_span = FALSE;
}
}
>>>
% `<...gif font mag...`>
\<vars\><<<
static BOOL start_span = FALSE, in_span_ch = FALSE;
>>>
A span special might happen before any font has been defined (fntdef DVI
command seen). In that case, |cur_fnt| will still be $-1$, and we must
avoid indexing |font_tbl|.
See report at
https://puszcza.gnu.org.ua/bugs/?611.
\<span char block\><<<
if( span_on && !in_span_ch && !ignore_chs && !in_accenting
&& cur_fnt >= 0 && (default_font != font_tbl[cur_fnt].num) ){
if( (ch < 137) && (ch != `<insert rule + move op`>) ){
in_span_ch = TRUE; start_span = TRUE;
} }
else if ( in_span_ch ){
if( !span_on ||
(ch == `<insert rule + move op`>) ||
((136 < ch) && (ch < `<mv hor 1-byte`>)) ||
(ch > `<dx.2 store and mv hor 4-byte`>)
){
in_span_ch = FALSE;
if( *end_span[0] ){
`<open output file`>
(IGNORED) fprintf(cur_o_file, "%s", end_span[0]);
}
} }
>>>
\<end accented span\><<<
if( span_on && in_span_ch ){
if( *end_span[0] ){
in_span_ch = FALSE;
`<open output file`>
(IGNORED) fprintf(cur_o_file, "%s", end_span[0]);
} }
>>>
\<end css for chars\><<<
>>>
\<trace dvi char block\><<<
if( trace_dvi_C && !in_trace_char ){
if( (ch < 137) && (ch != `<insert rule + move op`>) ){
in_trace_char = TRUE; block_start = TRUE;
} }
else if ( in_trace_char ){
if( !trace_dvi_C || (ch > 136) || (ch == `<insert rule + move op`>) ){
in_trace_char = FALSE;
} }
>>>
\SubSection{Setting the Delimiters}
The special is assumed to offer a pattern of the form \`'type-ch
del-ch ..... del-ch ....'.
\<delimiters for trace dvi\><<<
U_CHAR type, ch, *p, *q, *pp=0, *qq=0, pre[256], post[256];
special_n -= 2; type = get_char(); ch = get_char();
p = pre;
while( special_n-- > 0 ) {
if ( (*(p++)=get_char() ) == ch ) { p--; break; }
}
*p = '\0';
p = post;
while( special_n-- > 0 ) { *(p++)=get_char(); } *p='\0';
>>>
\<delimiters for trace dvi\><<<
p = m_alloc(char, 1 + (int) strlen((char *) pre));
(IGNORED) strcpy((char *) p, (char *) pre );
q = m_alloc(char, 1 + (int) strlen((char *) post));
(IGNORED) strcpy((char *) q, (char *) post );
>>>
\<delimiters for trace dvi\><<<
switch ( type ){
case 'P': {
pp = trace_dvi_del_P; trace_dvi_del_P = p;
qq = end_trace_dvi_del_P; end_trace_dvi_del_P = q;
break; }
case 'C': {
pp = trace_dvi_del_C; trace_dvi_del_C = p;
qq = end_trace_dvi_del_C; end_trace_dvi_del_C = q;
break; }
case 'V': {
pp = trace_dvi_del_V; trace_dvi_del_V = p;
qq = end_trace_dvi_del_V; end_trace_dvi_del_V = q;
break; }
case 'H': {
pp = trace_dvi_del_H; trace_dvi_del_H = p;
qq = end_trace_dvi_del_H; end_trace_dvi_del_H = q;
break; }
case 'R': {
pp = trace_dvi_del_R; trace_dvi_del_R = p;
qq = end_trace_dvi_del_R; end_trace_dvi_del_R = q;
break; }
case 'p': {
pp = trace_dvi_del_p; trace_dvi_del_p = p;
qq = end_trace_dvi_del_p; end_trace_dvi_del_p = q;
break; }
case 'c': {
pp = trace_dvi_del_c; trace_dvi_del_c = p;
qq = end_trace_dvi_del_c; end_trace_dvi_del_c = q;
break; }
case 'v': {
pp = trace_dvi_del_v; trace_dvi_del_v = p;
qq = end_trace_dvi_del_v; end_trace_dvi_del_v = q;
break; }
case 'h': {
pp = trace_dvi_del_h; trace_dvi_del_h = p;
qq = end_trace_dvi_del_h; end_trace_dvi_del_h = q;
break; }
case 'r': {
pp = trace_dvi_del_r; trace_dvi_del_r = p;
qq = end_trace_dvi_del_r; end_trace_dvi_del_r = q;
break; }
default: { ; }
}
free((void *) pp);
free((void *) qq);
>>>
\<vars\><<<
static BOOL in_trace_char = FALSE, block_start = FALSE;
static int trace_dvi_P = 0, trace_dvi_C = 0,
trace_dvi_H = 0, trace_dvi_R = 0, trace_dvi_V = 0;
static U_CHAR *trace_dvi_del_P, *end_trace_dvi_del_P,
*trace_dvi_del_p, *end_trace_dvi_del_p,
*trace_dvi_del_C, *end_trace_dvi_del_C,
*trace_dvi_del_c, *end_trace_dvi_del_c,
*trace_dvi_del_H, *end_trace_dvi_del_H,
*trace_dvi_del_h, *end_trace_dvi_del_h,
*trace_dvi_del_R, *end_trace_dvi_del_R,
*trace_dvi_del_r, *end_trace_dvi_del_r,
*trace_dvi_del_V, *end_trace_dvi_del_V,
*trace_dvi_del_v, *end_trace_dvi_del_v;
static int push_depth=0, push_id=0, push_st[256];
>>>
\<cmd line trace groups\><<<
trace_dvi_P++;
if( !( *trace_dvi_del_P || *end_trace_dvi_del_P
|| *trace_dvi_del_p || *end_trace_dvi_del_p
)
){
trace_dvi_del_P =
(char *) r_alloc((void *) trace_dvi_del_P,
(size_t) 4);
(IGNORED) strcpy((char *) trace_dvi_del_P, "[G " );
end_trace_dvi_del_P =
(char *) r_alloc((void *) end_trace_dvi_del_P,
(size_t) 2);
(IGNORED) strcpy((char *) end_trace_dvi_del_P, "]" );
trace_dvi_del_p =
(char *) r_alloc((void *) trace_dvi_del_p,
(size_t) 5);
(IGNORED) strcpy((char *) trace_dvi_del_p, "[/G " );
end_trace_dvi_del_p =
(char *) r_alloc((void *) end_trace_dvi_del_p,
(size_t) 2);
(IGNORED) strcpy((char *) end_trace_dvi_del_p, "]" );
}
>>>
\<init traces\><<<
set_del( &trace_dvi_del_P, &end_trace_dvi_del_P);
set_del( &trace_dvi_del_p, &end_trace_dvi_del_p);
set_del( &trace_dvi_del_C, &end_trace_dvi_del_C);
set_del( &trace_dvi_del_c, &end_trace_dvi_del_c);
set_del( &trace_dvi_del_H, &end_trace_dvi_del_H);
set_del( &trace_dvi_del_h, &end_trace_dvi_del_h);
set_del( &trace_dvi_del_R, &end_trace_dvi_del_R);
set_del( &trace_dvi_del_r, &end_trace_dvi_del_r);
set_del( &trace_dvi_del_V, &end_trace_dvi_del_V);
set_del( &trace_dvi_del_v, &end_trace_dvi_del_v);
>>>
\<header functions\><<<
static void set_del( ARG_II(char **, U_CHAR **) );
>>>
\<functions\><<<
`[
static void set_del( del, end_del )
U_CHAR ** del`;
U_CHAR ** end_del
;{
*del = m_alloc(char, 1); **del = '\0';
*end_del = m_alloc(char, 1); **end_del = '\0';
}
>>>
\SubSection{Using the Delimiters}
\<trace h spaces\><<<
if( trace_dvi_H && !ch_map_flag ){
if( *trace_dvi_del_H != '\0' ){
(IGNORED) fprintf(cur_o_file, "%s%d", trace_dvi_del_H, (int) dx);
}
(IGNORED) fprintf(cur_o_file, "%s", end_trace_dvi_del_H);
}
>>>
\<trace v spaces\><<<
if( trace_dvi_V && !ch_map_flag ){
if( *trace_dvi_del_V != '\0' ){
(IGNORED) fprintf(cur_o_file, "%s%d", trace_dvi_del_V, d);
}
(IGNORED) fprintf(cur_o_file, "%s", end_trace_dvi_del_V);
}
>>>
\<trace x rules\><<<
if( trace_dvi_R && !ch_map_flag ){
if( *trace_dvi_del_R != '\0' ){
(IGNORED) fprintf(cur_o_file, "%s%d %d",
trace_dvi_del_R, (int) x_val, (int) y_val);
}
(IGNORED) fprintf(cur_o_file, "%s", end_trace_dvi_del_R);
}
>>>
\<end trace x rules\><<<
if( trace_dvi_R && !ch_map_flag ){
if( *trace_dvi_del_r != '\0' ){
(IGNORED) fprintf(cur_o_file, "%s%d %d",
trace_dvi_del_R, (int) right, (int) up);
}
(IGNORED) fprintf(cur_o_file, "%s", end_trace_dvi_del_r);
}
>>>
\<trace dvi push\><<<
if( push_depth<256 ) { push_st[push_depth] = push_id++; }
if( trace_dvi_P && !ch_map_flag ){
`<open output file`>
if( *trace_dvi_del_P != '\0' ){
(IGNORED) fprintf(cur_o_file, "%s%d %d",
trace_dvi_del_P, push_depth,
push_st[(push_depth<256)? push_depth:256]);
}
(IGNORED) fprintf(cur_o_file, "%s", end_trace_dvi_del_P);
}
push_depth++;
>>>
\<trace dvi pop\><<<
push_depth--;
if( trace_dvi_P && !ch_map_flag ){
`<open output file`>
if( *trace_dvi_del_p != '\0' ){
(IGNORED) fprintf(cur_o_file, "%s%d %d",
trace_dvi_del_p, push_depth,
push_st[(push_depth<256)? push_depth:256]);
}
(IGNORED) fprintf(cur_o_file, "%s", end_trace_dvi_del_p);
}
>>>
\<trace dvi char\><<<
if( trace_dvi_C ){
if( !ch_map_flag ){
`<open output file`>
if( *trace_dvi_del_C != '\0' ){
(IGNORED) fprintf(cur_o_file,
block_start? "%s%s %d B" : "%s%s %d",
trace_dvi_del_C, font_tbl[cur_fnt].name, ch);
}
(IGNORED) fprintf(cur_o_file,"%s", end_trace_dvi_del_C);
}
block_start = FALSE;
}
>>>
\<end trace dvi char\><<<
if( trace_dvi_C && !ch_map_flag ){
`<open output file`>
(IGNORED) fprintf(cur_o_file, "%s%s",
trace_dvi_del_c, end_trace_dvi_del_c);
}
>>>
\Chapter{Semantics of Dvi Code}
[\Link{dvcd}{}example of a dvi file\EndLink]
\Section{Scanning the Characters}
\<process ch according to the case\><<<
{ register int ch_1;
ch_1 = ch;
`<auto quit halign`>
`<process indirect character ch`>
`<get back sub/sup before ch`>
`<trace dvi char block`>
`<span char block`>
`<pre accent symbol`>
if( ch < 132 ) {
x_val += math_class_on? `<set-ch-class(ch-1)`>
: insert_ch(ch_1); `%typset and move`%
if( max_x_val < x_val ) max_x_val = x_val;
} else switch( ch ) {
case 133: case 134: case 135: case 136: {`%typset and don't move`%
INTEGER w;
w = math_class_on? `<set-ch-class(ch-1)`> : insert_ch(ch_1);
max_x_val = ( x_val + w > max_x_val )? x_val + w : max_x_val;
break;
}
`<process dvi op 'ch'`>
}
}
>>>
Characters are inserted into the html file as they are
encountered. Spaces are inserted when \`'x_val' is larger than
\`'max_x_val'.
\<vars\><<<
static long int x_val = 0, max_x_val = -10000,
max_y_val = 0, prev_y_val = 0;
>>>
\Section{Spaces}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Line Breaks}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Conditionally start new line.
\<header functions\><<<
static void try_new_line( ARG_I(void) );
>>>
\<functions\><<<
static void try_new_line(MYVOID)
{ long int v;
double dy;
dy = (cur_fnt == -1)? 0.0 : (`<sign of ex`> * `<size of ex`>) ;
v = y_val - prev_y_val;
if( !text_on && (y_val > max_y_val) ){
if( v > dy/2.5 ){
`<cond insert new line`> max_x_val = -10000;
prev_y_val = max_y_val = stack_n? y_val : 0;
}
}else{
if( v > dy ){ `<cond insert new line`> max_x_val = x_val;
prev_y_val = stack_n? y_val : 0;
}else if( v < -(dy / 1.4) ) prev_y_val = stack_n? y_val : 0;
} }
>>>
The 2.5 divisor provide a line break before b, but not before 5 and a
in \'+${A'}_{5_{a_b}}$+.
\<sign of ex\><<<
(`<size of ex`> < 0? -1 : 1)
>>>
\`+<size of ex>+ is negative in hclassic font of hebrew, and probably also in
other right-to-left fonts.
In default font, one ex is about 4.5pt. We take distances greatr than
1.7ex for distance between lines and distances smaller than of
(1.7/1.3)ex from base line to superscript. My TeX inserts superscripts
before subscripts into the dvi files, no matter what order they have
in the input (otherwise, TeX obeys the input order in the output).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Move Horizontally}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<process dvi op 'ch'\><<<
case `<mv hor 1-byte`>: {;}
case `<mv hor 2-byte`>: {;}
case `<mv hor 3-byte`>: {;}
case `<mv hor 4-byte`>: {
try_new_line();
(void) move_x((INTEGER) get_int(ch - `<mv hor 1-byte`> + 1 ));
break; }
>>>
\<process dvi op 'ch'\><<<
case `<mv hor dist dx.1`>: {
(void) move_x( dx_1 ); break; }
case `<dx.1 store and mv hor 1-byte`>: {;}
case `<dx.1 store and mv hor 2-byte`>: {;}
case `<dx.1 store and mv hor 3-byte`>: {;}
case `<dx.1 store and mv hor 4-byte`>: {
try_new_line();
dx_1 = move_x((INTEGER) get_int(ch - `<mv hor dist dx.1`> ));
break; }
>>>
\<process dvi op 'ch'\><<<
case `<mv hor dist dx.2`>: {
(void) move_x( dx_2 ); break; }
case `<dx.2 store and mv hor 1-byte`>: {;}
case `<dx.2 store and mv hor 2-byte`>: {;}
case `<dx.2 store and mv hor 3-byte`>: {;}
case `<dx.2 store and mv hor 4-byte`>: {
try_new_line();
dx_2 = move_x((INTEGER) get_int(ch - `<mv hor dist dx.2`> ));
break; }
>>>
\<vars\><<<
static INTEGER dx_1 = 0, dx_2 = 0;
>>>
\<header functions\><<<
static INTEGER move_x( ARG_I(register INTEGER) );
>>>
\<functions\><<<
`[
static INTEGER move_x( d )
register INTEGER d
;{ register long i, dx;
x_val += d;
if( (x_val > max_x_val) && x_val ){
if( max_x_val == -10000) max_x_val = x_val - d;
`<insert space`>
} else if( d && text_on && (x_val != max_x_val) ){
`<space for llap`>
}
return d;
}
>>>
\''x_val == max_x_val' typically occurs in push after
\ifHtml[\HPage{hbox}\Verbatim
\documentclass{article}
\usepackage[html,3.2]{tex4ht}
\begin{document}
\parindent=0pt
\section{foo}
\leavevmode=1=\hbox{=2N=}=N3= %[2694266,1347133,2694266]
\leavevmode=4N=\llap{=N5Y=} =Y6= %[983040,-1347133,2330173]
\leavevmode=4=\llap{= 7 =} = 8 = %[983040,-1347133,2330173]
\leavevmode\llap{= 9 = } = 0 =
\end{document}
\EndVerbatim\EndHPage{}]\fi
to compansate for loss of space due to a pop..
The \''\llap' create problems for spaces, e.g., in section number at
section heads. The same might happen for \''\hrule'.
The following provides a solution.
\<space for llap\><<<
if( !ignore_spaces ){
i = (INTEGER) ( (double) (dx = d) / word_sp + 0.5 );
if( i<0 ) i=0;
if( !i ) i = dx>99999L;
if( i ){ put_char(' '); }
}
>>>
Originally, we had also \`'(max_x_val == -10000)' in the else part.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Move Vertically}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<process dvi op 'ch'\><<<
case `<mv ver 1-byte`>: {;}
case `<mv ver 2-byte`>: {;}
case `<mv ver 3-byte`>: {;}
case `<mv ver 4-byte`>: {
(void) move_y( (INTEGER) get_int(ch - `<mv ver 1-byte`> + 1 ));
break; }
>>>
\<process dvi op 'ch'\><<<
case `<mv ver dist dy.1`>: { (void) move_y( dy_1 ); break; }
case `<dy.1 store and mv ver 1-byte`>: {;}
case `<dy.1 store and mv ver 2-byte`>: {;}
case `<dy.1 store and mv ver 3-byte`>: {;}
case `<dy.1 store and mv ver 4-byte`>: {
dy_1 = move_y( (INTEGER) get_int(ch - `<mv ver dist dy.1`> ));
break; }
>>>
\<process dvi op 'ch'\><<<
case `<mv ver dist dy.2`>: { (void) move_y( dy_2 ); break; }
case `<dy.2 store and mv ver 1-byte`>: {;}
case `<dy.2 store and mv ver 2-byte`>: {;}
case `<dy.2 store and mv ver 3-byte`>: {;}
case `<dy.2 store and mv ver 4-byte`>: {
dy_2 = move_y( (INTEGER) get_int(ch - `<mv ver dist dy.2`> ));
break; }
>>>
\<vars\><<<
static INTEGER dy_1 = 0, dy_2 = 0;
static long int y_val = 0;
>>>
\<header functions\><<<
static INTEGER move_y( ARG_I(register INTEGER) );
>>>
\<functions\><<<
`[
static INTEGER move_y( d ) register INTEGER d
;{ y_val += d;
`<trace v spaces`>
return d;
}
>>>
\SubSection{Typeset Indirect Characters}
Read the character to be typsetted, insert the character, and either
move the cursor (ops 128--131) or don't (ops 133--136).
\<process indirect character ch\><<<
if( (ch > 127) && (ch < 137) && (ch != `<insert rule + move op`>) ){
ch_1 = (int) get_unt( (ch - (ch>132)) % 4 +1);
}
>>>
NOTE. Didn't take care yet of character codes beyond c[1] (e.g., unicode).
\ifHtml[\HPage{more}\Verbatim
\documentclass{report}
\usepackage{t1enc}
\input tex4ht.sty \Preamble{html}
\begin{document}
\EndPreamble
\def\x{ � (252) � (253)}
\x ======== \Picture*{} \x \EndPicture{}
\end{document}
\EndVerbatim\EndHPage{}]\fi
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Utilities}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Spaces are inserted when \`'x_val' is larger than
\`'max_x_val'.
\<insert space\><<<
i = (INTEGER) ( (double) (dx = x_val - max_x_val)
/ (text_on? word_sp : margin_sp)
+ 0.5 );
`<try word space if i=0`>
if( i<0 ) i=0;
if( i==0 ){ `<missing space`> }
if( i ){ `<trace h spaces`> }
if( !ignore_spaces ){
`<end text accent`>
while( i-- ) { text_on=TRUE; put_char(' '); }
} else { recover_spaces = (int) i; }
max_x_val = x_val;
>>>
\<try word space if i=0\><<<
if( i==0 ){
i = (INTEGER) ( (double) dx
/ word_sp
+ 0.5 );
}
>>>
\<recover ignored space\><<<
while( recover_spaces-- ){ text_on=TRUE; put_char(' '); }
recover_spaces = 0;
>>>
\<ignore spaces\><<<
ignore_spaces++;
>>>
\<end ignore spaces\><<<
ignore_spaces--;
>>>
\<unhskip vars\><<<
U_CHAR *unhskip_mark;
long retract_addr;
BOOL unhskip;
int cr_fnt, ch, unskip_depth;
>>>
In the case of unhskip we don't want to inore embedded font changes.
\<ignore chs\><<<
if( special_n ){
`<unhskip vars`>
cr_fnt = cur_fnt;
unskip_depth = 0;
unhskip_mark = get_str( (int) special_n ); special_n=0;
retract_addr = ftell(dvi_file);
`<conditional unhskip`>
cur_fnt = cr_fnt;
free((void *) unhskip_mark);
} else { ignore_chs++;; }
>>>
\<conditional unhskip\><<<
unhskip = TRUE;
while( unhskip ){
if( (ch = get_char()) >= 128 ) {
switch( ch ){
`<ignore font def on preview pass`>
`<ignore on preview pass`>
`<ignore indirect chars on preview pass`>
`<push/pop for unhskip`>
`<hooks for conditional unhskip`>
`<fonts and default ignored on preview pass`>
}
} }
`<scan condition`>
>>>
\<scan condition\><<<
do{
long int i;
char *mark;
ch = get_char();
if(
( ch==`<special 1`>) || ( ch==`<special 2`>) ||
( ch==`<special 3`>) || ( ch==`<special 4`>)
)
{
if( tex4ht_special( &ch, &i ) ){
mark = get_str( (int) i );
if( (ch=='@') && ( *mark=='?') && eq_str(mark+1,unhskip_mark)){
break;
} }
}
(IGNORED) fseek(dvi_file, (long) retract_addr, `<abs file addr`>);
} while(FALSE);
>>>
\<ignore indirect chars on preview pass\><<<
case 128: case 129: case 130: case 131: case 133:
case 134: case 135: case 136: {
(void) get_unt( (ch-(ch>132)) % 4 +1);
break;
}
>>>
\<push/pop for unhskip\><<<
case `<sv loc op`>:
case `<retrieve loc op`>: { break; }
>>>
\<hooks for conditional unhskip\><<<
case `<special 1`>: case `<special 2`>:
case `<special 3`>: case `<special 4`>: { long int i;
if( tex4ht_special( &ch, &i ) ){ char *mark;
mark = get_str( (int) i );
if( i ){
if( (ch=='@') && eq_str(mark+1,unhskip_mark) ){
switch( *mark ){
case '[': { unskip_depth++; break; }
case ']': {
unhskip = !(--unskip_depth);
break;
}
default: { ; }
} } }
}else{ `<ignore non-t4ht special`> }
break;
}
>>>
\<end ignore chs\><<<
if( special_n ){
while( special_n-- > 0 ){ (void) get_char(); }
} else { ignore_chs--; }
>>>
\<cond ignore chs\><<<
while( special_n-- > 0 ){ (void) get_char(); }
>>>
\<get eoln str\><<<
if( eoln_str ){ free((void *) eoln_str); }
if( special_n ){
eoln_str = get_str( (int) special_n ); special_n=0;
} else { eoln_str = (char *) 0; }
>>>
\<insert eoln ch\><<<
if( eoln_str ){ print_f(eoln_str); }
else { (IGNORED) put_4ht_ch( ch, cur_o_file ); }
recover_spaces = 0;
>>>
\<vars\><<<
static U_CHAR *eoln_str = (char *)0;
>>>
\<get space str\><<<
if( space_str ){ free((void *) space_str); }
if( special_n ){
space_str = get_str( (int) special_n ); special_n=0;
} else { space_str = (char *) 0; }
>>>
\<insert space ch\><<<
if( space_str ){ print_f(space_str); }
else { (IGNORED) put_4ht_ch( ch, cur_o_file ); }
>>>
\<vars\><<<
static U_CHAR *space_str = (char *)0;
>>>
\<vars\><<<
static int ignore_chs=0, ignore_spaces=0, recover_spaces=0;
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Out of Place Spaces}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<missing space\><<<
long curr_pos;
BOOL done;
int ch, cr_fnt;
curr_pos = ftell(dvi_file);
done = FALSE;
while( !done ){
ch = get_char();
switch( ch ){
`<h-move get space info`>
`<h-move skip font loading`>
`<case: skip font def`>
case `<sv loc op`>:
case `<retrieve loc op`>: { break; }
default: {
if( (ch < `<font 0`>) || (ch > `<font 63`>) ){
done = TRUE;
} else {
`<check alternative space`>
} } } }
(IGNORED) fseek(dvi_file, curr_pos, `<abs file addr`>);
>>>
\<check alternative space\><<<
double word_sp;
cr_fnt = ch - `<font 0`>;
cr_fnt = search_font_tbl( cr_fnt );
word_sp = design_size_to_pt( font_tbl[cr_fnt].word_sp )
* (double) font_tbl[cr_fnt].scale;
i = (INTEGER) ( (double) dx
/ (text_on? word_sp : margin_sp)
+ 0.5 );
`<try word space if i=0`>
if( i>0 ){ i =1; }
>>>
\<h-move get space info-WAIT\><<<
case `<font 1-byte`>:
case `<font 2-bytes`>:
case `<font 3-bytes`>:
case `<font int`>: {
INTEGER n;
int ch;
n = ch - `<font 1-byte`> + 1;
cr_fnt = (int) ((n==4)? get_int(4) : get_unt((int) n));
cr_fnt = search_font_tbl( cr_fnt );
break;
}
>>>
Removed \`'if( !i ) i = dx>99999L;' after \`'if( i<0 ) i=0;', and
insertex \`+<missing space>+ instead. It created problems for cases
like
\List{*}
\item
\Verbatim
\documentclass{article}
\begin{document}
$\mathcal{ABCDEFGHIJKLMNOPQRSTUVWXYTJZ}$
\end{document}
\EndVerbatim
It gives \`'dx=121058' on \`'word_sp=500255.625000'.
A 0.24 ratio with a large space.
\item
But that is problematic for:
\Verbatim
\documentclass{article}
\renewcommand{\rmdefault}{ptm}
\immediate\write16{........\the\textwidth}
\setlength\textwidth{478.00812pt}%
\begin{document}
The first is stylistic - there is inconsistent indenting, the
constants are given non-meaningful names, and are not shared between
\texttt{func} and
\end{document}
\EndVerbatim
The end is disassebled into
\Verbatim
006066: Y0
006067: PUSH
006068: FNT_DEF1: 16
006070: checksum : -538297224
006074: scale : 655360
006078: design : 655360
006082: name : cmtt10
006090: FONT_16
006091: Char: func
006095: RIGHT3: 163840
006099: FONT_15
006100: Char: and
006103: POP
006104: Y0
\EndVerbatim
We have \`'dx=163840' with \`'word_sp=344061.25' for font 16 and
\`'word_sp=163840' fot font 15. The space of 163840 seems fitting the
font that follows.
TeX is cheating here, it should have placed the font change before
the space.
\EndList
%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Rulers}
%%%%%%%%%%%%%%%%%%%%%%%%%%
\<process dvi op 'ch'\><<<
case `<insert rule + move op`>: {
(void) rule_x( TRUE ); break;
}
case `<insert rule + nomove op`>: {
(void) rule_x( FALSE ); break;
}
>>>
\<vars\><<<
static BOOL text_on = FALSE;
>>>
Originally, we didn't have \`'text_on' in the computation of i within
\`'rule_x' and \`'move_x'. This caused a problem in case that we have
a line of the form \`'<math stuff><regular stuff>', because changes to
the variable (WHICH VARIABLE? \''x_val'?) are lost once we get out of
the math stuff. The foollowing example had the problem also after the
introduction of \Verb+text_on+, and got fixed with the
segment \Verb+<try word space if i=0>+.
\Verbatim
\documentclass[twocolumn]{article}
\def\chem#1{\ensuremath {\mathrm {#1}}}
\def\un#1{\ensuremath {\unskip \,\mathrm {#1}}}
\setlength{\textwidth}{180mm}
\begin{document}
The bands we have identified as linear chains come in pairs, the
states are parity doublets. The structure of the intrinsic shapes
has been discussed in many works as given in the introduction
(sect.~xx). We compare the moments of inertia of the
bands in \chem{{\HCode{}}^{14}C} with the moments of inertia of other
molecular bands in light nuclei in table~xx. The
proposed bands in \chem{{\HCode{}}^{14}C} have very large values of
$\hbar^2/2\theta\approx 120$\un{keV}, consistent with the concept
of chain states.
\end{document}
\EndVerbatim
\<header functions\><<<
static void rule_x( ARG_I(BOOL) );
>>>
\<functions\><<<
`[
static void rule_x( tag )
BOOL tag
;{ long i, right, up;
up = (INTEGER) get_int(4);
right = (INTEGER) get_int(4);
if( ch_map_flag ){ `<ruler into ch map`> }
else if( pos_dvi ){
`<pos dvi x rule`>
if( tag ) x_val += right;
} else if( (up>0) && (right>0) ) {
`<typset positive rule x`>
if( tag ) x_val += right;
} else {
`<don't typset rule x`>
if( tag ) x_val += right;
} }
>>>
\<vars\><<<
static U_CHAR rule_ch = '_';
static BOOL `<radical-line-off`> = FALSE;
>>>
\<ruler ch\><<<
if( !special_n ){ rule_ch = '\0'; }
else { while( special_n-- > 0 ){ rule_ch = get_char(); }
}
>>>
\<write to lg file with loc stamp\><<<
struct files_rec *p;
while( special_n-- > 0 ) (void) putc( get_char(), log_file );
for( p = opened_files; p != (struct files_rec*) 0; p = p->next ){
if( p->file == cur_o_file) {
(IGNORED) fprintf(log_file, "%d %s\n",
(int) ftell(cur_o_file), p->name);
break;
} }
>>>
\<typset positive rule x\><<<
if( (x_val + right) &&
( ((x_val + right) > max_x_val)
|| ( !text_on && !ignore_chs )
)
){
if( (max_x_val == -10000) || ((x_val + right) <= max_x_val) )
{ max_x_val = x_val; }
i = (INTEGER) ( (double) (x_val + right - max_x_val)
/ (text_on? word_sp : margin_sp)
+ 0.5 );
`<try word size if i=0`>
if( i && !text_on ) try_new_line();
`<trace x rules`>
while( i-- ) { text_on=TRUE;
if( rule_ch && !`<radical-line-off`> ){ put_char(rule_ch); }
}
`<end trace x rules`>
max_x_val = x_val + right;
}
>>>
\<try word size if i=0\><<<
if( i==0 ){
i = (INTEGER) ( (double) (x_val + right - max_x_val)
/ word_sp
+ 0.5 );
}
>>>
\<radical-line-off\><<<
rule_ch_off
>>>
\<don't typset rule x\><<<
`<trace x rules`>
`<end trace x rules`>
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Command Characters}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Fonts}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<process dvi op 'ch'\><<<
`<case: skip font def`>
>>>
\<case: skip font def\><<<
case `<def 4 byte font`>: (void) get_char();
case `<def 3 byte font`>: (void) get_char();
case `<def 2 byte font`>: (void) get_char();
case `<def 1 byte font`>: {
for( i=0; i<14; i++ ){ ch = get_char(); }
for( i=ch + get_char(); i>0; i--) (void) get_char();
break;
}
>>>
\<process dvi op 'ch'\><<<
case `<xdv-native-font-def op`>:
if( version_id == `<version xdv`> ){
`<skip font xdv def`>
break;
}
case `<xdv-glyph-array op`>:
if( version_id == `<version xdv`> ){
`<do glyph array`>
break;
}
case `<xdv-glyph-string op`>:
if( version_id == `<version xdv`> ){
`<do glyph string`>
break;
}
>>>
\<process dvi op 'ch'\><<<
default: {
if( (ch < `<font 0`>) || (ch > `<font 63`>) ) {
bad_char(ch);
} else { cur_fnt = ch - `<font 0`>;
`<search font in font-tbl`> }
break;
}
>>>
\<process dvi op 'ch'\><<<
case `<font 1-byte`> :
case `<font 2-bytes`>:
case `<font 3-bytes`>:
case `<font int`> : {
INTEGER n;
n = ch - `<font 1-byte`> + 1;
cur_fnt = (int) ((n==4)? get_int(4) : get_unt((int) n));
`<search font in font-tbl`>
break; }
>>>
\<vars\><<<
static int cur_fnt = -1; `% should be INTEGER for DOS`%
>>>
\Section{Specials: Extensions to dvi Primitives}
\<process dvi op 'ch'\><<<
case `<special 1`>: {;}
case `<special 2`>: {;}
case `<special 3`>: {;}
case `<special 4`>: { `<process the specials`> break; }
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Chapter{Specials of TeX4ht}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{The Options}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<process the specials\><<<
long int special_n;
`<end text accent`>
if( tex4ht_special( &ch, &special_n) ) { int sv; sv = ch;
special_on = TRUE; `<process tex4ht special`> special_on = FALSE;
`<check for extra special chars`>
} else { `<non-t4ht special`> }
>>>
\<non-t4ht special\><<<
while( special_n-- ) (void) get_char();
>>>
\<header functions\><<<
static BOOL tex4ht_special( ARG_II( int*, long int*) );
>>>
\<functions\><<<
`[
static BOOL tex4ht_special( chr, special_n)
int *chr`;
long int *special_n
;{ BOOL tex4ht;
int i;
long unsigned N;
tex4ht = FALSE;
`<get special length`>
if( *special_n > (long int) 4 ){
for(i=4; i<9; i++) special_hd[i]=get_char();
special_hd[9]='\0';
`<t4ht special?`>
*chr = special_hd[8];
tex4ht = tex4ht && ( (*chr == '=') || (*chr == '<') ||
(*chr == '>') || (*chr == '*') || (*chr == '@') ||
(*chr == ':') || (*chr == '"') || (*chr == '~') ||
(*chr == ';') || (*chr == '.') || (*chr == '^') ||
(*chr == '|') || (*chr == '+') || (*chr == '!') );
*special_n -= 5; }
else{ special_hd[4]='\0'; }
return tex4ht;
}
>>>
We want to allow both for \`'\special{t4ht...}' and
\`'\special{T4HT...}', for cases that the \''\special' is within
\''\uppercase' (e.g., \`'
\def~{\special{t4ht=+}}
\edef\x{\uppercase{a~b}}\x
\uppercase{a~b}'). We end up with all combinations of upper and
lower case. ELIMINTE/HANDLE also all possible \''\special''s with
letters after the first four!!!!!!!!!
\<t4ht special?\><<<
tex4ht = (special_hd[4] == 't') || (special_hd[4] == 'T');
tex4ht = tex4ht && special_hd[5] == '4';
tex4ht = tex4ht && ((special_hd[6] == 'h') || (special_hd[6] == 'H'));
tex4ht = tex4ht && ((special_hd[7] == 't') || (special_hd[7] == 'T'));
if( tex4ht && trace_special ){
`<trace specials`>
}
>>>
\<get special length\><<<
*special_n = (long int) (N = get_unt(*chr - `<special 1`> + 1));
for(i=4; i--; ){
special_hd[i] = (unsigned char) (N & 0xFF);
N = N >> 8; }
>>>
\<vars\><<<
static U_CHAR special_hd[10];
>>>
\<process tex4ht special\><<<
try_new_line();
switch( ch ){
case '*': { `<other specials`> break; }
case '@': { `<insert char code`> break; }
case '+': { `<ivd code in main pass`> break; }
case '=': { `<insert verbatim record`> break; }
case '<':
case '>': { `<redirect html output`> break; }
case '!': { `<handle char map mode`> break; }
case '|': { gif_ch = !gif_ch; break; }
case ':': { `<arithmetics within specials`> break; }
case ';': { `<css for characters`> break; }
case '"': { `<positioned content`> break; }
case '~': { `<grouped-base delivery content`> break; }
case '.': { `<change ext of root file`> break; }
case '^': { `<classes for math symbols`> break; }
}
>>>
The \LinkPort\<handle requests for dvi to gif code\>{}handle requests ...\EndLink{} is
used for deciding which dvi code should be extracted for gif pictures.
\Verbatim
\specials within current version of TeX4ht
\special{t4ht=...content...}
Insert the specified content to the html output, under
edef mode of processing, and without using the mapping
of the htf fonts. Used in \HCode{...}.
\special{t4ht>...file-name...}
Open a new file, if needed, and direct future output
to the specified file. Used in \File{...}.
\special{t4ht<...file-name...}
Close the specified file. Used in \EndFile{...}.
\special{t4ht++file-name}...dvi...\special{t4ht+}
Pipe the dvi code into a dvi page in the secondary dvi file
`jobname.idv'. Used by \Picture{...}, e.g., for requesting
gif's.
\special{t4ht+embeded-specials within idv}
\special{t4ht!...optional-parameters....}...dvi...\special{t4ht!}
Create an approximated character map for the dvi code.
Used in \Picture{...}, e.g., for ALT of IMG
\special{t4ht|}...\special{t4ht|}
Use the non-pictorial characters of the htf fonts.
Used for character maps of \Picture{....}
\special{t4ht@?...} string for marking errors in html output.
\special{t4ht@-}....\special{t4ht@-}
Remove left margin from character map. Used in \Picture{...}.
\special{t4ht@@}....\special{t4ht@@}
Insert the character codes, instead of their mappings through
the htf fonts. Used in \JavaScript...
\special{
[email protected]...}
Insert the character code to the output.
Used in \Char{-...}, typically, for characters out of the range
of the available keyboards and/or TeX output.
\special{
[email protected]...}
Replace the character code introduced by the next character
with the current char code.
Used in \Char{...}, typically, for symbols out of the range
of the available keyboards and/or TeX output. Unlike \Char{-...},
the current command inherites the font info from the next character
\special{t4ht@+...string...}
Replace the character code introduced by the next character
with the specified string.
Used in \Char{+...} and it inherites the font info from the
next character
\special{t4ht@+. ...string...} configure insertions at eoln
\special{t4ht@+(} ignore spaces
\special{t4ht@+)} end ignore spaces
\special{t4ht@+[} ignore chs and spaces
\special{t4ht@+]} end ignore chs and spaces
\special{t4ht@+!} get last ignored space (none, if from previous lines).
\special{
[email protected]...}
Send message to the lg file. Used in the \Needs{...} command.
\special{t4ht@/} on/off tracing of specials
\special{t4ht.extension for root file}
Addition to next version of TeX4ht:
\special{t4ht;....}
Decorations for htf characters. Useful,for instance, where
style sheets (like css) are available. Provide a full
solution for the problem of fonts, and eliminate the need
for the incomplete 'fonts' option of TeX4ht. Used in
\Configure{characters}.....
\special{t4ht;8....}
No and EndNo for decorating characters.
The next version of TeX4ht will probably also have the following
\special's, without yet fully employing their capabilities.
\special{t4ht"...}
Request for positioned elements. Used only experimentally so far,
due to lack of sufficient support for such feature from
the available browsers. A possible alternative for pictorial
math and drawings.
\special{t4ht~...}
Grouped-base two-way delivery for content created by
inline commands like \over. Feature for xml-oriented
code.
\special{t4ht@_....}
Control output character for rulers.
\special{t4ht*<file}
Input file (with no processing)
\special{t4ht*! system comamnd}
System call
\special{t4ht@%*...open-del....*...close-del....}
Request partial dvi tracing. Not in current use, but might become
handy for postprocessors.
\special{t4ht:....}
Dvi-mode arithmetics. Not in current use, but might become
handy for postprocessors.
\EndVerbatim
\ifHtml[\HPage{more}\Verbatim
Subject: Re: Dvi->HTML
I attach the list of specials in TeX4ht. As you probably noticed, the
\special{t4ht=...} is the most prominnet one. The other specials are
rarely needed; I strongly propose you postpone using them until latter
stages of your work. I normally refer to such instructions indirectly
through low-level \HCode instructions, and high-level
\Configue... instructions. The web pages
http://www.cse.ohio-state.edu/~gurari/tug97/
illustrate such usages.
> My problem is -- glacing through the documentation --
Very little documentation is out, in particular for people that want
to change the system. Please don't hasitate to ask for help when you
need it.
> I tried to find a way to use \special{t4ht=...} direct.
> Unfortunally do I fail, due to the missing comments in the *.sty and
> *.c files. Moreover the use of several macros makes the .c quite
> illegible :-(
Agree. I can't read these files either. They are outputs of
literate programs.
> Could you tell me which commands the .c program needs?
> It wouldd be sufficent if you would give the neccessary commands allown
> (a simple \special{t4ht=<HTML><HEAD></HEAD><BODY>...} does not work.)
Try the following.
1. Prepare a file x.tex having the following content.
\special{t4ht=<HTML><HEAD></HEAD><BODY>}
Just trying.
\special{t4ht=</BODY></HTML>}
\bye
2. Compile the file with tex (`tex x.tex').
3. Compile the outcome with tex4ht (`tex4ht x').
4. Visit the file x.html
\EndVerbatim\EndHPage{}]\fi
%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Insert Code (HCode)}
%%%%%%%%%%%%%%%%%%%%%%%%%%
\<insert verbatim record\><<<
while( special_n-- > 0 ){
int ch;
BOOL flag;
struct hcode_repl_typ *q;
ch = get_char();
q = hcode_repl;
flag = FALSE;
while( q != (struct hcode_repl_typ*) 0 ){
if( ch == *(q->str) ){ flag = TRUE; break; }
q = q->next;
}
if( flag ){
char *chr;
chr = (q->str) + 1;
while( *chr != 0 ){ put_char( *chr ); chr++; }
} else { put_char( ch ); }
}
>>>
\List{*}
\item
\''\special{t4ht*=}' clears all the mappings of characters.
\item
\''\special{t4ht*=xx}' clears the mapping of the character `x'.
\item
\''\special{t4ht*=x....}' asigns a mapping to the character `x'.
\EndList
\<configure hcode\><<<
char *str, *repl;
struct hcode_repl_typ *p, *q;
BOOL flag;
if( special_n ){
repl = str = m_alloc(char, (int) special_n + 1);
while( special_n-- > 0 ){
*str = get_char(); str++;
}
*str = 0;
`<delete old hcode pattern`>
`<add new hcode pattern`>
} else {
`<delete all hcode patterns`>
}
>>>
\<delete all hcode patterns\><<<
while( hcode_repl != (struct hcode_repl_typ*) 0 ){
p = hcode_repl;
hcode_repl = hcode_repl->next;
free((void *) p->str);
free((void *) p);
}
>>>
\<delete old hcode pattern\><<<
if( hcode_repl != (struct hcode_repl_typ*) 0 ){
if( *(hcode_repl->str) == *repl ){
p = hcode_repl;
hcode_repl = hcode_repl->next;
free((void *) p->str);
free((void *) p);
} else {
p = hcode_repl;
while( TRUE ){
q = p->next;
if( q == (struct hcode_repl_typ*) 0 ){ break; }
if( *(q->str) == *repl ){
p->next = q->next;
free((void *) q->str);
free((void *) q);
break;
}
p = q;
} } }
>>>
\<add new hcode pattern\><<<
flag = *repl != *(repl+1);
if( !flag ){ flag = *(repl+2) != 0; }
if( flag ){
p = (struct hcode_repl_typ *) m_alloc(struct hcode_repl_typ, 1);
p->str = repl;
p->next = hcode_repl;
hcode_repl = p;
}
>>>
\<types\><<<
struct hcode_repl_typ { char *str;
struct hcode_repl_typ *next; };
>>>
\<vars\><<<
static struct hcode_repl_typ *hcode_repl
= (struct hcode_repl_typ*) 0;
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Insert Char Code}
%%%%%%%%%%%%%%%%%%%%%%%%%%
\`'\special{t4ht@i}' inserts the character code i, if
i is negative. If i is positive, the font info is sent as a replacement to
the next chracter code (allowing to use that letter character
font info for decoration).
The following is ignored within char maps, so we don't have
to worry about the bound of \`'\<design bound\>'.
\<insert char code\><<<
int code, digit;
special_n--;
switch ( code = get_char() ){
case '%': { `<dvi trace`> break; }
case '@': { verb_ch = !verb_ch; break; }
case '/': { `<on/off special trace`> break; }
case 'e': { `<get err str`> break; }
case '!': { `<recover ignored space`> break; }
case '(': { `<ignore spaces`> break; }
case ')': { `<end ignore spaces`> break; }
case '[': { `<ignore chs`> break; }
case ']': { `<end ignore chs`> break; }
case '?': { `<cond ignore chs`> break; }
case '-': {
if( special_n ) { code = 0; `<code := ...`>
put_char( code );
} else { nomargin = TRUE; }
break; }
case '*': { `<send string to after next ch`> }
case '+': { `<send string to next ch`> break; }
case '.': { `<get eoln str`> break; }
case ',': { `<get space str`> break; }
case '_': { `<ruler ch`> break; }
case 'D': { `<write to lg file with loc stamp`> break; }
case 'u': { `<on/off unicode`> break; }
default: { `<send ch to next ch`> }
}
>>>
\<vars\><<<
static BOOL nomargin = FALSE;
static int next_char = -1;
static U_CHAR *next_str = (char *) 0;
>>>
\<defines\><<<
#define IGNORED void
>>>
\<code := ...\><<<
while( special_n-- > 0 ){
digit = get_char() - '0';
if ( (digit < 0) || (digit > 9) ) { warn_i_int(41,digit+'0') ; }
else { code = code * 10 + digit; }
}
if ( (code < 0) || (code > 255) ) { code = '?'; warn_i_int(41,'?') ; }
>>>
\<send ch to next ch\><<<
code -= '0'; `<code := ...`> next_char = code;
if( `<next-str`> ){ print_f(next_str);
free((void *) next_str); next_str = (char *) 0; }
>>>
Before reading a tring to be submitted forward, we look
for a previous submission. If such exists, we dump it.
\<send string to next ch\><<<
if( `<next-char`> != -1 ) {
`<open output file`>
(IGNORED) put_4ht_ch( `<next-char`> , cur_o_file );
`<next-char`> = -1;
}
if( `<next-str`> ){ print_f(next_str);
free((void *) next_str); next_str = (char *) 0; }
next_str = get_str( (int) special_n ); special_n = 0;
`<set indirect ch codes`>
>>>
Indirect characters are expressed by character codes enclosed within
braces. For instance, `\Verb!\special{t4ht@+\string&{35}x0142;}x!'.
\<set indirect ch codes\><<<
{ char *front, *back;
int i;
back = front = next_str;
while( *front != '\0' ){
if( *front == '{' ){
i = *(++front) - '0';
while( *(++front) != '}' ){ i = i*10 + (*front - '0'); }
*front = (char) i;
}
*(back++) = *(front++);
}
*back = '\0';
}
>>>
\<next-char\><<<
next_char
>>>
\<next-str\><<<
next_str
>>>
\<send string to after next ch\><<<
keepChar=1;
>>>
\<current char for forwarded string\><<<
if( keepChar ){
keepChar=FALSE;
{ `<insert font char`> }
}
>>>
% (IGNORED) put_4ht_ch( ch, cur_o_file );
\<vars\><<<
static BOOL keepChar = FALSE;
>>>
%%%%%%%%%%%%%%%%%
\Section{Files}
%%%%%%%%%%%%%%%%%
Whenever a new file is opened (\''\special{t4ht>...}'), its record is
placed on the top of a stack list. Whenever it is closed
(\''\special{t4ht<...}'), the record is removed from the stack, and
the file at the top of the stack get opened. A reopening of a file
doesn't affect the stack.
The top of the stack is pointed by \`'opened_files', the next file
with \`'next', and the backward with \`'prev'. Each record also holds
a \`'from_file' pointer telling from where the file was last opened.
One can ask for a return to the file that activated the current file
with the command \''\special{t4ht*>}', and to send the record to the
bottom of the stack with \''\special{t4ht*>file-name}'.
\<redirect html output\><<<
`<name = file name`>
`<open output file`>
`<p = pointer to file record`>
if( ch == '>' ){ `<open tex4ht file`> }
else { `<close tex4ht file`> }
cur_o_file = ( out_file == (FILE *) 0 )? root_file
: out_file;
>>>
\<retreat file\><<<
if( special_n > 0 ){
`<send file record to bottom`>
} else {
`<back up to previous output file`>
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Open File}
\<open tex4ht file\><<<
if( p != (struct files_rec*) 0 ){
out_file = p->file;
p->prev_file = cur_o_file;
free((void *) name );
} else {
if( !(*name) ) out_file = (FILE *) 0;
else { `<open non-root tex4ht file`> }
}
>>>
\<open non-root tex4ht file\><<<
p = m_alloc(struct files_rec, 1);
if( opened_files != (struct files_rec*) 0 ) opened_files->prev = p;
p->prev = (struct files_rec *) 0;
p->next = opened_files; opened_files = p;
p->name = name;
p->file = out_file = open_html_file(name);
p->prev_file = cur_o_file;
>>>
\SubSection{Close File}
\<close tex4ht file\><<<
if( p == (struct files_rec *) 0 ) bad_special( name );
else { /* if p is null, do nothing more */
`<fix pointers around file to be closed`>
if( opened_files != (struct files_rec*) 0 )
{ if( out_file == p->file ) out_file = opened_files->file; }
else out_file = (FILE *) 0;
(IGNORED) fclose( p->file ); free((void *) p->name );
free((void *) p );
}
>>>
\<fix pointers around file to be closed\><<<
if( p->prev != (struct files_rec*) 0 ) (p->prev)->next = p->next;
else opened_files = p->next;
if( p->next != (struct files_rec*) 0 ) (p->next)->prev = p->prev;
>>>
\SubSection{Implicitly Closed Files}
\<close active output files\><<<
while( opened_files != (struct files_rec*) 0 )
{
(IGNORED) fclose( opened_files->file );
opened_files = opened_files->next;
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Backup to Previous File}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<back up to previous output file\><<<
`<find record of cur output file`>
>>>
\<find record of cur output file\><<<
static struct files_rec *p, *q;
for( p = opened_files; p != (struct files_rec*) 0; p = p->next ){
if( (p->file == cur_o_file) && p->prev_file ){
`<error if prev file is closed`>
cur_o_file = p->prev_file;
p->prev_file = (FILE *) 0;
break;
}
}
>>>
\<error if prev file is closed\><<<
for( q = opened_files; q != (struct files_rec*) 0; q = q->next ){
if( q->file == p->prev_file ){
break;
}
}
if( q == (struct files_rec*) 0 ){
warn_i_str(51,q->name);
break;
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Intermediate Files to Bottom of Stack}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<send file record to bottom\><<<
static struct files_rec *p, *q;
U_CHAR name[256];
int i;
`<find file record`>
if( p != (struct files_rec*) 0 ){
`<move the file record`>
}
>>>
\<find file record\><<<
i = 0;
name[(int) special_n] = '\0';
while( special_n-- > 0 ){ name[i++] = get_char(); }
for( p = opened_files; p != (struct files_rec*) 0; p = p->next ){
if( eq_str(p->name, name) ){ break; }
}
>>>
\<move the file record\><<<
for( q = p; q->next != (struct files_rec*) 0; q = q->next ){ }
if( q != p ){
q->next = p;
(p->next)->prev = p->prev;
if( opened_files == p ){ opened_files = p->next; }
else { (p->prev)->next = p->next; }
p->prev = q;
p->next = (struct files_rec*) 0;
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Get File Name}
%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<name = file name\><<<
int i=0;
U_CHAR *name;
name = m_alloc(char, (int) special_n+1);
*(name + (int) special_n) = '\0';
while( special_n-- > 0 ) *(name + i++) = get_char();
>>>
\SubSection{Find File Record}
\<p = pointer to file record\><<<
for( p = opened_files; p != (struct files_rec*) 0; p = p->next )
{ if( eq_str(p->name, name) ) break; }
>>>
\<vars\><<<
static struct files_rec
*opened_files = (struct files_rec *) 0, *p;
>>>
\<defines\><<<
struct files_rec{
FILE *file, *prev_file;
char* name;
struct files_rec *next, *prev;
};
>>>
\Section{Import Files, System Calls}
\<other specials\><<<
if( special_n ){
special_n--;
switch ( get_char() ){
case '<': { `<import file`> break; }
case '>': { `<retreat file`> break; }
case '!': { `<system call`> break; }
case '^': { `<accent specials`> break; }
case '@': { `<halign specials`> break; }
case '=': { `<configure hcode`> break; }
default: { `<consume unused specials`> }
}
} else { `<unused special`> }
>>>
The library \`'<stdlib.h>' includes a function
\`'int system(const char *cmdstring);'. When cmdstring is NULL,
the return value is 0 iff the platform does not support system calls.
\''mathml.4ht-9-22: \special{t4ht*!perl m2webeq > tmpa.tmp}%'
\<system call\><<<
U_CHAR name[256], ch;
int i=0, n;
struct sys_call_rec *p;
BOOL flag;
name[(int) special_n] = '\0';
while( special_n-- > 0 ){ name[i++] = get_char(); }
(IGNORED) printf("System call: %s\n", name);
`<flag = permission for system calls`>
if( flag ){
(IGNORED) printf("System return: %d\n",
system_yes? (int) system(name) : -1 );
} else { (IGNORED) printf("No permission for system call\n"); }
>>>
\<get from tex4ht.env file\><<<
(IGNORED) fseek(dot_file, 0L, `<abs file addr`>);
while ( search_dot_file( 'P' ) ){ struct sys_call_rec *q;
U_CHAR *p, str[256];
q = m_alloc(struct sys_call_rec, 1);
q->next = system_calls;
system_calls = q;
p = str;
do
*(p++) = ch = (int) getc(dot_file);
while( (ch !='\n') && (ch != EOF) );
p--;
*p = '\0';
q->filter = m_alloc(char, (int) strlen((char *) str)+1);
(IGNORED) strcpy((char *) q->filter, (char *) str);
}
>>>
\<permission for system calls\><<<
{ struct sys_call_rec *q;
q = m_alloc(struct sys_call_rec, 1);
q->next = system_calls;
q->filter = p + 2;
system_calls = q;
}
>>>
\<flag = permission for system calls\><<<
flag = FALSE;
p = system_calls;
while( p ){
if( (n = (int) strlen((char *) p->filter)) == 1 ) {
flag = flag || (*(p->filter) == '*');
} if( strlen((char *) name) >= (unsigned int) n ) {
ch = name[n]; name[n] = '\0';
flag = flag || eq_str(p->filter,name);
name[n] = ch;
}
p = p->next;
}
>>>
\<defines\><<<
struct sys_call_rec{
char* filter;
struct sys_call_rec *next;
};
>>>
\<vars\><<<
static BOOL system_yes;
static struct sys_call_rec *system_calls = (struct sys_call_rec *) 0;
>>>
\<main's init\><<<
{ U_CHAR *yes = NULL;
system_yes = (system( yes ) != 0);
}
>>>
\<import file\><<<
U_CHAR name[256];
int i=0;
FILE* file;
name[(int) special_n] = '\0';
while( special_n-- > 0 ){ name[i++] = get_char(); }
file = f_open(name, READ_TEXT_FLAGS);
if( file ) {
`<open output file`>
while( (ch = getc(file)) >=0 ){
(IGNORED) put_4ht_ch(ch,cur_o_file);
}
(IGNORED) fclose(file);
} else { warn_i_str( 1, name ); }
>>>
\Section{Arithmetics}
\List{disc}
\item \`':+...' increment by 1( define, if not defined)
\item \`':-...' decrement by 1
\item \`':>...' push current value
\item \`':<...' pop current value
\item \`':!...' display current value
\item \`':|...' display top value
\EndList
\<arithmetics within specials\><<<
if( special_n-- ){
int code, n;
U_CHAR str [255], *p;
struct count_rec *q;
code = get_char();
while( special_n > 254 ){ (void) get_char(); special_n--; }
p = str; n = special_n;
while( special_n-- ) { *(p++) = get_char(); }
*p = '\0';
`<search counter`>
`<act on counter`>
}
>>>
\<types\><<<
struct count_rec{
char* str;
int i, depth, max;
int* stack;
struct count_rec* next;
};
>>>
\<vars\><<<
static struct count_rec *counter = (struct count_rec *) 0;
>>>
\<search counter\><<<
q = counter;
while( q ){
if( eq_str(str,q->str) ) break; q = q->next;
}
if( !q ){
q = m_alloc(struct count_rec, 1);
q->i = q->depth = 0; q->max = 10;
q->next = counter; counter = q;
q->str = m_alloc(char, (int) n+1);
(IGNORED) strcpy((char *) q->str, (char *) str );
q->stack = m_alloc(int, q->max);
}
>>>
\<push counter\><<<
if( q->depth == q->max ){
q->max += 10;
if( (q->stack = (int *) r_alloc( (void *) q->stack,
(size_t) (q->max * sizeof(int)))) == NULL) bad_mem;
}
q->stack[q->depth++] = q->i;
>>>
\<pop counter\><<<
q->depth--;
if( q->max > q->depth + 20 ){ q->max -= 15;
if( (q->stack = (int *) r_alloc( (void *) q->stack,
(size_t) (q->max * sizeof(int)))) == NULL) bad_mem;
}
>>>
\<act on counter\><<<
switch ( code ){
case '+': { (q->i)++; break; }
case '-': { (q->i)--; break; }
case '>': { `<push counter`> break; }
case '<': { if( q->depth ){ `<pop counter`> }
break; }
case '!': { `<open output file`>
(IGNORED) fprintf(cur_o_file, "%d", q->i); break; }
case '|': { if( q->depth ){
`<open output file`>
(IGNORED) fprintf(cur_o_file, "%d", q->stack[q->depth - 1] );
}
break; }
default: { ; }
}
>>>
\Chapter{Character Maps}
\Section{Requests}
\<handle char map mode\><<<
ch_map_flag = !ch_map_flag;
if( ch_map_flag ){ `<enter char map`> }
else { `<exit char map`> }
>>>
\Section{Memory}
The character map is a sequence of lines that are dynamically
allocated. Each character is represented by a pair (str,design), where
str is a possible empty string and design is a character holding a
value 0, 1, 2, or 3.
\<types\><<<
struct ch_map_rec{
char* line;
int max, chars;
};
>>>
\<vars\><<<
static struct ch_map_rec ch_map[HEIGHT];
static int max_map_line, min_map_line;
>>>
The following might be too high of a bound. The agreed def of html
allows only 1024 characters in a map. However, with the permissible
def of html, extra characters hopefully are ignored without causing
problems. Also note that we probably going to have leading and
trailing row that are not in use, and 120 line for a full page figure
is not relly that big of a bound on the numer of lines per page.
\<defines\><<<
#define HEIGHT 120
>>>
\<h ch\><<<
1>>>
\<v ch\><<<
2>>>
\<black ch\><<<
3>>>
\<design bound\><<<
4>>>
The str is a string holding the text that belongs
to the corresponding position: representation for the character,
specials, etc. The design hold a drawing content corresponding to
space, horizontal line, vertical line, and filled area,
respectively. If the str is empty, the design is assumed to be the
content of the character. If the string is not empty, it is assumed to
be the content of the character.
The string holds character codes in
the range of 32--255. Hence, we still have a room to get more
sophisticated designs.
\<vars\><<<
static BOOL ch_map_flag = FALSE;
>>>
\<defines\><<<
#define NULL_MAP (struct map_line_type*) 0
>>>
\SubSection{Initialization}
\List{$\circ$}
\item \`'\special{t4ht!}' --- Default.
\item \`'\special{t4ht!i}'--- Magnified by i/100.
\item \`'\special{t4ht!i,j}'--- Magnified by (i/100,j/100).
\EndList
\<enter char map\><<<
init_ch_map();
xresolution = yresolution = 0;
while( special_n-- > 0 ){
ch = get_char();
if( (ch >= '0') && (ch <= '9') )
{ yresolution = yresolution * 10 + ch - '0'; }
else if( (ch == ',') && !xresolution && yresolution )
{ xresolution = yresolution; yresolution = 0; }
else { `<resolve boundary ch in maps`> }
}
if( !xresolution ) xresolution = yresolution;
if( !xresolution ){ xresolution = XRESOLUTION;
yresolution = YRESOLUTION; }
else { xresolution = xresolution * (INTEGER) (XRESOLUTION / 100);
yresolution = yresolution * (INTEGER) (YRESOLUTION / 100); }
>>>
Resolution can be an integer number or a pair of integer numbers
separated by a comma. The first for x-resolution, the second for
y-resolution. Then we can have a map for boundary characters made
up of pairs: character to be replaced followed by replcment.
\<resolve boundary ch in maps\><<<
xresolution = yresolution = 0;
`<special resolution err`>
>>>
\<vars\><<<
static INTEGER xresolution, yresolution;
>>>
\<defines\><<<
#define XRESOLUTION MARGINSP
#ifdef LONG
#define YRESOLUTION 786432L
#else
#define YRESOLUTION 786432
#endif
>>>
\<header functions\><<<v
static void init_ch_map( ARG_I(void) );
>>>
\<functions\><<<
static void init_ch_map(MYVOID)
{ int i;
for( i=0; i<HEIGHT; i++ ){
ch_map[i].max = 0; ch_map[i].chars = 0; ch_map[i].line = NULL; }
max_map_line = -1;
min_map_line = HEIGHT;
}
>>>
\Section{Fill Map}
\SubSection{With Characters}
The 0.75 constant has been derived by trial and error by centering
the nodes of \`'\TreeSpec(\SRectNode)()()
\Tree()(
3,dog//
0,boxer & 0,cocker & 3, schnauzer //
0,miniature~~schauzer & 0,standard~~schnauzer & 0,giant~~schnauzer//
)'.
\<insert ch to ch-map\><<<
insert_ch_map((char) ch, TRUE);
>>>
\<ch: 1, ruler: 0\><<<
tag>>>
\<header functions\><<<
static void insert_ch_map( ARG_II(char,BOOL) );
>>>
\<functions\><<<
`[
static void insert_ch_map( ch, `<ch: 1, ruler: 0`> )
U_CHAR ch`;
BOOL `<ch: 1, ruler: 0`>
;{ int row, col;
`<get row and col for ch`>
if(ch != 10){
if( (ch_map[row].max > MAX_MAP_LINE) || (col > MAX_MAP_LINE) ){
if( ok_map ){ warn_i_int_2( 25, MAX_MAP_LINE, ch);
ok_map = FALSE; }
}else{ `<adjust boundary ch`>
if( row < min_map_line ) min_map_line = row;
if( row > max_map_line ) max_map_line = row;
if( ch_map[row].max ){ `<insert char to nonempty map line`> }
else { `<insert char to empty map line`> }
} } }
>>>
Leave the following to the user responsibility of getting the char
mappings rightly in the `.htf' fonts.
\Verbatim
\<adjust boundary ch NO\><<<
switch( ch ){
case '>': { ch = 'x'; break; }
case '&': { ch = ''; break; }
case '"': { ch = ''; break; }
}
>>>
\EndVerbatim
\<vars\><<<
static U_CHAR ok_map = TRUE;
>>>
\<get row and col for ch\><<<
{ double x;
row = (int) ( (y_val>0? y_val : 0.0) / (double) yresolution + 0.5);
if( row >= HEIGHT ){
if( ok_map ){ warn_i_int_2( 34, row, ch); ok_map = FALSE; }
return; }
x = (x_val>0? x_val : 0.0 ) / (double) xresolution + 0.75;
col = (int) x;
if( (ch > ' ') && (ch != '-') && (ch != '|') ){
if( row == prevrow ){
if( (col == prevcol + 1) && (x > prev_x + 0.5) )
insert_ch_map(' ', TRUE);
else if( (col > prevcol + 1) && (x < prev_x+0.2)
&& ( ch != '&' ))
col = prevcol + 1;
}else prevrow = -1;
prev_x = x
+ (`<(double) char_width( design_ch? design_ch : ch )`>)
/ (double) xresolution;
prevcol = col;
}else prevrow = -1;
prevrow = row;
}
>>>
The `{\tt( ch != '\&' )}' above is to avoid breaking indirect
unicode characters \`'&...;' in character maps.
\<vars\><<<
static int prevcol = -1, prevrow;
static double prev_x;
>>>
\<defines\><<<
#define MAX_MAP_LINE 500
>>>
\<insert char to empty map line\><<<
int n;
char* p;
ch_map[row].chars = (n = (col + 2 + 5) / 5 * 5) - `<ch: 1, ruler: 0`>;
ch_map[row].max = n - 1;
ch_map[row].line = p = m_alloc(char, n);
while( n-- ){ *(p++) = 0; }
*(ch_map[row].line + col) = ch;
>>>
\Verbatim
\<insert char to empty map lineNO\><<<
int n;
char* p;
ch_map[row].chars = (n = (col + 2 + 5) / 5 * 5)
- `<ch: 1, ruler: 0`>;
ch_map[row].max = n - 1;
ch_map[row].line = p = m_alloc(char, n);
while( n-- ){ *(p++) = 0; }
*(ch_map[row].line + col) = filter_bound_ch(ch);
>>>
\EndVerbatim
\<insert char to nonempty map line\><<<
int n;
char* p;
if( ch_map[row].chars > col ){
`<insert char within map line`> }
else{ `<insert char beyond end of map line`> }
>>>
Below: 8 = 1 (col starts at 0) + 2 (new ch=ch+bound) + 5 (rounding)
The rounding to 5 to (hopefully) help garbage collection.
\<insert char beyond end of map line\><<<
n = (col - ch_map[row].chars + 8) / 5 * 5;
ch_map[row].chars += n - `<ch: 1, ruler: 0`>;
ch_map[row].max += n;
ch_map[row].line = (char *)
r_alloc((void *) ch_map[row].line,
(size_t) ch_map[row].max + 1);
while( n-- ) *(ch_map[row].line + ch_map[row].max - n) = 0;
*(ch_map[row].line + ch_map[row].max
- (ch_map[row].chars - col) + !`<ch: 1, ruler: 0`> ) = ch;
>>>
\Verbatim
\<insert char beyond end of map lineNO\><<<
n = (col - ch_map[row].chars + 8) / 5 * 5;
ch_map[row].chars += n - `<ch: 1, ruler: 0`>;
ch_map[row].max += n;
ch_map[row].line = (char *)
r_alloc((void *) ch_map[row].line,
(size_t) ch_map[row].max + 1);
while( n-- ) *(ch_map[row].line + ch_map[row].max - n) = 0;
*(ch_map[row].line + ch_map[row].max
- (ch_map[row].chars - col) + !`<ch: 1, ruler: 0`> ) =
filter_bound_ch(ch);
>>>
\EndVerbatim
\<insert char within map line\><<<
if( `<ch: 1, ruler: 0`> ){
if( *(ch_map[row].line + ch_map[row].max - 1)
|| (ch_map[row].chars - col == 1) ){ `<get room for ch`> }
col = (ch_map[row].chars--) - col;
p = ch_map[row].line + ch_map[row].max;
while( col ){ unsigned char temp_ch;
if( ((unsigned char) (*p)) < `<design bound`> ) col--;
temp_ch = *(--p); *(p+1) = temp_ch; }
} else {
col = ch_map[row].chars - col;
p = ch_map[row].line + ch_map[row].max;
while( col ){
if( ((unsigned char) (*p)) < `<design bound`> ) col--; p--; }
}
*(++p) = ch;
>>>
\Verbatim
\<insert char within map lineNO\><<<
if( `<ch: 1, ruler: 0`> ){
if( *(ch_map[row].line + ch_map[row].max - 1)
|| (ch_map[row].chars - col == 1) ){ `<get room for ch`> }
col = (ch_map[row].chars--) - col;
p = ch_map[row].line + ch_map[row].max;
while( col ){ if( ((unsigned char) (*p)) < `<design bound`> ) col--;
*(p+1) = *(--p); }
} else {
col = ch_map[row].chars - col;
p = ch_map[row].line + ch_map[row].max;
while( col ){
if( ((unsigned char) (*p)) < `<design bound`> ) col--; p--; }
}
*(++p) = filter_bound_ch(ch);
>>>
\EndVerbatim
\<get room for ch\><<<
ch_map[row].max += 5;
ch_map[row].line = (char *)
r_alloc((void *) ch_map[row].line,
(size_t) ch_map[row].max + 1 );
for( n = 0; n<5; n++ )
*(ch_map[row].line + ch_map[row].max - n) = 0;
ch_map[row].chars += 5;
>>>
REMOVE FILTER!!!!!!
\Verbatim
\<header functionsNO\><<<
INTEGER filter_bound_ch( ARG_I(INTEGER) );
>>>
\EndVerbatim
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{With Rulers}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<ruler into ch map\><<<
long int sv_x_val, sv_y_val, sv_right, sv;
int ch;
sv_x_val = x_val;
sv_y_val = y_val;
sv_right = right;
y_val-=up;
if( right < 0 ){ x_val += right; right = -right; }
if( up < 0 ){ y_val += up; up = -up; }
ch = ( (right > xresolution) && (up > yresolution) ) ?
`<black ch`> : ( ( right > up )? `<h ch`> : `<v ch`> );
right += x_val;
up += sv = y_val;
for( ; x_val < right; x_val += xresolution )
for( y_val = sv ; y_val < up; y_val += yresolution )
insert_ch_map((char) ch, FALSE);
x_val = sv_x_val;
y_val = sv_y_val;
if( sv_x_val + sv_right > max_x_val ) max_x_val = sv_x_val + sv_right;
if( `<ch: 1, ruler: 0`> ) x_val += sv_right;
>>>
\Section{Dump Map}
\<exit char map\><<<
dump_ch_map();
>>>
We need memory of order 2 times the number of characters.
\<dump ch-map at end of page\><<<
if( ch_map_flag ){
warn_i(27); `% dump_ch_map();`% init_ch_map(); }
>>>
\`'dump_ch_map();' creates overflow problems here.
\<header functions\><<<
static void dump_ch_map( ARG_I(void) );
>>>
% if( (min_map_line < 0) || (max_map_line >= HEIGHT) ){ return; }
\<functions\><<<
static void dump_ch_map(MYVOID)
{ int n, i, min, k, extra_sp;
U_CHAR *p;
`% struct map_line_type *q; `%
`<min := start of bounding box`>
for( i=min_map_line; i<=max_map_line; i++ ){
if( ( n = ch_map[i].max) > 0 ){
p = ch_map[i].line; k = min; extra_sp = 0;
`<ignore trailing spaces`>
while( 1 + n-- ){
if( --k < 0 ){
if( extra_sp && (((unsigned char) *p) < `<design bound`>)
&& (((unsigned char) *(p+1)) < `<design bound`>) )
{ extra_sp--;
} else { switch( *p ){ `<dump ch`> } }
}
p++;
}
free((void *) ch_map[i].line );
}
if( i<max_map_line ) put_char('\n');
}
nomargin = FALSE;
}
>>>
\<min := start of bounding box\><<<
{ int max;
min = 100; max = 0;
for( i=min_map_line; i<=max_map_line; i++ ){
p = ch_map[i].line;
n = ch_map[i].max; if( max < n ) max = n;
k = 0; while( n-- ){ if(*(p++)) break; k++; }
if( ch_map[i].max && (k < min) ) min = k; }
if( (max < 78) && !nomargin ) min = 0;
}
>>>
\<ignore trailing spaces\><<<
{ U_CHAR *s;
s = p + n;
while( n && !(*s) && !(*(s-1)) ){ n--; s--; }
if( n && !(*s) && (((unsigned char) *(s-1)) < `<design bound`>) ) n--;
}
>>>
\<dump ch\><<<
case 0: { put_char(' '); break; }
case `<h ch`>: { put_char('-'); break; }
case `<v ch`>: { put_char('|'); break; }
case `<black ch`>: { put_char('#'); break; }
case ' ': { extra_sp++; }
default: { `<insert ch from map into file`> break; }
>>>
\<insert ch from map into file\><<<
BOOL tag;
INTEGER count;
tag = TRUE; count = 0;
do{ if( *p == '<' ) tag = FALSE;
else if( *p == '>' ) tag = TRUE;
else count += tag;
put_char( *p ); n--;
}while( ((unsigned char) *(++p)) >= `<design bound`> );
if( !count ){ n++; p--; }
>>>
COUNT the number of characters and issue a warning if
over limit (1024 default).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Chapter{Extract Code of Figures}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%
\Section{Outline}
%%%%%%%%%%%%%%%%%%
A figure is taken to be a chunck of dvi code enclosed between
\`'+' specials. They are to be translated to, for instance, gif code.
\<vars\><<<
static BOOL dvi_flag = FALSE, dvi_page = FALSE;
static FILE *idv_file;
>>>
\<open idv file\><<<
job_name[job_name_n-1] = 'v';
job_name[job_name_n-2] = 'd';
job_name[job_name_n-3] = 'i';
if( (idv_file = fopen(job_name, WRITE_BIN_FLAGS)) == NULL )
bad_in_file(job_name);
`%job_name[job_name_n-3] = '\0';`%
>>>
\<main's vars\><<<
long int eof_op_n, begin_postamble;
int dis_pages;
>>>
\<extract dvi chunks\><<<
job_name[job_name_n-3] = '\0';
`<extract preamble`>
page_n = 0;
`<start first idv page`>
while( dis_pages ){ `<extract pages`> }
`<error notice into lg`>
`<extract symbols`>
`<extract postamble`>
>>>
\<error notice into lg\><<<
if( errCode > 0 ){
(IGNORED) fprintf(log_file, "tex4ht.c error: %d\n", errCode);
}
>>>
\<vars\><<<
static int errCode = 0;
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Ignore in Main Pass}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
We had \`=case '+': {;}= before. However, this is wrong because we
want to do nothing here with regarding to directives for the gif part.
\<ivd code in main pass\><<<
while( special_n-- > 0 ) (void) get_char();
>>>
%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Preamble}
%%%%%%%%%%%%%%%%%%%%%%%%
\<extract preamble\><<<
file_n = 14;
(IGNORED) fseek(dvi_file, 0L, `<abs file addr`>);
do{ ch = get_char();
idv_char( ch );
file_n++;
}while( ch == `<no op`> );
`<idv version`>
for( i=12; i ; i-- ){ idv_char( get_char() ); }
i = get_char();
idv_char( (int) i ); while( i-- ) idv_copy();
>>>
\<idv version\><<<
ch = get_char();
if( id_version != -1 ){ ch = id_version; }
idv_char( ch );
>>>
\<idv version replacement num\><<<
{ U_CHAR *q;
q = p + 2;
id_version = 0;
while( *q != '\0' ){
if( id_version != -1 ){
if( (*q < '0') || (*q > '9') ){
id_version = -1;
warn_i(53);
}
id_version = id_version * 10 + *q - '0';
}
q++;
} }
>>>
\<vars\><<<
static int id_version = -1;
>>>
%%%%%%%%%%%%%%%%%%%%
\Section{Postamble}
%%%%%%%%%%%%%%%%%%%%
\<extract postamble\><<<
idv_char(`<retrieve loc op`>); file_n += 2;
idv_char( `<end page op`> );
(IGNORED) fseek(dvi_file, begin_postamble, `<abs file addr`>);
begin_postamble = file_n;
idv_char( `<begin-postamble op`> ); file_n += 5;
idv_int( bop_addr ); (IGNORED) fseek(dvi_file, 5L, `<relative file addr`>);
for( i = 20; i; i-- ) idv_copy();
>>>
\<extract postamble\><<<
i = (INTEGER) get_int(2) + 1; idv_char( (int) i >> 8 ); `%stack depth`%
idv_char( (int) i & 0xFF ); file_n += 2;
if( !page_n ) page_n++; idv_char( page_n >> 8 ); `%page number`%
idv_char( (int) page_n & 0xFF ); file_n += 2;
(IGNORED) fseek(dvi_file, 2L, `<relative file addr`>);
>>>
\<extract postamble\><<<
eof_op_n -= 32; `%fonts`%
while( --eof_op_n ) idv_copy();
idv_int(begin_postamble); `%start postamble`%
(IGNORED) fseek(dvi_file, 4L, `<relative file addr`>); file_n += 4;
`<idv version`>
for( i = 8 - file_n % 4; i; i-- ) idv_char( `<eof op`> );
>>>
%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Body}
%%%%%%%%%%%%%%%%%%%%%%%%
\<extract pages\><<<
if( (ch = get_char()) < 128 ) { visible_cnt = TRUE; cond_idv_char( ch );}
else switch( ch ){ `<chars for idv`> }
>>>
\<chars for idv\><<<
case 128: case 129: case 130: case 131: case 133:
case 134: case 135: case 136: {
visible_cnt = TRUE; cond_string( ch, (ch - (ch>132)) % 4 +1 );
break;
}
>>>
Why the \`'cond_string' above?
\<chars for idv\><<<
case `<start page op`>: {
x_val = 0; y_val = 0; stack_n = 0;
(IGNORED) fseek(dvi_file, 44L, `<relative file addr`>); break; }
case `<end page op`>: { dis_pages--; }
case `<no op`>: { break; }
>>>
\<chars for idv\><<<
case `<mv hor dist dx.1`>: {
cond_idv_char( ch ); x_val += dx_1; break; }
case `<mv hor dist dx.2`>: {
cond_idv_char( ch ); x_val += dx_2; break; }
case `<mv ver dist dy.1`>: {
cond_idv_char( ch ); y_val += dy_1; break; }
case `<mv ver dist dy.2`>: {
cond_idv_char( ch ); y_val += dy_2; break; }
>>>
\<chars for idv\><<<
case `<mv hor 1-byte`>: case `<mv hor 2-byte`>:
case `<mv hor 3-byte`>: case `<mv hor 4-byte`>: {
cond_idv_char( ch );
x_val += cond_int( ch - `<mv hor 1-byte`> + 1 ); break; }
case `<dx.1 store and mv hor 1-byte`>:
case `<dx.1 store and mv hor 2-byte`>:
case `<dx.1 store and mv hor 3-byte`>:
case `<dx.1 store and mv hor 4-byte`>: {
cond_idv_char( ch );
dx_1 = (INTEGER) cond_int( ch - `<dx.1 store and mv hor 1-byte`> + 1);
x_val += dx_1; break; }
case `<dx.2 store and mv hor 1-byte`>:
case `<dx.2 store and mv hor 2-byte`>:
case `<dx.2 store and mv hor 3-byte`>:
case `<dx.2 store and mv hor 4-byte`>: {
cond_idv_char( ch );
dx_2 = (INTEGER) cond_int( ch - `<dx.2 store and mv hor 1-byte`> + 1);
x_val += dx_2; break; }
>>>
\<chars for idv\><<<
case `<mv ver 1-byte`>: case `<mv ver 2-byte`>:
case `<mv ver 3-byte`>: case `<mv ver 4-byte`>: {
cond_idv_char( ch );
y_val += cond_int( ch - `<mv ver 1-byte`> + 1);
break; }
case `<dy.1 store and mv ver 1-byte`>:
case `<dy.1 store and mv ver 2-byte`>:
case `<dy.1 store and mv ver 3-byte`>:
case `<dy.1 store and mv ver 4-byte`>: {
cond_idv_char( ch );
dy_1 = (INTEGER) cond_int( ch - `<dy.1 store and mv ver 1-byte`> + 1);
y_val += dy_1; break; }
case `<dy.2 store and mv ver 1-byte`>:
case `<dy.2 store and mv ver 2-byte`>:
case `<dy.2 store and mv ver 3-byte`>:
case `<dy.2 store and mv ver 4-byte`>: {
cond_idv_char( ch );
dy_2 = (INTEGER) cond_int( ch - `<dy.2 store and mv ver 1-byte`> + 1);
y_val += dy_2; break; }
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Rules}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<chars for idv\><<<
case `<insert rule + move op`>:{
visible_cnt = TRUE; cond_string( ch,4 ); x_val += cond_int(4);
break;
}
case `<insert rule + nomove op`>:{
visible_cnt = TRUE; cond_string( ch, 8 );
break;
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Specials}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Only \`'+' tex4ht specials are significant here. The ones of the form
\`'\special{t4ht++...}...\special{t4ht+}' act as delimiters for
segments of dvi code to be trasmitted to the dvi driver (probably
dvips). The other ones of the form \`'\special{t4ht+...}' are ignored
outside such segments, and are replaced by sub-specilas
\`'\special{...}' within the segments. For instance,
\`'\special{t4ht+ps:abc}' translates to \`'\special{ps:abc}' within
these segments. That is, the latter ones are indirect transpoters for
special code (why we need this transportes?).
The non-tex4ht specials are introduced into the idv code. Those that
are parts of the picture environment, are included there (\''dvi_page=TRUE').
Those that not, are included in separate pages. The latter ones might be
definitions and directives for the pictures, as is the case for pstricks
which sends them to dvips.
\<chars for idv\><<<
case `<special 1`>: case `<special 2`>:
case `<special 3`>: case `<special 4`>: { long int i;
int special_nr;
special_nr = ch;
if( tex4ht_special( &ch, &i ) ){
if( ch == '+' ){
`<handle requests for dvi to gif code`> }
else while( i-- ) (void) get_char();
}else if( dvi_flag ){ `<insert non-t4ht special into picture`>
}else { `<insert non-t4ht special on non-picture page`> }
break;
}
>>>
\<insert non-t4ht special on non-picture page\><<<
if( dvi_page || !page_n ){ dvi_page = FALSE; `<advance idv page`> }
dvi_flag = TRUE;
`<insert non-t4ht special`>
dvi_flag = FALSE;
>>>
\<insert non-t4ht special on non-picture pageNO\><<<
while( i-- ) (IGNORED) get_char();
>>>
Replace the last piece with a seek!
(IGNORED) fseek(dvi_file, (long)i, `<relative file addr`>);
\<insert non-t4ht special into picture\><<<
visible_cnt = TRUE; `<insert non-t4ht special`>
>>>
\<insert non-t4ht special\><<<
{
U_CHAR *ch;
int j;
ch = special_hd;
(IGNORED) putc( (unsigned) `<special 4`>, idv_file ); file_n++;
for(j=4; j--; ){ (IGNORED) putc( *ch, idv_file ); file_n++; ch++; }
while( *ch ){ (IGNORED) putc( *ch, idv_file ); file_n++; ch++; }
file_n += (int) i;
while( i-- ) (IGNORED) putc( get_char(), idv_file );
}
>>>
\<handle requests for dvi to gif code\><<<
if( i==0 ){ if( dvi_flag ){ dvi_flag = 0; `<end dvi page`> } }
else{
if( dvi_flag ){ `<make a special for dvi driver`> }
else switch( get_char() ){
case '+': { `<out gif filename`>
dvi_flag = TRUE; dvi_page = TRUE;
`<advance idv page`> break; }
case '@': { `<write to lg file`> break; }
default: { while( --i ) (void) get_char(); break; }
} }
>>>
\<write to lg file\><<<
while( --i ) (void) putc( get_char(), log_file );
(IGNORED) putc( '\n', log_file );
>>>
\Verbatim
\<handle requests for dvi to gif codeNO\><<<
if( i==0 ){ if( dvi_flag ){ dvi_flag = 0; `<end dvi page`> } }
else{
if( dvi_flag ){ `<make a special for dvi driver`> }
else if( '+'==get_char() ){
`<out gif filename`>
dvi_flag = 1; `<advance idv page`> }
else while( --i ) (void) get_char();
}
>>>
\EndVerbatim
\<out gif filename\><<<
{ U_CHAR str[256], *ch;
ch = str; while( --i ) *(ch++) = get_char(); *ch = '\0';
script(font_gif, job_name ,page_n+1, str);
}
>>>
\`'printf("%s", str);'--- tooo much info for screen, and not that significant.
\Verbatim
\<handle requests for dvi to gif codeNO\><<<
if( i ){ `<make a special for dvi driver`> }
else{ dvi_flag = !dvi_flag;
if( dvi_flag ){ `<advance idv page`> }
else { `<end dvi page`> } }
>>>
\EndVerbatim
\<make a special for dvi driver\><<<
cond_idv_char( special_nr );
cond_idv_int( i, special_nr - `<special 1`> + 1 );
while( i-- ) cond_idv_char( get_char() );
visible_cnt = TRUE;
>>>
\<advance idv page\><<<
visible_cnt = FALSE;
bop_addr = advance_idv_page( bop_addr, cur_font );
stack_depth = 0;
set_loc( `<mv hor 1-byte`>, x_val );
set_loc( `<mv ver 1-byte`>, y_val );
>>>
\<idv vars\><<<
INTEGER bop_addr;
>>>
\<header functions\><<<
static void set_loc( ARG_II(int, long int) );
>>>
\<functions\><<<
`[
static void set_loc( op, d )
int op`;
long int d
;{
idv_char( op + 3 ); int_to_dvi( d, 4 ); file_n += 5;
}
>>>
\SubSection{Stack}
\<chars for idv\><<<
case `<sv loc op`>: {
push_stack();
stack_depth++;
cond_idv_char( ch );
break; }
case `<retrieve loc op`>: { INTEGER cur_x, cur_y;
stack_depth--;
cur_x = (INTEGER) x_val; cur_y = (INTEGER) y_val; pop_stack();
if( dvi_flag ){
if( stack_depth<0 ){ warn_i_int( 24, page_n );
`<push curr state`> }
cond_idv_char( ch );
}
break; }
>>>
\<push curr state\><<<
cond_idv_char( `<mv hor 4-byte`> );
idv_int( x_val - cur_x - dx_1 - dx_2 );
cond_idv_char( `<dx.1 store and mv hor 4-byte`> );
idv_int( dx_1 );
cond_idv_char( `<dx.2 store and mv hor 4-byte`> );
idv_int( dx_2 );
cond_idv_char( `<mv ver 4-byte`> );
idv_int( y_val - cur_y - dy_1 - dy_2 );
cond_idv_char( `<dy.1 store and mv ver 4-byte`> );
idv_int( dy_1 );
cond_idv_char( `<dy.2 store and mv ver 4-byte`> );
idv_int( dy_2 );
cond_idv_char( `<sv loc op`> ); file_n += 24;
>>>
\<end dvi page\><<<
if( !visible_cnt ) { U_CHAR str[256];
(IGNORED) sprintf(str, "--- empty picture --- %sidv[%d] ---\n",
job_name,page_n);
(IGNORED) printf("%s", str); (IGNORED) fprintf(log_file, "%s",str); }
while( stack_depth-- > 0 ){
idv_char(`<retrieve loc op`>); file_n++; }
>>>
\<idv vars\><<<
int stack_depth=0;
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Fonts Definition}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<chars for idv\><<<
case `<def 4 byte font`>:
case `<def 3 byte font`>:
case `<def 2 byte font`>:
case `<def 1 byte font`>: { idv_char( ch ); file_n++;
for( i=14; i; i-- ){ ch = get_char(); idv_char( ch ); file_n++; }
i = ch; i += ch = get_char(); idv_char( ch ); file_n++;
while( i-- ){ idv_copy(); }
break; }
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Fonts Activation}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
The following must be consisted with the usage
\`'for( i=1; i<=cur_font[0]; i++ ){'.
\<chars for idv\><<<
case `<font 1-byte`>:
case `<font 2-bytes`>:
case `<font 3-bytes`>:
case `<font int`>: { int i;
idv_char( ch ); file_n++;
cur_font[0] = ch - `<font 1-byte`> + 2;
cur_font[1] = ch;
for( i=2; i <= cur_font[0]; i++ ){
ch = get_char(); idv_char( ch );
cur_font[i] = ch; file_n++; }
break; }
>>>
\<chars for idv\><<<
default: {
if( (ch < `<font 0`>) || (ch > `<font 63`>) ){
if( `<font defs for xdv's idv?`> ){
`<xdv font def for idv`>
} else { err_i(23); }
}
else { idv_char( ch ); file_n++;
cur_font[0] = 1; cur_font[1] = ch; }
break;
}
>>>
\<idv vars\><<<
char cur_font[6];
BOOL visible_cnt=FALSE;
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Symbols from Fonts}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<extract symbols\><<<
{ int ch, i, mag;
U_CHAR str[256];
(IGNORED) fprintf(log_file, "%s", begin_char_gif);
dvi_flag = TRUE;
for( cur_fnt = font_tbl_size; cur_fnt--; ){
`<output to lg file`>
for( i = font_tbl[cur_fnt].char_l - font_tbl[cur_fnt].char_f + 1;
i--; )
if( get_bit( font_tbl[cur_fnt].gif_on, i) ){
bop_addr = advance_idv_page( bop_addr, cur_font );
set_loc( `<mv hor 1-byte`>, (long int) mid_page_x );
set_loc( `<mv ver 1-byte`>, (long int) mid_page_y );
`<get font of symbol`>
`<insert the char code`> }
}
(IGNORED) printf("Execute script ``%slg'\n",
job_name);
(IGNORED) fclose( log_file );
}
>>>
\<insert the char code\><<<
if( (ch = i + font_tbl[cur_fnt].char_f) > 127 ) {
if( ch < 256 ) cond_idv_char(133); else warn_i(23); }
cond_idv_char( ch );
mag = (int) ((double) font_tbl[cur_fnt].scale /
font_tbl[cur_fnt].design_sz * 10 );
`<insert gif symbol to lg file`>
script(font_gif, job_name ,page_n, str);
>>>
\`'printf("%s", str);' --- too mach info
\<open log file\><<<
{ U_CHAR str[256];
(IGNORED) strcpy((char *) str, (char *) job_name);
str[job_name_n-1] = '\0';
str[job_name_n-2] = 'g';
str[job_name_n-3] = 'l';
if( (log_file = fopen(str, WRITE_TEXT_FLAGS)) == NULL )
bad_in_file(str);
}
>>>
\<vars\><<<
static FILE* log_file;
>>>
\<max page dimensions\><<<
mid_page_y = (INTEGER) get_unt(4) / 2;
mid_page_x = (INTEGER) get_unt(4) / 2;
>>>
\<vars\><<<
static INTEGER mid_page_y, mid_page_x;
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Activate Font}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<get font of symbol\><<<
{ INTEGER num;
num = font_tbl[cur_fnt].num;
if( num <= `<number of direct fonts`> )
cond_idv_char( (int) (num + `<font 0`>) );
else if( dvi_flag ){
if( (num < 0) || (num > 16777215L) ) idv_int(`<font int`>);
else if( num < 256 ) { idv_char(`<font 1-byte`>); file_n++; }
else if( num < 65536L ) int_to_dvi((long int) `<font 2-bytes`>,2);
else int_to_dvi((long int) `<font 3-bytes`>,3);
cond_idv_char( (int) num );
} }
>>>
The above seems to be incorrect for big numbers!!!!!!!!!!!!!!
\Section{Utilities}
\<header functions\><<<
static void idv_char( ARG_I(int) );
>>>
\<functions\><<<
`[
static void idv_char( n ) int n
;{ (IGNORED) putc( n, idv_file ); }
>>>
\<header functions\><<<
static void cond_idv_char( ARG_I(int) );
>>>
\<functions\><<<
`[
static void cond_idv_char( n ) int n
;{
if( dvi_flag ){ (IGNORED) putc( n, idv_file ); file_n++; }
}
>>>
\<header functions\><<<
static void idv_copy( ARG_I(void) );
>>>
\<functions\><<<
static void idv_copy( MYVOID )
{ idv_char( get_char() ); file_n++; }
>>>
\<defines\><<<
#define idv_int(val) int_to_dvi((long int) val,4)
>>>
\<header functions\><<<
static void cond_idv_int( ARG_II(long int, int) );
>>>
\<functions\><<<
`[
static void cond_idv_int( val, n ) long int val`;
int n
;{
if( dvi_flag ){ int_to_dvi((long int) val, n ); file_n += n; }
}
>>>
\<header functions\><<<
static void int_to_dvi( ARG_II(long int, int) );
>>>
\<functions\><<<
`[
static void int_to_dvi( val, n ) long int val`;
int n
;{ unsigned U_CHAR ch2, ch3, ch4;
ch4 = (unsigned char) (val & 0xFF); val = val >> 8;
ch3 = (unsigned char) (val & 0xFF); val = val >> 8;
ch2 = (unsigned char) (val & 0xFF); val = val >> 8;
switch( n ){
case 4: idv_char( (int) val );
case 3: idv_char( ch2 );
case 2: idv_char( ch3 );
case 1: idv_char( ch4 ); }
`% if( val & 0x80 ) val -= 0x100;`%
}
>>>
\<header functions\><<<
static void cond_string( ARG_II(int, int) );
>>>
\<functions\><<<
`[
static void cond_string(ch, n) int ch`; int n
;{ cond_idv_char( ch );
while( n-- ) cond_idv_char( get_char() );
}
>>>
\SubSection{Advance Page}
\<start first idv page\><<<
x_val = 0; y_val = 0; stack_n = 0;
idv_char( `<start page op`> );
idv_int( page_n + 1 ); for( i=36; i--; ) idv_char( 0);
idv_int( -1 ); bop_addr = file_n; file_n += 45;
idv_char(`<sv loc op`>); file_n++;
>>>
\<vars\><<<
static int page_n, file_n;
>>>
\<header functions\><<<
static INTEGER advance_idv_page( ARG_II( INTEGER,char*) );
>>>
\<functions\><<<
`[
static INTEGER advance_idv_page( bop_addr, cur_font )
INTEGER bop_addr`;
char* cur_font
;{ int i;
if( page_n++ ){
idv_char(`<retrieve loc op`>); file_n++;
idv_char(`<end page op`>); file_n++;
idv_char( `<start page op`> );
idv_int( page_n ); for( i=36; i--; ) idv_char( 0);
idv_int( bop_addr ); bop_addr = file_n; file_n += 45;
idv_char(`<sv loc op`>); file_n++;
for( i=1; i<=cur_font[0]; i++ ){
idv_char( cur_font[i] ); file_n++;
} }
`<set dx-1, dx-2, dy-1, dy-2`>
return bop_addr;
}
>>>
What \`'cur_font[0]' holds?
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Set Store-Move Variables}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Dvips requires font definitions before the following code in the first
dvi page.
\<set dx-1, dx-2, dy-1, dy-2\><<<
store_mv(`<dx.1 store and mv hor 4-byte`>, dx_1);
store_mv(`<dx.2 store and mv hor 4-byte`>, dx_2);
store_mv(`<dy.1 store and mv ver 4-byte`>, dy_1);
store_mv(`<dy.2 store and mv ver 4-byte`>, dy_2);
>>>
\<header functions\><<<
static void store_mv( ARG_II( int, INTEGER) );
>>>
\<functionsNO-sep-9\><<<
`[
static void store_mv(op, d) int op`; INTEGER d
;{
cond_idv_char(op); cond_idv_int( (INTEGER) -d);
cond_idv_char(op); cond_idv_int( (INTEGER) d);
}
>>>
\<functions\><<<
`[
static void store_mv(op, d) int op`; INTEGER d
;{
if( dvi_flag ){
cond_idv_char(op); idv_int( (INTEGER) -d);
cond_idv_char(op); idv_int( (INTEGER) d); file_n += 8; }
}
>>>
\Chapter{Tables}
\Section{The Options}
In TeX, we initiate tables of the form
\Verbatim
\haligh{
... & ... & ... \cr
... & ... & ... \cr
}
\EndVerbatim
Such tables translate to dvi tables of the form
\Verbatim
<group>
<group> ... </group>
<group> ... </group>
<group> ... </group>
</group>
<group>
<group> ... </group>
<group> ... </group>
<group> ... </group>
</group>
\EndVerbatim
The option \`'@' asks to start puting the entries over
the following groups.
Two problems arise:
\List{a}
\item
Unless the tables are followed by
external end-groups, we don't have any mark telling where they end.
The \`'/' special can be used in the sources to identify the
ends.
\item The \`'\nohalign' commands provide dirty entries between the rows.
\EndList
\<halign specials\><<<
int i;
i = 0;
special_n--;
switch ( get_char() ){
case '8': { i++; }
case '7': { i++; }
case '6': { i++; }
case '5': { i++; }
case '4': { i++; }
case '3': { i++; }
case '2': { i++; }
case '1': { `<new halign setting`> break; }
case '/': { if( special_n ){
`<consume unused specials`>
} else { `<output end halign table`> }
break;
}
case '&': { i++; }
case '@': { i++;
if( special_n ){
`<consume unused specials`>
} else {
`<start halign table`>
}
break;
}
default: { `<consume unused specials`> }
}
>>>
\Section{Info for Entries}
\SubSection{Inilialization of Base}
The base entries are stored in a set of global variables
\`'str[i]', and the number of usages is stored in
\`'refs[i]'. The strings can be used from the global shared base
or the local groups.
\<vars\><<<
static struct halign_rec *halign[8];
>>>
\<defines\><<<
struct halign_rec{
char * str;
int refs;
};
>>>
\<main's init\><<<
{ int i;
for( i=8; i--; ){
halign[i] = m_alloc(struct halign_rec, 1);
halign[i]->str = m_alloc(char, 1);
*(halign[i]->str) = '\0';
halign[i]->refs = 1;
}
}
>>>
\SubSection{Modifications to Base}
\<new halign setting\><<<
if( halign[i]->refs == 1 ){
free((void *) halign[i]->str );
} else {
(halign[i]->refs)--;
halign[i] = m_alloc(struct halign_rec, 1);
halign[i]->refs = 1;
}
halign[i]->str = get_str( (int) special_n );
special_n=0;
>>>
\SubSection{Initializations for Groups}
Each group level has its local set of variables. Whenever a
request \`'@' or \`'&'arrives, we verify that the local entries
equal to the current global entries. If not, the local entries are
replaced with copies of the current ones.
\<struct stack_entry\><<<
struct halign_rec *halign[8];
BOOL halign_on, halign_info, row_no, col_no;
>>>
\<initialized entries for grouping stack\><<<
stack[i].halign_info = FALSE;
stack[i].halign_on = FALSE;
>>>
\SubSection{Modifications for Groups at Requests}
Upon requesting an insertion for \''\halign', through
\''\special{t4ht*@}' or \''\special{t4ht*&}', we modify the
entries on the current group.
\<start halign table\><<<
new_halign = i * TRUE;
>>>
\<vars\><<<
static BOOL new_halign = FALSE;
>>>
\<insert start of halign\><<<
stack[stack_n].halign_on = new_halign;
if( stack[stack_n].halign_info )
{ int j;
for( j=8; j--; ){
if( stack[stack_n].halign[j] != halign[j] ){
if( ! (--(stack[stack_n].halign[j]->refs) ) ){
free((void *) stack[stack_n].halign[j]->str );
free((void *) stack[stack_n].halign[j] );
}
stack[stack_n].halign[j] = halign[j];
(halign[j]->refs)++;
}
}
} else { int j;
stack[stack_n].halign_info = TRUE;
for( j=8; j--; ){
stack[stack_n].halign[j] = halign[j];
(halign[j]->refs)++;
} }
print_f( stack[stack_n].halign[0]->str );
stack[stack_n].row_no = 0;
new_halign = FALSE;
>>>
\SubSection{Release at Starts of Groups}
The following are for introducing tabular tags for containing rows and
cols of tables. They are encounter before \''stack_n' changes its
value.
\<halign at entry to group\><<<
if( new_halign ){
`<insert start of halign`>
}
if( stack[stack_n].halign_on )
{
print_f( stack[stack_n].halign[2]->str );
if( stack[stack_n].halign_on > TRUE ){
stack[stack_n].row_no++;
stack[stack_n].col_no = 0;
(IGNORED) fprintf(cur_o_file, "%d%s",
stack[stack_n].row_no, stack[stack_n].halign[6]->str );
} }
if( stack_n ){
if( stack[stack_n-1].halign_on )
{
print_f( stack[stack_n-1].halign[4]->str );
if( stack[stack_n-1].halign_on > TRUE ){
stack[stack_n-1].col_no ++;
(IGNORED) fprintf(cur_o_file, "%d%s",
stack[stack_n-1].col_no, stack[stack_n-1].halign[7]->str );
}
} }
>>>
\SubSection{Release at Ends of Groups}
\<halign at exit from group\><<<
`<output end halign table`>
`<output end halign row`>
`<output end halign col`>
if( stack[stack_n].halign_info )
{ int j;
for( j=8; j--; ){
if( ! (--(stack[stack_n].halign[j]->refs) ) ){
free((void *) stack[stack_n].halign[j]->str );
free((void *) stack[stack_n].halign[j] );
} }
stack[stack_n].halign_info = FALSE;
}
>>>
The ouput for end of table is for the current level of table, for end
of row is an immediately containig tables, and for a col is for second
generation containing table.
\<auto quit halign\><<<
if( stack[stack_n].halign_on )
{
switch( ch ){
case `<mv ver 1-byte`>: { ; }
case `<mv ver 2-byte`>: { ; }
case `<mv ver 3-byte`>: { ; }
case `<mv ver 4-byte`>: { ; }
case `<mv ver dist dy.1`>: { ; }
case `<dy.1 store and mv ver 1-byte`>: { ; }
case `<dy.1 store and mv ver 2-byte`>: { ; }
case `<dy.1 store and mv ver 3-byte`>: { ; }
case `<dy.1 store and mv ver 4-byte`>: { ; }
case `<mv ver dist dy.2`>: { ; }
case `<dy.2 store and mv ver 1-byte`>: { ; }
case `<dy.2 store and mv ver 2-byte`>: { ; }
case `<dy.2 store and mv ver 3-byte`>: { ; }
case `<dy.2 store and mv ver 4-byte`>: { ; }
case `<insert rule + move op`>: { ; }
case `<insert rule + nomove op`>: { ; }
case `<font 1-byte`>: { ; }
case `<font 2-bytes`>: { ; }
case `<font 3-bytes`>: { ; }
case `<font int`>: { ; }
case `<sv loc op`>:
{ break; }
default: { `<print end of halign table`> }
} }
>>>
\<output end halign table\><<<
if( stack[stack_n].halign_on )
{ `<print end of halign table`> }
>>>
\<print end of halign table\><<<
print_f( stack[stack_n].halign[1]->str );
stack[stack_n].halign_on = FALSE;
>>>
\<output end halign row\><<<
if( stack_n ){
if( stack[stack_n-1].halign_on )
{
print_f( stack[stack_n-1].halign[3]->str );
} }
>>>
\<output end halign col\><<<
if( stack_n-1 ){
if( stack[stack_n-2].halign_on )
{
print_f( stack[stack_n-2].halign[5]->str );
} }
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Chapter{Groups and Group-Based Inline Operations}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
For instance, \''\over'. Such operations require delivery of
information backward and forward to the group boundaries.
For backward delivery, a preprocessing phase is introduced.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Data Transfer}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Upon encountering a \''\special{t4ht~...}' we arrive here. An empty
content is an on/off flag. When it is turned on, we make a
preprocessing pass to collect information.
\List{*}
\item \`'\special{t4ht\string~} ... \special{t4ht\string~}' --- start
/ end switches for backward submissions.
\item
\`'\special{t4ht\string~<i...}', \`'\special{t4ht\string~>i...}' ---
send to group at relative level i.
\item
\`'\special{t4ht\string~<*...}' --- send back to start of previous
token / group.
\item \`'\special{t4ht~<[}...\special{t4ht~<]}' --- hide region
from back token / group submission
\item \`'\special{t4ht~<-} ... \special{t4ht~<+}' --- latex mode
of token / group submissions (what is that???)
\item \`'\special{t4ht~<)} \special{t4ht~<(}...\special{t4ht~<)}'
--- activate / hide back token / group submissions
\EndList
\<grouped-base delivery content\><<<
if( special_n ){
`<main pass for group special`>
} else if( (group_dvi = !group_dvi) == TRUE ){
long curr_pos;
int ch, sv_stack_n;
`<subp vars`>
`<init subp vars`> stack_id = 0;
curr_pos = ftell(dvi_file); sv_stack_n = stack_n;
`<send info backward`>
`<exit early pass`>
(IGNORED) fseek(dvi_file, curr_pos, `<abs file addr`>);
group_dvi = TRUE; stack_n = sv_stack_n; stack_id = 0;
} else { `<exit group special`> }
>>>
% SEEK_SET);
\<vars\><<<
static BOOL group_dvi = FALSE;
>>>
\<main's vars\><<<
int stack_id=0;
>>>
\SubSection{Main Pass}
\<main pass for group special\><<<
U_CHAR in_ch;
if( (in_ch = get_char()) == '>' ) {
`<store forward submissions`>
} else if( in_ch == '!' ) {
`<store path-based forward submissions`>
} else {
if( !group_dvi ){ warn_i(42); }
(IGNORED) fseek(dvi_file, (long) --special_n,
`<relative file addr`>);
special_n = 0;
}
>>>
\ifHtml[\HPage{example}\Verbatim
\def\:temp{\special{t4ht\string~<<NOMINATOR>}\HCode{</NOMINATOR><DENOMINATOR>}\o:over:
\special{t4ht\string~></DENOMINATOR>}}
\Let\over=\:temp
aa
\special{t4ht\string~}$
{A \over B} \over C
$\special{t4ht\string~}
\EndVerbatim\EndHPage{}]\fi
%%%%%%%%%%%%%%%%%%%%%
\SubSection{Memory}
%%%%%%%%%%%%%%%%%%%%%
\<vars\><<<
static int stack_n = 0;
static struct stack_entry* stack;
>>>
\<types\><<<
struct stack_entry{
long int x_val, y_val;
INTEGER dx_1, dx_2, dy_1, dy_2;
BOOL text_on;
BOOL `<BOOl stack accented`>;
`<struct stack_entry`>
};
>>>
\<stack = m-alloc...\><<<
stack = m_alloc(struct stack_entry,`<size of stack for nested boxes`>);
`<initialize grouping stack`>
>>>
\<size of stack for nested boxes\><<<
((int) stack_len + 2)
>>>
The \`'\special{t4ht^>*...*...}' asks for delimiters on the next group
or token, whichever comes first. That information is stored in the
stack even for characters, which implies an implicit stack greater by
one than that requested by the dvi code. The extra 2 in size reults
from the pointer being on the next empty entry?
% (IGNORED) fprintf(cur_o_file, "\n..end := begin := 0.......%d-%d",stack_id,stack_n);
\<struct stack_entry\><<<
int stack_id;
struct group_info * begin;
struct stack_end_entry * end;
>>>
\<types\><<<
struct group_info{
int stack_id;
U_CHAR *info;
struct group_info* next;
};
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Initialization}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<initialize grouping stack\><<<
{ int i;
for( i=`<size of stack for nested boxes`>-1; i>=0; i--){
stack[i].begin = (struct group_info *) 0;
stack[i].end = (struct stack_end_entry *) 0;
stack[i].stack_id = -1;
`<initialized entries for grouping stack`>
} }
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Submissions and Retievals}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{``Forward'' to End of Group}
\<store forward submissions\><<<
if( special_n == 1 ){
special_n--;
switch( get_char() ){
case '[': { ignore_end_group++; break; }
case ']': { ignore_end_group--; break; }
default: { `<unused special`> }
}
} else {
struct stack_end_entry *p;
U_CHAR *q;
int j;
j = get_char() - '0' + stack_n - 1;
if( --special_n ){
if (j >= stack_len ) { j = stack_len - 1; }
p = m_alloc(struct stack_end_entry,1);
p->next = stack[ j ].end;
stack[ j ].end = p;
q = p->send = m_alloc(char,special_n+1);
while( --special_n ) *q++ = get_char();
*q = '\0';
} }
>>>
We create a linked list for forward submissions.
\<types\><<<
struct stack_end_entry{
struct stack_end_entry *next;
U_CHAR *send;
};
>>>
\<retrieve forward submissions\><<<
struct stack_end_entry *q, *p, *t;
q = stack[ stack_n-1 ].end;
p = stack[ stack_n-1 ].end = (struct stack_end_entry *) 0;
while( q ){
t = q->next; q->next = p; p = q; q = t;
}
while( p ){
if( ! ignore_end_group ){ print_f( p->send ); }
free((void *) p->send );
q = p; p = p->next; free((void *) q );
}
>>>
% (IGNORED) fprintf(cur_o_file, "\n%d. SEND FORWARD.........stack[%d] [SEND %s]\n",
% stack_id, stack_n-1, stack[ stack_n-1 ].end);
\<get forward deliveries\><<<
while( stack[stack_n-1].end ){
`<retrieve forward submissions`>
}
>>>
\<vars\><<<
static int ignore_end_group;
>>>
% (IGNORED) fprintf(cur_o_file, "\n%d. GET FORWARD.........stack[%d] [GET: %s]\n",
% stack_id, stack_n, stack[ stack_n ].end);
\<exit group special\><<<
{ int stack_n;
for( stack_n=`<size of stack for nested boxes`>;
stack_n>0; stack_n--){
group_dvi = TRUE; `<get forward deliveries`>
group_dvi =FALSE;
`<check forward deliveries`>
} }
>>>
\<check forward deliveries\><<<
while( stack[stack_n-1].begin ){
struct group_info *p;
warn_i_str(44, stack[stack_n-1].begin->info);
p = stack[stack_n-1].begin;
stack[stack_n-1].begin = p->next;
free((void *) p );
}
stack[stack_n-1].stack_id = -1;
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{``Backward'' to Begin of Groups}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
The following is needed for pops that appear before the leading push.
Such pops can emerge from preceding code.
\<send info back to group open\><<<
struct group_info *p;
U_CHAR *q;
int j;
j = ch - '0' + stack_n - 1;
if (j >= stack_len ) { j = stack_len - 1; }
p = m_alloc(struct group_info,1);
p->next = stack[ j ].begin; stack[ j ].begin = p;
p->stack_id = stack[ j ].stack_id;
q = p->info = m_alloc(char,i+1);
while( --i ) *q++ = get_char();
*q = '\0';
>>>
``Backward'' submissions to levels higher than 0 are forward
transmission and they can be moved to the main pass. That will buy
a little saving in memory, and possibly less fragmentation there
due to dynamic allocation of mem.
% stack[stack_n].end = (struct stack_end_entry *) 0;
\<push/pop on backward submissions\><<<
case `<sv loc op`>: {
`<stack-id into linked list`>
stack[stack_n].stack_id = stack_id++;
`<push-id + 1`> stack_n++;
if( stack_n > `<size of stack for nested boxes`> ){ warn_i(40); }
break;
}
case `<retrieve loc op`>: {
stack_n--; `<pop-id + 1`>
stack[stack_n].stack_id = -1;
break;
}
>>>
The following changes the previously unknown stack-id, represented by
-1, of a future deeper PUSH with the stack-id of that PUSH. That PUSH
is just have been reached. We also want to reverse the order within
that sub-list.
\<stack-id into linked list\><<<
{ struct group_info *p, *last;
if( (last = p = stack[ stack_n ].begin) != (struct group_info *)0 )
if( p->stack_id == -1 ){
`<modify id of sub sequence`>
`<reverse stack sub sequence`>
}
}
>>>
\<modify id of sub sequence\><<<
while( p ){
if( p->stack_id != -1 ){ break; }
p->stack_id = stack_id;
last = p;
p = p->next;
}
>>>
\<reverse stack sub sequence\><<<
while ( stack[ stack_n ].begin != last ){
p = (stack[ stack_n ].begin) -> next;
(stack[ stack_n ].begin) -> next = last->next;
last->next = stack[ stack_n ].begin;
stack[ stack_n ].begin = p;
}
>>>
\<get backward deliveries\><<<
{ struct group_info *p;
if( group_dvi &&
( (p = stack[stack_n].begin ) != (struct group_info *)0)
){
while( p ){
if( p->stack_id != stack_id ) break;
print_f(p->info);
stack[stack_n].begin = p->next;
free((void *) p );
p = stack[stack_n].begin;
}
}
stack_id++;
}
>>>
% if( group_dvi ){
% (IGNORED) fprintf(cur_o_file, "\n..%d.......%d-%d",
% stack[stack_n].begin,stack_id,stack_n);
% }
% (IGNORED) fprintf(cur_o_file,
% "\n%d. GET BACKWARD.........stack[%d]=%d \n",
% stack_id, stack_n,stack_id);
The following reverses the list at the end of the preprocessing pass.
\<exit early pass\><<<
{ struct group_info *first, *second, *temp;
int i;
for(i = stack_len; i >= 0; i--){
first = stack[i].begin;
if( first ) {
second = first->next;
while( second ){
temp = second->next;
second->next = first;
first = second;
second = temp;
}
(stack[i].begin)->next = (struct group_info *) 0;
stack[i].begin = first;
} } }
>>>
\Section{Preprocessing Pass}
Used for collecting information to be sent backward to the entry
point of the group. The information is stored in linked lists
of type \`'struct group_info', accessed through
\`'stack[ level-of-nesting ].begin'.
Each PUSH is offered an id. Upon reaching a PUSH, its id is stored in
\`'stack[ level-of-nesting ].stack_id'. Upon reaching a
\`'\special{t4ht~<...message...}', the message is inserted into the linked
list, together with its PUSH id. The messages are retrieved in the
main pass, as the PUSHES are traversed for the second time.
\<send info backward\><<<
while( group_dvi ){
`<extract backward submissions`>
}
>>>
\SubSection{Specials}
Only \`'~' tex4ht specials are significant here. The ones of the form
\`'\special{t4ht~}' act as end-points for the preprocessing,
and \`'\special{t4ht~<...}' act as backward info delivers.
\<hooks for backward submissions\><<<
case `<special 1`>: case `<special 2`>:
case `<special 3`>: case `<special 4`>: { long int i;
if( tex4ht_special( &ch, &i ) ){
if( ch == '~' ){
`<handle requests for backward submissions`>
} else {
(IGNORED) fseek(dvi_file, (long) i, `<relative file addr`>);
}
}else{ `<ignore non-t4ht special`> }
break;
}
>>>
\<ignore non-t4ht special\><<<
U_CHAR *ch;
ch = special_hd + 4;
while( *ch ){ ch++; }
(IGNORED) fseek(dvi_file, (long) i, `<relative file addr`>);
>>>
% while( i-- ) (IGNORED) get_char();
% change to
% (IGNORED) fseek(dvi_file, (long) i, `<relative file addr`>);
% also in \<chars for idv\>
\<handle requests for backward submissions\><<<
if( i==0 ){
group_dvi = FALSE ;
}else{
switch( get_char() ){
case '<': {
if( i-- ){ U_CHAR ch;
if( (ch = get_char()) == '*' )
{ `<send back over token / group`> }
else if( (ch == '[') && (i==1) ){
i--; `<hide back token / group`>
}
else if( (ch == ']') && (i==1) ){
i--; `<end hide back token / group`>
}
else if( (ch == '-') && (i==1) ){
i--; `<latex back token / group`>
}
else if( (ch == '+') && (i==1) ){
i--; `<end latex back token / group`>
}
else if( (ch == '(') && (i==1) ){
i--; `<back token / group`>
}
else if( (ch == ')') && (i==1) ){
i--; `<end back token / group`>
}
else { `<send info back to group open`> }
}
break; }
default: { (IGNORED) fseek(dvi_file, (long) --i,
`<relative file addr`>); break; }
} }
>>>
% sv_stack_n<stack_n
When \''sv_stack_n==stack_n' the sending back NEED NOT be in the level
of the opening \''\special{t4ht~}'. This is so because it can happen
that we have, for instance, \`'\special ~<POP><PUSH>'
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Fonts and Default}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Fonts Definition:
\<ignore font def on preview pass\><<<
case `<def 4 byte font`>: (void) get_char();
case `<def 3 byte font`>: (void) get_char();
case `<def 2 byte font`>: (void) get_char();
case `<def 1 byte font`>: { int i;
for( i=14; i; i-- ){ ch = get_char(); }
i = ch + get_char();
(IGNORED) fseek(dvi_file, (long) i, `<relative file addr`>);
break; }
>>>
Fonts Activation:
\<fonts and default ignored on preview pass\><<<
case `<font 1-byte`>:
case `<font 2-bytes`>:
case `<font 3-bytes`>:
case `<font int`>: {
INTEGER n;
n = ch - `<font 1-byte`> + 1;
cr_fnt = (int) ((n==4)? get_int(4) : get_unt((int) n));
cr_fnt = search_font_tbl( cr_fnt );
break; }
default: {
if( (ch < `<font 0`>) || (ch > `<font 63`>) ) {
if( ch == `<end page op`> ) { warn_i(46); }
else { warn_i_int(45,ch); }
} else { cr_fnt = ch - `<font 0`>;
cr_fnt = search_font_tbl( cr_fnt );
}
break;
}
>>>
\<subp vars\><<<
int cr_fnt;
>>>
\<init subp vars\><<<
cr_fnt = cur_fnt;
>>>
\SubSection{Skipped Content}
\<extract backward submissions\><<<
if( (ch = get_char()) >= 128 ) {
switch( ch ){
`<indirect chars on early backward submissions pass`>
`<ignore font def on preview pass`>
`<ignore on preview pass`>
`<push/pop on backward submissions`>
`<hooks for backward submissions`>
`<fonts and default ignored on preview pass`>
}
} else { `<ch-id + 1`> }
>>>
\<indirect chars on early backward submissions pass\><<<
case 128: case 129: case 130: case 131: case 133:
case 134: case 135: case 136: {
ch = (int) get_unt( (ch-(ch>132)) % 4 +1);
`<ch-id + 1`>
break;
}
>>>
\<ignore on preview pass\><<<
case `<insert rule + move op`>:
case `<insert rule + nomove op`>:{
(IGNORED) fseek(dvi_file, 8L, `<relative file addr`>);
break;
}
>>>
\<ignore on preview pass\><<<
case `<start page op`>: {
(IGNORED) fseek(dvi_file, 44L, `<relative file addr`>); break; }
>>>
\<ignore on preview pass\><<<
case `<mv hor 1-byte`>: case `<mv hor 2-byte`>:
case `<mv hor 3-byte`>: case `<mv hor 4-byte`>: {
(IGNORED) (get_int( ch - `<mv hor 1-byte`> + 1 )); break; }
case `<dx.1 store and mv hor 1-byte`>:
case `<dx.1 store and mv hor 2-byte`>:
case `<dx.1 store and mv hor 3-byte`>:
case `<dx.1 store and mv hor 4-byte`>: {
(IGNORED) (get_int( ch - `<dx.1 store and mv hor 1-byte`> + 1));
break; }
case `<dx.2 store and mv hor 1-byte`>:
case `<dx.2 store and mv hor 2-byte`>:
case `<dx.2 store and mv hor 3-byte`>:
case `<dx.2 store and mv hor 4-byte`>: {
(IGNORED) (get_int( ch - `<dx.2 store and mv hor 1-byte`> + 1));
break; }
case `<mv ver 1-byte`>: case `<mv ver 2-byte`>:
case `<mv ver 3-byte`>: case `<mv ver 4-byte`>: {
(IGNORED) (get_int( ch - `<mv ver 1-byte`> + 1));
break; }
case `<dy.1 store and mv ver 1-byte`>:
case `<dy.1 store and mv ver 2-byte`>:
case `<dy.1 store and mv ver 3-byte`>:
case `<dy.1 store and mv ver 4-byte`>: {
(IGNORED) (get_int( ch - `<dy.1 store and mv ver 1-byte`> + 1));
break; }
case `<dy.2 store and mv ver 1-byte`>:
case `<dy.2 store and mv ver 2-byte`>:
case `<dy.2 store and mv ver 3-byte`>:
case `<dy.2 store and mv ver 4-byte`>: {
(IGNORED) (get_int( ch - `<dy.2 store and mv ver 1-byte`> + 1));
break; }
>>>
\<ignore on preview pass\><<<
case `<mv hor dist dx.1`>:
case `<mv hor dist dx.2`>:
case `<mv ver dist dy.1`>:
case `<mv ver dist dy.2`>:
{ break; }
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Push/Pop Locations}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<process dvi op 'ch'\><<<
case `<sv loc op`>: { `<push values to stack`> break; }
case `<retrieve loc op`>: { `<pop values from stack`> break; }
>>>
The dvi stacking code refers to the information \`'x_val', \`'y_val',
\`'dx_1', \`'dx_2', \`'dy_1', and \`'dy_2'.
\<push values to stack\><<<
`<halign at entry to group`>
`<get backward deliveries`>
stack[stack_n].text_on = text_on;
push_stack(); `<get back sub/sup before group`>
`<path group at entry to group`>
`<trace dvi push`>
`<add push class del`>
>>>
\<header functions\><<<
static void push_stack( ARG_I(void) );
>>>
\<functions\><<<
static void push_stack(MYVOID)
{
stack[stack_n].x_val = x_val;
stack[stack_n].dx_1 = dx_1;
stack[stack_n].dx_2 = dx_2;
stack[stack_n].y_val = y_val;
stack[stack_n].dy_1 = dy_1;
stack[stack_n].dy_2 = dy_2;
`<push inherit del`>
stack_n++;
if( stack_n > `<size of stack for nested boxes`> ){
warn_i(40);
}
`<init end math accented`>
}
>>>
\<push inherit del\><<<
stack[stack_n+1].sv_no_left_del = stack[stack_n+1].no_left_del;
stack[stack_n+1].no_left_del = stack[stack_n].no_left_del;
>>>
\<pop uninherit del\><<<
stack[stack_n].no_left_del = stack[stack_n].sv_no_left_del;
>>>
The first statement partially handles spaces after text that is inserterted
to the left of the current location (e.g., items of list).
\<pop values from stack\><<<
`<add pop class del`>
`<path group at exit from group`>
`<get forward deliveries`>
`<trace dvi pop`>
`<halign at exit from group`>
pop_stack();
if( ((x_val+0.6*word_sp) < stack[stack_n].x_val) ) put_char(' ');
text_on = stack[stack_n].text_on;
>>>
\<header functions\><<<
static void pop_stack( ARG_I(void) );
>>>
\<functions\><<<
static void pop_stack(MYVOID)
{
`<end math accented`>
`<pop uninherit del`>
--stack_n;
x_val = stack[stack_n].x_val;
dx_1 = stack[stack_n].dx_1;
dx_2 = stack[stack_n].dx_2;
y_val = stack[stack_n].y_val;
dy_1 = stack[stack_n].dy_1;
dy_2 = stack[stack_n].dy_2;
}
>>>
\Section{Backward Token / Group Submissions}
TeX places subscripts and superscripts on the most recent group or
token. We are looking where the most recent of these tokens starts,
and store there the desired info.
% \`'\special{...[}...\special{...]}' is ignore when looking for
% tokens. \`'\special{...]...}' sends back its content to the start of
% the most recent entity. \`'\special{...[*...*...}' sets open-close
% math codes---how?
\SubSection{Scan to Record Recieving Points}
We keep a delimiter stack for the parenthesis, and include there also
the opening and closing of groups. The latter one are employed
to deal with parenthese that are not balanced, for instance,
\`'\left( ... \right.'.
\<pop-id + 1\><<<
if( !back_id_off ){
if( !id_hide ){ ch_token = FALSE;
sv_id = stack[stack_n].stack_id; }
while( del_stack != (struct del_stack_entry*) 0 ){
struct del_stack_entry* p;
int id;
del_stack = (p = del_stack)->next;
id = p->id;
free((void *) p );
if( id == -1 ) break;
} }
>>>
\<push-id + 1\><<<
if( !back_id_off )
{ struct del_stack_entry *p;
p = m_alloc(struct del_stack_entry,1);
p->next = del_stack;
p->id = p->fnt = -1;
del_stack = p;
}
>>>
\<ch-id + 1\><<<
ch_id++;
if(!back_id_off ){
if( !id_hide ){ ch_token = TRUE; sv_id = ch_id; }
switch( math_class_of( ch, cr_fnt ) ){
case `<math open`>: { del_stack = push_del( (char) ch, cr_fnt);
break; }
case `<math close`>: {
del_stack = pop_del( (char) ch, id_hide, cr_fnt); break; }
default:{ ; }
} }
>>>
\<math close\><<<
5
>>>
\<math open\><<<
4
>>>
Assumption made: closing del is in the same font as the opening one.
\<header functions\><<<
static struct del_stack_entry* push_del( ARG_II(char, int) );
>>>
\<functions\><<<
`[
static struct del_stack_entry* push_del(ch, cr_fnt) U_CHAR ch`;
int cr_fnt
;{ struct del_stack_entry *p;
p = m_alloc(struct del_stack_entry,1);
p->next = del_stack;
p->ch = ch;
p->fnt = cr_fnt;
p->id = ch_id;
return p;
}
>>>
\<header functions\><<<
static struct del_stack_entry* pop_del( ARG_III(char,int,int) );
>>>
\<functions\><<<
`[
static struct del_stack_entry* pop_del(ch, id_hide, cr_fnt)
U_CHAR ch`;
int id_hide`;
int cr_fnt
;{
if( del_stack != (struct del_stack_entry*) 0 ){
if( (cr_fnt == del_stack->fnt) &&
( *(font_tbl[cr_fnt].math + (ch - font_tbl[cr_fnt].char_f))
== del_stack->ch) ){
struct del_stack_entry * p;
if( !id_hide && !id_latex ){ sv_id = del_stack->id; }
del_stack = (p = del_stack)->next; free((void *) p );
} }
return del_stack;
}
>>>
What the \''id_latex' does here?
\<vars\><<<
static struct del_stack_entry *del_stack;
>>>
\<main's init\><<<
del_stack = (struct del_stack_entry *) 0;
>>>
\<types\><<<
struct del_stack_entry{
struct del_stack_entry *next;
U_CHAR ch;
int fnt, id;
};
>>>
\<vars\><<<
static int ch_id, sv_id, id_latex, back_id_off;
>>>
\<main's init\><<<
back_id_off = 1; id_latex = 0;
>>>
\<init ch-id\><<<
ch_id = 0;
>>>
\<subp vars\><<<
BOOL ch_token;
int id_hide;
>>>
\<init subp vars\><<<
sv_id = 0; `<init ch-id`>
id_hide = 0; ch_token = TRUE;
while( del_stack != (struct del_stack_entry*) 0 ){
struct del_stack_entry* p;
del_stack = (p = del_stack)->next;
free((void *) p );
}
>>>
\<hide back token / group\><<<
id_hide++;
>>>
\<end hide back token / group\><<<
id_hide--;
>>>
\<latex back token / group\><<<
id_latex++;
>>>
\<end latex back token / group\><<<
id_latex--;
>>>
\<back token / group\><<<
back_id_off++;
>>>
\<end back token / group\><<<
back_id_off--;
>>>
\SubSection{Memory for Linked Lists}
\<types\><<<
struct send_back_entry{
struct send_back_entry *next;
U_CHAR *send;
int id;
};
>>>
\<vars\><<<
static struct send_back_entry *back_token, *back_group;
>>>
\<main's init\><<<
back_token = back_group = m_alloc(struct send_back_entry,1);
back_token->id = -1;
>>>
\SubSection{Scan Backward Submissions}
\<send back over token / group\><<<
struct send_back_entry *p, *q, *t=0;
if( back_id_off ){
while( i-- ){ (IGNORED) get_char(); }
} else {
p = m_alloc(struct send_back_entry,1);
p->send = get_str( (int)( i - 1 ));
if( ch_token ){
`<send back to char`>
} else {
p->id = (sv_id<0? 0 : sv_id) + push_id;
if( back_group->id < p->id )
{ p->next = back_group; back_group = p; }
else
{ q = back_group;
while( q->id >= p->id ) { t = q; q = q->next; }
p->next = t->next; t->next = p;
}
}
}
>>>
Keep \ifHtml[\HPage{consistency}\Verbatim
$
a
\Send{BACK}{1}
\Send{BACK}{2}
\Send{BACK}{3}
\Send{BACK}{4}
\hbox{ab}
\Send{BACK}{1}
\Send{BACK}{2}
\Send{BACK}{3}
\Send{BACK}{4}
$
produce 4321a4321ab
\EndVerbatim\EndHPage{}]\fi{} between back on group and back on token
\<send back to char\><<<
p->id = sv_id;
if( sv_id > back_token->id ){
p->next = back_token; back_token = p;
} else {
q = back_token;
while( sv_id <= q->id ){ t = q; q = q->next; }
p->next = t->next; t->next = p;
}
>>>
The following reverses the linked lists for ids and groups, so that
the early entries will get at the head instead of the tail.
\<exit early pass\><<<
back_group = rev_list( back_group );
back_token = rev_list( back_token );
back_token = back_insert ( back_token, 0);
`<init ch-id`>
>>>
\<header functions\><<<
static
struct send_back_entry * rev_list( ARG_I(struct send_back_entry *) );
>>>
\<functions\><<<
`[
static struct send_back_entry * rev_list(back_group)
struct send_back_entry *back_group
;{ struct send_back_entry *p, *q, *t;
if( back_group->id == -1 ){ return back_group; }
p = back_group; q = p->next;
while( p->id != -1 ){
t = q->next; q->next = p; p = q; q = t;
}
back_group->next = p;
return p->next;
}
>>>
\SubSection{Use Backward Submissions}
\<get back sub/sup before group\><<<
if( group_dvi ) {
back_group = back_insert ( back_group, push_id);
}
>>>
\<get back sub/sup before ch\><<<
if( group_dvi ){
if( ( ch < 132 ) ||
( (ch > 127) && (ch < 137) && (ch != `<insert rule + move op`> ) )
){
ch_id++;
back_token = back_insert ( back_token, ch_id);
} }
>>>
\<header functions\><<<
static struct send_back_entry *
back_insert( ARG_II(struct send_back_entry *, int) );
>>>
\<functions\><<<
`[
static struct send_back_entry * back_insert(back, id)
struct send_back_entry *back`;
int id
;{
while( back->id == id ){
struct send_back_entry *p;
print_f( back->send );
back = (p = back)->next;
free((void *) p->send );
free((void *) p );
}
return back;
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Actions on Forward Groups Accessed Through Pathes}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Motivation}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
The current instructions are motivated by the \`'\sqrt' structure
\Verbatim
PUSH
PUSH
......sqrt et al symbols, possibly with PUSH-POP......
POP
PUSH
W3: 655361
DOWN3: -1020474
PUT_RULE: height: 26213
length: 1091179
DOWN3: 1020474
PUSH
.....body.........
POP
POP
POP
\EndVerbatim
and the \`'\root ...\of {...}' construct
\Verbatim
PUSH
...root value...
POP
PUSH
PUSH
PUSH
...root sign characters with PUSH-POP...
POP
PUSH
RIGHT4: 15213546
DOWN3: -1553322
PUT_RULE: height: 26213
length: 1197667
DOWN3: 1553322
PUSH
...root content...
POP
POP
POP
POP
\EndVerbatim
For instance,
\Verbatim
$
\special{t4ht\string~}
\special{t4ht=<msqrt>}
%%%%%%%%
\special{t4ht\string~!e<[BEFORE]} % insert at start of path
\special{t4ht\string~!e>[AFTER]} % insert at end of path
\special{t4ht\string~!ee/} % ignore content within group
\special{t4ht\string~!ese-} % ignore until rulers until next group
%%%%%%
\sqrt 1
\special{t4ht\string~}
$
$
\special{t4ht\string~}%
\special{t4ht=<mroot>}%
%%%%%%%%
\special{t4ht\string~!see<[BEFORE]}
\special{t4ht\string~!see>[AFTER]}
\special{t4ht\string~!seee/}
\special{t4ht\string~!seese-}
%%%%%%
\root 2\of 3%
\special{t4ht\string~}
$
\EndVerbatim
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Record Request}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<struct stack_entry\><<<
struct group_path * path_start, * path_end;
>>>
\<types\><<<
struct group_path{
U_CHAR action;
U_CHAR *path;
U_CHAR *info;
struct group_path * next;
};
>>>
\<initialized entries for grouping stack\><<<
stack[i].path_start = (struct group_path *) 0;
stack[i].path_end = (struct group_path *) 0;
>>>
\<store path-based forward submissions\><<<
struct group_path *p, *t;
U_CHAR *q, str[256];
int n;
p = m_alloc(struct group_path,1);
`<record action and path`>
`<record info for path`>
n = stack_n - 1;
if( p->action == '>' ){
p->next = stack[ n ].path_end;
stack[ n ].path_end = p;
} else {
p->next = (struct group_path *) 0;
if( stack[n].path_start == (struct group_path *) 0 ) {
stack[n].path_start = p;
} else {
t = stack[n].path_start;
while( t->next != (struct group_path *) 0 ) { t = t->next; }
t->next = p;
} }
>>>
\<record action and path\><<<
n = 0;
while( --special_n ) {
str[n] = get_char();
if( ( str[n] != 'e') && (str[n] != 's') ){ break; }
n++;
}
if((
( str[n] != '<') && (str[n] != '>') &&
( str[n] != '/') && (str[n] != '-')
) || (n==0) ){
str[n+1] = '\0';
err_i_str(38,str);
}
p->action = str[n]; str[n] = '\0';
p->path = m_alloc(char,n+1);
(IGNORED) strcpy((char *) p->path, (char *) str);
>>>
\<record info for path\><<<
q = p->info = m_alloc(char,special_n+1);
while( --special_n ) *q++ = get_char();
*q = '\0';
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Process Path Lists At Entry To Group}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<path group at entry to group\><<<
{
`<vars for path processing`>
if( `<radical-line-off`> ){
`<end ignore spaces`>
`<radical-line-off`> = FALSE;
}
if( stack_n > 1 ){
p = stack[stack_n - 2].path_start;
if( p != (struct group_path *) 0 ){
`<traverse path starts on entry`>
}
p = stack[stack_n - 2].path_end;
if( p != (struct group_path *) 0 ){
`<traverse path ends on entry`>
}
`<connect the revised path lists`>
} }
>>>
\<vars for path processing\><<<
struct group_path *start_head, *start_tail,
*parent_start_head, *parent_start_tail,
*end_head, *end_tail,
*parent_end_head, *parent_end_tail,
*p, *q;
int place=0;
start_head = start_tail = parent_start_head = parent_start_tail
= end_head = end_tail = parent_end_head
= parent_end_tail = (struct group_path *) 0;
>>>
\<connect the revised path lists\><<<
stack[stack_n - 1].path_start = start_head;
stack[stack_n - 1].path_end = end_head;
stack[stack_n - 2].path_start = parent_start_head;
stack[stack_n - 2].path_end = parent_end_head;
>>>
\<traverse path starts on entry\><<<
while( p != (struct group_path *) 0 ){
`<process a path-start at entry`>
q = p;
p = p->next;
q->next = (struct group_path *) 0;
`<update temporary start paths`>
}
>>>
\<traverse path ends on entry\><<<
while( p != (struct group_path *) 0 ){
`<process a path-end at entry`>
q = p;
p = p->next;
q->next = (struct group_path *) 0;
`<update temporary end paths`>
}
>>>
\<update temporary start paths\><<<
switch( place ){
case `<store at parent path-start`>:
if( parent_start_head == (struct group_path *) 0 ){
parent_start_head = parent_start_tail = q;
} else {
parent_start_tail = parent_start_tail->next = q;
}
break;
case `<store at path-start`>:
if( start_head == (struct group_path *) 0 ){
start_head = start_tail = q;
} else {
start_tail = start_tail->next = q;
}
break;
case `<delete path entry`>:
`<delete path entry q`>
break;
}
>>>
\<update temporary end paths\><<<
switch( place ){
case `<store at parent path-end`>:
if( parent_end_head == (struct group_path *) 0 ){
parent_end_head = parent_end_tail = q;
} else {
parent_end_tail = parent_end_tail->next = q;
}
break;
case `<store at path-end`>:
if( end_head == (struct group_path *) 0 ){
end_head = end_tail = q;
} else {
end_tail = end_tail->next = q;
}
break;
case `<delete path entry`>:
`<delete path entry q`>
break;
}
>>>
\<store at parent path-start\><<<
0 >>>
\<store at parent path-end\><<<
1 >>>
\<store at path-start\><<<
2 >>>
\<store at path-end\><<<
3 >>>
\<delete path entry\><<<
4 >>>
\<delete path entry q\><<<
free((void *) q->path );
free((void *) q->info );
free((void *) q );
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Process Individual Records at Entry To Group}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
The `e' records at the parent's path-start are moved to the path-start
list of the current group, with the leading `e' removed from the path.
The only exception is for paths of length 1 with actions \`'<' and
\`'-'. They contribute their content to the output, and then
discarded.
The `s' records at the parent's path-start list stay at that list,
with the `s' character removed from the path.
The other records remain at the parent path-start list, to be removed
at the end of that group.
\<process a path-start at entry\><<<
if( *(p->path ) == 'e' ) {
(IGNORED) strcpy((char *) p->path, (char *) p->path+1);
if( *(p->path) == '\0' ) {
switch( p->action ){
case '<': print_f( p->info );
place = `<delete path entry`>;
break;
case '/': ignore_chs++;
place = `<store at path-start`>; break;
case '-': `<radical-line-off`> = TRUE;
`<ignore spaces`>
place = `<delete path entry`>; break;
}
} else {
place = `<store at path-start`>;
}
} else {
if( *(p->path ) == 's' ) {
(IGNORED) strcpy((char *) p->path, (char *) p->path+1);
}
place = `<store at parent path-start`>;
}
>>>
The `s' records at the parent's path-end list stay at that list,
with the `s' character removed from the path.
The `e' records are moved to the path-end list of the current group,
with the leading `e' removed from the path.
The other records are left intact at the parent's path-end list.
\<process a path-end at entry\><<<
if( *(p->path ) == 'e' ) {
(IGNORED) strcpy((char *) p->path, (char *) p->path+1);
place = `<store at path-end`>;
} else {
if( *(p->path ) == 's' ) {
(IGNORED) strcpy((char *) p->path, (char *) p->path+1);
}
place = `<store at parent path-end`>;
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Path Group At Exit From Group}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<path group at exit from group\><<<
{
struct group_path *p, *q;
if( stack_n > 1 ){
p = stack[stack_n - 1].path_start;
if( p != (struct group_path *) 0 ){
`<traverse path starts on exit`>
}
p = stack[stack_n - 1].path_end;
if( p != (struct group_path *) 0 ){
`<traverse path ends on exit`>
} } }
>>>
\<traverse path starts on exit\><<<
while( p != (struct group_path *) 0 ){
`<process a path-start at exit`>
q = p;
p = p->next;
`<delete path entry q`>
}
>>>
\<traverse path ends on exit\><<<
while( p != (struct group_path *) 0 ){
`<process a path-end at exit`>
q = p;
p = p->next;
`<delete path entry q`>
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Process Individual Records at Exit from Group}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<process a path-start at exit\><<<
if( *(p->path) != '\0' ) {
`<warn 38 for path`>
} else {
switch( p->action ){
case '/': ignore_chs--; break;
default: {
`<warn 38 for path`>
break;
} } }
>>>
\<process a path-end at exit\><<<
if( *(p->path) != '\0' ) {
`<warn 38 for path`>
} else {
switch( p->action ){
case '>': print_f( p->info ); break;
default: {
`<warn 38 for path`>
break;
} } }
>>>
\<warn 38 for path\><<<
char str[256];
(IGNORED) strcpy(str, "...."); *(str+3) = p->action;
(IGNORED) strct(str,p->info); warn_i_str(38,str);
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Chapter{Position-Based Decoration}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Background}
We assume a rectangualar canvase for which we can provide the
measurements, as well as the location of each character and ruler in it.
A region starts and ends with a \`'\special{t4ht"}'. The entry point
gets coordinates (0,0). It is configured
wuth
\Verbatim
\special{t4ht"%
* before-all
* after-all %right %left %height %depth
.............
** before-char %x %y
* after-char
* line %x %y %thickness %length
* x-coefficients: A-magnification, B-displacement %A(x) + %B
* y-coefficients: C-magnification, D-displacement, E/F-origin
%C(y + (%E(up) | %F(height+depth)) ) + %D
}
\EndVerbatim
The \`'*' is a seperator character, and it can be replaced by other
characters.
We assume C-type templates, where values are assumed to be in float
decimals (e.g., \`'%.2f'; replacing 2 by 0, implies an integer).
The \`'* after-all' can be repeated arbitrary many times (must appear
at least once). The last one is found when \`'**' is encountered.
The first character in each \`'after-all' is treated as a code: x-min
x, X-max x, y-min , Y-max y, w-dx, h-dy, otherwise-string with no
value. We need the codes to enable arbitrary combinations of values.
The \`'before-char' must be non-empty for \`'%x %y' to be printed.
The case is similar for the ruler. If both are empty, we just
measure the dvi dimensions--can be used for applets.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Compute Character Position}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<pos dvi ch\><<<
if( pos_dvi ){ long int d;
if( *pos_text ){
`<open output file`>
(IGNORED) fprintf(cur_o_file, pos_text,
pos_x_A * (x_val - base_pos_x) + pos_x_B,
pos_y_C * (y_val - base_pos_y) + pos_y_D);
}
if( x_val < min_pos_x ) min_pos_x = x_val;
if( (d = x_val + `<ch width`>) > max_pos_x ) max_pos_x = d;
if( (d = y_val - `<ch height`>) < min_pos_y ) min_pos_y = d;
if( (d = y_val + `<ch depth`>) > max_pos_y ) max_pos_y = d;
}
>>>
% pos_y_D * (`<(double) char_height( ch )`> - base_pos_h) +
\<end pos dvi ch\><<<
if( pos_dvi ){
print_f(end_pos_text);
}
>>>
\ifHtml[\HPage{more}\Verbatim
\ifx \documentclass\undef \else
\documentclass{book}
\fi
\input DraTex.sty \input AlDraTex.sty
\input tex4ht.sty \Preamble{html,sty,4.0s}
\ifx \documentclass\undef \else
\begin{document}
\fi
\ScriptCommand{\CssFile}{\HCode{<STYLE>\EolnCh}}{\HCode{</STYLE>}}
\CssFile
BODY{ font-size : 200% }
.canvas { position: relative; }
SPAN.cmr-7 .char ,
SPAN.cmmi-7 .char ,
SPAN.cmsy-7 .char { font-size: 70% ; }
SPAN.cmr-5 .char ,
SPAN.cmmi-5 .char { font-size: 50% ; }
\EndCssFile
\EndPreamble % \Validate{temp/\jobname.html}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\special{t4ht"*<SPAN\EolnCh
CLASS="canvas">*<SPAN STYLE="position:relative;\EolnCh
width:\%.2fex"></SPAN></SPAN>*<SPAN\EolnCh
STYLE="position:absolute; left:\%.2fex;
top:\%.2fex"\EolnCh
CLASS="xy"><SPAN\EolnCh CLASS="char">*</SPAN></SPAN>*<SPAN top="\%.2f"
left="\%.2f" CLASS="xy">*</SPAN>*0.00000290646*0.0*0.00000290646*0.0*0.5}
aa
\special{t4ht"}$
\SUBSUPOff
\let\Picture=\empty
.\qquad...............a^{c^1}_{b_2}a^c_bccccc...c
$\special{t4ht"}XX
aa
\special{t4ht"}$
\SUBSUPOff
\let\Picture=\empty
{A+C \over B+D}+F \over X*Y
$\special{t4ht"}...........
x
\end{document}
1ex = 344061
Addresses in dvi are on baseline. The same is true for
positioned text in html. That is,
the point of reference of the printed character is placed at
the specified position (\ifHtml[\HPage{margin}\Verbatim
<DIV STYLE="position:absolute;
left:100; top:50;">
(/\/\/\/\/\)
</DIV>
<DIV STYLE="position:absolute;
left:100; top:50;">
........................
</DIV>
\EndVerbatim\EndHPage{}]\fi).
\ifHtml[\HPage{?}\Verbatim
Positioning of characters?
The attached source tries three renderings for `A<sub>x</sub>'. My
understanding of the positioning mechanism is that, in all three cases,
the reference point for `x' should be 0.7ex under the right leg of A.
Moreover, the measurement 0.7ex should be with respect to the
dimension of the font of A.
/\
/--\
/ \...........
\/ } 0.7ex within font of A
./\.......}
Is that understanding correct?
NN 4.04 provides the desired outcome in the first case, but not in the
second and third cases. IE 4.0, on the other hand, produces the
desired outcome for the second and the third cases, but not the first.
-eitan
<HTML><HEAD> <TITLE>aa.html</TITLE> </HEAD><BODY>
<P> A<SPAN
STYLE="position:relative"><SPAN
STYLE="position:absolute; top:0.7ex"><SPAN
STYLE="font-size: 70% ;">x</SPAN></SPAN></SPAN>
<P> A<SPAN
STYLE="position:relative"><SPAN
STYLE="position:relative; top:0.7ex"><SPAN
STYLE="font-size: 70% ;">x</SPAN></SPAN></SPAN>
<P> A<SPAN
STYLE="position:relative"><SPAN
STYLE="position:absolute; top:0.7ex"><SPAN
STYLE="font-size: 100% ;">x</SPAN></SPAN></SPAN>
<P> y
</BODY></HTML>
\EndVerbatim\EndHPage{}]\fi
\Section{Compute Ruler Position}
\<pos dvi x rule\><<<
long int d;
if( (up > 0) && (right > 0) ){
if( *pos_line ){
double from_x, from_y;
`<open output file`>
from_x = pos_x_A * (x_val - base_pos_x) + pos_x_B;
from_y = pos_y_C * (y_val - pos_y_E * up - base_pos_y) + pos_y_D;
switch (rect_pos){
case 1: {
(IGNORED) fprintf(cur_o_file, pos_line,
from_x, from_y,
pos_x_A * right + pos_x_B + from_x,
pos_y_C * up + pos_y_D + from_y );
break; }
case 2: {
(IGNORED) fprintf(cur_o_file, pos_line,
from_x, from_y,
pos_x_A * right + pos_x_B + from_x,
from_y,
pos_y_C * up + pos_y_D );
break; }
default: {
(IGNORED) fprintf(cur_o_file, pos_line,
from_x, from_y,
pos_x_A * right,
pos_y_C * up);
}
}
}
if( x_val < min_pos_x ) min_pos_x = x_val;
if( (d = x_val + right) > max_pos_x ) max_pos_x = d;
if( (d = y_val - up) < min_pos_y ) min_pos_y = d;
if( y_val > max_pos_y ) max_pos_y = y_val;
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Boundary Conditions}
%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<positioned content\><<<
if( special_n ){
`<read pos instructions`>
} else if( (pos_dvi = !pos_dvi) == TRUE ){
print_f(pos_body);
min_pos_x = max_pos_x = base_pos_x = x_val;
min_pos_y = max_pos_y = base_pos_y = y_val ;
} else { U_CHAR *p;
double dim=0.0;
BOOL dim_on;
`<open output file`>
p = end_pos_body;
while( *p ){
dim_on = TRUE;
switch( *p ){
`<dim = pos value`>
default: { dim_on = FALSE; }
}
p++;
if( dim_on ){ (IGNORED) fprintf(cur_o_file, p, dim); }
else { (IGNORED) fprintf(cur_o_file, "%s", p); }
while( * (p++) );
}
}
>>>
The pattern of of \`'end_pos_body' has a \`'\0' at the end
of each segment, and the last segment is empty. Hence, it
ends with a \`'\0\0'.
\<dim = pos value\><<<
case 'X': { dim = pos_x_A * (max_pos_x - base_pos_x) + pos_x_B;
break; }
case 'x': { dim = pos_x_A * (base_pos_x - min_pos_x) + pos_x_B;
break; }
case 'd': { dim = pos_x_A * (max_pos_x - min_pos_x) + pos_x_B;
break; }
case 'y': { dim = pos_y_C * (base_pos_y - min_pos_y - 1) + pos_y_D;
break; }
case 'Y': { dim = pos_y_C * (max_pos_y - base_pos_y) + pos_y_D;
break; }
case 'D': { dim = pos_y_C * (max_pos_y - min_pos_y) + pos_y_D;
break; }
>>>
\<vars\><<<
static BOOL pos_dvi = FALSE;
static U_CHAR *pos_body, * pos_text, * pos_line,
*end_pos_body, * end_pos_text;
static double pos_x_A, pos_x_B, pos_y_C, pos_y_D, pos_y_E;
static long int base_pos_x, base_pos_y, min_pos_x,
max_pos_x, min_pos_y, max_pos_y;
static short rect_pos;
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Scan Coefficients}
%%%%%%%%%%%%%%%%%%%%%%%%%%%
Note that \`'pos_body' points to the left-most byte in the template.
\<main's init\><<<
pos_text = pos_line = end_pos_body = end_pos_text = pos_body =
m_alloc(char, (int) 1);
(IGNORED) strcpy((char *) pos_text, "" );
>>>
\<read pos instructions\><<<
{ U_CHAR * p, ch, i;
ch = get_char();
p = pos_text = pos_line = end_pos_text
= end_pos_body = pos_body
= (char *) r_alloc((void *) pos_body,(size_t) special_n + 1);
i = 0; `<scan templates for pos`>
`<scan constants for pos`>
if( (i != 10) || special_n ){
warn_i_str(39,pos_text);
*(pos_text = end_pos_body = pos_line =
end_pos_text = pos_body) = '\0';
}
}
>>>
\<scan templates for pos\><<<
{ BOOL after_star=0;
while( special_n-- > 0 ){
if( (*p = get_char()) == ch ){
*p = '\0'; i++;
if( i==1 ){ end_pos_body = p + 1; after_star = FALSE; }
else if( i==2 ){ pos_text = p + 1;
if( !after_star ){ i--; after_star = TRUE; }
}
else if( i==3 ){ end_pos_text = p + 1; }
else if( i==4 ){ pos_line = p + 1; }
else { p++; break; }
} else { after_star = FALSE; }
p++;
}
}
>>>
\<scan constants for pos\><<<
{ long int v=0;
double w[5];
int j;
U_CHAR ch, sign;
BOOL done;
for(j=0;j<5;j++){
`<v = int part; sign = ...`>
if( done ){
i++;
w[j] = sign * ((double) v + pos_dbl( &special_n ));
}
}
pos_x_A = w[0]; pos_x_B = w[1];
pos_y_C = w[2]; pos_y_D = w[3]; pos_y_E = w[4];
}
rect_pos = (special_n == 2);
if( rect_pos ){ special_n -= 2; rect_pos = get_char() - '0';}
>>>
\<v = int part; sign = ...\><<<
done = FALSE; sign = 1;
if( --special_n > 0 ){
if( (ch = get_char()) == '-' ){ sign = -1; v=0; }
else v = ch - '0';
if( (v<0) || (v>9) ) done = TRUE;
}
if( !done )
while( --special_n > 0 ){
ch = get_char();
if( ('0' <= ch ) && (ch <= '9' ) ){
v = v * 10 + ch - '0'; }
else{ done = TRUE; break; }
}
>>>
\<header functions\><<<
static double pos_dbl( ARG_I(long int *) );
>>>
\<functions\><<<
`[
static double pos_dbl( special_n ) long int * special_n
;{
U_CHAR ch;
double v;
int d;
v = 0.0; d = 10;
while( --(*special_n) > 0 ){
ch = get_char();
if( ('0' <= ch ) && (ch <= '9' ) ){
v += (double) (ch -'0') / d; d *= 10; }
else break;
}
return v;
}
>>>
\<err int pos\><<<
123454321
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Chapter{Font Information from TeX Font Metric (tfm) Files}
%%%%%%%%%%%%%%%%%%%%%%%%%%%
Managed by load-font (\LinkPort\<font 0\>font 0\EndLink
--\LinkPort\<font 63\>font 63\EndLink,
\LinkPort\<font 1-byte\>font 1-byte\EndLink
--\LinkPort\<font int\>font int\EndLink) and activate-font
(\LinkPort\<def 1 byte font\>def 1-byte font\EndLink
--\LinkPort\<def 4 byte font\>def 4 byte font\EndLink) commands.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Memory and Initialization}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
A font def looks like: \Link{}{fntdef} \EndLink{}
\Verbatim
FNT_DEF1...FNT_DEF4
1,2,3 ubytes or 4 bytes font number
4 ubytes checksum
4 ubytes scale
4 ubytes design size
1 byte length of directory name
1 byte length of file name
dir-ln + file-ln bytes fontname
\EndVerbatim
Checksums are used to verify consistency between tfm and gf font files.
\Verbatim
000284: FNT_DEF1: 29
000286: checksum : -538297224
000290: scale : 655360
000294: design : 655360
000298: name : cmtt10
000306: FNT_DEF1: ....
\EndVerbatim
\<types\><<<
struct font_entry {
INTEGER num;
INTEGER scale;
INTEGER design_sz;
`<mag of font`>
`<family+size of font`>
`<entries from font file into font-tbl`>
`<entries for html chars in font-tbl`>
`<entries for math classes`>
`<entries for xetex`>
};
>>>
\<defines\><<<
#define design_size_to_pt(n) ((double)n / (double)(1L<<20))
>>>
\<vars\><<<
static struct font_entry* font_tbl;
static int font_tbl_size = 0;
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Storage for New Font}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
The standard fonts of Knuth have design size of 0x90000 for fonts of
size 9, 0xa0000 for size 10, etc.
% INTEGER `%, new_font_dir`%
\<scan font entry\><<<
#ifdef MAXFONTS
if( (font_tbl_size + 1) < MAXFONTS )
#endif
{
INTEGER new_font_checksum;
int font_name_n;
font_tbl = font_tbl_size?
(struct font_entry *) r_alloc((void *) font_tbl,
(size_t) ((font_tbl_size+1)
* sizeof(struct font_entry)))
: m_alloc(struct font_entry, 1);
if( (version_id == `<version xdv`>)
&& (ch == `<xdv-native-font-def op`>)
){
`<scan xdv font entry`>
} else {
`<scan dvi font entry`>
}
`<get html characters`>
`<get css entries`>
free((void *) new_font_name); font_tbl_size++;
}
#ifdef MAXFONTS
else err_i_int(17, MAXFONTS);
#endif
>>>
\<scan dvi font entry\><<<
`<scan font id`>
new_font_checksum = (INTEGER) get_int(4);
new_font.scale = (INTEGER) get_unt(4);
new_font.design_sz = (INTEGER) get_unt(4);
`<get font name`>
`<get font info`>
>>>
\<vars\><<<
static char* new_font_name;
>>>
\<defines\><<<
#define new_font font_tbl[font_tbl_size]
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Get Data}
%%%%%%%%%%%%%%%%%%%%%%%%%%
\<scan font id\><<<
switch( ch ){
case `<def 1 byte font`>:
case `<def 2 byte font`>:
case `<def 3 byte font`>: {
new_font.num = (INTEGER)
get_unt(ch - `<def 1 byte font`> + 1); break; }
case `<def 4 byte font`>: {
new_font.num = (INTEGER) get_int(4); break; }
default: err_i(8);
}
>>>
\<get font name\><<<
{ int n, area_ln;
U_CHAR *ch;
area_ln = (int) get_unt(1);
font_name_n = (int) get_unt(1);
n = area_ln + font_name_n + 1;
ch = new_font_name = m_alloc(char, n);
while( --n ){ *ch = (int) get_unt(1); ch++; }
*ch = '\0';
}
>>>
\<get font info\><<<
`<first definition of font?`>
`<open font file`>
new_font_name[font_name_n] = '\0';
new_font.name = m_alloc(char, font_name_n + 1);
(IGNORED) strcpy((char *) new_font.name, (char *) new_font_name );
`<family and size of font`>
`<compute mag of font`>
>>>
The following is computed from \`'scale' and \`'design_sz'.
We take it here relative to 100.
\<compute mag of font\><<<
new_font.mag = new_font.scale / (new_font.design_sz / 100);
>>>
\<mag of font\><<<
INTEGER mag;
>>>
\<family+size of font\><<<
char *family_name, *font_size;
>>>
\<family and size of font\><<<
{ int n, i;
for( n=0; n<font_name_n; n++ ){
if( ( '0' <= new_font_name[n] ) && ( new_font_name[n] <= '9' )){
break;
}
}
`<real size?`>
new_font.family_name = m_alloc(char, n + 2);
new_font.font_size = m_alloc(char, font_name_n - n + 1 );
for( i=0; i<n; i++ ){
new_font.family_name[i] = new_font_name[i];
}
new_font.family_name[i] = '\0'; i = 0;
while( n<font_name_n ){
new_font.font_size[i++] = new_font_name[n++];
}
new_font.font_size[i] = '\0';
}
>>>
\<real size?\><<<
{ int m;
for( m=n; m<font_name_n; m++ ){
if( ( new_font_name[m] < '0' ) || ( new_font_name[m] > '9' )){
n=font_name_n;
break;
} } }
>>>
When the size is not zero, n+2 can be reduced to n+1.
\<output to lg file\><<<
(IGNORED) fprintf(log_file, lg_font_fmt,
font_tbl[cur_fnt].family_name,
font_tbl[cur_fnt].font_size,
(int)( font_tbl[cur_fnt].design_sz * 100 / 655360 / 10),
font_tbl[cur_fnt].mag);
>>>
Fosome font names (e.g., Chinese: gbkhei65) we don't want the number to
be treated as font-size:
\<check name decomposition\><<<
if( (strlen((char *) new_font.family_name) +
strlen((char *) new_font.font_size) + 4) == strlen((char *) name) ){
new_font.family_name = (char *) r_alloc((void *) new_font.family_name,
(size_t) (strlen((char *) name)+1));
(IGNORED) strcat((char *) new_font.family_name, (char *) new_font.font_size);
*(new_font.font_size)='\0';
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Script lg-font-fmt}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Font information to decorate characters, e.g., for style files.
\<h-defines\><<<
#ifndef LGFNT
#define LGFNT "Font(\"%s\",\"%s\",\"%d\",\"%d\")\n"
#endif
>>>
\<vars\><<<
static U_CHAR *lg_font_fmt = NULL;
>>>
\<get from tex4ht.env file\><<<
lg_font_fmt = (char *) get_script(lg_font_fmt,LGFNT,'f');
>>>
%%%%%%%%%%%%%
\Section{XeLaTeX: Scan Xdv Font Entry (Native Font Def)}
%%%%%%%%%%%%%
`xelatex -no-pdf example'
\Link[
http://en.wikipedia.org/wiki/XeTeX]{}{}XeTeX\EndLink
The xetex code is at
\Link[
http://scripts.sil.org/svn-public/xetex/TRUNK/]{}{}
http://scripts.sil.org/svn-public/xetex/TRUNK/\EndLink{}
and xdvipdfmx is found under the texk directory.
%%%%%%%%%%%%%
\SubSection{Font Definition}
%%%%%%%%%%%%%
\<scan xdv font entry\><<<
unsigned short flags;
new_font.num = (INTEGER) get_unt(4);
new_font.scale = (INTEGER) get_unt(4);
new_font.design_sz = new_font.scale;
flags = (INTEGER) get_unt(2);
`<get xdv font name`>
new_font.layout_dir = (flags & `<xdv flag vertical`>) ? 1 : 0;
new_font.rgba_color = (flags & `<xdv flag colored`>)?
(unsigned long) get_unt(4)
:
0xffffffff;
if( flags & `<xdv flag variations`> ){
int n = (INTEGER) get_unt(2); `%number of variations`%
int i;
for (i = 0; i < n; ++i) { `%ignore axis`%
(void) get_unt(4);
}
for (i = 0; i < n; ++i) { `%ignore value of variation`%
(void) get_int(4);
} }
(IGNORED) printf("(--- xdv font = %s (not implemented) ---)\n", new_font_name);
`<get xdv font info`>
>>>
\<get xdv font name\><<<
{ int n, family_name_n, style_name_n;
U_CHAR *ch;
`% char* family_name;
char* style_name;`%
font_name_n = (INTEGER) get_unt(1);
family_name_n = (INTEGER) get_unt(1);
style_name_n = (INTEGER) get_unt(1);
n = font_name_n + 1;
ch = new_font_name = m_alloc(char, n);
while( --n ){ *ch = (int) get_unt(1); ch++; }
*ch = '\0';
n = family_name_n + 1;
`% ch = family_name = m_alloc(char, n);`%
while( --n ){ `%*ch = (int)`% (void) get_unt(1); ch++; }
*ch = '\0';
n = style_name_n + 1;
`% ch = style_name = m_alloc(char, n);`%
while( --n ){ `%*ch = (int)`% (void) get_unt(1); ch++; }
*ch = '\0';
}
>>>
REMOVE allocated mem at end of usage!!
\<entries for xetex\><<<
INTEGER layout_dir;
unsigned long rgba_color;
>>>
% char *fam_name, *sty_name;
% INTEGER used; new_font.used = 0;
\<xdv flag vertical\><<<
0x0100
>>>
\<xdv flag colored\><<<
0x0200
>>>
\<xdv flag variations\><<<
0x0800
>>>
%%%%%%%%%%%%%
\SubSection{Within IVD}
%%%%%%%%%%%%%
\<font defs for xdv's idv?\><<<
(version_id == `<version xdv`>)
&&
(
(ch == `<xdv-pic-file op`>)
||
(ch == `<xdv-native-font-def op`>)
||
(ch == `<xdv-glyph-array op`>)
||
(ch == `<xdv-glyph-string op`>)
)
>>>
\<xdv flag fonttype icu\><<<
0x0002
>>>
\<xdv flag fonttype atsui\><<<
0x0001
>>>
\<xdv font def for idv\><<<
switch( ch ){
case `<xdv-native-font-def op`>:
`<xdv/ivd native font def`>
break;
case `<xdv-glyph-string op`>:
`<xdv/idv glyph string`>
break;
default:
printf(" ===> ---------- xdv's idv ------------ <==== %d\n" , ch);
}
>>>
\<xdv/idv glyph string\><<<
{
int i, glyphCount;
(void) get_unt(4);
glyphCount = (INTEGER) get_unt(2);
for( i = 0; i < glyphCount; ++i ){
(void) get_int(4);
}
for (i = 0; i < glyphCount; ++i){
(void) get_unt(2);
}
}
>>>
\<xdv/ivd native font def\><<<
{ int i, flags;
(void) get_unt(4); `%tex_id`%
(void) get_unt(4); `%scale`%
flags = (INTEGER) get_unt(2);
if ((flags & `<xdv flag fonttype icu`>) || (flags & `<xdv flag fonttype atsui`>)) {
for (
i =
(INTEGER) get_unt(1) `% font name `%
+ (INTEGER) get_unt(1) `% family name `%
+ (INTEGER) get_unt(1) `% style name `%
;
i>0;
i--
){ (void) get_unt(1); }
if( flags & `<xdv flag colored`> ){
(void) get_unt(4);
}
if( flags & `<xdv flag variations`> ){
int n = (INTEGER) get_unt(2); `%number of variations`%
for (i = 0; i < n; ++i) { `%ignore axis`%
(void) get_unt(4);
}
for (i = 0; i < n; ++i) { `%ignore value of variation`%
(void) get_int(4);
} }
}
}
>>>
%%%%%%%%%%%%%
\SubSection{????????????????}
%%%%%%%%%%%%%
\<get xdv font info\><<<
`<first definition of font?`>
`<open xdv font file`>
new_font_name[font_name_n] = '\0';
new_font.name = m_alloc(char, font_name_n + 1);
(IGNORED) strcpy((char *) new_font.name, (char *) new_font_name );
`<family and size of font`>
`<compute mag of xdv font`>
>>>
\<compute mag of xdv font\><<<
new_font.mag = new_font.scale / (new_font.design_sz / 100);
>>>
\<open xdv font file\><<<
{ `% FILE *font_file;
U_CHAR file_name[256]; `%
`<search dirs for xdv font`>
/*
if( font_file == NULL ){
dump_env(); err_i_str(1,file_name);
missing_fonts = TRUE;
new_font.char_f = 2;
new_font.char_l = 1;
} else {
*/
`<get xdv chars dimensions`>
/*
(IGNORED) fclose(font_file);
}
*/
}
>>>
\<get xdv chars dimensions\><<<
{ `<vars for reading xdv font file`>
`<scan xdv dimension of components of font file`>
`<scan XDV header of font file`>
`<scan xdv char info words`>
`<scan xdv width table`>
`<scan xdv height table`>
`<scan XDV depth table`>
`<scan XDV italic correction table`>
`<scan XDV lig/kern table`>
`<scan XDV kern table`>
`<scan XDV extensible character table`>
`</xdv fontdimen parameters`>
}
>>>
\</xdv fontdimen parameters\><<<
`<xdv font slant`> `%all need design_size_to_pt(n)`%
`<xdv interword space`>
`<XDV interword stretch`>
`<XDV interword shrink`>
`<xdv ex height`>
`<XDV em width (=quad width)`>
`<XDV extra space`>
>>>
\<xdv font slant\><<<
new_font.it = 0;
>>>
\<xdv interword space\><<<
new_font.word_sp = 350000;
`<adjust inter-word space`>
>>>
\<ex height\><<<
new_font.ex = 450000;
>>>
\<scan xdv dimension of components of font file\><<<
new_font.char_f = 0;
new_font.char_l = 255;
new_font.wtbl_n = 0;
new_font.htbl_n = 0;
new_font.dtbl_n = 0;
>>>
it_correction_table_length = 0;
lig_kern_table_length = 0;
kern_table_length = 0;
extensible_char_table_length = 0;
num_font_parameters = 0;
\<vars for reading xdv font file \><<<
int
it_correction_table_length,
lig_kern_table_length,
kern_table_length,
extensible_char_table_length, `%<= 256`%
num_font_parameters;
>>>
\<scan xdv char info words\><<<
{ U_CHAR *ch, *hidp;
int i;
ch = new_font.char_wi = m_alloc(char, new_font.char_l
- new_font.char_f + 1);
hidp = new_font.char_hidp = m_alloc(char, new_font.char_l
- new_font.char_f + 1);
for( i = new_font.char_f; i <= new_font.char_l; i++ ){
*(ch++) = 10;
*(hidp++) = 26;
} }
>>>
\<scan xdv width table\><<<
{ INTEGER *p; `%needs design_size_to_pt(n)`%
int i;
p = new_font.wtbl = m_alloc( INTEGER, new_font.wtbl_n);
for( i = 0; i < new_font.wtbl_n; i++ ){
*(p++) = 600000;
} }
>>>
\<scan xdv height table\><<<
{ INTEGER *p; `%needs design_size_to_pt(n)`%
int i;
p = new_font.htbl = m_alloc( INTEGER, new_font.htbl_n);
for( i = 0; i < new_font.htbl_n; i++ ){
*(p++) = 600000;
} }
>>>
\<skip font xdv def\><<<
unsigned short flags;
for( i=0; i<8; i++ ){ ch = get_char(); }
flags = (INTEGER) get_unt(2);
for( i = (INTEGER) get_unt(1) `% font name `%
+ (INTEGER) get_unt(1) `% font family name`%
+ (INTEGER) get_unt(1) `% font style name `%
; i>0
; i-- ){ ch = get_char(); }
if( flags & `<xdv flag colored`> ){ (void) get_unt(4); }
if( flags & `<xdv flag variations`> ){
int n = (INTEGER) get_unt(2);
int i;
for (i = 0; i < n; ++i) {
(void) get_unt(4);
}
for (i = 0; i < n; ++i) {
(void) get_int(4);
} }
>>>
\<do glyph array\><<<
doGlyphArray(TRUE);
>>>
\<do glyph string\><<<
doGlyphArray(FALSE);
>>>
\<header functions\><<<
static void doGlyphArray( ARG_I(BOOL) );
>>>
\<functions\><<<
`[
static void doGlyphArray(yLocs)
BOOL yLocs
;{ int i, glyphCount;
(void) get_unt(4);
glyphCount = (INTEGER) get_unt(2);
for( i = 0; i < glyphCount; ++i ){
(void) get_int(4);
if( yLocs ){ (void) get_int(4); }
}
for (i = 0; i < glyphCount; ++i){
(void) get_unt(2);
}
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Connecting to the Font Table}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Verify that Font has not been Already Defined}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<first definition of font?\><<<
{ int i;
for( i=font_tbl_size-1; i>0; i-- )
if( new_font.num == font_tbl[i].num ) warn_i(10); }
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Access to Fonts}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<search font in font-tbl\><<<
cur_fnt = search_font_tbl( cur_fnt );
word_sp = `<size of space`>;
>>>
\<header functions\><<<
static int search_font_tbl( ARG_I(int) );
>>>
\<functions\><<<
`[
static int search_font_tbl( cur_fnt )
int cur_fnt
;{ int i;
for( i=0; i<font_tbl_size; i++){
if( font_tbl[i].num == cur_fnt ){ return i; }
}
err_i_int( 6,cur_fnt );
return 0;
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Opening a Font File}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<open font file\><<<
{ FILE *font_file;
U_CHAR file_name[256];
`<search dirs for font`>
if( font_file == NULL ){
dump_env(); err_i_str(1,file_name);
missing_fonts = TRUE;
new_font.char_f = 2;
new_font.char_l = 1;
} else {
`<get chars dimensions`>
(IGNORED) fclose(font_file);
} }
>>>
Some TeX systems provide virtual fonts without tfm files. Instead, they
provide file of aliases with records of the form `{\it virtual font
name}={\it actual font name}'. In such a case, the location of a file of
aliases can be provided within an a-record in \''tex4ht.env'.
The tex4ht program ignores records having form different than `{\it
virtual font name}={\it actual font name}'.
The full names should be provided, without the .tfm extensions (e.g.,
gbksong33=GBKSONG33).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Scanning a Font File}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
TFM = TeX font metric
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Layout}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Dimensions are expressed in printers' point units.
\<get chars dimensions\><<<
{ `<vars for reading font file`>
`<scan dimension of components of font file`>
`<scan header of font file`>
`<scan char info words`>
`<scan width table`>
`<scan height table`>
`<scan depth table`>
`<scan italic correction table`>
`<scan lig/kern table`>
`<scan kern table`>
`<scan extensible character table`>
`</fontdimen parameters`>
}
>>>
% (IGNORED) fseek(G_fontfp,(long)0,FROM_START);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Breakdown}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<scan dimension of components of font file\><<<
file_length = (INTEGER) fget_int(font_file,2);
header_length = (int) fget_int(font_file,2);
new_font.char_f = (int) fget_int(font_file,2);
new_font.char_l = (int) fget_int(font_file,2);
new_font.wtbl_n = (int) fget_int(font_file,2);
new_font.htbl_n = (int) fget_int(font_file,2);
new_font.dtbl_n = (int) fget_int(font_file,2);
it_correction_table_length = (int) fget_int(font_file,2);
lig_kern_table_length = (int) fget_int(font_file,2);
kern_table_length = (int) fget_int(font_file,2);
extensible_char_table_length = (int) fget_int(font_file,2);
num_font_parameters = (int) fget_int(font_file,2);
if( file_length != ( 6 + header_length
- new_font.char_f + new_font.char_l + 1
+ new_font.wtbl_n + new_font.htbl_n
+ new_font.dtbl_n + it_correction_table_length
+ lig_kern_table_length + kern_table_length
+ extensible_char_table_length + num_font_parameters )
){ err_i_str(15,file_name); }
>>>
Shouldn't the hight table be of the same dim of the width and depth table?
\<vars for reading font file\><<<
INTEGER file_length; `%all lengths are in words`%
int header_length,
it_correction_table_length,
lig_kern_table_length,
kern_table_length,
extensible_char_table_length, `%<= 256`%
num_font_parameters;
>>>
\<entries from font file into font-tbl\><<<
INTEGER design_pt; `%needs design_size_to_pt(n)`%
int char_f, char_l; `%0 --- 255`%
U_CHAR *char_wi;
U_CHAR *char_hidp;
int wtbl_n;
int htbl_n;
int dtbl_n;
INTEGER *wtbl; `%needs design_size_to_pt(n)`%
INTEGER *htbl; `%needs design_size_to_pt(n)`%
INTEGER *dtbl; `%needs design_size_to_pt(n)`%
INTEGER word_sp; `%needs design_size_to_pt(n)`%
INTEGER it; `%needs design_size_to_pt(n)`%
INTEGER ex; `%needs design_size_to_pt(n)`%
>>>
Character codes beyond 255 assume the same width as their
counterpart modulo 256 characters.
\Verbatim
header : array [0 -- lh - 1] of stuff
char_info: array [bc -- ec] of char_info_word
width : array [0 -- nw - 1] of fix_word
height: array [0 -- nh - 1] of fix_word
depth : array [0 -- nd - 1] of fix_word
italic: array [0 -- ni - 1] of fix_word
lig_kern: array [0 -- nl - 1] of lig_kern_command
kern: array [0 -- nk - 1] of fix_word
exten: array [0 -- ne - 1] of extensible_recipe
param : array [1 -- np] of fix_word
\EndVerbatim
\SubSection{Header Information}
\<scan header of font file\><<<
`<scan checksum in font file`>
`<scan design size in font file`>
(IGNORED) fseek(font_file, (long) ((header_length - 2) * 4), `<relative file addr`>);
>>>
Incompatible fonts shouldn't agree in their check sum. A zero check
sum should be disregarded.
\<scan checksum in font file\><<<
{ INTEGER checksum;
checksum = ( INTEGER) fget_int(font_file,4);
if( checksum && new_font_checksum
&& (checksum != new_font_checksum) )
{ warn_i(16);
(IGNORED) fprintf(stderr,"%s: %d\ndvi file: %d\n",file_name,
checksum, new_font_checksum);
} }
>>>
% if( ((checksum = ( INTEGER) fget_int(font_file,4)) != NULL )
The design size of a font is represented in
point units of \TeX, and it must be greater than 1.0.
The representation uses 32-bit words, with the leading 12 bits
providing the integer part and the two-complement of a word providing
negative values. The allowed values are in the range of
$2048-2^{-20}$ to $-2048$.
\<scan design size in font file\><<<
new_font.design_pt = ( INTEGER) fget_int(font_file,4); `%needs design_size_to_pt(n)`%
>>>
Skipped optional identification of coding scheme (40 bytes),
identification of font family (20 bytes), etc.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Character Information}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<scan char info words\><<<
{ U_CHAR *ch, *hidp;
int i;
ch = new_font.char_wi = m_alloc(char, new_font.char_l
- new_font.char_f + 1);
hidp = new_font.char_hidp = m_alloc(char, new_font.char_l
- new_font.char_f + 1);
for( i = new_font.char_f; i <= new_font.char_l; i++ ){
*(ch++) = (int) fget_unt(font_file,1);
*(hidp++) = (int) fget_unt(font_file,1);
(IGNORED) fseek(font_file, 2L, `<relative file addr`>);
} }
>>>
Next comes the \`'char_info' array, in which
each word contains the following information for one character.
\List{$\circ$}
\item Width index: 8 bits
\item Height index: 4 bits
\item Depth index: 4 bits
\item Italic index: 6 bits
\item Tag: 2 bits
\item Other: 8 bits whose interpretation depends on the value of the tag.
\List{}
\item {0.} Ignore.
\item {1.} An index into the \`'lig_kern' table.
\item {2.} The character code of the next character in
a list of size-inreasing characters.
\item {3.} An index, called extensible index, into the table \`'exten'
where the components of the current composite character are specified.
\EndList
\EndList
The width of a character is \`'width[width index] * design_pt', etc.
This compression scheme allows characters of a font to choose from 256
width-values, 16 height-values, etc. Since
\`'width[0]=height[0]=depth[0]=italic[0]=0', a character in the font
should never have a zero index.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Width Table}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<scan width table\><<<
{ INTEGER *p; `%needs design_size_to_pt(n)`%
int i;
p = new_font.wtbl = m_alloc( INTEGER, new_font.wtbl_n);
for( i = 0; i < new_font.wtbl_n; i++ ){
*(p++) = ( INTEGER) fget_int(font_file,4);
} }
>>>
The above fits with measurements made for the fixed-sized font cmtt10, but
not with the following measurements made for cmsy10.
\Verbatim
182045, 327681, 509726, 509726, 436908, 436908, 436908, 509726,
509726, 655361, 327681, 327681, 509726, 509726, 655361, 400498,
509726, 509726, 436908, 436908, 364090, 364090, 509726, 655361,
655361, 655361, 473316, 327681, 509726, 509726, 509726, 436908,
344061, 655361, 327681, 327681, 655361, 655361, 655361, 212991,
655361, 655361, 327681, 400498, 327681, 509726, 436908, 473316,
180223, 655361, 436908, 436908, 582543, 582543, 344061, 344061,
364090, 364090, 436908, 327681, 473316, 473316, 509726, 509726,
400498, 523286, 450377, 383297, 523745, 404502, 536119, 428716,
559789, 405233, 565248, 508817, 452018, 787021, 634289, 539946,
509726, 535213, 555420, 446006, 523509, 475226, 455478, 701238,
563429, 491886, 527019, 436908, 327681, 436908, 436908, 247723,
400498, 400498, 291271, 291271, 291271, 291271, 327681, 327681,
254863, 254863, 182045, 327681, 327681, 400498, 671742, 182045,
546135, 491521, 546135, 345886, 436908, 436908, 509726, 509726,
291273, 291271, 291271, 327681, 182045, 327681, 509726, 345886
\EndVerbatim
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Height Table}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<scan height table\><<<
{ INTEGER *p; `%needs design_size_to_pt(n)`%
int i;
p = new_font.htbl = m_alloc( INTEGER, new_font.htbl_n);
for( i = 0; i < new_font.htbl_n; i++ ){
*(p++) = ( INTEGER) fget_int(font_file,4);
} }
>>>
For instance, the following table for cmr10
\Verbatim
0, 110683, 384696, 451470, 524288, 553416, 595357, 611670,
644958, 659002, 675749, 700301, 716526, 728178, 767499, 786432
\EndVerbatim
\SubSection{Depth Table}
\<scan height table\><<<
{ INTEGER *p; `%needs design_size_to_pt(n)`%
int i;
p = new_font.dtbl = m_alloc( INTEGER, new_font.dtbl_n);
for( i = 0; i < new_font.dtbl_n; i++ ){
*(p++) = ( INTEGER) fget_int(font_file,4);
} }
>>>
\<scan depth tableNO\><<<
`%needs design_size_to_pt(n)`%
(IGNORED) fseek(font_file, (long) (depth_table_length * 4),
`<relative file addr`>);
>>>
\SubSection{Italic Correction Table}
Used for italic correction explicitly requested with \`'\/', and for
isolated character in math mode (MetaFont 315--316).
\<scan italic correction table\><<<
`%needs design_size_to_pt(n)`%
(IGNORED) fseek(font_file, (long) (it_correction_table_length * 4),
`<relative file addr`>);
>>>
\SubSection{Ligature Table}
Substitutions for specified runs of chrarcters
\<scan lig/kern table\><<<
(IGNORED) fseek(font_file, (long) (lig_kern_table_length * 4),
`<relative file addr`>);
>>>
\SubSection{kern table}
Space to add between specified pairs of characters.
\<scan kern table\><<<
`%needs design_size_to_pt(n)`%
(IGNORED) fseek(font_file, (long) (kern_table_length * 4),
`<relative file addr`>);
>>>
\SubSection{Extensible Table}
\<scan extensible character table\><<<
(IGNORED) fseek(font_file, (long) (extensible_char_table_length * 4),
`<relative file addr`>);
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Font Dimension Parameters}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
At least seven parameters used in \''\fontdimen', each occupying four
bytes (= one word). With the exception of the first parameter, all
the other parameters should be magnified for a font that is magnified
with \`'at' or \`'scaled'.
\</fontdimen parameters\><<<
`<font slant`> `%all need design_size_to_pt(n)`%
`<interword space`>
`<interword stretch`>
`<interword shrink`>
`<ex height`>
`<em width (=quad width)`>
`<extra space`>
>>>
\List{$\circ$}
\item The slant per point of a font, used for determining the height
of accent characters.
\<font slant\><<<
new_font.it = ( INTEGER) fget_int(font_file,4);
>>>
\item In math mode the inner word space is zero and italic correction values are used.
\<interword space\><<<
new_font.word_sp = ( INTEGER) fget_int(font_file,4);
`<adjust inter-word space`>
>>>
The following variable needs initialization that is used before
the loading of any font. Without it, the program will enter an infinite
loop.
\<defines\><<<
#ifdef LONG
#define MARGINSP 344061L `%tt at 10pt`%
#else
#define MARGINSP 344061
#endif
>>>
\<vars\><<<
static double word_sp = 999999.0, margin_sp;
>>>
Turbo C didn't let me to do casting within initialization.
\<main's init\><<<
margin_sp = (double) MARGINSP; `%/ (double)(1L<<20) * (double) 655360;`%
>>>
\<adjust inter-word space\><<<
if( new_font.word_sp == 0 ) {
int i;
for( i = new_font.char_f; i <= new_font.char_l; i++ ){
new_font.word_sp =
( new_font.word_sp
+
*(new_font.wtbl + (int)(*(new_font.char_wi + i - new_font.char_f)))
) / (new_font.char_f<i? 2:1);
} }
if( new_font.word_sp == 0 ) new_font.word_sp = MARGINSP; `%fix`%
>>>
We took for space size an approximated character width.
\<interword stretch\><<<
(IGNORED) fseek(font_file, 4L, `<relative file addr`>);
>>>
\<interword shrink\><<<
(IGNORED) fseek(font_file, 4L, `<relative file addr`>);
>>>
\item The design height of letters.
\<ex height\><<<
new_font.ex = (INTEGER) fget_int(font_file,4);
>>>
\item The size of one em in the font.
\<em width (=quad width)\><<<
>>>
\item Extra interword space at the end of sentences.
\<extra space\><<<
>>>
\EndList
\Section{Computing Font Info}
\SubSection{Characters' Width}
\<return char width\><<<
return (INTEGER)(
`<(double) char_width( ch )`>
);
>>>
\<(double) char_width( chr )\><<<
design_size_to_pt( *(font_tbl[cur_fnt].wtbl +
(int) `<chr width of`>) )
* (double) font_tbl[cur_fnt].scale
>>>
\<chr width of\><<<
*(font_tbl[cur_fnt].char_wi +
(chr - font_tbl[cur_fnt].char_f)% 256)
>>>
\<(double) char_width( design_ch? design_ch : ch )\><<<
design_size_to_pt( *(font_tbl[cur_fnt].wtbl
+ (int) (`<design-ch / ch width of`>)) )
* (double) font_tbl[cur_fnt].scale
>>>
\<design-ch / ch width of\><<<
*(font_tbl[cur_fnt].char_wi + (int)
( (design_ch? design_ch : ch) - font_tbl[cur_fnt].char_f)% 256)
>>>
\<(double) char_width( ch )\><<<
design_size_to_pt( *(font_tbl[cur_fnt].wtbl
+ (int) (`<ch of width`>) )
)
* (double) font_tbl[cur_fnt].scale
>>>
\<ch of width\><<<
*(font_tbl[cur_fnt].char_wi + (int)
( ch - font_tbl[cur_fnt].char_f)% 256)
>>>
\<ch width\><<<
(int)(`<(double) char_width( ch )`>)
>>>
\SubSection{Characters' Height}
\<(double) char_height( ch )\><<<
design_size_to_pt( *(font_tbl[cur_fnt].htbl
+ (int) (`<ch of height`>)) )
* (double) font_tbl[cur_fnt].scale
>>>
\<ch of height\><<<
( *(font_tbl[cur_fnt].char_hidp + (int)
( ch - font_tbl[cur_fnt].char_f)% 256)
>> 4
) & 0x0F
>>>
\<ch height\><<<
(int)(`<(double) char_height( ch )`>)
>>>
\SubSection{Characters' Depth}
\<(double) char_depth( ch )\><<<
design_size_to_pt( *(font_tbl[cur_fnt].dtbl
+ (int) (`<ch of depth`>)) )
* (double) font_tbl[cur_fnt].scale
>>>
\<ch of depth\><<<
( *(font_tbl[cur_fnt].char_hidp + (int)
( ch - font_tbl[cur_fnt].char_f)% 256)
) & 0x0F
>>>
\<ch depth\><<<
(int)(`<(double) char_depth( ch )`>)
>>>
\SubSection{Other Sizes}
\<size of ex\><<<
design_size_to_pt( 1.7 * (double) font_tbl[cur_fnt].ex )
* (double) font_tbl[cur_fnt].scale
>>>
\<size of space\><<<
design_size_to_pt( font_tbl[cur_fnt].word_sp )
* (double) font_tbl[cur_fnt].scale
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Chapter{Font Information from Htf Tables}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
The htf fonts are used only within the text part of the html files.
Each char code $i$ in a font may be assigned a string representation
and a gif option. When the gif option is provided, the character is
taken to be a picture with ALT getting the string for nongraphical
reviewers.
For character code i, $0\leq i\leq 255$, we maintain
\List{$\circ$}
\item A bit in \`'*gif' to determine if this option exists: \''*gif[] >>
i & 1'
\item A bit in \`'*gif_on' to determine if this option is used:
\''*gif_on[] >> i & 1'
\item A bit in \`'*ch_str' to determine if the string has length different
than one.
\item A character \`'*ch' that holds the character when the string
is of length one,
and holds an index to \`'*str' when the string is longer than 1.
\item A pointer \`'**str[*ch[i]]' to a string, if the character has a
string of length that differs from 1.
\EndList
In the `.htf' files, uses line entries of the form \`'string1string2...',
where the strings have the same arbitrary open and close
delimiters. An empty second string indicate no-option gif.
\<entries for html chars in font-tbl\><<<
char *name, *gif_on, *ch_str, ch255; `%2000`%
unsigned U_CHAR **str, *ch, *gif1;
>>>
The following provides a value for characters of missing fonts out of
the range 32--127. Within that range, the character codes are being
used.
\<get ignored chars\><<<
char *digit = p+2;
ignore_ch = 0;
while( *digit != '\0' ){
if( (*digit < '0') || (*digit > '9') ){
ignore_ch = 0; break;
}
ignore_ch = 10 * ignore_ch + (*digit - '0');
if( ignore_ch > 255 ){ ignore_ch = 0; break; }
digit++;
}
>>>
\<vars\><<<
static int ignore_ch = 0;
>>>
%%%%%%%%%%%%%%%%%%%%%%%
\Section{Load Characters}
%%%%%%%%%%%%%%%%%%%%%%%
In case of shared htf fonts, we can have much more sharing below.
Lynx introduces a space before and after each figure.
\<get html characters\><<<
{ U_CHAR str[256];
int i, design_n, n_gif;
`<font looping variables`>
n_gif = new_font.char_l - new_font.char_f + 1;
new_font.ch255 = 0;
`<initiate gif flags`>
`<initiate htf accents`>
new_font.ch = m_alloc(unsigned char, n_gif );
`<set default characters`>
new_font.str = m_alloc(unsigned char*, n_gif);
new_font.str[0] = &null_str;
design_n = 0;
`<load file of characters`>
new_font.str = (unsigned U_CHAR **) r_alloc((void *) new_font.str,
(size_t) ( (design_n?design_n:1) * sizeof(char *)) );
`<merge new html font with old ones`>
}
>>>
A value 0 corresponds to an empty string. A value greater than 0 points to
a character string.
\`' design_n = 0;' had value 1 earlier. Probably because we had \`'design_n',
and not \`'(design_n?design_n:1)', which asked for allocation of memory of size 0. This caused a problem to mallocate, because it produced a non-valid 0 ( \`'insuffient
memory') error
\<vars\><<<
static unsigned U_CHAR null_str = '\0'; `% /*NEW*/ `%
>>>
\<initiate gif flags\><<<
{ int n_gif_bytes;
n_gif_bytes = (n_gif + 7) / 8;
new_font.gif_on = m_alloc(char, n_gif_bytes );
new_font.ch_str = m_alloc(char, n_gif_bytes );
`<memory for math classes`>
for( i=n_gif_bytes; i--; ) {
`<init math-closing`> new_font.ch_str[i] = new_font.gif_on[i] = 0;
}
new_font.gif1 = m_alloc(unsigned char, n_gif );
for( i=n_gif; i--; ) {
`<init math`> new_font.gif1[i] = 0;
} }
>>>
\<set default characters\><<<
for( i = new_font.char_f; i <= new_font.char_l ; i++ ){
new_font.ch[i - new_font.char_f] =
(char) (((31<i) && (i<128))? i : ignore_ch);
}
>>>
The first iteration searches for the font directly. The second
iteration searches for aliased font.
\<load file of characters\><<<
{ char search_font_name [256];
(IGNORED) strcpy((char *) search_font_name, (char *) new_font.name);
while( 1 ){ BOOL flag;
`<font looping test`>
flag = TRUE;
for( ; font_name_n; font_name_n-- ){ FILE* file;
int char_f, char_l;
new_font_name[font_name_n] = '\0';
`<file = .htf file`>
if( file != NULL){
INTEGER x_char_l;
`<scan header line in html font file`>
if( x_char_l == HTF_ALIAS) {
`<scan aliased htf file`>
(IGNORED) fclose(file); flag = FALSE; break;
}
`<scan info of html font file`>
`<scan trailer line in html font file`>
htf_to_lg(html_font, new_font_name, fonts_n, file);
`<trace htf files`>
(IGNORED) fclose(file); break;
} }
if( flag ){ break; }
}
if( font_name_n == 0 ){
if( errCode == 0 ){ errCode= 21; }
warn_i_str(21,search_font_name);
(IGNORED) fprintf(stderr,
"%d--%d)\n", new_font.char_f, new_font.char_l);
dump_env();
} else { `<trace env file`> }
}
>>>
\<font looping variables\><<<
int loopBound = 0;
U_CHAR loopName[256];
loopName[0] = '\0';
>>>
\<font looping test\><<<
if( eq_str( new_font_name, loopName) ){
U_CHAR name[256];
(IGNORED) sprintf(name, "%s.htf", new_font_name);
err_i_str(1, name);
} else {
(IGNORED) strcpy((char *) loopName, (char *) new_font_name);
}
loopBound++;
if( loopBound > 10 ){
U_CHAR name[256];
(IGNORED) sprintf(name, "%s.htf", new_font_name);
err_i_str(1, name);
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Aliases}
%%%%%%%%%%%%%%%%%%%%%%%
\<if alias return 0\><<<
if( ( name == new_font_name ) && (n == 19) && (ch=='.') ){
return HTF_ALIAS;
}
>>>
\<defines\><<<
#define HTF_ALIAS 10000000
>>>
\ifHtml[\HPage{more}
Was \`' printf("%d--%d)\n", new_font.char_f, new_font.char_l);'
\Verbatim
Something I noticed while working on the redirection program:
- When a warning message is given about a missing .htf file, the first
part:
--- warning --- Couldn't find `.htf' file for font `cmitt10' (char codes:
is printed to stderr, while the second part:
0--127)
is printed to stdout. This looks silly if you redirect stdout but not
stderr, as you'll see only the second halve in the log file. (I do
intend to expand my redirection utility to include stderr, if desired,
but I think this should be fixed anyway).
\EndVerbatim
\EndHPage{}]\fi
\<scan aliased htf file\><<<
{ int chr;
font_name_n=0;
while( (chr = get_html_ch(file)) != '\n' ){
if( chr != ' ' ) search_font_name[font_name_n++] = chr;
}
search_font_name[font_name_n] = '\0';
if( eq_str( search_font_name, new_font_name) ){ err_i_str(20, new_font_name); }
(IGNORED) printf("Searching ``%s.htf' for ``%s.htf'\n",
search_font_name, new_font.name);
htf_to_lg(html_font, new_font_name, fonts_n, file);
new_font_name = (char *) r_alloc((void *) new_font_name,
(size_t) (font_name_n+1));
(IGNORED) strcpy((char *) new_font_name, (char *) search_font_name);
font_name_n = strlen((char *) new_font_name);
}
>>>
\<get replacement fontNO\><<<
if( dot_file ){ BOOL tag;
(IGNORED) fseek(dot_file, 0L, `<abs file addr`>);
tag = TRUE;
while( tag ){
switch( getc(dot_file) ){
case EOF: { j++; tag = FALSE; break; } `%no replacement`%
case 'a': { `<scan replacement font`> }
default: { int ch;
do ch = getc(dot_file);
while( (ch != '\n') && (ch != EOF) );
break; }
} } }
>>>
\<scan replacement fontNO\><<<
int chr;
U_CHAR *ch;
ch = new_font.name;
while( *ch++ == (chr = getc(dot_file)) );
if( chr==' ' ){
tag = FALSE;
while( (new_font_name[0] = getc(dot_file)) == ' ' );
font_name_n=1;
while( (chr = getc(dot_file)) != '\n' )
if( chr != ' ' ) new_font_name[font_name_n++] = chr;
new_font_name[font_name_n] = '\0';
(IGNORED) printf("Loading ``%s.htf' for ``%s.htf'\n",
new_font_name, new_font.name);
}
>>>
\Section{Merge Tables}
Don't we loose some vital info here? Were this comes from?
\<merge new html font with old ones\><<<
for( i = fonts_n; i--; )
if( eq_str(html_font[i].name, new_font_name) ){ int k;
k = html_font[i].i;
free((void *) new_font.gif1 ); new_font.gif1= font_tbl[ k ].gif1;
free((void *) new_font.ch ); new_font.ch = font_tbl[ k ].ch;
free((void *) new_font.str ); new_font.str = font_tbl[ k ].str;
free((void *) new_font.ch_str );
new_font.ch_str = font_tbl[ k ].ch_str;
`<merge math entries`>
break; }
if( i < 0 ){ `<record new tex4ht font`> }
>>>
\<record new tex4ht font\><<<
html_font = fonts_n? (struct html_font_rec *) r_alloc((void *) html_font,
(size_t) ((fonts_n+1) * sizeof(struct html_font_rec) ))
: m_alloc(struct html_font_rec, 1);
html_font[fonts_n].name = m_alloc(char, font_name_n + 1);
(IGNORED) strcpy((char *) html_font[fonts_n].name, (char *) new_font_name);
html_font[fonts_n].i = font_tbl_size;
fonts_n++;
>>>
\<scan fonts vars\><<<
int fonts_n;
struct html_font_rec *html_font=0;
>>>
\<init scan fonts vars\><<<
fonts_n = 0;
>>>
\<free html font memory\><<<
if( html_font ){
while( fonts_n-- ) free((void *) html_font[fonts_n].name);
free((void *) html_font );
}
>>>
EXPLAIN THE CONTENT OF THE FOLLOWING
\<types\><<<
struct html_font_rec{ char* name;
int i; };
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Scan Characters}
%%%%%%%%%%%%%%%%%%%%%%%%%
\<scan info of html font file\><<<
if( char_f <= new_font.char_l ){ U_CHAR del;
int j, n;
`<skip front part in font file`>
n = ((char_l < new_font.char_l)? char_l : new_font.char_l)
- char_f + 1;
for( i = char_f - new_font.char_f; i < n; i++ ){
`<scan html char in font file`>
`<store html character`> }
`<skip back part of font file`>
}
>>>
\<skip front part in font file\><<<
while( char_f < new_font.char_f ){
while( get_html_ch(file) != '\n' );
char_f++; }
>>>
\<skip back part of font file\><<<
while( char_l > new_font.char_l ){
while( get_html_ch(file) != '\n' );
char_l--; }
>>>
We should consider characters having representations of \`'\decimal
digits\', \`'\x hexadecimal digits\', and \`'\o octal digits\'.
\<scan html char in font file\><<<
{ int indirect_ch, base=0, value=0, digit, ch1`%, ch2`%;
indirect_ch = 0;
del = get_html_ch(file); j=0;
while( (str[j++] = get_html_ch(file)) != del )
{ `<check for encoded chars`> };
str[j-1] = '\0';
while( get_html_ch(file) != del );
ch1 = 0;
while( ((ch = (int) get_html_ch(file)) != del) ){
if( (ch < '0') || (ch > '9') ) break;
ch1 = ch1 * 10 + ch - '0'; }
`<propagate 4hf info into htf`>
new_font.gif1[i] = ch1 % 256;
do{
if( (ch = get_html_ch(file)) == del ){
`<scan htf accent`> break;
}
} while( ch != '\n' );
if( ch != '\n' ){
do{
if( (ch = get_html_ch(file)) == del ){
`<scan htf accented`> break;
}
} while( ch != '\n' );
}
if( ch != '\n' )
{ while( get_html_ch(file) != '\n' ); }
}
>>>
\<check for encoded chars\><<<
if( (digit=str[j-1]) == '\\' )
if( (indirect_ch = !indirect_ch) != 0) {
switch( value=get_html_ch(file) ){
case 'x': { base = 16; value = 0; j--; break; }
case 'o': { base = 8; value = 0; j--; break; }
default: {
if( (value < '0') || (value > '9') ) {
indirect_ch = !indirect_ch; str[j-1] = value;
} else { value -= '0'; base = 10; j--; }
} } }
else{ if( value>255 ){
warn_i_int(28,value);
value = 32; dump_htf( file );
}
str[j-1] = value;
}
else if ( indirect_ch ){
j--; digit -= (digit>'9')? 'A'-10 : '0';
if( (digit<0) || (digit>=base) ){
warn_i_int(29, str[j]);
digit = 0; dump_htf( file );
}
value = value*base + digit;
} else if ( str[j-1]==10 ){
dump_htf( file );
err_i_int(48, i+1);
}
>>>
We might be able in the following to share strings by comparing with
corresponding characters in other fonts.
The variable j holds the number of characters in the string plus
one.
\<store html character\><<<
add_bit( new_font.ch_str, i, j!=2 );
switch( j ){
case 1: { new_font.ch[i] = 0; break; }
case 2: { new_font.ch[i] = *str; break; }
default: { unsigned U_CHAR *p;
new_font.str[design_n] = p = m_alloc(unsigned char, j);
if( design_n>255 ){ design_n--; warn_i(35);}
if( i==255 ){
if( design_n == 255 ){
new_font.ch[i] = 0; new_font.ch255 = 1;
} else { new_font.ch[i] = ++design_n; }
} else { new_font.ch[i] = ++design_n; }
while( j-- ) p[j] = str[j]; `%2000`%
} }
>>>
\<header functions\><<<
static int get_html_ch( ARG_I(FILE*) );
>>>
\<functions\><<<
`[
static int get_html_ch( file ) FILE* file
;{ int ch;
if( (ch = (int) getc(file)) == EOF ) {
dump_htf( file );
err_i_str(20, new_font_name);
}
return ch;
}
>>>
\<header functions\><<<
static FILE* f_open( ARG_II(const char*,const char*) );
>>>
\<functions\><<<
`[
static FILE* f_open( name, flags )
const char* name `;
const char* flags
;{ FILE* file;
if( (file = fopen(name,flags) ) != NULL ) {
(IGNORED) printf("(%s)\n",name);
}
return file;
}
>>>
\<header functions\><<<
static void dump_htf( ARG_I(FILE*) );
>>>
\<functions\><<<
`[
static void dump_htf( file ) FILE* file
;{ int i, j, ch, chr=0;
(IGNORED) fseek(file, 0L, 0);
i=-1; j=0;
while( (ch = getc(file)) != EOF ){
if( !j ){ chr = ch; }
j += ch==chr;
(IGNORED) putc(ch,stderr);
if( ch == '\n' ){
if( (i>-1 ) && (j<4) && (dump_htf_files<2) ){
(IGNORED) printf("missing delimiter \n");
}
(IGNORED) fprintf(stderr,"%d: ",++i);
j=0;
} } }
>>>
\<vars\><<<
static short dump_htf_files = 0;
static BOOL dump_env_files = FALSE;
>>>
\<dump htf files\><<<
dump_htf_files = 1;
>>>
\<dump env file\><<<
dump_env_files = TRUE;
>>>
\<trace htf files\><<<
if( dump_htf_files ){
dump_htf_files++; dump_htf( file ); dump_htf_files--;
}
>>>
\<trace env file\><<<
if( dump_env_files ){ dump_env(); }
>>>
\<header functions\><<<
static void dump_env( ARG_I(void) );
>>>
\<functions\><<<
static void dump_env( MYVOID )
{
int ch;
if( !dumped_env ){
dumped_env = TRUE;
(IGNORED) fseek(dot_file, 0L, `<abs file addr`>);
(IGNORED) fprintf(stderr,
"\n----------------------------------------------------\n");
(IGNORED) fprintf(stderr, "environment file\n");
(IGNORED) fprintf(stderr,
"----------------------------------------------------\n");
while( (ch = getc(dot_file)) != EOF ){
(IGNORED) putc(ch,stderr);
}
(IGNORED) fprintf(stderr,
"----------------------------------------------------\n");
} }
>>>
\<vars\><<<
static BOOL dumped_env = FALSE;
>>>
\<header functions\><<<
static void htf_to_lg( ARG_IV(struct html_font_rec*,char*,int,FILE*));
>>>
\<functions\><<<
`[
static void htf_to_lg(html_font, new_font_name, fonts_n, file)
struct html_font_rec* html_font`;
char* new_font_name`;
int fonts_n`;
FILE* file
;{
int ch, i;
for( i = 0 ; i<fonts_n; ){
if( eq_str(html_font[i].name, new_font_name) ){ break; }
i++;
}
if( i == fonts_n ){
BOOL content;
int prev_ch;
prev_ch = '\n'; content = FALSE;
while( (ch = (int) getc(file)) != EOF ) {
if( (ch != '\n') || (prev_ch != '\n') ){
(void) putc( ch, log_file );
prev_ch = ch; content = TRUE;
} }
if( content && (prev_ch != '\n') ){
(void) putc( '\n', log_file );
}
} }
>>>
\<scan header line in html font file\><<<
x_char_l =
get_html_file_id(file, new_font.char_f, new_font.char_l, 19);
if( x_char_l != HTF_ALIAS) {
char_f = (int) (x_char_l / 1000.0 + 0.5) + new_font.char_f;
x_char_l -= (char_f-new_font.char_f) * 1000 - new_font.char_l;
char_l = (int) x_char_l;
}
>>>
\<scan trailer line in html font file\><<<
(void) get_html_file_id(file, new_font.char_f, new_font.char_l, 18);
>>>
\<header functions\><<<
static INTEGER get_html_file_id( ARG_IV(FILE*, int, int, int) );
>>>
\<functions\><<<
`[
static INTEGER get_html_file_id(file, first, last, n)
FILE* file`;
int first`; int last`; int n
;{ int ch, i, bound, cnt;
INTEGER diff;
char* name;
name = new_font_name;
while( *name == (ch = get_html_ch(file)) ) name++;
`<if alias return 0`>
if( (*name != '\0') || (ch != ' ') ) warn_i_str(n, name);
bound = first; diff = 0;
for( cnt = 0; cnt < 2; cnt++ ){
while( ch == ' ' ) ch = get_html_ch(file);
i = 0;
while( (ch >= '0') && (ch <= '9') ){
i = i * 10 + ch - '0'; ch = get_html_ch(file); }
if( i != bound ){
`<warning(22`>, new_font_name, i, bound); show_err_context();
diff = diff * 1000 + i - bound; }
bound = last; }
while( ch != '\n' ) ch = get_html_ch(file);
return diff;
}
>>>
\<defines\><<<
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef BOOL
#define BOOL int
#endif
>>>
\Section{Use of Characters}
When \''next_char' is not equal to -1, there is a character waiting to
replace the current character.
\<insert ch to text\><<<
`<set begin accents`>
`<css for chars`> `<trace dvi char`>
`<left class del`> `<pos dvi ch`>
`<mark accented ch`>
if( `<next-char`> != -1 ) {
`<open output file`>
(IGNORED) put_4ht_ch( `<next-char`>, cur_o_file );
`<next-char`> = -1;
} else if( `<next-str`> ) {
`<current char for forwarded string`>
print_f_4ht(next_str); free((void *) next_str);
next_str = (char *) 0;
} else {
if( verb_ch ){
`<open output file`>
(IGNORED) put_4ht_ch( ch, cur_o_file );
} else { `<insert font char`> }
}
`<end mark accented ch`>
`<end pos dvi ch`> `<right class del`>
`<end trace dvi char`> `<end css for chars`>
`<set end accents`>
>>>
Does \`'verb_ch' correspond to \''\Char'?
\<insert font char\><<<
int gif_flag, chr, r_ch;
BOOL ch_str_flag;
r_ch = ch - font_tbl[cur_fnt].char_f;
gif_flag = font_tbl[cur_fnt].gif1[r_ch];
ch_str_flag = get_bit( font_tbl[cur_fnt].ch_str, r_ch);
chr = ((r_ch == 255) && font_tbl[cur_fnt].ch255 )? 256 :
*(font_tbl[cur_fnt].ch + r_ch);
if( (gif_flag % 2) || ch_str_flag ){ design_ch = ch;
{ `<insert design chr`> } design_ch = 0; }
else { `<insert standard char`> }
>>>
\<vars\><<<
static BOOL verb_ch = FALSE;
>>>
A design character is either a picture (\''gif_flag') or
a string character (\''ch_str_flag').
\<insert design chr\><<<
U_CHAR str[256], *p;
BOOL sv;
int mag;
sv = special_on; special_on = TRUE;
if( gif_ch && (gif_flag % 2) ){
`<open output file`>
if( !gif_open[gif_flag] ){
`<img char without template`>
} else if( !get_bit( class_on, gif_flag ) ) {
notify_class_info(gif_flag);
store_bit_I( class_on, gif_flag );
}
`<<IMG SRC="...`>
`<..."ALT="...`>
`<..."CLASS="family...`>
`<...gif font size...`>
`<...gif font mag...`>
`<...gif sym ord...`>
`<..."/IMG>`>
} else { `<insert string char`> }
special_on = sv;
>>>
\<header functions\><<<
static void notify_class_info( ARG_I(int) );
>>>
\<functions\><<<
`[
static void notify_class_info( gif_flag ) int gif_flag
;{ U_CHAR str[256], *p;
str[0] = '\0';
p = gif_open[gif_flag];
if( p )
if( *p ) (IGNORED) strct(str,p);
p = gif_alt[gif_flag];
if( p )
if( *p ) (IGNORED) strct(str,p);
p = gif_class[gif_flag];
if( p )
if( *p ) (IGNORED) strct(str,p);
p = gif_size[gif_flag];
if( p )
if( *p ) (IGNORED) strct(str,p);
p = gif_mag[gif_flag];
if( p )
if( *p ) (IGNORED) strct(str,p);
p = gif_ord[gif_flag];
if( p )
if( *p ) (IGNORED) strct(str,p);
p = gif_end[gif_flag];
if( p )
if( *p ) (IGNORED) strct(str,p);
p = str;
while( *p ){
if( *p == '\n' ) *p = ' ';
p++;
}
(IGNORED) fprintf(log_file, class_fmt,
gif_flag,
gif_id[gif_flag]? gif_id[gif_flag] : "",
str);
}
>>>
For some reason, bordlan cc+ complains if the
\`'p = gif_open[gif_flag]' are placed within the if's.
\`'configuration for class %d' below stands for
\`'\\special{t4ht;|%d...}'
\<img char without template\><<<
(IGNORED) sprintf(str,
"configuration for htf class %d (char %d of %s.htf)",
gif_flag, ch,font_tbl[cur_fnt].name
);
warn_i_str(50,str);
>>>
\<img char without template\><<<
gif_open[gif_flag] = m_alloc(char,
`<1 + strlen((char *) <IMG SRC="+"ALT="+++++">+)`>);
(IGNORED) strcpy((char *) gif_open[gif_flag],
`<<IMG SRC="+"ALT="+++++">+`>);
gif_alt[gif_flag] = gif_open[gif_flag]+11;
*(gif_alt[gif_flag] - 1) = '\0';
gif_class[gif_flag] = gif_open[gif_flag]+19;
*(gif_class[gif_flag] - 1) = '\0';
gif_size[gif_flag] = gif_open[gif_flag]+20;
*(gif_size[gif_flag] - 1) = '\0';
gif_mag[gif_flag] = gif_open[gif_flag]+21;
*(gif_mag[gif_flag] - 1) = '\0';
gif_ord[gif_flag] = gif_open[gif_flag]+22;
*(gif_ord[gif_flag] - 1) = '\0';
gif_end[gif_flag] = gif_open[gif_flag]+23;
*(gif_end[gif_flag] - 1) = '\0';
gif_id[gif_flag] = gif_open[gif_flag]+28;
*(gif_id[gif_flag] - 1) = '\0';
>>>
\<<IMG SRC="+"ALT="+++++">+\><<<
"<img src=\"+\" alt=\"+++++\" />+"
>>>
\<1 + strlen((char *) <IMG SRC="+"ALT="+++++">+)\><<<
29
>>>
% `<..."IMG OPTIONS="...NOT`>
What is the difference between the following two cases?
\<insert standard char\><<<
if( !gif_flag || (gif_flag % 2) || ch_map_flag ) { put_char(chr);
} else{ `<put char(chr)`> }
>>>
If \`'gif_flag % 2' is the case we have a picture which we want to
insert for it just the string part. (The !gif_flag is for text with
pattermn 0?)
\<insert string char\><<<
if( !gif_flag || (gif_flag % 2) || ch_map_flag ) {
put_alt_ch(chr,ch_str_flag); }
else{ `<put string-char(chr)`> }
>>>
\SubSection{Script class-fmt}
Font information, e.g., for style files.
\<h-defines\><<<
#ifndef LGCLS
#define LGCLS "Font_Class(%d,\"%s\"): %s\n"
#endif
>>>
\<vars\><<<
static U_CHAR *class_fmt = NULL;
>>>
\<get from tex4ht.env file\><<<
class_fmt = (char *) get_script(class_fmt,LGCLS,'c');
>>>
\SubSection{File Names for Gifs}
Symbol i from base font x gets into file \`'x-i.gif', and from
c-magnified font goes into \`'x-c-i.gif'. In old LaTeX,
\`'\documentstyle[...12p..]{..}' gives a based-10 font magnified into 12.
\<defines\><<<
#define GIF_I "-%x%s"
#define GIF_II "-%x-%x%s"
#define GIF_VII "%s"
>>>
\<h-defines 2\><<<
#ifdef DOS
#define HTM
#endif
>>>
\<defines\><<<
#ifdef HTM
#define DOS_GIF_FILE
#endif
>>>
\<insert gif symbol to html file\><<<
if( !dos_file_names ){
print_f(font_tbl[cur_fnt].name);
if( mag == 10 ) (IGNORED) sprintf(str, GIF_I, design_ch, gif);
else (IGNORED) sprintf(str, GIF_II, mag, design_ch, gif);
}
>>>
\<insert gif symbol to lg file\><<<
if( !dos_file_names ){
if( mag == 10 ) (IGNORED) sprintf(str, "%s-%x%s",
font_tbl[cur_fnt].name, ch, gif);
else (IGNORED) sprintf(str, "%s-%x-%x%s",
font_tbl[cur_fnt].name, mag, ch, gif);
}
>>>
Transform long file names to 8 characters.
\<insert gif symbol to html file\><<<
`%
#ifdef DOS_GIF_FILE
`%
if( dos_file_names ){
dos_gif_file(str, mag, design_ch);
print_f(str);
(IGNORED) sprintf(str, GIF_VII, gif);
}
`%
#endif
`%
>>>
\<insert gif symbol to lg file\><<<
`%
#ifdef DOS_GIF_FILE
`%
if( dos_file_names ){
(IGNORED) strcpy((char *) str, (char *) font_tbl[cur_fnt].name);
dos_gif_file(str, mag, ch);
strct(str,gif);
}
`%
#endif
`%
>>>
The following writes into the \`'.lg' file. The \`'templt' ends with
a percent (\''%\0'). The algorithm collects the characters
until a \''%' is encountered. Then it prints out the segment of
characters accumulated, and goes to process whatever follows the \`'%'.
\<header functions\><<<
static void script( ARG_IV(char *, U_CHAR *, int, U_CHAR *) );
>>>
\<functions\><<<
`[
static void script(templt, job, page, font)
U_CHAR *templt`;
U_CHAR *job`;
int page`;
U_CHAR *font
;{ U_CHAR *ch, *p;
U_CHAR fmt[256];
job[ (int) strlen((char *) job) - 1 ] ='\0';
p = ch = templt;
while( TRUE ){
if( *ch == '%' ){
`<print segment of templet`>
`<get format of output`>
switch( *(++ch) ){
case '1':{ *p = 's';
(IGNORED) fprintf(log_file, fmt, job); break;}
case '2':{ *p = 'd';
(IGNORED) fprintf(log_file, fmt, page); break;}
case '3':{ *p = 's';
(IGNORED) fprintf(log_file, fmt, font); break;}
case '%':{ *p = 'c';
(IGNORED) fprintf(log_file, fmt, '%'); break;}
default:{ job[ (int) strlen((char *) job) ] ='.'; return;}
}
p = ++ch;
}else ch++;
} }
>>>
\<print segment of templet\><<<
*ch = '\0'; (IGNORED) fprintf(log_file, "%s", p);
*(ch++) = '%';
>>>
The jobname var contains a period at the end, removed
temporarily for the script. The default case above should have been
an error, or at least a warning.
\<get format of output\><<<
p=fmt; *(p++) = '%';
if( *ch == '\0' ){ job[ (int) strlen((char *) job) ] ='.'; return; }
while( *ch != '%' ){ *(p++) = *(ch ++); }
*(p+1) = '\0';
>>>
\Verbatim
> Instead of a single-line script
> scall convert %1 %2 %3\nif errorlevel 1 goto end
> you are allowed also to have multi-lines scripts
> scall convert %1 %2 %3
> sif errorlevel 1 goto end
> Would this work? If '\n' is a superior solution I
> see no problems implementing it.
Yes, this works fine for the "if errorlevel" problem. However, I have
found out in the meantime (RTMF is always a good idea ;-) that the
emTeX driver can create _all_ pcx files in one run, giving them names
like out01.gif, out02.gif, ... (or out001.gif, out002.gif, ... - it
depends on command line options). Display has a similar option. This
would significantly reduce the total conversion time, so I'm inclined to
use this option. (I think it may even outweigh the advantage of not
recreating the gifs after the b line, although I may be able to
implement that anyway with a little trickery). Since DOS has no way to
increase counters, I'd like to see the page numbers tex4ht gives also as
"01" (or "001"), etc. This would make it easy for me to use the emTeX
functionality. I've implemented this in tex4ht.c by changing the page
number format from %d to %03d. I assume this won't break anything else?
> I didn't get the idea.
The emTeX dvidot driver, when given an output filename like 'out???',
creates an output file for each page in the dvi file in one run, named
out001, out002, out003, etc. If the page numbers tex4ht produces are of
the same form (001 instead of 1), I can simply append them to 'out' like
this: out%2. This makes it easier to process the created files further.
> The change is in the following line?
> case '2':{ (IGNORED) fprintf(log_file, "%d", page); break;}
Yes.
> Is %03d the same as %0.3d? If not, what is the difference?
For integers, yes; it is also the same as %.3d. I just prefer the
first form, as it makes more sense to me (things after the dot are
called the precision specifier, and I don't want to specify precision
but field width).
\EndVerbatim
\SubSection{Script for Translation into Gifs}
\<h-defines\><<<
#ifndef LGPIC
#define LGPIC "--- needs --- %%1.idv[%%2] ==> %%3 ---\n%"
#endif
>>>
\<vars\><<<
static U_CHAR *font_gif = NULL;
>>>
\<get gif script from com ln\><<<
font_gif = p+2;
>>>
\<get from tex4ht.env file\><<<
font_gif = (char *) get_script(font_gif,LGPIC,'s');
`<inser percent-ch at end of s-script`>
>>>
\<inser percent-ch at end of s-script\><<<
{ int n;
n = (int) strlen((char *) font_gif);
if( font_gif[n-1] != '%' ){ font_gif[n] = '%'; font_gif[n+1] = '\0'; }
}
>>>
\SubSection{Script lg file Seperator}
Separates general picturs from symbols.
\<h-defines\><<<
#ifndef LGSEP
#define LGSEP "--- characters ---\n"
#endif
>>>
\<vars\><<<
static U_CHAR *begin_char_gif = NULL;
>>>
\<get start char-gif from com ln\><<<
begin_char_gif = p+2;
>>>
\<get from tex4ht.env file\><<<
begin_char_gif = (char *) get_script(begin_char_gif,LGSEP,'b');
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Default Format for Pictures}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<h-defines\><<<
#ifndef LGTYP
#define LGTYP ".png"
#endif
>>>
\<vars\><<<
static U_CHAR *gif = NULL;
>>>
\<get gif-type from com ln\><<<
gif = p+2;
>>>
In the extension name we don't want the line break at the end.
\<get from tex4ht.env file\><<<
gif = (char *) get_script(gif,LGTYP,'g');
{ int n;
n = (int) strlen((char *) gif) - 1;
if( gif[n] == '%' ){ gif[n] = '%'; }
else if( gif[n] == '\n' ) { gif[n] = '\0'; }
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Encoding for Dos Font Names}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
We set font name + mag (2 chars) + ch number (2 ch). If the length is
no greater than 8, we are done. Otherwise, we take the first n-2
characters and map them to a string of length 4. They are the most
stable in the names, so they minimize replacements.
\<header functions\><<<
`%
#ifdef DOS_GIF_FILE
`%
static void dos_gif_file( ARG_III(char *, int, int) );
`%
#endif
`%
>>>
\<functions\><<<
`%
#ifdef DOS_GIF_FILE
`%
`[
static void dos_gif_file(str, mag, design_ch)
U_CHAR *str`;
int mag`;
int design_ch
;{ int n, m, i;
struct gif_file_rec * p, * q;
U_CHAR *ch;
m = n = (int) strlen((char *) str);
if( n > 4 ){
`<p = prefix of font file`>
if( p == NULL ){ `<p = new prefix font file`> }
gif_file = p;
for( n=0; n<4; n++ ) str[n] = xeh[(int)(*(p->code + n))];
}
str[n++] = xeh[mag<16? 0: mag / 16];
str[n++] = xeh[mag % 16];
str[n++] = xeh[design_ch<16? 0: design_ch / 16];
str[n++] = xeh[design_ch % 16];
str[n] = '\0';
}
`%
#endif
`%
>>>
We keep collisions table to ensure agains collisions. We do it in
circular linked list so that last visited in previous turn will be the
first to be visited now. The prefix, and its suggested
\<types\><<<
`%
#ifdef DOS_GIF_FILE
`%
struct gif_file_rec{
U_CHAR code[4];
U_CHAR *name;
struct gif_file_rec *next; };
`%
#endif
`%
>>>
\<vars\><<<
`%
#ifdef DOS_GIF_FILE
`%
static struct gif_file_rec * gif_file = (struct gif_file_rec *) 0;
`%
#endif
`%
>>>
\<p = prefix of font file\><<<
if( (p = gif_file) != NULL ){
while( TRUE ){
if( eq_str( str, p->name ) ) break;
if( (p = p->next) == gif_file ){ p = NULL; break; }
} }
>>>
\<p = new prefix font file\><<<
p = m_alloc(struct gif_file_rec, 1);
`<get tentative prefix replacement`>
`<adjust prefix replacement`>
(IGNORED) printf("\nRenaming ``%s____%s' to ``%c%c%c%c____%s'\n",
str, gif,
xeh[(int)(ch[0])], xeh[(int)(ch[1])],
xeh[(int)(ch[2])], xeh[(int)(ch[3])]
, gif);
p->name = m_alloc(char,m+1);
(IGNORED) strcpy((char *) p->name, (char *) str );
if( gif_file ){ p->next = gif_file->next; gif_file->next = p; }
else p->next = p;
>>>
We choose the scheme `sum(char codes of str) / 4' to find a number i,
and then we cycle around hex in jumps of i to choose four characters
not starting with digit.
\<get tentative prefix replacement\><<<
for(i=str[n]; n; ){ i+=str[--n]; if( i > (INT_MAX / 8) ) i/=2; }
if( (n=i % BASE) <10 ) n+= 10 + i%(BASE-20);
*(ch = p->code)= n; n += i;
ch[1] = n%BASE; n += i;
ch[2] = n%BASE; n += i;
ch[3] = n%BASE;
>>>
To allow for adjustments, we srtoe indexes of xeh, and not the
characters themselve.
We allow $BASE^3$ options of adjustments!!!
\<adjust prefix replacement\><<<
if( gif_file ){
q = gif_file->next;
while( TRUE ){
if( (*(q->code) == *ch) && (*(q->code+1) == ch[1]) &&
(*(q->code) == ch[2]) && (*(q->code+2) == ch[3]) ){
ch[3] ++; ch[2] += ch[3]/ BASE; ch[3] = ch[3] % BASE;
ch[1] += ch[2]/ BASE; ch[2] = ch[2] % BASE;
ch[1] = ch[1] % BASE;
q = gif_file;
} else if( q == gif_file ) break;
q = q->next;
} }
>>>
Instead of hex we take xeh.
\<defines\><<<
#define BASE 36
>>>
\<vars\><<<
`%
#ifdef DOS_GIF_FILE
`%
static U_CHAR xeh[]="0123456789abcdefghijklmnopqrstuvxyz";
`%
#endif
`%
>>>
\SubSection{Other}
\`'<<' is NOT a safe operation, beyond char size, for transporting across platforms.
\<defines\><<<
#define store_bit_I(ch,i) ch[(i)/8]|=(1<<((i)%8));
#define store_bit_Z(ch,i) ch[(i)/8]&=~(1<<((i)%8))
#define add_bit(ch,i,b) ch[(i)/8] |= ((b) << ((i)%8))
#define get_bit(ch,i) ((ch[(i)/8] >> ((i)%8)) & 1)
>>>
%#define store_bit(ch,i,b) if(b){ ch[(i)/8]|=(1<<((i)%8));}\
% else ch[(i)/8]&=~(1<<((i)%8))
\<header functions\><<<
static void put_alt_ch( ARG_II( int, BOOL) );
>>>
\<functions\><<<
`[
static void put_alt_ch(chr,ch_str_flag)
int chr`;
BOOL ch_str_flag
;{
if( !ch_str_flag ) put_char( chr );
else if( chr > 0 ){ `<put htf char string`> }
}
>>>
The \`'gif_ch' is true when we put the character outside of
\`'\special{t4ht|}...\special{t4ht|}', and is false inside that range.
Within that range, used in ALT's, we want to eliminate the html
comamnds \`'<...>' and the delimiter characters \`'"'.
\<put htf char string\><<<
unsigned U_CHAR * p;
p = font_tbl[cur_fnt].str[chr-1];
if( gif_ch ) print_f( (char *) p ); `% /*new*/`%
else while( *p ){
switch( *p ){
case '<': { while( *p && (*p != '>') ) p++; break; }
case '>':
case '"': { p++; break; }
case '\'': { p++; break; }
default: { put_char( (int) *p ) ; p++; }
} }
>>>
% case '&': { alt_ch_length = strlen((char *) p); }
%\<vars\><<<
%static int alt_ch_length = 0;
%>>>
\<vars\><<<
static BOOL gif_ch = TRUE;
static int design_ch = 0;
>>>
\Section{Css Trace on Characters}
\`'\Configure{characters}{<SPAN\:newlnch NAME="}{">}{</SPAN>}
{<IMG SRC="}{"ALT="}{"NAME="}{">}'
\<css for characters\><<<
int n, code;
U_CHAR *p, *q;
code = get_char();
n = 1 + ((--special_n>254)? 254 : special_n);
q = p = m_alloc(char, (int) n);
while( special_n > 254 ){ (void) get_char(); special_n--; }
while( special_n-- ) { *(q++) = get_char(); }
*q = '\0'; q = p;
switch ( code ){
case '8': { pause_style--; break; }
case '9': { pause_style++; break; }
case '-': { default_font = font_tbl[cur_fnt].num;
base_font_size = font_tbl[cur_fnt].scale / 100;
break; }
case '+': { default_font = -1; break; }
case '%': { `<insert font size`> break; }
case '=': { `<insert css name of ch`> break; }
case '|': { `<record string for htf class`> break; }
case ',': { `<don't report next htf class to lg`> break; }
default: { warn_i_int( 36, code); }
}
span_on = span_name_on && !pause_style;
if( q ) free((void *) q);
>>>
Since htf fonts are shred within tex fonts, and since they can easily
be modified by scripts in the output html files, changes of their classes
from within the 4ht files is not supported.
By taking integer for \`'pause_style' we allow for nested pausing of fonts.
\<vars\><<<
static int pause_style = 0, default_font = -1, base_font_size=6533;
static BOOL
span_name_on = FALSE,
span_on = FALSE;
>>>
\SubSection{4: Src Prefix}
\<record img-srcOUT\><<<
q = img_src; img_src = p;
>>>
\<varsOUT\><<<
static U_CHAR * img_src;
>>>
\<main's initOUT\><<<
img_src = m_alloc(char, (int) 12);
(IGNORED) strcpy((char *) img_src, "<img \nsrc=\"" );
>>>
\<<IMG SRC="...\><<<
p= gif_open[gif_flag];
if( p )
if( *p ){
print_f(p); (IGNORED) strcpy((char *) str, (char *) font_tbl[cur_fnt].name);
mag = (int) ((double) font_tbl[cur_fnt].scale /
font_tbl[cur_fnt].design_sz * 10 );
`<insert gif symbol to html file`>
print_f(str);
add_bit( font_tbl[cur_fnt].gif_on, r_ch, 1 );
}
>>>
\SubSection{5: Alt Prefix}
\<record img-altOUT\><<<
q = img_alt; img_alt = p;
>>>
\<varsOUT\><<<
static U_CHAR * img_alt;
>>>
\<main's initOUT\><<<
img_alt = m_alloc(char, (int) 7);
(IGNORED) strcpy((char *) img_alt, "\"alt=\"" );
>>>
\<..."ALT="...\><<<
p = gif_alt[gif_flag];
if( p )
if( *p ){
print_f(p); put_alt_ch(chr,ch_str_flag);
}
>>>
\SubSection{6: Img-CLASS Prefix}
\<..."CLASS="family...\><<<
p = gif_class[gif_flag];
if( p )
if( *p ){
(IGNORED) fprintf(cur_o_file, p,
font_tbl[cur_fnt].family_name); }
>>>
%--------------------------------------
\SubSection{s: Img-CLASS Size}
\<...gif font size...\><<<
p = gif_size[gif_flag];
if( p )
if( *p ){
(IGNORED) fprintf(cur_o_file, p,
font_tbl[cur_fnt].font_size); }
>>>
\SubSection{m: Img-CLASS Magnitude}
\<...gif font mag...\><<<
p = gif_mag[gif_flag];
if( p )
if( *p && (font_tbl[cur_fnt].mag != 100) ){
(IGNORED) fprintf(cur_o_file, p, font_tbl[cur_fnt].mag);
}
>>>
\SubSection{o: Img-CLASS Ord}
\<...gif sym ord...\><<<
p = gif_ord[gif_flag];
if( p )
if( *p ){
(IGNORED) fprintf(cur_o_file, p, ch);
}
>>>
%--------------------------------------
\SubSection{7: End Image}
\<..."/IMG>\><<<
p = gif_end[gif_flag];
if( p )
if( *p ){ print_f( p ); }
>>>
\SubSection{Htf-based Classes}
\<record string for htf class\><<<
{ int bad_str, m;
U_CHAR ch, *t[`<n spans`>], err_str[256];
bad_str=`<n spans-1`>; (IGNORED) strcpy((char *) err_str, (char *) p);
if( n>`<n spans+2`> ){
m = 100*( *p-'0' ) + 10*( *(p+1)-'0' )+ *(p+2)-'0';
if( (m>-1) && (m<256) ){
ch = *(p + 3); t[0]=p;
while( (*p = *(p+4)) != '\0' ){
if( ch == *p ){ *p = '\0';
if( bad_str-- > 0 ) t[`<n spans-1`> - bad_str] = p+1;
}
p++;
} }
if( !bad_str ){
if( m==0 ){ span_name_on = n>`<n spans+3`>; }
q = span_open[m]; span_open[m] = t[0];
span_name[m] = t[1]; span_size[m] = t[2];
span_mag[m] = t[3]; span_ord[m] = t[4];
span_ch[m] = t[5]; end_span[m] = t[6];
gif_id[m] = t[7];
if( not_notify ) {
store_bit_I( class_on, m );
not_notify = FALSE;
} else store_bit_Z( class_on, m );
}
}
if( bad_str ){ warn_i_str(37,err_str); }
}
>>>
\<don't report next htf class to lg\><<<
not_notify = TRUE;
>>>
\<vars\><<<
static BOOL not_notify = FALSE;
>>>
\<n spans\><<<
8>>>
\<n spans-1\><<<
7>>>
\<n spans+2\><<<
10>>>
\<n spans+3\><<<
11>>>
\<vars\><<<
static U_CHAR * span_name[256], * span_open[256], * span_size[256],
* span_mag[256], * span_ch[256], * end_span[256],
* span_ord[256], * gif_id[256];
static U_CHAR class_on[32];
>>>
\<main's init\><<<
{ int i; i=256; while( i-- ) {
span_name[i] = span_open[i] = span_size[i] =
span_mag[i] = span_ch[i] = end_span[i] =
span_ord[i] = gif_id[i] = NULL;
if( (i>0) && !(i%2) ) { store_bit_Z( class_on, i ); }
else { store_bit_I( class_on, i ); }
}
}
>>>
\<defines\><<<
#define gif_open span_open
#define gif_alt span_name
#define gif_class span_size
#define gif_size span_mag
#define gif_mag span_ord
#define gif_ord span_ch
#define gif_end end_span
>>>
%\<main's init\><<<
%{ int i; i=256; while( i-- ) { htf_class[i] = 0 ; } }
%>>>
\`'0': character without decoration; \`'1' gif
without decoration. Even numbers
(\`'...0') are reserved for ch with decoration, and odd numbers
(\`'...1') are for gifs with decoration.
A character template takes the form: +open + family-format +
size-format + mag-format + ord + close +after-font-info; The delimiter
can be +, or any other character.
\<put char(chr)\><<<
`<open output file`>
`<ensure for chr`>
if( span_on ){
`<open for chr`>
`<font-family of chr`>
`<font-size of chr`>
`<font-mag of chr`>
`<ord of chr`>
`<close for chr`>
}
put_char(chr);
if( span_on ){
`<end for chr`>
}
>>>
\<put string-char(chr)\><<<
`<ensure for chr`>
if( span_on ){
`<open for chr`>
`<font-family of chr`>
`<font-size of chr`>
`<font-mag of chr`>
`<ord of chr`>
`<close for chr`>
}
put_alt_ch(chr,ch_str_flag);
if( span_on ){
`<end for chr`>
}
>>>
\<ensure for chr\><<<
if( gif_flag && !get_bit( class_on, gif_flag ) ) {
notify_class_info(gif_flag);
store_bit_I( class_on, gif_flag );
}
>>>
\<open for chr\><<<
if( span_open[gif_flag] )
if( *span_open[gif_flag] ){
print_f( span_open[gif_flag] );
}
>>>
\<end for chr\><<<
if( end_span[gif_flag] )
if( *end_span[gif_flag] ){
print_f( end_span[gif_flag] );
}
>>>
\<close for chr\><<<
if( span_ch[gif_flag] )
if( *span_ch[gif_flag] ){
print_f( span_ch[gif_flag] );
}
>>>
\<font-family of chr\><<<
if( span_name[gif_flag] )
if( *span_name[gif_flag] ){
(IGNORED) fprintf(cur_o_file, span_name[gif_flag],
font_tbl[cur_fnt].family_name);
}
>>>
\<font-size of chr\><<<
if( span_size[gif_flag] )
if( *span_size[gif_flag] ){
(IGNORED) fprintf(cur_o_file, span_size[gif_flag],
font_tbl[cur_fnt].font_size);
}
>>>
\<font-mag of chr\><<<
if( span_mag[gif_flag] )
if( *span_mag[gif_flag] ){
(IGNORED) fprintf(cur_o_file, span_mag[gif_flag],
font_tbl[cur_fnt].mag);
}
>>>
\<ord of chr\><<<
if( span_ord[gif_flag] )
if( *span_ord[gif_flag] ){
(IGNORED) fprintf(cur_o_file, span_ord[gif_flag], chr);
}
>>>
\SubSection{Font Properties}
\<insert font size\><<<
int f;
f = 0; while( *p ){ f = 10*f + *(p++) - '0'; }
`<open output file`>
(IGNORED) fprintf(cur_o_file, "%d",
(font_tbl[cur_fnt].scale / base_font_size - 100) * f / 100 +100
);
>>>
\<insert css name of ch\><<<
`<open output file`>
(IGNORED) fprintf(cur_o_file, "%s", font_tbl[cur_fnt].name);
if( font_tbl[cur_fnt].mag != 100 ){
(IGNORED) fprintf(cur_o_file,"_%d", font_tbl[cur_fnt].mag);
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Chapter{Cascade Style Sheets}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%
\Section{Command Line}
%%%%%%%%%%%%%%%%%%
\<get cascade style sheet data\><<<
struct css_ext_rec * css = m_alloc(struct css_ext_rec, 1);;
css->name = p + 2;
css->next = css_ext;
css_ext = css;
>>>
\<post scan comm ln args\><<<
if( css_ext == (struct css_ext_rec *) 0 ){
struct css_ext_rec * css = m_alloc(struct css_ext_rec, 1);;
css->name = css_default;
css->next = css_ext;
css_ext = css;
}
>>>
\<main's vars\><<<
static struct css_ext_rec * css_ext = (struct css_ext_rec *) 0;
static char css_default[] = "4cs";
>>>
\<types\><<<
struct css_ext_rec{ char* name;
struct css_ext_rec *next; };
>>>
%%%%%%%%%%%%%%%%%%
\Section{Retrieve Data}
%%%%%%%%%%%%%%%%%%
\<get css entries\><<<
{ static struct css_ext_rec * search_css_ext;
for( search_css_ext = css_ext;
search_css_ext != (struct css_ext_rec *) 0;
search_css_ext = search_css_ext->next ){
int css_name_n = (int) strlen((char *) new_font.name );
char * css_file_name = m_alloc(char, css_name_n + 1);
(IGNORED) strcpy((char *) css_file_name, (char *) new_font.name);
for( ; css_name_n; css_name_n-- ){
FILE* file;
css_file_name[css_name_n] = '\0';
`<file = .css file`>
}
free((void *) css_file_name);
} }
>>>
\<file = .css file\><<<
{ U_CHAR name[256];
(IGNORED) sprintf(name, "%s.%s", css_file_name,
search_css_ext->name);
`<search file in htf locations`>
if( file != NULL ){
`<write css file to lg file`>
(IGNORED) fclose(file); break;
}
}
>>>
%%%%%%%%%%%%%
\Section{Copy CSS File}
%%%%%%%%%%%%%
\<write css file to lg file\><<<
`<check visited`>
if( !is_visited ){
`<mark visited`>
while( 1 ){
int ch;
do{
ch = (int) getc(file);
} while ( (ch == ' ') || (ch == '\n') || (ch == '\t') );
if( ch == EOF ){ break; }
do{
(void) putc( ch, log_file );
ch = (int) getc(file);
} while ( (ch != '\n') && (ch != EOF) );
(void) putc( '\n', log_file );
if( ch == EOF ){ break; }
} }
>>>
%%%%%%%%%%%%%
\Section{Duplicates Avoidance}
%%%%%%%%%%%%%
\<scan fonts vars\><<<
struct visited_file_rec * visited_file =
(struct visited_file_rec *) 0;
>>>
\<end loading fonts\><<<
while( visited_file != (struct visited_file_rec *) 0 ){
struct visited_file_rec * v = visited_file;
visited_file = visited_file->next;
free((void *) v->name);
free((void *) v);
}
>>>
\<mark visited\><<<
struct visited_file_rec * v = m_alloc(struct visited_file_rec, 1);
v->name = m_alloc(char, (int) strlen((char *) name ) + 1 );
(IGNORED) strcpy((char *) v->name, (char *) name);
v->next = visited_file;
visited_file = v;
>>>
\<check visited\><<<
BOOL is_visited = FALSE;
struct visited_file_rec * v = visited_file;
while( v != (struct visited_file_rec *) 0 ){
if( eq_str(v->name, name) ){
is_visited = TRUE; break;
}
v = v->next;
}
>>>
\<types\><<<
struct visited_file_rec{ char *name;
struct visited_file_rec *next; };
>>>
%%%%%%%%%%%%%%%%%%%%%%
\Chapter{Accents}
%%%%%%%%%%%%%%%%%%%%%%
\Section{Htf Font Info}
Each htf font has between 2 to 4 fields per entry. The first field is
the textual representation, the second is the textual and pictorial
flags and decorations, the third is an the accent code, and the fourth
is a pointer into an accented code table (tex4ht.htf).
font.htf:
\Verbatim
'text-rep' 'text/pic-deco' 'accent-code' 'pointer to tex4ht.htf'
\EndVerbatim
The accent code uses universaly agreed upon codes for accent symbols.
For instance, 18 for \'=\`=, and 94 for \'=\^=. 0 signify no pointer.
The accented code uses universaly agreed upon codes for accented
symbols. We assume a unicode code-based system, but deviate from
it when it is convinient.
tex4ht.c makes move the third and fourt entries of each font into arrays,
and puts into the entries index-pointers to the array. By doing so, we can
allocate just one byte to an entry.
\Draw \ArrowHeads(1)
\DrawRect(80,-150)
\Move(5,-20)
{\Do(1,10){\Move(0,-10)
{ \PaintRect(10,3)
\Move(20,0) \PaintRect(10,3)
\Move(20,0) \PaintRect(10,3)
\Move(20,0) \PaintRect(10,3)}}
}
\PaintRect(10,3)
\Move(20,0) \PaintRect(10,3)
\Move(20,0) \PaintRect(10,3)\FcNode(a)
\Move(20,0) \PaintRect(10,3)\FcNode(a2)
\MoveTo(100,-140) {\Move(5,10) \FcNode(b) \ArrowHeads(1) \Edge(a,b)}
\Do(1,4){\DrawRect(20,10) \Move(20,0)}
\MoveTo(100,-100)
{\Move(5,10) \FcNode(b) \ArrowHeads(1) \Edge(a2,b)}
\Do(1,4){\DrawRect(20,10) \Move(20,0)}
\MoveTo(130,-30) {\Move(-5,-5) \FcNode(b)
\Move(15,0) \Text(--A--)
\Move(35,0) \Text(--\string^--)
{\Move(15,25) \Text(--\^A--)}
\Move(35,0) \Text(--\string^\string^--)
{\Move(15,25) \Text(--$\hat {\hat A}$--)}
}
\Do(1,4){\DrawRect(20,10) \Move(0,-10)}
\MoveTo(150,-35)
{\Move(-20,0)\FcNode(a) \Move(-20,-60) \FcNode(b) \VHEdge(b,a)}
\Do(1,4){\FcNode(a) \Move(15,0) \FcNode(b)
{\Edge(a,b)} \Move(0,-5) \DrawRect(20,10)
{\Move(10,10)\FcNode(a) \Move(0,15) \FcNode(b) {\Edge(a,b)}
\DrawRect(20,10)}
\Move(20,5)
}
\EndDraw
The accented code matrix tex4ht.htf is the closest file to the font on
the directory path from the root. Different paths may employ a
different tex4ht.htf accent tables. An
(accent-code,accented-code) pair maps to accented-code in the table,
or the maping is undefined. Each accented-code in the matrix hold
textual representation.
\<entries for html chars in font-tbl\><<<
unsigned U_CHAR *accent, *accented;
unsigned int *accent_array, *accented_array, accent_N, accented_N;
>>>
\<initiate htf accents\><<<
new_font.accent = m_alloc(unsigned char, n_gif );
new_font.accented = m_alloc(unsigned char, n_gif );
new_font.accent_array = (unsigned int *) 0;
new_font.accented_array = (unsigned int *) 0;
new_font.accent_N = new_font.accented_N = 0;
for( i=n_gif; i--; ) {
new_font.accent[i] = new_font.accented[i] = 0;
}
>>>
\<scan htf accent\><<<
ch1 = 0;
while( ((ch = (int) get_html_ch(file)) != del) ){
if( (ch < '0') || (ch > '9') ){ warn_i_int(48,i); break; }
ch1 = ch1 * 10 + ch - '0'; }
new_font.accent_array = new_font.accent_N++?
(unsigned int *) r_alloc((void *) new_font.accent_array,
(size_t) (new_font.accent_N * sizeof(unsigned int)))
: m_alloc(unsigned int, 1);
new_font.accent_array[new_font.accent_N - 1] = ch1;
new_font.accent[i] = new_font.accent_N;
>>>
\<scan htf accented\><<<
ch1 = 0;
while( ((ch = (int) get_html_ch(file)) != del) ){
if( (ch < '0') || (ch > '9') ){ warn_i_int(48,i); break; }
ch1 = ch1 * 10 + ch - '0'; }
new_font.accented_array = new_font.accented_N++?
(unsigned int *) r_alloc((void *) new_font.accented_array,
(size_t) (new_font.accented_N * sizeof(unsigned int)))
: m_alloc(unsigned int, 1);
new_font.accented_array[new_font.accented_N - 1] = ch1;
new_font.accented[i] = new_font.accented_N;
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Integrated accents}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Remote Accents}
%%%%%%%%%%%%%%%%%%%%%%%%
If the accent character is for text (\''\accent'), we'll be looking
for the next character to be the accented character. If the accent
character is for math (\''\mathaccent'), we'll be looking for the next
group of characters (i.e., subformula) to be the accented. If the
subformula consists of a single character, we probably should assume
an accented character instead of an accented subformula.
\List{*}
\item \''\special{t4ht*\string^t}'---requests to consume the
next character, tailored to be placed just before an \''\accent'. In addition,
if a template exists for text accents, the family-name and
character-code of the accent are printed before the accented
parameter together with the first three parts of the template. In such
a case, the fourth part of the template is printed after the content.
\item
\''\special{t4ht*\string^t#1 #2 #1 #3 #1 #4 #1 #5 #1 #6}'---provides
template consisting of four parts, where \''#1' is assumed to
be a distinguished character separating the components. If the
separators appears only twice or three times, the template is
annihilated.
\item \''\special{t4ht*\string^m...}'---Variants of the above cases
for \''\mathaccent'. However, the fourth part of the template is
ignored, and assumed to be requested througth a
\''Send{EndGroup}{1}{...}' (i.e., \''\special{t4ht\string~>1...}').
\EndList
\SubSection{Open Accent}
\<accent specials\><<<
special_n--;
switch ( get_char() ){
case 't': { if( special_n ){
`<text accent template`>
} else { `<text accent`> }
break;
}
case 'm': { if( special_n ){
`<math accent template`>
} else { `<math accent`> }
break;
}
case 'a': { `<accented template`>
break;
}
case 'i': { `<accenting template`>
break;
}
default: { `<consume unused specials`> }
}
>>>
A accent is a character from a given font. Given a content to be
accented, the text/math accent marks it \`'#1 font #2 char-no #3
content #4' according to the accent/mathaccent template. Each
character in the content is marked by \`'#1 font #2 char-no #3
character #4' according to the accented template.
Text accent can be on empty content or on character, and the accent
itself is empty if it consists of a character code greater than 127.
Math accent is
placed on the center of a formula.
\<text accent\><<<
needs_accent_sym = TRUE * 2;
>>>
\<math accent\><<<
needs_accent_sym = TRUE;
>>>
\<accented = true\><<<
needs_accented_sym++;
>>>
The \''\special' tells tex4ht where the accent starts, and tex4ht
consumes the following character for a accent. At that place, tex4ht
also place the opening tag of the element.
\<pre accent symbol\><<<
if( in_accenting ){
`<post accent symbol`>
} else if( `<is accent char?`> ){
if( needs_accent_sym ){
BOOL needs_end_accent;
needs_end_accent = (needs_accent_sym == 2 * TRUE);
if( needs_end_accent && t_accent_template ){
(IGNORED) fprintf(cur_o_file, "%s%s%s%d%s%d%s",
t_accent_first, font_tbl[cur_fnt].family_name,
t_accent_second, ch, t_accent_third,
font_tbl[cur_fnt].accent[ch]?
font_tbl[cur_fnt].accent_array[font_tbl[cur_fnt].accent[ch]-1]
: 0,
t_accent_fourth);
} else if( m_accent_template ){
(IGNORED) fprintf(cur_o_file, "%s%s%s%d%s%d%s",
m_accent_first, font_tbl[cur_fnt].family_name,
m_accent_second, ch, m_accent_third,
font_tbl[cur_fnt].accent[ch]?
font_tbl[cur_fnt].accent_array[font_tbl[cur_fnt].accent[ch]-1]
: 0,
m_accent_fourth);
}
if( i_accent_template ){
(IGNORED) fprintf(cur_o_file, "%s", i_accent_first); }
in_accenting = TRUE;
}
}
>>>
\<post accent symbol\><<<
long int width;
if( i_accent_template ){
(IGNORED) fprintf(cur_o_file, "%s", i_accent_second); }
needs_end_accent = (needs_accent_sym == 2 * TRUE);
if( needs_end_accent && t_accent_template )
{ `<accented = true`> }
else if( m_accent_template )
{ `<accented = true`> stack[stack_n-1].accented = TRUE; }
needs_accent_sym = FALSE;
width = (INTEGER)( `<(double) char_width( ch )`> );
if( needs_end_accent ){ needs_end_accent = x_val + 9 * width / 10; }
in_accenting = FALSE;
>>>
\<main's vars\><<<
BOOL in_accenting;
>>>
\<main's init\><<<
in_accenting = FALSE;
>>>
The following needs to be fixed for cases that ch is greater than 127.
\<is accent char?\><<<
needs_accent_sym && (ch < 128)
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Closure for Text Accent}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<end text accent\><<<
if( needs_end_accent && t_accent_template ){
`<end accented span`>
(IGNORED) fprintf(cur_o_file, "%s", t_accent_fifth);
needs_end_accent = FALSE; `<exit text accented`>
}
>>>
The following in needed for cases of text-accents on empty content,
immeddiately followed by a character (e.g., \`'\^{}Xx').
\<set begin accents\><<<
if( x_val > needs_end_accent ){
`<end text accent`>
}
>>>
The following takes care of closing a text accent after a character.
\<set end accents\><<<
`<end text accent`>
>>>
%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Tailoring}
%%%%%%%%%%%%%%%%%%%%%%
before-family-name, between-family-char-number, after char-number
\<text accent template\><<<
(IGNORED) get_open_accent(&t_accent_template,
&t_accent_first, &t_accent_second,
&t_accent_third, &t_accent_fourth,
&t_accent_fifth, &special_n);
>>>
\<math accent template\><<<
(IGNORED) get_open_accent(&m_accent_template,
&m_accent_first, &m_accent_second,
&m_accent_third, &m_accent_fourth,
&m_accent_fifth, &special_n);
>>>
\<vars\><<<
static BOOL needs_accent_sym = FALSE, needs_end_accent = FALSE;
static char * t_accent_template = (char *) 0,
* t_accent_first, * t_accent_second,
* t_accent_third, * t_accent_fourth, * t_accent_fifth,
* m_accent_template = (char *) 0,
* m_accent_first, * m_accent_second,
* m_accent_third, * m_accent_fourth, * m_accent_fifth;
>>>
\<header functions\><<<
static void get_open_accent(
ARG_VII(char**, char**, char**, char**, char**, char**, long int*));
>>>
\<functions\><<<
`[
static void get_open_accent(all,first,second,third,fourth,fifth,n)
char **all`;
char **first`;
char **second`;
char **third`;
char **fourth`;
char **fifth`;
long int *n
;{ char *p, *q;
int i;
if( *all ){ free((void *) *all); }
*all = p = get_str( (int) *n ); *n=0;
i = 2;
*first = q = p + 1;
while ( TRUE ){
if( *q == *p ){
*q = '\0';
switch( i ){
case 2:{ *second = q+1; break; }
case 3:{ *third = q+1; break; }
case 4:{ *fourth = q+1; break; }
case 5:{ *fifth = q+1; break; }
}
if( i++ == 5 ){ break; }
} else if( !*q ){
free((void *) *all); *all = (char *) 0; break;
}
q++;
} }
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Mark Accented Content}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<mark accented ch\><<<
if( a_accent_template && needs_accented_sym ){
(IGNORED) fprintf(cur_o_file, "%s%s%s%d%s%d%s",
a_accent_first, font_tbl[cur_fnt].family_name,
a_accent_second, ch, a_accent_third,
font_tbl[cur_fnt].accented[ch]?
font_tbl[cur_fnt].accented_array[font_tbl[cur_fnt].accented[ch]-1]
: 0,
a_accent_fourth);
}
>>>
\<end mark accented ch\><<<
if( a_accent_template && needs_accented_sym ){
(IGNORED) fprintf(cur_o_file, "%s", a_accent_fifth);
}
>>>
Accents can be embedde, for instance, \`'\hat{a\hat b}'. Hence,
\''needs_accented_sym' is treated as a number.
The math accented end at the end of the group, sent there by
\`'\special{t4ht~>...}' in the sty file.
\<exit math accented\><<<
needs_accented_sym--;
>>>
\<exit text accented\><<<
needs_accented_sym--;
>>>
\<accented template\><<<
(IGNORED) get_open_accent(&a_accent_template,
&a_accent_first, &a_accent_second,
&a_accent_third, &a_accent_fourth,
&a_accent_fifth, &special_n);
>>>
\<vars\><<<
static BOOL needs_accented_sym = 0;
static char * a_accent_template = (char *) 0,
* a_accent_first, * a_accent_second,
* a_accent_third, * a_accent_fourth, * a_accent_fifth;
>>>
\<accenting template\><<<
(IGNORED) get_open_accent(&i_accent_template,
&i_accent_first, &i_accent_second,
&i_accent_third, &i_accent_fourth,
&i_accent_fifth, &special_n);
>>>
\<vars\><<<
static char * i_accent_template = (char *) 0,
* i_accent_first, * i_accent_second,
* i_accent_third, * i_accent_fourth, * i_accent_fifth;
>>>
\`'* i_accent_second,
* i_accent_third, * i_accent_fourth;' are dummy
provided for consistency.
\<BOOl stack accented\><<<
accented
>>>
\<end math accented\><<<
if( stack[stack_n].accented ){
`<exit math accented`>
stack[stack_n].accented=FALSE;
}
>>>
\<init end math accented\><<<
stack[stack_n].accented = FALSE;
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Chapter{Math Classes for Symbols}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Tex assignes class numbers 0--7 to the atoms of math formulas:
0--ordinary symbol, 1--large operator, 2--binary operation,
3--relational operation, 4--math delimiter, 5--right delimiter,
6--punctuation mark, and 7--adjustable.
We use classes 7, 8, 9 as extra free ones.
\Section{Scanning Requests}
\`'{\everypar{}\special{t4ht^i}$sym... i=0, 1, 2, 3, 4,..., a, b, ...., ~
$\special{t4ht^}}'
A right delimiter immediately after a math openimiter ammuses
a matching pair.
\List{*}
\item \`'\special{t4ht^i}' - on class i-'0' (i='0', '1', ..., \`'~'
\item \`'\special{t4ht^-}'--pause from processing delimiters
\item \`'\special{t4ht^+}'--continue processing delimiters
\item
\`'\special{t4ht^}'--off, if in scan-mode of class values.
on/off show-class, otherwise
\item
\`'\special{t4ht^i*...*...}'---Configure delimiters for class i
\item \`'\special{t4ht^ix}'--put delimiters of class i on next group.
If x=\`'>' ignore delimiters within the group.
\item \`'\special{t4ht^i)*...*...}'--put the specified delimiters of
class on next group, instead of the default ones.
Ignore delimiters within the group.
\item \`'\special{t4ht^i(*...*...}'--put the specified delimiters of
class on next group, instead of the default ones.
Don't ignore delimiters within the group.
\EndList
\<classes for math symbols\><<<
switch( special_n ){
case 0:{ if( math_class_on ){
open_del = 256; pause_class = ignore_subclass_del = 0;
math_class_on = FALSE; `<recall intrusive flags`>
} else { show_class = !show_class; }
break;
}
case 1:{ `<save intrusive flags`>
special_n--;
if( (math_class = scan_class(1)) == `<num of math classes: 79`> )
{ math_class = 0; }
else math_class_on = TRUE;
break;
}
case 2:{ `<class delimiters on group`>
break; }
default:{ `<set config class dels`> }
}
>>>
\<vars\><<<
static BOOL math_class_on = FALSE, show_class = FALSE;
static int open_del = 256, math_class, pause_class, ignore_subclass_del;
>>>
\`'open_del' is used by close delimiter, if they are immediate
neighbors.
\<vars\><<<
static int sv_group_dvi, sv_trace_dvi_C, sv_in_trace_char, sv_span_on,
sv_in_span_ch;
>>>
\<save intrusive flags\><<<
sv_group_dvi = group_dvi;
sv_trace_dvi_C = trace_dvi_C;
sv_in_trace_char = in_trace_char;
sv_span_on = span_on;
sv_in_span_ch = in_span_ch;
>>>
\<recall intrusive flags\><<<
group_dvi = sv_group_dvi;
trace_dvi_C = sv_trace_dvi_C;
in_trace_char = sv_in_trace_char;
span_on = sv_span_on;
in_span_ch = sv_in_span_ch;
>>>
\<header functions\><<<
static int scan_class( ARG_I(int) );
>>>
\<functions\><<<
`[
static int scan_class( flag )
int flag
;{ int math_class;
math_class = get_char();
if( (math_class >= '0' )
&& (math_class < '0' + `<num of math classes: 79`>) ){
{ math_class -= '0'; }
} else {
if( flag== 1 ) {
switch( math_class ){
case '-': { math_class = `<num of math classes: 79`>;
pause_class++; break; }
case '+': { math_class = `<num of math classes: 79`>;
pause_class--; break; }
default: { math_class = 0; }
} } else if( flag== 2 ) {
switch( math_class ){
case `<ignore sub dels ch`>: {
math_class = `<ignore sub dels val`>; break; }
case `<end ignore sub dels ch`>:
{ math_class = `<end ignore sub dels val`>; break; }
default: { math_class = 0; }
}
} else { math_class = 0; }
}
return math_class;
}
>>>
\Section{Setting Requests}
\<header functions\><<<
static INTEGER set_ch_class( ARG_I(int) );
>>>
\<functions\><<<
`[
static INTEGER set_ch_class(ch) int ch
;{ int r_ch;
r_ch = ch - font_tbl[cur_fnt].char_f;
if( math_class == `<math close`> ){
store_bit_I( font_tbl[cur_fnt].math_closing, r_ch );
*(font_tbl[cur_fnt].math + r_ch) =
(char) ((open_del == 256)? ch : open_del);
} else {
store_bit_Z( font_tbl[cur_fnt].math_closing, r_ch );
*(font_tbl[cur_fnt].math + r_ch) = math_class;
}
open_del = ( math_class == `<math open`> )? ch : 256;
`<return char width`>
}
>>>
\<set-ch-class(ch-1)\><<<
set_ch_class(ch_1)
>>>
\<set config class dels\><<<
{ U_CHAR str[256], *p, ch, **q;
math_class = scan_class(2); ch = get_char();
special_n -= 2; p = str;
while( special_n-- > 0 ){
if( (*(p++) = get_char()) == ch ){ p--; break; }
}
*p = '\0';
q = (math_class > `<math classes 0..78`>)? &(`<push class del := ...`>)
: &(open_class[math_class]);
*q = (char *) r_alloc((void *) open_class[math_class],
1 + (size_t) strlen((char *) str));
(IGNORED) strcpy((char *) *q, (char *) str);
q = (math_class > `<math classes 0..78`>) ? &(`<pop class del := ...`>)
: &(close_class[math_class]);
p = *q = (char *) r_alloc((void *) *q, 1 + (size_t) special_n);
while( special_n-- > 0 ){ *(p++) = get_char(); }
*p = '\0';
if( math_class > `<math classes 0..78`>){ `<next delimiters on group`> }
}
>>>
When \`'math_class > max math class' is true, the delimiters are
placed on the next group. In such a case a math class of \`')' also
requests that no subdelimiter will be placed there.
\<header functions\><<<
static int math_class_of( ARG_II(int,int) );
>>>
\<functions\><<<
`[
static int math_class_of(ch,cur_fnt) int ch`; int cur_fnt
;{ int math_class;
math_class = ch - font_tbl[cur_fnt].char_f;
return ((get_bit( font_tbl[cur_fnt].math_closing, math_class))?
`<math close`> : *( math_class + font_tbl[cur_fnt].math));
}
>>>
\Section{Memory and Initialization}
\<vars\><<<
static U_CHAR *open_class[`<mems for math classes (79 + 2 )`>],
*close_class[`<mems for math classes (79 + 2 )`>];
>>>
There are 79 characters in the range between the digit `0' and
the character \`'~'.
\<math classes 0..78\><<<
78
>>>
\<num of math classes: 79\><<<
79
>>>
The following should be characters below the digit `0'
\<ignore sub dels ch\><<<
')'
>>>
\<end ignore sub dels ch\><<<
'('
>>>
\<ignore sub dels val\><<<
(`<num of math classes: 79`> + 1)
>>>
\<end ignore sub dels val\><<<
(`<num of math classes: 79`> + 2)
>>>
\<mems for math classes (79 + 2 )\><<<
82 `%one extra is needed?`%
>>>
\<main's init\><<<
for( math_class=0; math_class<`<num of math classes: 79`>; math_class++ ){
open_class[math_class] = m_alloc(char, 1);
close_class[math_class] = m_alloc(char, 1);
*(open_class[math_class]) = *(close_class[math_class]) = '\0';
}
>>>
\<entries for math classes\><<<
char *math, *math_closing;
>>>
\<memory for math classes\><<<
new_font.math_closing = m_alloc(char, n_gif_bytes );
new_font.math = m_alloc(char, n_gif );
>>>
\<init math-closing\><<<
new_font.math_closing[i] =
>>>
\<init math\><<<
new_font.math[i] =
>>>
\<merge math entries\><<<
free((void *) new_font.math_closing );
new_font.math_closing = font_tbl[ k ].math_closing;
free((void *) new_font.math );
new_font.math = font_tbl[ k ].math;
>>>
\Section{Delimiters Over Next Group}
\SubSection{Requests}
\`'\special{t4ht^ix}'--put delimiters of class i on next group/token. If
{\tt x=`)'} ignore delimiters within the group.
\<class delimiters on group\><<<
special_n -= 2; math_class = scan_class(0);
stack[stack_n+1].ignore_subclass_del =
(`<ignore sub dels ch`> == get_char());
stack[stack_n+1].active_class_del = TRUE;
stack[stack_n+1].temp_class_del = FALSE;
stack[stack_n+1].no_left_del = TRUE;
stack[stack_n+1].class_open = open_class[math_class];
stack[stack_n+1].class_close = close_class[math_class];
>>>
\`'\special{t4ht^)x*...*...}'--put the specified delimiters of
class on next group, instead of the default ones.
If {\tt x=`)'}, ignore delimiters within the group.
\<next delimiters on group\><<<
stack[stack_n+1].ignore_subclass_del =
(math_class == `<ignore sub dels val`>);
stack[stack_n+1].temp_class_del = TRUE;
stack[stack_n+1].active_class_del = TRUE;
>>>
\<pop class del := ...\><<<
stack[stack_n+1].temp_class_close
>>>
\<push class del := ...\><<<
stack[stack_n+1].temp_class_open
>>>
\SubSection{Insertions}
\<add push class del\><<<
if( stack[stack_n].active_class_del ){
if( show_class && !pause_class && !ignore_subclass_del ){
`<print open del of group class`>
}
ignore_subclass_del = ignore_subclass_del
+ stack[stack_n].ignore_subclass_del;
stack[stack_n+1].no_left_del= FALSE;
}
>>>
\<add pop class del\><<<
if( stack[stack_n].active_class_del ){
ignore_subclass_del = ignore_subclass_del
- stack[stack_n].ignore_subclass_del;
if( show_class && !pause_class && !ignore_subclass_del ){
`<print right del of group class`>
}
stack[stack_n].active_class_del = FALSE;
}
>>>
\SubSection{Memory and Initialization}
\<struct stack_entry\><<<
char *class_open, *class_close,
*temp_class_open, *temp_class_close;
BOOL temp_class_del, ignore_subclass_del, active_class_del,
no_left_del, sv_no_left_del;
>>>
The math class can be on a group or a token. Upon reaching the
\''\special', the \''no_left_del' is set to false. Upon reaching to
the start of the next group (not token), the flag is set to true.
\<initialized entries for grouping stack\><<<
stack[i].class_open = stack[i].class_close
= (char *) 0;
stack[i].temp_class_open = m_alloc(char, 1 );
stack[i].temp_class_close = m_alloc(char, 1 );
stack[i].ignore_subclass_del = stack[i].temp_class_del
= stack[i].active_class_del
= FALSE;
>>>
\Section{Delimiters Over Next Character}
\<left class delNO\><<<
if( show_class && !pause_class && !ignore_subclass_del){
if(stack[stack_n+1].no_left_del && stack[stack_n+1].active_class_del ){
`<open del of group class on ch`>
} else {
math_class = math_class_of( ch, cur_fnt );
(IGNORED) print_f( open_class[math_class]);
} }
>>>
\<left class del\><<<
if( show_class && !pause_class && !ignore_subclass_del){
if( !stack[stack_n].no_left_del && stack[stack_n+1].active_class_del ){
`<open del of group class on ch`>
if( !stack[stack_n+1].ignore_subclass_del ){
`<left del of class on ch`>
}
} else {
`<left del of class on ch`>
} }
>>>
\<left del of class on ch\><<<
math_class = math_class_of( ch, cur_fnt );
(IGNORED) print_f( open_class[math_class]);
>>>
The \`'stack[stack_n].no_left_del' is true if we have a request for
math delimiters on next group/token, and no left delimiter had
been placed on a group.
\<right class delNO\><<<
if( show_class && !pause_class && !ignore_subclass_del ){
if(stack[stack_n+1].no_left_del && stack[stack_n+1].active_class_del ){
`<right del of group class on ch`>
stack[stack_n+1].active_class_del = FALSE;
} else {
(IGNORED) print_f( close_class[math_class]);
}
}
>>>
\<right class del\><<<
if( show_class && !pause_class && !ignore_subclass_del ){
if( !stack[stack_n].no_left_del && stack[stack_n+1].active_class_del ){
if( !stack[stack_n+1].ignore_subclass_del ){
`<right del of class on ch`>
}
`<right del of group class on ch`>
stack[stack_n+1].active_class_del = FALSE;
} else {
`<right del of class on ch`>
stack[stack_n+1].active_class_del = FALSE; `%%%%%%%%NEW%%%%`%
}
}
>>>
\<right del of class on ch\><<<
(IGNORED) print_f( close_class[math_class]);
>>>
\<print right del of group class\><<<
(IGNORED) print_f( (stack[stack_n].temp_class_del)?
stack[stack_n].temp_class_close
: stack[stack_n].class_close);
>>>
\<right del of group class on ch\><<<
(IGNORED) print_f( (stack[stack_n+1].temp_class_del)?
stack[stack_n+1].temp_class_close
: stack[stack_n+1].class_close);
>>>
\<print open del of group class\><<<
(IGNORED) print_f( (stack[stack_n].temp_class_del)?
stack[stack_n].temp_class_open
: stack[stack_n].class_open);
>>>
\<open del of group class on ch\><<<
(IGNORED) print_f( (stack[stack_n+1].temp_class_del)?
stack[stack_n+1].temp_class_open
: stack[stack_n+1].class_open);
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Chapter{Files and Directories}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Environment File (tex4ht.env)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\List{1}
\item Try the file given by the -e switch of tex4ht command.
\item Search the current directory for one (i.e., open "tex4ht.env").
\item Search the program's directory (dos/w32)
\item Home directory (\`'~` unix; \`'C:' dos/w32)
\item Compiled directory within ENVFILE
\EndList
First character is a flag character to be consistent with command line
options. Undefined flags signal comment lines.
Search along the path: command line file $\rightarrow$ work
directory $\rightarrow$ home directory $\rightarrow$ system file.
\<get .env directory from com ln\><<<
tex4ht_env_file = p+2;
>>>
% if( !access(p+2,F_OK) ) tex4ht_env_file = p+2;
% else warn_i_str(12,p+2);
\<main's vars\><<<
char* tex4ht_env_file = (char *) 0;
char* dos_env_file =
#if defined(__MSDOS__)
`<get dos env file`>;
#endif
#if !defined(__MSDOS__)
(char *) 0;
#endif
>>>
In dos we can't have file names consisted only of the extension
\`'.tex4ht', let alone the length of the extension.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{General}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<vars\><<<
static BOOL dump_env_search = FALSE;
>>>
\<trace env search\><<<
dump_env_search = TRUE;
>>>
\<find tex4ht.env\><<<
{ U_CHAR str[PATH_MAX], *TEX4HTENV;
`<env file from command switch`>
`<env file at work dir`>
if( !dot_file ){
`<TEX4HTENV env`>
}
`<env file at root dir`>
`<ENVFILE env`>
`<env from prog dir`>
`<kpathsea env file`>
if( !dot_file ) { bad_in_file(`<warn 1`>); } /* give up if no tex4ht.env */
}
>>>
\<env file from command switch\><<<
if( dump_env_search && tex4ht_env_file ){
(IGNORED) printf("-e: %s?\n", tex4ht_env_file);
}
dot_file = tex4ht_env_file?
f_open_pathed_filename( tex4ht_env_file, READ_TEXT_FLAGS ) : NULL;
>>>
\<env file at root dir\><<<
if( !dot_file ){
if( HOME_DIR ){
(IGNORED) sprintf(str,`<"s/tex4ht.env"`>, HOME_DIR);
if( dump_env_search ){ (IGNORED) printf("%s?\n", str); }
dot_file = f_open(str,READ_TEXT_FLAGS);
} }
#ifndef DOS_WIN32
if( !dot_file ){
if( HOME_DIR ){
(IGNORED) sprintf(str,"%s/.tex4ht", HOME_DIR);
if( dump_env_search ){ (IGNORED) printf("%s?\n", str); }
dot_file = f_open(str,READ_TEXT_FLAGS);
} }
#endif
#if defined(__MSDOS__)
if( !dot_file ){
if( dump_env_search ){ (IGNORED) printf("%s?\n", "C:/tex4ht.env"); }
dot_file = f_open("C:/tex4ht.env",READ_TEXT_FLAGS);
}
#endif
>>>
\<env from prog dir\><<<
if( !dot_file && dos_env_file){
if( dump_env_search ){ (IGNORED) printf("%s?\n", dos_env_file); }
dot_file = f_open( dos_env_file, READ_TEXT_FLAGS );
}
>>>
\<env file at work dir\><<<
if( !dot_file ){
if( dump_env_search ){ (IGNORED) printf("%s?\n", "tex4ht.env"); }
dot_file = f_open("tex4ht.env", READ_TEXT_FLAGS);
}
#ifndef DOS_WIN32
if( !dot_file ){
if( dump_env_search ){ (IGNORED) printf("%s?\n", ".tex4ht"); }
dot_file = f_open(".tex4ht", READ_TEXT_FLAGS);
if( dot_file ){
printf("(%s)\n", ".tex4ht");
} }
#endif
>>>
\<ENVFILE env\><<<
#ifdef ENVFILE
if( !dot_file ){
if( dump_env_search ){ (IGNORED) printf("ENVFILE: %s?\n", ENVFILE); }
dot_file = f_open_pathed_filename( ENVFILE,READ_TEXT_FLAGS);
}
#else
if( dump_env_search ){
(IGNORED) printf("tex4ht compiled without ENVFILE\n");
}
#endif
>>>
\<TEX4HTENV env\><<<
TEX4HTENV = getenv("TEX4HTENV");
if( TEX4HTENV ){
if( dump_env_search ){
(IGNORED) printf("TEX4HTENV: %s?\n", TEX4HTENV); }
dot_file = f_open_pathed_filename(TEX4HTENV,READ_TEXT_FLAGS);
} else {
if( dump_env_search ){
(IGNORED) printf("getenv(\"TEX4HTENV\")=\n");
} }
>>>
\<h-defines\><<<
#ifndef ENVFILE
#endif
>>>
\<"s/tex4ht.env"\><<<
#if defined(__DJGPP__)
is_forward_slash(HOME_DIR)? "%s/tex4ht.env" : "%s\\tex4ht.env"
#else
"%s/tex4ht.env"
#endif
>>>
\<warn 1\><<<
#ifdef DOS_WIN32
"tex4ht.env"
#else
"tex4ht.env | .tex4ht"
#endif
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{For Dos}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
One last remark about the source: I have implemented a routine that
changes the functionality of searching for tex4ht.env a bit. DOS
organises programs differently than Unix; it is convention to place the
program and all related files, including configuration files, in a
dedicated directory. In addition, DOS (being single-user) doesn't have
home directories. Knowing this, the best way to find the tex4ht.env file
is:
\List{1}
\item Search the current directory for one (i.e., open "tex4ht.env").
\item Search the program's directory
\item Give up (no point in checking the root directory).
\EndList
Under DOS, argv[0] usually includes the full path to the program -
even if it wasn't typed in. This can be used to find the program's own
directory, and hence the configuration file. I've implemented this
already as follows:
\<get dos env file\><<<
get_env_dir(argv[0])
>>>
\<functions\><<<
#if defined(__MSDOS__)
`<get tex4ht.env in dos`>
#endif
>>>
\<get tex4ht.env in dos\><<<
`[
static char *get_env_dir(progname)
U_CHAR *progname
;{ int i;
U_CHAR *p;
if(! progname || ! *progname) return NULL; `% Safety `%
i = (int) strlen((char *) progname);
while( (progname[--i] != (int) dir_path_slash(progname) )
&& (i > 0) ) ; `%Search for dir`%
if(i == 0) return NULL; `%Not found? Give up`%
p = (char *) malloc(i+12);
if(p == NULL) return NULL; `%Space for new extention after dot`%
strncpy(p, progname, i+1); `%Copy up to slash`%
(IGNORED) strcpy((char *) &p[i+1], "tex4ht.env"); `%Append new extention`%
return p;
}
>>>
The 12 above is for accomodating \''tex4ht.env'.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Get Script}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Options
\List{}
\item{s}
Block of lines
starting with the same
character: s (set
format of
script commands), e.g., \`'s--- needs --- %%1.idv[%%2] ==> %%3 ---'
\item{b} Begin-of-characters/end-of-figures comment, e.g., \`'b--- characters ---'
\item{g} Gif extension, e.g., \`'g.gif'
\item{a} Font alias, e.g., \`'acmtex cmtt'
\item{t} Directory of tfm fonts, e.g., \`'t/n/candy/0/tex/teTeX/texmf/fonts/tfm/!'
\item{i} Directory of htf fonts, e.g., \`'i/n/gold/5/gurari/tex4ht.dir/
i/n/gold/5/gurari/tex4ht.dir/ht-fonts/iso88591/!'.
\EndList
Priority (the easier to change, the higher in priority).
\List{disk}
\item Command line
\item Environment File
\item Hard coded in program
\EndList
\<header functions\><<<
static char* get_script( ARG_III(char *, const U_CHAR *, int) );
>>>
\<functions\><<<
`[
static char* get_script(name, inln,x)
char * name`;
const U_CHAR * inln`;
int x
;{
if( !name )
{ U_CHAR str[256], *ch;
(IGNORED) fseek(dot_file, 0L, `<abs file addr`>);
if( search_dot_file( x ) ){
`<read x-script from dot file`>
} else {(IGNORED) strcpy((char *) str, inln); }
ch = m_alloc(char, (int) strlen((char *) str)+2);
(IGNORED) strcpy((char *) ch, (char *) str);
return ch;
}else return name;
}
>>>
We allocate extra character for possible addition of \`'%'
at end of string.
\<read x-script from dot file\><<<
ch = str; str[254] = '\0';
do{ int int_ch;
while((*(ch++) = (char)
(int_ch = (int) getc(dot_file))
) != '\n'){
if( int_ch == EOF ){ *(ch-1)='\n'; break; }
if( str[254] ){ warn_i_int(33, x); break; }
}
}while( (int) getc(dot_file) == x );
*ch = '\0';
>>>
The \`'if( *(ch-1) == EOF ){...}'
is for the case that the last line not terminates with a return.
%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Search Env File}
%%%%%%%%%%%%%%%%%%%%%%%%%
\<header functions\><<<
static BOOL search_dot_file( ARG_I( int) );
>>>
\<functions\><<<
`[
static BOOL search_dot_file( ch )
int ch
;{ int chr;
while( TRUE ){
chr = getc(dot_file);
if( chr == ch ){ return TRUE; }
if( chr == '<' ) {
`<skip non -c env segments`>
continue;
}
if( chr == '\n' ){ continue; }
do
if( chr == EOF ) return FALSE;
while( (chr = getc(dot_file)) != '\n' );
} }
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% *p,
\<skip non -c env segments\><<<
U_CHAR match[256];
int i;
for( i = 0; (chr != '\n') && (chr != EOF ) ; i++){
chr = (int) getc(dot_file);
match[i] = (U_CHAR) chr;
}
match[i-1] = '\0';
if( match[0] != '/' ){
BOOL env_skip;
for( i = 0;
(match[i] != '>') && (match[i] != '\n') && (match[i] != EOF );
i++){}
if( match[i] == '>' ){ match[i] = '\0'; }
`<env-skip = skip segment match[1]`>
if( env_skip ){
`<skip env segment`>
} }
>>>
\<skip env segment\><<<
U_CHAR cur_block[90];
BOOL status;
(IGNORED) strcpy((char *) cur_block, (char *) match);
status = FALSE;
while( !status && (chr != EOF) ){
chr = 'x';
for( i = 0; (chr != '\n') && (chr != EOF ) ; i++){
chr = (int) getc(dot_file);
match[i] = (U_CHAR) chr;
}
match[i-1] = '\0';
for(i=0; match[i]!='\0'; i++){
if( match[i] == '>' ){ break; }
}
if( (match[0] == '<') && (match[1] == '/')
&& (match[i] == '>') ){
match[i]='\0';
status = eq_str(match+2, cur_block);
} else { status = FALSE; }
}
>>>
\<defines\><<<
struct env_c_rec{
char *option;
struct env_c_rec *next;
};
>>>
\<vars\><<<
static struct env_c_rec *envChoice
= (struct env_c_rec*) 0;
>>>
\<env blocks selector\><<<
struct env_c_rec *temp = (struct env_c_rec*)
m_alloc(struct env_c_rec, (int) 1);
temp->option = p+2;
temp->next = envChoice;
envChoice = temp;
>>>
\<env-skip = skip segment match[1]\><<<
if( envChoice == (struct env_c_rec*) 0 ){
env_skip = !eq_str(match, "default" );
} else {
struct env_c_rec *p;
env_skip = TRUE;
for( p=envChoice; p!=(struct env_c_rec*) 0 ; p = p->next ){
if( eq_str(match, p->option ) ){ env_skip = FALSE; }
} }
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Locating the TFM Fonts}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Flag `t' identifies font directories.
Command line (can be multiple) $\rightarrow$ work directory
$\rightarrow$ dot file $\rightarrow$ system directory.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Command Line Options}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\List{*}
\item Specifying multiple directories is possible; paths are separated
with ':' under Unix, ';' under DOS. (This isn't really important
perhaps, but it seems a nice feature).
Instead of concatenated directories we allow multiple ones.
\item If a path specification ends in a '!', it's subdirectories are
searched too, else only the specified dir is searched. (This is the
way many TeX installations seem to do it, too).
\EndList
\<get tfm font dir from com ln\><<<
com_dir(p); fontdir[fontdir_count++] = p+2;
>>>
The \`'access' function returns 0 if the path is fine; -1 for error.
\<vars\><<<
static U_CHAR *fontdir[MAXFDIRS];
static int fontdir_count = 0;
>>>
What the use of the following? Do we wants to make it h-defines.
\<defines\><<<
#ifndef MAXFDIRS
#define MAXFDIRS 100
#endif
>>>
Also, check how many directories and fonts are
used. \`'tex4ht.log' claims 55 fonts with 255 as limit.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{The Search}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\'' struct cache_font_rec *cur_cache_font; '?
\<search dirs for font\><<<
{ `% int i; `%
font_file = NULL;
(IGNORED) sprintf(file_name, "%s.tfm", new_font_name);
`<if kpathsea tfm files else`>
`<search font in cache dirs`>
if( !font_file ){ int i;
for( i = fontdir_count; i--; ){
if( (font_file = search_file_base(file_name, fontdir[i],
READ_BIN_FLAGS, tfm_dirs))
!= NULL ) break; }
}
if( !font_file ) font_file = f_open(file_name, READ_BIN_FLAGS);
if( !font_file && dot_file )
font_file = search_in_dot_file( 't', file_name,
READ_BIN_FLAGS, tfm_dirs);
`<search TFMDIR`>
`<endif kpathsea tfm files`>
}
>>>
KPATHSEA has a database LS-R which corresponds to a \`'ls -R *'
listing of files and the directories including them. This allow for a
quick search of files within the image of the directory stored within
the database, instead of live search in the directories themselve which
consume disk access time.
\<search TFMDIR\><<<
#ifdef TFMDIR
if( !font_file )
font_file = search_file_base(file_name, TFMDIR,
READ_BIN_FLAGS, tfm_dirs);
#endif
>>>
\<h-defines\><<<
#ifndef TFMDIR
#endif
>>>
\SubSection{Get Font Env Variables}
The environment variables, if defined, hold a sequence of directory
names. Each directory name can be an absolute address, or relative to
the home directory (signaled by the prefix \`'~'). The directories must be separated by a distinguished character, which must also act as delimiter at the start and the end of the string.
\<check env font variables\><<<
#ifndef KPATHSEA
tfm_dirs = get_env_var("TEX4HTTFM");
#endif
htf_dirs = get_env_var("TEX4HTHTF");
>>>
\<header functions\><<<
static struct env_var_rec * get_env_var( ARG_I(const char *) );
>>>
\<functions\><<<
`[
static struct env_var_rec * get_env_var( env_var )
const char *env_var
;{ U_CHAR *TEX4HTTFM, *from;
struct env_var_rec *tfm_dirs, *p;
int env_var_len;
tfm_dirs = (struct env_var_rec *) 0;
TEX4HTTFM = getenv( env_var );
if( TEX4HTTFM ){
env_var_len = (int) strlen((char *) TEX4HTTFM);
if ( *TEX4HTTFM == *(TEX4HTTFM + env_var_len - 1 ) ){
from = TEX4HTTFM + env_var_len - 1;
*from = '\0';
do{
from--;
if( *from == *TEX4HTTFM ){ char * base;
*from = '\0';
base = from + 1;
`<verify env var dir`>
if( base ){
`<add env var dir`>
} }
} while (from > TEX4HTTFM );
} else { warn_i_str2( 49, env_var, TEX4HTTFM); }
}
return tfm_dirs;
}
>>>
We preseve the order of the directories founnd in the environment
variables.
\<add env var dir\><<<
p = m_alloc(struct env_var_rec, 1);
p->next = tfm_dirs;
p->base = base;
tfm_dirs = p;
>>>
\<verify env var dir\><<<
{ U_CHAR *str;
if( *(from+1) == '~' ){
if( HOME_DIR ){
str = m_alloc(char, strlen((char *) HOME_DIR)+strlen((char *) base));
(IGNORED) sprintf(str,"%s%s", HOME_DIR, base+1);
if( access(str,F_OK) ) {
warn_i_str2(49, env_var, str); base = NULL; }
free((void *) str);
} else {
if( access(base,F_OK) ) {
warn_i_str2(49, env_var, base); base = NULL; }
}
} else {
if( access(base,F_OK) ) {
warn_i_str2(49, env_var, base); base = NULL; }
} }
>>>
\<scan fonts vars\><<<
#ifndef KPATHSEA
struct env_var_rec *tfm_dirs;
#endif
struct env_var_rec *htf_dirs;
>>>
\<types\><<<
struct env_var_rec{ char* base;
struct env_var_rec *next; };
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Cache Directories}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<search font in cache dirs\><<<
for( cur_cache_font = cache_font;
cur_cache_font;
cur_cache_font = cur_cache_font->next )
if( (font_file = search_file(file_name,
cur_cache_font->dir,
READ_BIN_FLAGS))
!= NULL) break;
>>>
\<put dir in cache\><<<
{ int found;
found = FALSE;
for( cur_cache_font = cache_font;
cur_cache_font;
cur_cache_font = cur_cache_font->next )
{ found = found || eq_str(cur_cache_font->dir, dir ) ;
if( found ) break; }
if( !found ){
cur_cache_font = m_alloc(struct cache_font_rec, 1);
`<init new cache list`>
cur_cache_font->dir = m_alloc(char, n+1);
(IGNORED) strcpy((char *) cur_cache_font->dir, dir);
if( !cache_font ){
cur_cache_font->next = cache_font;
cache_font = cur_cache_font;
} else if ( gt_str(cache_font->dir, dir) ) {
cur_cache_font->next = cache_font;
cache_font = cur_cache_font;
} else {
struct cache_font_rec * after_cache_font;
after_cache_font = cache_font;
while( after_cache_font->next ){
if ( gt_str(after_cache_font->next->dir, dir) ) { break; }
after_cache_font = after_cache_font->next;
}
cur_cache_font->next = after_cache_font->next;
after_cache_font->next = cur_cache_font;
} } }
>>>
\<free html font memory\><<<
while( (cur_cache_font = cache_font) != (struct cache_font_rec *)0 ){
cache_font = cache_font->next;
free((void *) cur_cache_font->dir );
free((void *) cur_cache_font ); }
>>>
\<vars\><<<
static struct cache_font_rec *cache_font, *cur_cache_font;
>>>
\<init scan fonts vars\><<<
cache_font = (struct cache_font_rec *) 0;
cur_cache_font = (struct cache_font_rec *) 0;
>>>
\<types\><<<
struct cache_font_rec{ char* dir;
struct cache_file_rec * cache_file;
struct cache_font_rec* next; };
struct cache_file_rec{ struct cache_file_rec* next;
U_CHAR * file; };
>>>
\SubSection{Default Built-In During Compilation}
On my HP the standard
TeX fonts are in the directory
%\Link[/usr/local/tex/lib/texmf/fonts/public/cm/tfm/]{}%
\Link[/usr/local/tex/lib/texmf/fonts/public/cm/tfm/]{}{}%
/usr/local/tex/lib/texmf/fonts/public/cm/tfm/\EndLink,
and the standard latex fonts are in
\Link[/usr/local/tex/lib/texmf/fonts/public/latex/tfm/]{}{}%
/usr/local/tex/lib/texmf/fonts/public/latex/tfm/\EndLink,
on my SUN the directory is ???.
We also might want to put the current directory in.
The function \`'int access(const U_CHAR *pathname, int mode)', and the mode
\`'F_OK' that tests for the existence of file, are defined in the
following directory. The function returns 0 if ok and -1 on error .
Where access comes from in dos? in \''<io.h>'
\<h-include\><<<
#ifndef F_OK
#ifdef DOS_WIN32
#define F_OK 0 `% does file exist `%
#endif
#ifndef KPATHSEA
#ifndef DOS_WIN32
#define HAVE_UNISTD_H
#endif
#endif
#endif
#ifdef HAVE_IO_H
#include <io.h>
#endif
#ifndef KPATHSEA
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#endif
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Htf Font Files}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Search along the path:
command line switch 'i' directory $\rightarrow$
work directory $\rightarrow$ directories specified in tex4ht.env
(switch 'i' and aliased 'a') $\rightarrow$ system directory
specified in HTFDIR. \ifHtml[\HPage{more}\Verbatim
> This I think leads to a problem in the parsing of ht-font
> information (the ``a'' lines in the .env file): according to your rules, only
> the first setting takes effect. So of t4ht has previsouly fou nd and read
^^^^
tex4ht
> a tex4ht.env file, then the upshot is that the user's ``a'' section doesn't
> override the default. At least this is what I think must be happening
> based on some experiments I'm doing with a custom .env file.
The search for the htf files should be along the following route
in the directories stated within `-i' switches of the tex4ht command line
in the work directory
in the directories specified within `i' records of the env file
in a directory compiled into tex4ht during its compilation
The search for the env file should be along the following route
in the file stated within the `-e' switch of the tex4ht command line
in the work directory
in the home directory (~-unix; C:-dos/win)
in a directory compiled into tex4ht during its compilation
\EndVerbatim\EndHPage{}]\fi
\<file = .htf file\><<<
{ U_CHAR name[256];
(IGNORED) sprintf(name, "%s.htf", new_font_name);
`<search file in htf locations`>
if( file ){ `<check name decomposition`> }
}
>>>
\<search file in htf locations\><<<
file = NULL;
`<search htf font along cmd switches`>
if( !file ){
if( ((file = f_open(name, READ_TEXT_FLAGS)) == NULL) && dot_file )
file = search_in_dot_file( 'i', name, READ_TEXT_FLAGS, htf_dirs);
#ifdef HTFDIR
if( !file ) file = search_file_base(name, HTFDIR,
READ_TEXT_FLAGS, htf_dirs);
#endif
`<file = kpathsea .htf file`>
}
>>>
\<types\><<<
struct htf_com_rec{ char* name;
struct htf_com_rec* next; };
>>>
\<main's vars\><<<
struct htf_com_rec* htf_font_dir = (struct htf_com_rec *) 0;
>>>
\<get htf font dir from com ln\><<<
com_dir(p);
{
struct htf_com_rec *q, *t;
q = m_alloc( struct htf_com_rec, 1);
q->name = p+2;
q->next = (struct htf_com_rec *) 0;
if( htf_font_dir ){
t = htf_font_dir;
while( t->next ){ t = t->next; }
t->next = q;
} else {
htf_font_dir = q;
}
}
>>>
\<header functions\><<<
static void com_dir( ARG_I(char*) );
>>>
\<functions\><<<
`[
static void com_dir(p)
char* p
;{ int i; U_CHAR str[256];
(IGNORED) strcpy((char *) str, (char *) p+2 );
i = (int) strlen((char *) str) - 1;
if( str[i] == '!' ) str[i] = '\0';
}
>>>
% if( !access(str,F_OK) ) return TRUE;
% warn_i_str(12,p+2); return FALSE;
\<search htf font along cmd switches\><<<
{
struct htf_com_rec *p;
p = htf_font_dir;
while( p ){
file = search_file_base(name, p->name, READ_TEXT_FLAGS, htf_dirs);
if( file ){
#ifndef KPATHSEA
tex4ht_fls = TRUE;
#endif
break;
}
p = p->next;
}
}
>>>
\<h-defines\><<<
#ifndef HTFDIR
#endif
>>>
Example \`' #define HTFDIR "/n/gold/5/gurari/tex4ht-fonts.dir/"'
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{External File Cache}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<init new cache list\><<<
cur_cache_font->cache_file = (struct cache_file_rec *) 0;
>>>
\<store filename in cache\><<<
{
struct cache_file_rec *file_rec, *prev_file_rec;
prev_file_rec = (struct cache_file_rec *) 0;
file_rec = cur_cache_font->cache_file;
while( file_rec ) {
if( !gt_str(name,file_rec->file) ) break;
prev_file_rec = file_rec;
file_rec = file_rec->next;
}
{
struct cache_file_rec * file_entry;
BOOL flag;
flag = TRUE;
if( file_rec ) {
if( eq_str(name,file_rec->file) ){ flag = FALSE; }
}
if( flag ) {
`<insert file name`>
}
}
}
>>>
\<insert file name\><<<
file_entry = m_alloc(struct cache_file_rec, 1);
file_entry->file = m_alloc(char, strlen(name)+1);
(IGNORED) strcpy((char *) file_entry->file, name);
if( ! cur_cache_font->cache_file ){
cur_cache_font->cache_file = file_entry;
file_entry->next = (struct cache_file_rec *) 0;
} else if( !prev_file_rec ){
file_entry->next = cur_cache_font->cache_file;
cur_cache_font->cache_file = file_entry;
} else {
file_entry->next = prev_file_rec->next;
prev_file_rec->next = file_entry;
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Search File in Aux Cache from dot directories}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
The cache file holds directory names and file names. Directory names
are preceded by a space, and file names start at column one.
\Verbatim
/usr/local/share/texmf/tex4ht/ht-fonts/iso8859/1/ae
ae.htf
aecsc.htf
/usr/local/share/texmf/tex4ht/ht-fonts/iso8859/1/cm
cmcsc.htf
cmr.htf
/usr/local/share/texmf/tex4ht/ht-fonts/unicode/cm
cmmi.htf
\EndVerbatim
We traverse the auxiliary cache file, recording on the way the last
found directory. Upon finding a match on file name, we check the last
recorded directory against the directories mentioned in the
environment file. If a match is found also there, we
open the appropriate file.
\<search font in cache files\><<<
U_CHAR cache_dir[256], dot_dir[256], *p, *q;
BOOL flag;
int n, ch;
(IGNORED) fseek(cache_files, 0L, `<abs file addr`>);
ch = (int) getc(cache_files);
while ( ch != EOF ){
if ( ch == ' ' ) { `<cache-dir = cache record`> }
else { `<flag = (file == cache record)`>
if( flag ){
`<flag = (cache-dir == env-dir)`>
if( flag ){ `<open file and, if success, return`> }
} }
if ( ch != EOF ){ ch = (int) getc(cache_files); }
}
>>>
\<cache-dir = cache record\><<<
q = cache_dir;
do
*(q++) = ch = (int) getc(cache_files);
while( (ch !='\n') && (ch != EOF) );
*(q-1 - (*(q-2) == `<directory '/'`>)
#ifdef DOS_WIN32
- (*(q-2) == '/')
#endif
) = '\0';
>>>
\<flag = (file == cache record)\><<<
p = name; flag = FALSE;
while( *(p++) == ch ){
ch = (int) getc(cache_files);
}
if( (*(p-1) == '\0') && ((ch == '\n') || (ch == EOF)) ){ flag = TRUE; }
else{
while( (ch != '\n') && (ch != EOF) ) { ch = (int) getc(cache_files); }
}
>>>
\<flag = (cache-dir == env-dir)\><<<
flag = FALSE;
(IGNORED) fseek(dot_file, 0L, `<abs file addr`>);
while( search_dot_file( typ ) && !flag ){ U_CHAR *q, save_ch;
int n, m;
q = dot_dir;
do
*(q++) = ch = (int) getc(dot_file);
while( (ch !='\n') && (ch != EOF) );
flag = *(q - 2) = '!';
q -= (flag? 2 : 1);
*(q - (*(q-1) == `<directory '/'`>)
#ifdef DOS_WIN32
- (*(q-1) == '/')
#endif
) = '\0';
if( (n = strlen((char *) dot_dir)) > (m = strlen((char *) cache_dir)) ){ flag = FALSE; }
else {
save_ch = *(cache_dir + n);
*(cache_dir + n) = '\0';
flag = eq_str(dot_dir,cache_dir) && ( flag || (m == n) );
*(cache_dir + n) = save_ch;
}
}
>>>
\<open file and, if success, return\><<<
n = (int) strlen((char *) cache_dir);
cache_dir[n] = dir_path_slash(cache_dir);
cache_dir[n+1] = '\0';
(IGNORED) strcat((char *) cache_dir, (char *) name);
>>>
\<BROKEN\><<<
if( (file = f_open(cache_dir,flags)) == NULL ) {
warn_i_str( 47, cache_dir);
} else {
return file;
}
>>>
\SubSection{Store New Auxiliary Cache}
\<set new cache file\><<<
if( tex4ht_fls ){
FILE *in_file, *out_file;
U_CHAR temp_file[256];
`<set temp file`>
`<backup cache file`>
if( (out_file = fopen(tex4ht_fls_name, WRITE_TEXT_FLAGS)) == NULL )
{ bad_in_file(tex4ht_fls_name);
} else {
if( (in_file = fopen(temp_file, READ_TEXT_FLAGS)) != NULL ){
`<output into new cache`>
(IGNORED) fclose(in_file);
}
(IGNORED) fclose(out_file);
} }
>>>
\<set temp file\><<<
(IGNORED) strcpy((char *) temp_file, (char *) job_name);
temp_file[job_name_n] = '\0';
temp_file[job_name_n-1] = 'p';
temp_file[job_name_n-2] = 'm';
temp_file[job_name_n-3] = 't';
>>>
\<vars\><<<
#ifndef KPATHSEA
static BOOL tex4ht_fls = FALSE;
static char *tex4ht_fls_name = (char *) 0;
#endif
>>>
\<cat /\><<<
#if defined(DOS_WIN32) || defined(__DJGPP__)
(is_forward_slash(files_cache)? "/" : "\\" )
#else
"/"
#endif
>>>
\<get dir of tex4ht.fls\><<<
{ U_CHAR *p;
if( !tex4ht_fls_name ){
tex4ht_fls_name = p = files_cache;
(IGNORED) fseek(dot_file, 0L, `<abs file addr`>);
if ( search_dot_file( 'l' ) ){
do
*(p++) = ch = (int) getc(dot_file);
while( (ch !='\n') && (ch != EOF) );
p--; *p = '\0';
} else { (IGNORED) strcpy((char *) p,
(char *) getenv("TEX4HTWR")? "~~/tex4ht.fls" : "tex4ht.fls");
}
}
`<relative tex4ht.fls addr`>
}
>>>
\<relative tex4ht.fls addr\><<<
if( *tex4ht_fls_name == '~' ){
tex4ht_fls_name = abs_addr(tex4ht_fls_name,getenv("TEX4HTWR"));
}
>>>
\<relative tex4ht.fls addrNO\><<<
if( *tex4ht_fls_name == '~' ){
`<fls temp var`>
if( HOME_DIR ){
(IGNORED) strcpy((char *) p, (char *) tex4ht_fls_name+1);
(IGNORED) sprintf(files_cache,"%s%s", HOME_DIR, p);
} else {
(IGNORED) strcpy((char *) files_cache, "tex4ht.fls");
}
tex4ht_fls_name = files_cache;
}
>>>
\<fls var\><<<
U_CHAR files_cache[PATH_MAX];
>>>
\<fls temp var\><<<
U_CHAR p[PATH_MAX];
>>>
\<vars\><<<
static U_CHAR *HOME_DIR;
>>>
\<main's init\><<<
HOME_DIR = getenv("HOME");
>>>
We might want more than one version of \`'tex4ht.fls', for different
combinations of subtrees of htf fonts.
\<get tex4ht.fls from com ln\><<<
#ifndef KPATHSEA
tex4ht_fls_name = p+2;
#endif
>>>
\<backup cache file\><<<
if( (out_file = fopen(temp_file, WRITE_TEXT_FLAGS)) == NULL )
{ bad_in_file(temp_file);
} else {
if( (in_file = fopen(tex4ht_fls_name, READ_TEXT_FLAGS)) != NULL ){
int ch;
while( (ch = getc(in_file)) != EOF ) {
(IGNORED) putc( ch, out_file );
}
(IGNORED) fclose(in_file);
}
(IGNORED) fclose(out_file);
}
>>>
\<output into new cache\><<<
U_CHAR dir[255], prev_dir[255], file[255], *p;
int ch;
BOOL is_dir;
struct cache_file_rec *file_rec, *prev_file_rec;
cur_cache_font = cache_font;
ch = 'n'; prev_dir[0] = '\0';
while( ch != EOF ){
ch = getc(in_file);
is_dir = (ch == ' ');
p = is_dir? dir : file;
while( ch != '\n' ) {
if( ch == EOF ) break;
if( ch != ' ' ) { *p++ = ch; }
ch = getc(in_file);
}
*p = '\0';
if( is_dir && (dir[0] != '\0') ){
`<output smaller internal directories`>
(IGNORED) fprintf(out_file," %s\n", dir);
(IGNORED) strcpy((char *) prev_dir, (char *) dir);
} else if( !is_dir && (file[0] != '\0') ){
`<output smaller internal files`>
(IGNORED) fprintf(out_file,"%s\n", file);
}
}
`<output remainder of internal cache`>
>>>
\<output smaller internal directories\><<<
while( cur_cache_font != (struct cache_font_rec *)0 ){
if( gt_str(dir,cur_cache_font->dir) ){
`<cache directory in external memory`>
} else break;
cur_cache_font = cur_cache_font->next;
}
>>>
\<cache directory in external memory\><<<
file_rec = cur_cache_font->cache_file;
if( file_rec ){
if( !eq_str( prev_dir, cur_cache_font->dir) ){
(IGNORED) fprintf(out_file, " %s\n", cur_cache_font->dir);
(IGNORED) strcpy((char *) prev_dir, (char *) dir);
}
cur_cache_font->cache_file = (struct cache_file_rec *) 0;
while( file_rec ) {
prev_file_rec = file_rec;
file_rec = file_rec->next;
(IGNORED) fprintf(out_file, "%s\n", prev_file_rec->file);
free((void *) prev_file_rec );
}
}
>>>
\<output smaller internal files\><<<
if( cur_cache_font != (struct cache_font_rec *)0 ){
if( eq_str(dir,cur_cache_font->dir) ){
file_rec = cur_cache_font->cache_file;
while( file_rec ) {
if( gt_str(file_rec->file,file) ){ break; }
else if( gt_str(file,file_rec->file) ){
(IGNORED) fprintf(out_file, "%s\n", file_rec->file);
}
prev_file_rec = file_rec;
file_rec = file_rec->next;
free((void *) prev_file_rec );
}
cur_cache_font->cache_file = file_rec;
}
}
>>>
\<output remainder of internal cache\><<<
while( cur_cache_font != (struct cache_font_rec *)0 ){
`<cache directory in external memory`>
cur_cache_font = cur_cache_font->next;
}
>>>
\<vars\><<<
#ifndef KPATHSEA
static FILE* cache_files;
#endif
>>>
\<open old cache file\><<<
cache_files = f_open(tex4ht_fls_name, READ_BIN_FLAGS);
>>>
\<close old cache file\><<<
if( cache_files != (FILE *) 0 ){ (IGNORED) fclose(cache_files); }
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Export TEX4HTFONTSET for kpathsea}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Background}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
A system call similar to \`'TEX4HTFONTSET={...}; export TEX4HTFONTSET'
is issued for ordering searches of htf fonts by kpathsea.
The system call assumes a pattern of the form \`'.....%%12...'
in the argument -DTEX4HTFONTSET provided to the compilation of tex4ht.
If none is provided, a default pattern is assumed.
To allow also opposite order of search, the
code should modify to answer also for patterns of the form
\`'.....%%21...'.
\Verbatim
The i-records (provided e.g., in tex4ht.env) are assumed to refer to
paths relative to the ht-fonts directory. If a record happens to
include the substring `/ht-fonts/', then only the portion that follows
that substring is considered.
For instance, the records
i~/tex4ht.dir/ht-fonts/iso8859/1/!
i~/tex4ht.dir/ht-fonts/alias/!
are equivalent to
iiso8859/1
ialias
If i-records are not provided, a ht-like script may contain
a call similar to
test -z "$3" || TEX4HTFONTSET=$3;export TEX4HTFONTSET
on unix and
set TEX4HTFONTSET={%%12}
on windows.
In texmf.cnf: TEX4HTFONTSET=iso8859/1,alias
TEX4HTFONTSET={iso8859/1,alias}; export TEX4HTFONTSET
set TEX4HTFONTSET={iso8859/1,alias}
\EndVerbatim
The user has two options of hand-made entries: i-records and
exportation. Tex4ht assumes the following priority schema:
\List{1}
\item
Entries provided in i-records (e.g., within tex4ht.env)
\item Entries exported by TEX4HTFONTSET from scripts (e.g., in htlatex)
\item Entries hard wired in TEX4HTFONTSET within texmf.cnf
(In such a case, i-records should not be provided. That is, in
the default setting, i-records should not be provided in tex4ht.env,
within tex4ht.c, or over tex4ht.c (in the ht... scripts). Users may
add such records, but the default installations should not contain
i-records.)
\EndList
xputenv() is a kpathsea routine for writing environment variables
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Collect Information}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<export TEX4HTFONTSET\><<<
#ifdef KPATHSEA
{ char str [256], *export_str, *postfix;
export_str = m_alloc(char, 1);
*export_str = '\0';
`<set the exported list`>
if( (int) strlen((char *) export_str) > 0 ){
(IGNORED) strcpy((char *) str, "%%12");
export_str = (char *) r_alloc((void *) export_str,
(int) strlen((char *) export_str) + (int) strlen((char *) str) + 1 );
postfix = str - 1;
while( *(++postfix) != '\0' ){
if( (*postfix=='%') && (*(postfix+1)=='%') &&
(*(postfix+2)=='1') && (*(postfix+3)=='2') ){
*postfix='\0'; postfix += 4; break;
} }
if( (int) strlen((char *) export_str) != 0 ){
`<insert export into pattern`>
`<system call for export`>
}
}
`<recording TEX4HTFONTSET`>
free((void *) export_str);
`<debug info about TEX4HTFONTSET`>
}
#endif
>>>
\<insert export into pattern\><<<
{ char *from_ch;
int i, n, m;
n = (int) strlen((char *) str);
m = (int) strlen((char *) export_str);
from_ch = export_str + m;
for( i = 0; i<=m; i++){
*(from_ch + n) = *(from_ch);
from_ch--;
}
for( i = 0; i<n; i++){
export_str[i] = str[i];
}
(IGNORED) strcat((char *) export_str, (char *) postfix);
}
>>>
An entry similar to the following one is assumed in texmf.cnf.
\Verbatim
TEX4HTINPUTS=.;$TEXMF/tex4ht/base//;$TEXMF/tex4ht/ht-fonts/{$TEX4HTFONTSET}//
\EndVerbatim
\<system call for export\><<<
{ U_CHAR * q;
if( dump_htf_search ) { U_CHAR *p, *q;
`<trace TEX4HTFONTSET`>
}
q = (U_CHAR *) kpse_var_value( "TEX4HTFONTSET" );
if( q ){
xputenv("TEX4HTFONTSET", export_str);
if( dump_htf_search ){
(IGNORED) printf("setting TEX4HTFONTSET={%s}\n", export_str);
}
} else if( dump_htf_search ) {
warn_i_str( 50, "TEX4HTFONTSET for kpathsea" );
}
}
>>>
\List{*}
\item
in the directories stated within `-i' switches of the tex4ht command line
\item
in the directories specified within `i' records of the env file
\item
in a directory compiled into tex4ht during its compilation
\EndList
\<set the exported list\><<<
{
struct htf_com_rec *q;
q = htf_font_dir;
while( q != (struct htf_com_rec *) 0 ){
(IGNORED) strcpy((char *) str, (char *) q->name);
export_htf( &export_str, str );
q = q->next;
} }
(IGNORED) fseek(dot_file, 0L, `<abs file addr`>);
while ( search_dot_file( 'i' ) ){
int ch;
char* p;
p = str;
do {
ch = (int) getc(dot_file);
if( ch != EOF) { *(p++) = ch;}
} while( (ch !='\n') && (ch != EOF) );
*p = '\0';
export_htf( &export_str, str );
}
#ifdef HTFDIR
(IGNORED) strcpy((char *) str, (char *) HTFDIR);
export_htf( &export_str, str );
#endif
`<pre assigned TEX4HTFONTSET`>
>>>
\<pre assigned TEX4HTFONTSET\><<<
{ U_CHAR * q;
q = (U_CHAR *) kpse_var_value( "TEX4HTFONTSET" );
if( q ){
if( (int) strlen((char *) q) > 0 ){
export_str = (char *) r_alloc((void *) export_str,
(int) strlen((char *) export_str) + (int) strlen((char *) q) + 2);
if( (int) strlen((char *) export_str) > 0 ){
(IGNORED) strcat((char *) export_str, ",");
}
(IGNORED) strcat((char *) export_str, (char *) q);
} } }
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Appending a Segment}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<header functions\><<<
#ifdef KPATHSEA
static void export_htf( ARG_II(char**, char[]) );
#endif
>>>
\<functions\><<<
#ifdef KPATHSEA
`[
static void export_htf(export_str, str)
char** export_str `;
char str[]
;{ int i;
char* p;
BOOL found;
i = (int) strlen((char *) str) - 1;
while( (i>=0) && (str[i] == '\n') ){ str[i--] = '\0'; }
while( (i>=0) && (str[i] == ' ') ) { str[i--] = '\0'; }
if( (i>=0) && (str[i] == '!') ){ str[i--] = '\0'; }
if( (i>=0) && ((str[i] == '/') || (str[i] == '\\')) ){
str[i--] = '\0'; }
i -= 8; found = FALSE;
while( --i>=0 ){
if( ((str[i] == '/') || (str[i] == '\\')) && (str[i+1]== 'h')
&& (str[i+2]=='t') && (str[i+3]=='-')
&& (str[i+4]=='f') && (str[i+5]=='o')
&& (str[i+6]=='n') && (str[i+7]=='t')
&& (str[i+8]=='s')
&& ((str[i+9] == '/') || (str[i+9] == '\\'))
){
p = str + i + 10; i=0;
while( *p ){ str[i++] = *(p++); }
str[i] = '\0';
found = TRUE; break;
} }
if( found ){
*export_str = (char *) r_alloc((void *) *export_str,
(int) strlen((char *) *export_str) + (int) strlen((char *) str) + 2 );
if( (int) strlen((char *) *export_str) > 0 ){
(IGNORED) strcat((char *) *export_str, ",");
}
(IGNORED) strcat((char *) *export_str, (char *) str);
}
}
#endif
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Recording TEX4HTFONTSET}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Kpathsea ignores the under ht-fonts paths requests coming from tex4ht.c,
exported like in \`'TEX4HTFONTSET={mozilla,iso8859/1,alias,unicode}'.
So tex4ht.c lets kpathsea find whatever path it likes, such as
\Verbatim
/n/gold/5/gurari/tex4ht.dir/texmf/tex4ht/ht-fonts/iso8859/1/cm/cmr.htf
\EndVerbatim
and then tex4ht.c tries to fix the path based on the information it
collected for TEX4HTFONTSET,
\Verbatim
/n/gold/5/gurari/tex4ht.dir/texmf/tex4ht/ht-fonts/unicode/cm/cmr.htf
\EndVerbatim
To that end, tex4ht.c breaks the first approximation to head
\`'.../ht-fonts/' and tail \`'....htf', while removing the path
segment \`'iso8859' just after \`'ht-fonts/'.
Then, tex4ht.c tries the intermediate path segments from
\`'TEX4HTFONTSET={mozilla,iso8859/1,alias,unicode}', possibly with
removing prefixes from tail.
\<vars\><<<
#ifdef KPATHSEA
static char * export_str_chars = (char *) 0;
#endif
>>>
\<scan fonts vars\><<<
#ifdef KPATHSEA
`<scan kpathsea vars`>
#endif
>>>
\<recording TEX4HTFONTSET\><<<
{ int n;
n = (int) strlen((char *) export_str);
if( n > 0 ){
export_str_chars = m_alloc(char, n+1);
(IGNORED) strcpy((char *) export_str_chars, (char *) export_str);
} }
>>>
\<scan kpathsea vars\><<<
int cardinality=0;
char ** fontset=0;
>>>
\<start loading fonts\><<<
#ifdef KPATHSEA
if( export_str_chars ){
`<decompose TEX4HTFONTSET`>
}
#endif
>>>
\<end loading fonts\><<<
#ifdef KPATHSEA
if( export_str_chars ){
free((void *) export_str_chars);
free((void *) fontset);
}
#endif
>>>
\<decompose TEX4HTFONTSET\><<<
{ U_CHAR *p;
int n;
cardinality = 1;
p = (U_CHAR *) export_str_chars;
while( *p != '\0' ){
if( *p == ',' ){ cardinality++; }
p++;
}
fontset = m_alloc(char *, cardinality);
p = (U_CHAR *) export_str_chars;
fontset[0] = p;
n=1;
while( *p != '\0' ){
if( *p == ',' ){ fontset[n++] = p+1; *p = '\0'; }
p++;
} }
>>>
\<correct htfname\><<<
{ U_CHAR * head, * tail, *p;
int n;
`<decompose htfname`>
htfname = (U_CHAR *) 0;
`<try alternative htfname`>
}
>>>
\<decompose htfname\><<<
n = (int) strlen((char *) htfname);
tail = head = m_alloc(char, n+1);
(IGNORED) strcpy((char *) head, (char *) htfname);
while( n>11 ){
if( (*tail=='\\') || (*tail=='/') ){
if( (*tail == *(tail+9)) && (*(tail+1) == 'h')
&& (*(tail+2) == 't') && (*(tail+3) == '-')
&& (*(tail+4) == 'f') && (*(tail+5) == 'o')
&& (*(tail+6) == 'n') && (*(tail+7) == 't')
&& (*(tail+8) == 's') ){
p = tail + 9; *(tail + 10) = '\0'; tail += 11;
while( (*tail != *p) && (*tail != '\0') ){ tail++; }
break;
} }
tail++; n--;
}
>>>
\<try alternative htfname\><<<
for( n = 0 ; (n < cardinality) && !htfname ; n++){
p = tail;
while( *p != '\0' ){
char * s, *nm;
s = m_alloc(char, (int) strlen((char *) head ) +
(int) strlen((char *) fontset[n] ) +
(int) strlen((char *) p ) + 1);
(IGNORED) strcpy((char *) s, (char *) head);
(IGNORED) strcat((char *) s, (char *) fontset[n]);
(IGNORED) strcat((char *) s, (char *) p);
nm = kpse_find_file (s, kpse_program_text_format, 0);
free((void *) s);
if ( nm ){
htfname = nm; break;
}
p++;
while( (*p != '\\') && (*p != '/') && (*p != '\0') ){ p++; }
} }
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Debugging info}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<debug info about TEX4HTFONTSET\><<<
if( dump_htf_search || dump_env_search ) {
U_CHAR *p, *q;
`<trace TEX4HTINPUTS`>
}
>>>
NOTE: The following code, activated by -hV or -hF, stops tex4ht.c with
and error under miktex/kpathse.
The execution arrives to
`\Verb+kpse_find_file ( "texmf.cnf", kpse_cnf_format, 0);+'
but does not print a message in the line that follows. The problem
probably was in \Verb=warn_i_str=. The code was modified from
\Verbatim
static U_CHAR warning[] = "--- warning --- ";
....
fprintf(stderr,"--- warning --- ");
\EndVerbatim
to
\Verbatim
fprintf(stderr,"--- warning --- ");
\EndVerbatim
with the assumption/hope that it will take care of the problem.
\<trace TEX4HTINPUTS\><<<
p = kpse_find_file ( "texmf.cnf", kpse_cnf_format, 0);
if( p ){
(IGNORED) printf( "texmf.cnf = %s\n", p);
} else { warn_i_str(1, "texmf.cnf" ); }
p = (U_CHAR *) kpse_var_value( "TEX4HTINPUTS" );
if( p ){
(IGNORED) printf("TEX4HTINPUTS = %s\n", p);
}
q = getenv("TEX4HTINPUTS");
if( q ){ (IGNORED) printf(
"Environment var TEX4HTINPUTS: %s\n", q);
}
if( !p && !q ){
(IGNORED) printf( "Missing TEX4HTINPUTS for kpathsea\n" );
}
>>>
\<trace TEX4HTFONTSET\><<<
p = (U_CHAR *) kpse_var_value( "TEX4HTFONTSET" );
if( p ){
(IGNORED) printf("given TEX4HTFONTSET = %s\n", p);
}
q = getenv("TEX4HTFONTSET");
if( q ){ (IGNORED) printf(
"Environmet var TEX4HTFONTSET: %s\n", q);
}
if( !p && !q ){
(IGNORED) printf( "Missing TEX4HTFONTSET for kpathsea\n" );
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Searching with KPATHSEA}
%%%%%%%%%%%%%%%%%%%%%%
\Link[
http://www.fptex.org/kpathsea/kpathsea\string_toc.html]{}{}Kpathsea
manual\EndLink
\<h-include\><<<
`<KPATHSEA h-include`>
>>>
\<kpathsea arg 0\><<<
#ifdef KPATHSEA
kpse_set_program_name (argv[0], "tex4ht");
#endif
>>>
\<kpathsea env file\><<<
#ifdef KPATHSEA
if( !dot_file ) { U_CHAR * envfile;
char *arch, *p, str[256];
`<arch = tail of SELFAUTOLOC`>
envfile = (char *) 0;
`<envfile = kpse-find-file architecture/tex4ht.env`>
if ( !envfile ){ `<envfile = kpse-find-file tex4ht.env`> }
if ( !envfile ){ `<envfile = kpathsea tex4ht.env`> }
if ( envfile ){
dot_file = kpse_open_file (envfile,
kpse_program_text_format);
(IGNORED) printf("(%s)\n", envfile);
} else if( dump_env_search ){
p = (char *) kpse_var_value( "TEX4HTINPUTS" );
if( p ){
(IGNORED) printf( "TEX4HTINPUTS = %s\n", p );
} else { warn_i_str( 50, "kpathsea variable TEX4HTINPUTS"); }
}
}
#endif
>>>
The location of tex4ht.c can be found from the kpathsea variable
`SELFAUTOLOC = /n/gold/5/gurari/tex4ht.dir/bin/solaris', from which
one can determine the architecture in use.
\Verbatim
> I still cannot catch why you need to determine the architecture, since
> all stuff belongs to texmf tree which is always the same for all systems,
> and tex4ht.env is found without problem.
> And T4HTINPUTS= should be set in texmf.cnf
In case we have different tex4ht.env files for different architectures
/usr/TeX/texmf/tex4ht/base/i386-linux/tex4ht.env
/usr/TeX/texmf/tex4ht/base/win32/tex4ht.env
the SELFAUTOLOC provides us information about the architecture in use
by inspecting from what branch tex4ht.c comes
/usr/TeX/bin/i386-linux
/usr/TeX/bin/win32
Seems to me that this problem is solved now.
\EndVerbatim
\<arch = tail of SELFAUTOLOC\><<<
p = arch = (char *) kpse_var_value( "SELFAUTOLOC" );
while( *p != '\0' ){
if( (*p == '/') || (*p == '\\') ){
arch = p;
}
p++;
}
>>>
\<envfile = kpse-find-file architecture/tex4ht.env\><<<
if( arch ){
(IGNORED) sprintf(str,"%s%ctex4ht.env", arch+1, *arch);
if( dump_env_search ){
(IGNORED) printf("kpse_open_file (\"%s\", ...)?\n", str );
}
envfile= kpse_find_file (str, kpse_program_text_format, 0);
}
>>>
\<envfile = kpse-find-file tex4ht.env\><<<
if( dump_env_search ){
(IGNORED) printf("kpse_open_file (\"tex4ht.env\", ...)?\n");
}
envfile= kpse_find_file ("tex4ht.env", kpse_program_text_format, 0);
>>>
\<if kpathsea tfm files else\><<<
#ifdef KPATHSEA
{
U_CHAR * tfmfile;
tfmfile = kpse_find_file (file_name, kpse_tfm_format, 0);
if( !tfmfile ){ `<tfmfile = kpathsea tfm file`> }
if ( tfmfile ){
(IGNORED) printf("(%s)\n", tfmfile);
font_file = kpse_open_file (tfmfile, kpse_tfm_format);
}
}
#else
>>>
% font_file = kpse_open_file (file_name, kpse_tfm_format);
\<endif kpathsea tfm files\><<<
#endif
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{kpathsea vs kpse-find-file}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
The \`'kpsewhich' utility can succeed where the \Verb|kpse_find_file|
function fails, if the program is named something other than \`'tex4ht'.
Otherwise, we don't find the \`'TEX4HTINPUTS' definition in
\`'texmf.cnf'.
However, this is not yet enough to actually make the program work under
an arbitrary name, because the \`'.htf' lookups fail; we need to reset
the Kpathsea progname. Other things to do, so let it stand. Bottom
line is that the program must be invoked as \`'tex4ht'.
\<envfile = kpathsea tex4ht.env\><<<
#define KPSEWHICH_CMD "kpsewhich --progname=tex4ht --format=othertext tex4ht.env"
if( dump_env_search ){
(IGNORED) printf("system(" KPSEWHICH_CMD ")?\n"); /* cpp concatenation */
}
if( system(KPSEWHICH_CMD ">tex4ht.tmp") == 0 ){
`<fileaddr = read tex4ht.tmp`>
envfile= kpse_find_file (fileaddr, kpse_program_text_format, 0);
if( envfile ){
warn_i_str( 50,
"search support for kpse_find_file--using kpsewhich calls instead");
} }
>>>
\<tfmfile = kpathsea tfm file\><<<
char s [256];
(IGNORED) strcpy(s, "kpsewhich ");
(IGNORED) strcat(s, file_name);
(IGNORED) strcat(s, " > tex4ht.tmp ");
if( system(s) == 0 ){
`<fileaddr = read tex4ht.tmp`>
tfmfile = kpse_find_file (fileaddr, kpse_program_text_format, 0);
}
>>>
Don't know how to avoid the use of intermediate file: popen(...) and
fork() are not stadard utilities. Also the redirection \Verb+>+ might
need to be changed, e.g., to \Verb+>&+.
\<fileaddr = read tex4ht.tmp\><<<
char fileaddr [256];
int loc = 0;
FILE* file = f_open("tex4ht.tmp", READ_TEXT_FLAGS);
if( file ){
while( (fileaddr[loc] = getc(file)) >=0 ){
if( fileaddr[loc] == '\n' ){ fileaddr[loc] = '\0'; break; }
loc++;
}
(IGNORED) fclose(file);
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{HTF Fonts}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<vars\><<<
static BOOL dump_htf_search = FALSE;
>>>
\<trace htf search\><<<
dump_htf_search = TRUE;
>>>
\<file = kpathsea .htf file\><<<
#ifdef KPATHSEA
if( !file ){ U_CHAR * htfname;
htfname= kpse_find_file (name, kpse_program_text_format, 0);
if ( htfname ){
`<correct htfname`>
if ( htfname ){
(IGNORED) printf("(%s)\n", htfname);
file= fopen(htfname,READ_TEXT_FLAGS);
} }
}
#endif
>>>
web2c/kpathsea version uses texmf.cnf with the following:
\Verbatim
TEX4HTFONTSET=alias,iso8859
TEX4HTINPUTS=.;$TEXMF/tex4ht/base//;$TEXMF/tex4ht/ht-fonts/{$TEX4HTFONTSET}//
T4HTINPUTS=.;$TEXMF/tex4ht/base//
\EndVerbatim
We can check values of variables of kpathsea with code fragments of
the following form.
\Verbatim
if( q = (U_CHAR *) kpse_var_value( "TEX4HTINPUTS" ) ){
(IGNORED) printf( "%s\n", q );
}
\EndVerbatim
\ifHtml[\HPage{more}\Verbatim
I'm trying to get tex4ht to work with the kpathsea libraries.
Unfortunately, it seems to be having problems finding tfm files, even
when the relevant tex environment variables appear to have been set
and the file exists:
mclellan:~/test% kpsewhich -progname=tex4ht cmsy10.tfm
/usr/local/tex/texmf/fonts/tfm/public/cm/cmsy10.tfm
mclellan:~/test% ls -l `kpsewhich -progname=tex4ht cmsy10.tfm`
-rw-r--r-- 1 tetex local 1124 Aug 14 1995 /usr/local/tex/texmf/fonts/tfm/public/cm/cmsy10.tfm
The OS is solaris 7, and I compiled tex4ht as follows:
gcc -O2 -DKPATHSEA -I/usr/local/teTeX/include -L/usr/local/teTeX/lib -o tex4ht tex4ht.c -DHAVE_DIRENT_H -lkpathsea
%
tex4ht: fatal: tfm file `cmtt10.tfm' not found.
%
\EndVerbatim\EndHPage{}]\fi
%
\ifHtml[\HPage{kpathsea note}\Verbatim
Date: Mon, 22 Dec 1997 13:06:18 +0000
There is an issue to resolve, which is how tex4ht finds its support
files, viz its tex4ht.env control file (in which the font-finding
lines are now ignored), and its .htf files. For the present, I have
set it up so that these are searched for on the TEXINPUTS path, and I
have located them in $TDS/tex/generic/tex4ht/base and
$TDS/tex/generic/tex4ht/htf. Ideally, we would invent a new font
category of "htf" to parallel vf, tfm etc, but that is quite a
widespread change to implement. Any views on that sort of thing, Olaf?
\EndVerbatim\EndHPage{}]\fi{}
\ifHtml[\HPage{more}\Verbatim
Date: Tue, 30 Dec 1997 21:32:40 +0000
> b) Insert the following definition at the beginning of the source file
>
> #ifdef KPATHSEA
> #define ENVFILE "...dir_in_kpathsea.../tex4ht.env"
> #endif
>
kpathsea's system allows for programs to locate files depending on the
location of binaries, so anything hard-wired would not work
\EndVerbatim\EndHPage{}]\fi
\ifHtml[\HPage{more}\Verbatim
Subject: tex4htk search paths.
Date: 04 Feb 1998 21:22:46 +0100
Lines: 43
X-Mailer: Gnus v5.4.66/Emacs 19.34
I'm wondering whether the texmf tree really is the proper place for
the tex4htk.env and .htf files. Unless these need to be read by TeX,
the proper location would be somewhere in
texmf/tex4htk/
which is accessible by using kpse_program_text_format (or
kpse_program_binary_format) in the kpse_find_file calls, and can be
modified by defining TEX4HTKINPUTS in texmf.cnf or the environment.
If so, the necessary changes would look something like this...
--- tex4ht.c- Fri Jan 30 17:00:55 1998
+++ tex4ht.c Wed Feb 4 21:20:33 1998
@@ -3038,7 +3038,7 @@
char *p, *q, *in_name = "", *out_name = "";
#ifdef KPATHSEA
- kpse_set_progname (argv[0]);
+ kpse_set_program_name (argv[0], NULL);
#endif
@@ -3175,7 +3175,7 @@
#ifdef KPATHSEA
if( !dot_file )
- dot_file = kpse_open_file ("tex4ht.env", kpse_tex_format);
+ dot_file = kpse_open_file ("tex4ht.env", kpse_program_text_format);
#endif
@@ -3648,7 +3648,7 @@
#ifdef KPATHSEA
if( !file ){ char * htfname;
- htfname= kpse_find_file (name, kpse_tex_format, 0);
+ htfname= kpse_find_file (name, kpse_program_text_format, 0);
if ( htfname) file= fopen(htfname,READ_TEXT_FLAGS);
}
#endif
--
Olaf Weber
\EndVerbatim\EndHPage{}]\fi
%
\ifHtml[\HPage{more}\Verbatim
> kpathsea was a valuable addition when tex4ht.c had not a booking
> mechanism of its own. It looks like we are getting to a point
> where we need to reevaluate the role of kpathsea within tex4ht.c.
the advantage of kpathsea is that it continues to work if you move the
entire TeX tree to a new disk, or the like. anything like tex4ht.env,
which has path names in it, cannot work eg on a mounted CD (where you
cannot predict the path)
\EndVerbatim\EndHPage{}]\fi
%
\ifHtml[\HPage{more}\Verbatim
I have
changed the second argument of 'kpse_set_program_name' from NULL (which would
effectively mean argv[0]) to 'tex4ht', as that saves having to add a
'T4HTINPUTS' (or 'T4HTKINPUTS') variable into the kpathsea configuration file
'texmf.cnf' pointing to the same place for 'tex4ht.env' as the 'TEX4HTINPUTS'
variable.
0a1,4
> /* t4htk.c = t4ht.c, but with KPATHSEA support
> * corrected/adjusted by Andrew Gray <
[email protected]>,
> * 11 March 1999
> */
94a99,102
> #ifdef KPATHSEA
> #include <kpathsea/progname.h>
> #include <kpathsea/tex-file.h>
> #endif
943c951
< kpse_set_program_name (argv[0], NULL);
---
> kpse_set_program_name (argv[0], ``tex4ht'');
\EndVerbatim\EndHPage{}]\fi
%
\ifHtml[\HPage{more}\Verbatim
I'm trying to get tex4ht to work with the kpathsea libraries.
Unfortunately, it seems to be having problems finding tfm files, even
when the relevant tex environment variables appear to have been set
and the file exists:
mclellan:~/test% kpsewhich -progname=tex4ht cmsy10.tfm
/usr/local/tex/texmf/fonts/tfm/public/cm/cmsy10.tfm
mclellan:~/test% ls -l `kpsewhich -progname=tex4ht cmsy10.tfm`
-rw-r--r-- 1 tetex local 1124 Aug 14 1995 /usr/local/tex/texmf/fonts/tfm/public/cm/cmsy10.tfm
The OS is solaris 7, and I compiled tex4ht as follows:
gcc -O2 -DKPATHSEA -I/usr/local/tex/include -L/usr/local/tex/lib -o tex4ht tex4ht.c -DHTFDIR='"/usr/contrib/share/tex4ht"' -DHAVE_DIRENT_H -lkpathsea
tex4ht.c (2000-03-01-16:28)
/usr/contrib/share/tex4ht/tex4ht: fatal: tfm file `cmsy10.tfm' not found.
\EndVerbatim\EndHPage{}]\fi
%
\ifHtml[\HPage{more}\Verbatim
1. Call `kpse_set_program_name' with `argv[0]' as the first argument;
the second argument is a string or `NULL'. The second argument is
used by Kpathsea as the program name for the `.PROGRAM' feature of
config files (*note Config files::.). If the second argument is
`NULL', the value of the first argument is used. This function
must be called before any other use of the Kpathsea library.
If necessary, `kpse_set_program_name' sets the global variables
`program_invocation_name' and `program_invocation_short_name'.
These variables are used in the error message macros defined in
`kpathsea/lib.h'. It sets the global variable `kpse_program_name'
to the program name it uses. It also initializes debugging
options based on the environment variable `KPATHSEA_DEBUG' (if
that is set). Finally, it sets the variables `SELFAUTOLOC',
`SELFAUTODIR' and `SELFAUTOPARENT' to the location, parent and
grandparent directory of the executable, removing `.' and `..'
path elements and resolving symbolic links. These are used in the
default configuration file to allow people to invoke TeX from
anywhere, specifically from a mounted CD-ROM. (You can use
`--expand-var=\$SELFAUTOLOC', etc., to see the values finds.)
\EndVerbatim\EndHPage{}]\fi
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Searching Utilities}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Search Files Listed in Environment File}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
The following is used for start searching files from directories mentioned
in the dot file.
\<header functions\><<<
static FILE* search_in_dot_file( ARG_IV( int, const U_CHAR *, const U_CHAR *,
struct env_var_rec *) );
>>>
\<functions\><<<
`[
static FILE* search_in_dot_file( typ, name, flags, env_dirs)
int typ`;
const U_CHAR *name`;
const U_CHAR *flags`;
struct env_var_rec *env_dirs
;{ U_CHAR *ch, dir[256];
FILE* file;
#ifndef KPATHSEA
if( cache_files != (FILE *) 0 ){
`<search font in cache files`>
}
#endif
(IGNORED) fseek(dot_file, 0L, `<abs file addr`>);
while( search_dot_file( typ ) ){
ch = dir;
while((*(ch++) = (int) getc(dot_file)) > ' ');
while(*(ch-1) != '\n'){
*(ch-1) = (int) getc(dot_file);
}
*(ch-1) = '\0';
file = search_file_base(name, dir, flags, env_dirs);
if( file != NULL ){
#ifndef KPATHSEA
tex4ht_fls = TRUE;
#endif
return file;
} }
return NULL;
}
>>>
\<header functions\><<<
static FILE* search_file_base( ARG_IV( const U_CHAR *, const U_CHAR *, const U_CHAR *,
struct env_var_rec *) );
>>>
\<functions\><<<
`[
static FILE* search_file_base( name, dir, flags, env_dirs)
const U_CHAR *name`;
const U_CHAR *dir`;
const U_CHAR *flags`;
struct env_var_rec *env_dirs
;{ U_CHAR *p;
FILE* file;
if( *dir == '~' ){
while( TRUE ){
p = abs_addr(dir, env_dirs? env_dirs->base : NULL);
file = search_file(name, p, flags);
free((void *) p);
if( file || !env_dirs ){ return file; }
env_dirs = env_dirs->next;
}
} else {
file = search_file(name, dir, flags);
}
return file;
}
>>>
\<header functions\><<<
static char * abs_addr( ARG_II( const U_CHAR *, const U_CHAR *) );
>>>
\<functions\><<<
`[
static char * abs_addr( dir, base)
const U_CHAR *dir`;
const U_CHAR *base
;{ U_CHAR *p;
p = m_alloc(char, (int) strlen( dir ) +
(base? (int) strlen( base ):0) +
(int) strlen((char *) HOME_DIR ) + 1 );
*p = '\0';
if( (*(dir+1) == '~') && base ){
if( *base == '~' ){
(IGNORED) strct(p, HOME_DIR);
(IGNORED) strct(p, base+1);
} else {
(IGNORED) strct(p, base);
}
(IGNORED) strct(p, dir+2);
} else {
(IGNORED) strct(p, HOME_DIR);
(IGNORED) strct(p, dir+1);
}
return p;
}
>>>
\SubSection{Search in Current Directory and Specified Directory}
If a path specification ends in a '!', it's subdirectories are
searched too, else only the specified dir is searched. (This is the
way many TeX installations seem to do it, too).
\<header functions\><<<
static FILE* search_file( ARG_III(const char *, const U_CHAR *, const U_CHAR *) );
>>>
\<functions\><<<
`[
static FILE* search_file( name, dir, flags )
const char *name`;
const U_CHAR *dir`;
const U_CHAR *flags
;{ FILE* file;
U_CHAR str[256];
int i;
BOOL subs;
`<search file in cur dir`>
(IGNORED) strcpy((char *) str, dir);
i = (int) strlen((char *) str) - 1;
subs = str[i] == '!';
if( subs ) str[i] = '\0'; else i++;
`<cat / if needed`>
`<search file in named dir`>
str[i] = '\0';
return subs? search_file_ext( name, str, flags):
NULL;
}
>>>
\<cat / if needed\><<<
(IGNORED) strct(str,
#if defined(__DJGPP__)
(( dir[i-1] == '/') || ( dir[i-1] == '\\'))
? ""
: (is_forward_slash(dir)? "/" : "\\" )
#else
(dir[i-1] == '/')? "" : "/"
#endif
);
>>>
\<search file in cur dir\><<<
if( (file = f_open(name, flags)) != NULL ){
return file; }
>>>
\<search file in named dir\><<<
(IGNORED) strct(str,name);
if( (file = f_open(str, flags)) != NULL ){
str[i] = '\0'; add_to_cache(str,name,i);
return file; }
>>>
\<header functions\><<<
static void add_to_cache( ARG_III(const char*,const char*,int) );
>>>
\<functions\><<<
`[
static void add_to_cache(dir,name,n)
const char* dir`; const char* name`; int n
;{
struct cache_font_rec *cur_cache_font;
`<put dir in cache`>
`<store filename in cache`>
}
>>>
\SubSection{Search in SubDirectories}
\<header functions\><<<
static FILE* search_file_ext( ARG_III(const char *, const U_CHAR *, const U_CHAR *) );
>>>
\<functions\><<<
`[
static FILE* search_file_ext( name, dir, flags )
const char *name`;
const U_CHAR *dir`;
const U_CHAR *flags
;{ U_CHAR str[256];
FILE* file;
int n;
n = (int) strlen(dir);
(IGNORED) sprintf(str,
#if defined(__DJGPP__)
(( dir[n-1] == '/') || ( dir[n-1] == '\\'))
? "%s%s"
: (is_forward_slash(dir)? "%s/%s" : "%s\\%s" )
#else
(dir[n-1] == '/')? "%s%s" : "%s/%s"
#endif
, dir, name);
if( (file = f_open(str,flags)) != NULL ){
add_to_cache(dir,name,n);
return file;
}
if( (str[n] == `<directory '/'`>)
#ifdef DOS_WIN32
|| (str[n] == '/' )
#endif
) n++;
str[n-1] = '\0';
#ifndef NOSUBDIR
#ifdef WIN32
`<file = search WIN32 subdir (name,str)`>
#else
`<file = search subdir (name,str)`>
#endif
#endif
return NULL;
}
>>>
Searches in subdirectories require opendir, closedir, readir. I can't
find them for dos, so I disabled them there.
On Windows/dos, both forward slash (\''/') and backslash (\''\') are
used as path separator characters. In other environments, it is the
forward slash (\''/').
\<directory '/'\><<<
#if defined(__DJGPP__)
'\\'
#else
'/'
#endif
>>>
\<defines\><<<
#if defined(__DJGPP__)
#define dir_path_slash(str) (is_forward_slash(str)? '/' : '\\')
#else
#define dir_path_slash(str) '/'
#endif
>>>
\<header functions\><<<
#if defined(__DJGPP__)
static BOOL is_forward_slash( ARG_I(const char*) );
#endif
>>>
\<functions\><<<
#if defined(__DJGPP__)
`[
static BOOL is_forward_slash(str)
const char* str
;{
while( *str ){ if( *(str++) == '/' ) { return TRUE; } }
return FALSE;
}
#endif
>>>
\<file = search subdir (name,str)\><<<
{ DIR *dp;
`<struct dirent`> *dirp;
struct STSTAT buf;
if( (dp = opendir( str )) != NULL ){
while( (dirp = readdir(dp)) != NULL ){
if( !eq_str(dirp->d_name, ".") &&
!eq_str(dirp->d_name, "..") )
{ `<reach into the subdirectory`>
} }
(void) closedir(dp);
} }
>>>
\<reach into the subdirectory\><<<
(IGNORED) strcpy((char *) str+n, (char *) dirp->d_name );
str[n-1] = dir_path_slash(str);
if( LSTAT(str, &buf) >= 0 )
if( S_ISDIR( buf.st_mode ) )
if( (file = search_file_ext(name, str, flags)) != NULL ){
(void) closedir(dp);
return file; }
>>>
\<h-include\><<<
#ifdef DOS_WIN32
#include <io.h>
#endif
>>>
\<defines\><<<
#ifndef S_ISDIR
#define S_ISDIR(M) (((M) & _S_IFMT)==_S_IFDIR) `% test for directory `%
#endif
#ifndef _S_IFDIR
#define _S_IFDIR S_IFDIR
#endif
#ifndef _S_IFMT
#define _S_IFMT S_IFMT
#endif
>>>
`lstat' returns info about the symbolic link, not the file referenced by the
symbolic link as `stat' does
STSTAT is for `struct stat', LSTAT is for the function.
Don't define STAT, aix's {\tt <sys/dir.h>} defines it as 1
(Peter Breitenlohner).
\Verbatim
1998-10-22 Eli Zaretskii <
[email protected]> DJGPP:
(LSTAT): Define to stat for every system that doesn't define
S_ISLNK.
\EndVerbatim
\<defines\><<<
#if defined(DOS_WIN32) || !defined(S_ISLNK)
#define LSTAT stat
#else
#define LSTAT lstat
#endif
#define STSTAT stat
>>>
\<h-include\><<<
#include <sys/stat.h>
`% stat _IF_DIR
`%
>>>
\SubSection{dirent}
readdir, opendir, closedir
\<h-defines DOS-WIN32\><<<
#ifdef DOS_WIN32
#define STRUCT_DIRENT
#endif
>>>
\<h-include\><<<
#ifdef HAVE_DIRENT_H
`<needs dirent.h`>
#else
#ifndef STRUCT_DIRENT
#define STRUCT_DIRECT
#endif
`<needs simulated dirent.h`>
#endif
>>>
\<needs dirent.h\><<<
#include <dirent.h>
>>>
\<needs simulated dirent.h\><<<
#ifdef HAVE_SYS_NDIR_H
#include <sys/ndir.h>
#endif
#ifdef HAVE_SYS_DIR_H
#include <sys/dir.h>
#endif
#ifdef HAVE_NDIR_H
#include <ndir.h>
#endif
>>>
\<struct dirent\><<<
#ifdef STRUCT_DIRECT
struct direct
#else
struct dirent
#endif
>>>
\ifHtml[\HPage{more}\Verbatim
I updated all of
ftp://www.tug.org/private/texk7.2drivers/
A small patch for tex4htk. It won't compile on systems without
<dirent.h>, therefore I copied this from gsftopk. I don't know how to
handle the MSVC_1_52_DOS correctly.
--- tex4ht.c.ORIG Wed Mar 18 11:07:26 1998
+++ tex4ht.c Wed Mar 18 11:17:39 1998
@@ -146,7 +146,21 @@
#ifndef MSVC_1_52_DOS
-#include <dirent.h>
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+typedef struct dirent struct_dirent;
+#else /* no <dirent.h> */
+typedef struct direct struct_dirent;
+#ifdef HAVE_SYS_NDIR_H
+#include <sys/ndir.h>
+#endif
+#ifdef HAVE_SYS_DIR_H
+#include <sys/dir.h>
+#endif
+#ifdef HAVE_NDIR_H
+#include <ndir.h>
+#endif
+#endif /* no <dirent.h> */
#endif
@@ -2598,7 +2612,7 @@
#ifndef NOSUBDIR
{ DIR *dp;
- struct dirent *dirp;
+ struct_dirent *dirp;
struct STSTAT buf;
if( (dp = opendir( str )) != NULL ){
while( (dirp = readdir(dp)) != NULL ){
\EndVerbatim\EndHPage{}]\fi
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Try to Open Path-Named File}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<header functions\><<<
static FILE* f_open_pathed_filename( ARG_II(const char*,const char*) );
>>>
\<functions\><<<
`[
static FILE* f_open_pathed_filename( name, flags )
const char* name `;
const char* flags
;{ FILE* file;
U_CHAR *str;
file = NULL;
if( *name == '~' ){
if( HOME_DIR ){
str = m_alloc(char, strlen((char *) HOME_DIR)+strlen(name));
(IGNORED) sprintf(str,"%s%s", HOME_DIR, name+1);
file = f_open(str,flags);
free((void *) str);
}
} else { file = f_open( name, flags ); }
return file;
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%
\Chapter{Unicode into Character Encoding}
%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Put Character}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<header functions\><<<
static INTEGER put_4ht_ch( ARG_II(int,FILE *) );
>>>
\<functions\><<<
`[
static INTEGER put_4ht_ch(ch,htFile) int ch `; FILE* htFile
;{
int c;
c = ch;
if( ch=='&' ){
`<flush incomplete unicode`>
if( put_4ht_off ){
c = putc( ch, htFile );
} else {
uni_code[0] = '&';
uni_code_p = 1;
put_4ht_file = htFile;
}
} else
if( uni_code_p ){
if( ch == ';' ){ `<process unicode`> uni_code_p = 0; }
else if ( ((uni_code_p+1) == MAX_UNI_CODE)
||
( ((ch<'0') || (ch>'9'))
&& ((ch<'a') || (ch>'f'))
&& ((ch<'A') || (ch>'F'))
&& (ch!='#')
&& (ch!='x')
&& (ch!='X')
)
)
{ `<flush incomplete unicode`>
c = putc( ch, htFile );
} else { uni_code[ uni_code_p++ ] = ch; }
} else { c = putc( ch, htFile ); }
return c;
}
>>>
\<vars\><<<
static FILE* put_4ht_file = (FILE *) 0;
static int put_4ht_off = 1;
static char uni_code[MAX_UNI_CODE];
static short uni_code_p = 0;
>>>
\<defines\><<<
#define MAX_UNI_CODE 20
>>>
\<on/off unicode\><<<
special_n--;
switch ( code = get_char() ){
case '+': { put_4ht_off++; `<flush incomplete unicode`> break; }
case '-': { if( put_4ht_off>0 ){ put_4ht_off--; }
else { warn_i_str(52, "@u-"); }
break; }
}
>>>
%%%%%%%%%%%%%
\Section{Flash Unrecognized Codes}
%%%%%%%%%%%%%
\<flush incomplete unicode\><<<
flush_uni();
>>>
\<header functions\><<<
static void flush_uni( ARG_I(void) );
>>>
\<functions\><<<
static void flush_uni( MYVOID )
{
int i;
for( i=0; i<uni_code_p; i++ ){
(IGNORED) putc( uni_code[i], put_4ht_file );
}
uni_code_p = 0;
put_4ht_file = (FILE *) 0;
}
>>>
%%%%%%%%%%%%%
\Section{Load unicode.4hf Table}
%%%%%%%%%%%%%
\<load unicode.4hf\><<<
{ U_CHAR name[256];
FILE* file;
(IGNORED) sprintf(name, "%s.4hf", "unicode");
`<search file in htf locations`>
if( file ){
`<unicode.4hf vars`>
`<mem for charset`>
`<read unicode.4hf`>
put_4ht_off = 0;
} else{ put_4ht_off = 1; `<no mem for charset`> }
}
>>>
\<unicode.4hf vars\><<<
int chr, delimiter, delimiter_n, line_no, digit, i, j;
U_CHAR in[512], *in_p, * start[4], *p;
BOOL char_on, err;
int value;
>>>
\<read unicode.4hf\><<<
err = FALSE;
line_no = 0;
while( TRUE ){
line_no++;
chr = (int) getc(file);
if( chr == EOF ){ break; }
if( (chr>32) && (chr<127) ){
`<scan 4hf fields`>
if( delimiter_n == 8 ){
if( *in != '?' ) {
if( `<not hexa unicode?`> ){ err = TRUE; }
else {
`<value = hex into int`>
if( start[3] == (in_p-1) ){
if( !err ){ `<store type-less 4hf endtry`> }
} else { `<store typed 4hf endtry`> }
} } }
else { err = TRUE; }
`<error 4hf fields`>
}
while( (chr != EOF) && (chr!='\n') ){
chr = (int) getc(file);
}
if( chr == EOF ){ break; }
}
>>>
\<not hexa unicode?\><<<
(*in != '&')
|| (*(in+1) != '#')
|| ( (*(in+2) != 'x') && (*(in+2) != 'X'))
|| (*(start[1] - 2) != ';')
>>>
\<scan 4hf fields\><<<
delimiter = chr;
delimiter_n = 1;
char_on = TRUE;
in_p = in;
while( TRUE ) {
chr = (int) getc(file);
if( (chr == EOF) || (chr=='\n') ){ break; }
if( chr == delimiter ){
if( char_on ){ *(in_p++) = '\0'; }
else{ start[ delimiter_n/2 ] = in_p; }
char_on = !char_on;
delimiter_n++;
} else if (char_on ) {
*(in_p++) = chr;
}
if( delimiter_n==8 ){ break; }
}
>>>
\<process 4hf fields\><<<
>>>
\<error 4hf fields\><<<
if( err ){
warn_i_int(48,line_no);
(IGNORED) printf( "%c", delimiter );
for( p=in; p != in_p; p++ ){
if( *p=='\0' ){
(IGNORED) printf("%c", delimiter);
if( p != in_p-1 ){ (IGNORED) printf(" %c", delimiter); }
}
else { (IGNORED) printf( "%c", *p ); }
}
(IGNORED) printf( "\n" );
err = FALSE;
}
>>>
%%%%%%%%%%%%%
\Section{Store Entry of Table}
%%%%%%%%%%%%%
\<types\><<<
struct charset_rec{ int ch;
char* str; };
>>>
\<vars\><<<
static int charset_n = 0, max_charset_n;
static struct charset_rec *charset;
>>>
\<mem for charset\><<<
max_charset_n = 256;
charset = m_alloc(struct charset_rec, 256);
>>>
\<no mem for charset\><<<
max_charset_n = 0;
>>>
\<value = hex into int\><<<
value = 0;
for( p=in+3; *p!=';'; p++){
digit = (int) *p;
if( (digit>='0') && (digit<='9') ){ digit -= '0'; }
else if( (digit>='A') && (digit<='F') ){ digit -= BASE_A; }
else if( (digit>='a') && (digit<='f') ){ digit -= BASE_a; }
else { digit=0; err = TRUE; }
value = 16*value + digit;
}
>>>
\<defines\><<<
#define BASE_A 55
#define BASE_a 87
>>>
\<store type-less 4hf endtry\><<<
`<mem for new 4ht entry`>
p = m_alloc(char, (int) (start[3] - start[2]) );
(IGNORED) strcpy((char *) p, (char *) start[2] );
i = charset_n;
while( i-- > 0 ){
if( charset[i].ch == value ){
free((void *) charset[i].str);
break;
} else {
if( (charset[i].ch < value)
|| ((charset[i].ch > value) && (i==0)) ){
if( charset[i].ch < value ){ i++; }
charset_n++;
for( j=charset_n; j>i; j-- ){
charset[j].ch = charset[j-1].ch;
charset[j].str = charset[j-1].str;
}
break;
} }
}
if(i == -1){ i = charset_n; }
if( i==charset_n ){ charset_n++; }
charset[i].str = p;
charset[i].ch = value;
>>>
The above backward search allows sorted files to be loaded in linear time.
\<mem for new 4ht entry\><<<
if( (charset_n+1) == max_charset_n){
max_charset_n += 10;
charset = (struct charset_rec *) r_alloc((void *) charset,
(size_t) ((max_charset_n) * sizeof(struct charset_rec) ));
}
>>>
%%%%%%%%%%%%%
\Section{Use Unicode Substitution}
%%%%%%%%%%%%%
\<process unicode\><<<
if( uni_code[1] != '#' ){
`<flush incomplete unicode`>
(IGNORED) putc( ch, htFile );
}
else{
int i, base, value, digit;
if( (uni_code[2] == 'x') || (uni_code[2] == 'X') ){
base =16; i=3;
} else { base=10; i=2; }
value = 0;
for( ; i<uni_code_p; i++ ){
digit = uni_code[i];
if( (digit>='0') && (digit<='9') ){ digit -= '0'; }
else if( (digit>='A') && (digit<='F') ){ digit -= BASE_A; }
else if( (digit>='a') && (digit<='f') ){ digit -= BASE_a; }
else { value = -1; break; }
if( digit >= base ){ value=-1; break; }
value = value*base + digit;
}
if( value<0 ){ `<flush incomplete unicode`>
(IGNORED) putc( ch, htFile );
} else {
`<search 4hf table`>
} }
>>>
\<search 4hf table\><<<
int bottom, mid, top;
BOOL found=FALSE;
bottom = 0; top = charset_n;
while( !found ){
mid = (bottom + top) / 2;
if( value == charset[mid].ch ){
`<put 4hf replacement`>
found = TRUE;
} else if( value < charset[mid].ch ){
if( bottom == top ){ break; }
top = mid;
}
else {
if ( bottom < mid ){ bottom = mid; }
else if ( bottom<top ){ bottom++; }
else{ break; }
}
}
if( ! found ){
if( u10 || utf8 ){ `<hex uni to base 10 or utf8`> }
`<flush incomplete unicode`>
if( !utf8 ){ (IGNORED) putc( ch, htFile ); }
}
>>>
\<put 4hf replacement\><<<
{ U_CHAR *p;
p = charset[mid].str;
while( *p != '\0' ){
if( *p=='\\' ){
p++;
if( *p=='\\' ){
(IGNORED) putc( '\\', htFile );
} else {
int i;
i = *p - '0';
while( *(++p) != '\\' ){ i = 10*i + *p - '0'; }
(IGNORED) putc( i, htFile );
} }
else {
(IGNORED) putc( *p, htFile );
if ( (*p=='&') && u10 ){ `<u10 for 4hf replacement`> }
}
p++;
} }
>>>
The utf8 option don't apply for unicode symbols coming from a 4hf fonts.
%%%%%%%%%%%%%
\Section{Base 10 for Entity Codes}
%%%%%%%%%%%%%
\<vars\><<<
static BOOL u10 = FALSE;
>>>
\<get unicode entity representations\><<<
if( eq_str(p+2, "10") ){ u10 = TRUE; }
`<else get utf8 unicode encoding`>
else{ bad_arg;}
>>>
\<hex uni to base 10 or utf8\><<<
short n;
long dec;
int ch;
char uni_10[MAX_UNI_CODE];
if( (uni_code[2] == 'x') || (uni_code[2] == 'X') ) {
dec = 0;
for( n=3; n<uni_code_p; n++ ){
ch = uni_code[n];
dec = 16*dec +
((ch > '9')?
( 10 + ((ch > 'Z')? (ch-'a') : (ch-'A')) )
: (ch-'0'));
}
if( u10 ){ `<dec to u10`> }
else { `<uni in utf8`> }
}
>>>
\<dec to u10\><<<
if( dec == 0 ){
uni_code_p = 3; uni_code[2] = '0';
} else {
n = 0;
while( dec > 0 ){ uni_10[ n++ ] = dec % 10 + '0'; dec /= 10; }
uni_code_p = 2;
while( n>0 ){ uni_code[ uni_code_p++ ] = uni_10[ --n ]; }
}
>>>
\<u10 for 4hf replacement\><<<
if ( *(p+1) == '#' ){
p++;
(IGNORED) putc( '#', htFile );
if ( (*(p+1) == 'x') || (*(p+1) == 'X') ){
int value, digit;
U_CHAR *q;
q = p+2;
value = 0;
digit = *(q++);
while( digit!=0 ){
if( (digit>='0') && (digit<='9') ){
value = value*16 + digit - '0';
}
else if( (digit>='A') && (digit<='F') ){
value = value*16 + digit - 'A'+10;
}
else if( (digit>='a') && (digit<='f') ){
value = value*16 + digit - 'a'+10; }
else {
if( digit == ';' ){
`<display value in u10`>
p=q-2;
}
break;
}
digit = *(q++);
}
} }
>>>
\<display value in u10\><<<
char uni_10[MAX_UNI_CODE];
int n;
n = 0;
while( value>0 ){
uni_10[ n++ ] = value % 10 + '0';
value /= 10;
}
while( n>0 ){
(IGNORED) putc( uni_10[--n], htFile );
}
>>>
%%%%%%%%%%%%%
\Section{UTF-8 for Entity Codes}
%%%%%%%%%%%%%
\Verbatim
U-00000000 - U-0000007F: 0xxxxxxx
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
\EndVerbatim
\<dec to utf8\><<<
if( dec < 0x80 ){
uni_code_p = 1; uni_code[0] = dec;
}
else if( dec < 0x800 ){
uni_code_p = 2;
uni_code[0] = (dec >> 6) | 0xC0;
uni_code[1] = (dec & 0x3F) | 0x80;
}
else if( dec < 0x10000 ){
uni_code_p = 3;
uni_code[0] = (dec >> 12) | 0xE0;
uni_code[1] = ((dec >> 6) & 0x3F) | 0x80;
uni_code[2] = (dec & 0x3F) | 0x80;
}
else if( dec < 0x200000 ){
uni_code_p = 4;
uni_code[0] = (dec >> 18) | 0xF0;
uni_code[1] = ((dec >> 12) & 0x3F) | 0x80;
uni_code[2] = ((dec >> 6) & 0x3F) | 0x80;
uni_code[3] = (dec & 0x3F) | 0x80;
}
else if( dec < 0x4000000 ){
uni_code_p = 5;
uni_code[0] = (dec >> 24) | 0xF8;
uni_code[1] = ((dec >> 18) & 0x3F) | 0x80;
uni_code[2] = ((dec >> 12) & 0x3F) | 0x80;
uni_code[3] = ((dec >> 6) & 0x3F) | 0x80;
uni_code[4] = (dec & 0x3F) | 0x80;
}
else if( dec <= 0x7FFFFFFF ){
uni_code_p = 6;
uni_code[0] = (dec >> 30) | 0xFC;
uni_code[1] = ((dec >> 24) & 0x3F) | 0x80;
uni_code[2] = ((dec >> 18) & 0x3F) | 0x80;
uni_code[3] = ((dec >> 12) & 0x3F) | 0x80;
uni_code[4] = ((dec >> 6) & 0x3F) | 0x80;
uni_code[5] = (dec & 0x3F) | 0x80;
}
>>>
\<vars\><<<
static BOOL utf8 = FALSE;
>>>
\<else get utf8 unicode encoding\><<<
else if( eq_str(p+2, "tf8") ){ utf8 = TRUE; }
>>>
\<uni in utf8\><<<
`<dec to utf8`>
else {
n = 0;
while( dec > 0 ){ uni_10[ n++ ] = dec % 10 + '0'; dec /= 10; }
uni_code_p = 2;
while( n>0 ){ uni_code[ uni_code_p++ ] = uni_10[ --n ]; }
}
>>>
%%%%%%%%%%%%%
\Section{Replacements for HTF}
%%%%%%%%%%%%%
\<store typed 4hf endtry\><<<
`<mem for new htf-4hf entry`>
(IGNORED) strcpy((char *) p, (char *) start[2] );
i = htf_4hf_n;
while( i-- > 0 ){
if( htf_4hf[i].ch == value ){
free((void *) htf_4hf[i].str);
break;
} else {
if( (htf_4hf[i].ch < value)
|| ((htf_4hf[i].ch > value) && (i==0)) ){
if( htf_4hf[i].ch < value ){ i++; }
htf_4hf_n++;
for( j=htf_4hf_n; j>i; j-- ){
htf_4hf[j].ch = htf_4hf[j-1].ch;
htf_4hf[j].str = htf_4hf[j-1].str;
htf_4hf[j].type1 = htf_4hf[j-1].type1;
htf_4hf[j].type2 = htf_4hf[j-1].type2;
}
break;
} } }
if(i == -1){ i = htf_4hf_n; }
if(i == htf_4hf_n){ htf_4hf_n++; }
htf_4hf[i].str = p;
htf_4hf[i].ch = value;
`<htf_4hf[i].type1 = ...`>
`<htf_4hf[i].type2 = ...`>
>>>
\<end loading fonts\><<<
for( i = 0; i<htf_4hf_n; i++){
free((void *) htf_4hf[i].str);
}
free((void *) htf_4hf);
>>>
\<mem for new htf-4hf entry\><<<
if( (htf_4hf_n+1) == max_htf_4hf_n){
max_htf_4hf_n += 10;
htf_4hf = (struct htf_4hf_rec *) r_alloc((void *) htf_4hf,
(size_t) ((max_htf_4hf_n) * sizeof(struct htf_4hf_rec) ));
}
p = m_alloc(char, (int) (start[3] - start[2]) );
>>>
\<htf_4hf[i].type1 = ...\><<<
value = 0;
p = start[1];
while( *p != '\0' ){
if( (*p < '0') || (*p > '9') ) break;
value = value * 10 + *p - '0';
p++;
}
htf_4hf[i].type1 = value;
>>>
\<htf_4hf[i].type1 = ...\><<<
value = 0;
p = start[3];
while( *p != '\0' ){
if( (*p < '0') || (*p > '9') ) break;
value = value * 10 + *p - '0';
p++;
}
htf_4hf[i].type2 = value;
>>>
\<types\><<<
struct htf_4hf_rec { int ch, type1, type2;
char* str; };
>>>
\<vars\><<<
static int htf_4hf_n = 0, max_htf_4hf_n;
static struct htf_4hf_rec *htf_4hf;
>>>
\<mem for charset\><<<
max_htf_4hf_n = 256;
htf_4hf = m_alloc(struct htf_4hf_rec, 256);
/* Zero the array, since otherwise some elements of htf_4hf may be used
before it's initialized. See
https://puszcza.gnu.org.ua/bugs/?611.
Eitan never wrote a wrapper like malloc_chk for calloc, so
just do it manually. */
memset (htf_4hf, 0, 256 * sizeof (struct htf_4hf_rec));
>>>
\<no mem for charset\><<<
max_htf_4hf_n = 0;
>>>
%%%%%%%%%%%%%
\Section{Propagate Changes into the HTF Fonts}
%%%%%%%%%%%%%
\<propagate 4hf info into htf\><<<
if(
(*str == '&')
&& (*(str+1) == '#')
&& ( (*(str+2) == 'x') || (*(str+2) == 'X'))
&& (*(str + strlen((char *) str) - 1) == ';')
) {
char* p;
int value = 0;
BOOL err = FALSE;
for( p=str+3; *p!=';'; p++){
int digit = (int) *p;
if( (digit>='0') && (digit<='9') ){ digit -= '0'; }
else if( (digit>='A') && (digit<='F') ){ digit -= BASE_A; }
else if( (digit>='a') && (digit<='f') ){ digit -= BASE_a; }
else { digit=0; err = TRUE; }
value = 16*value + digit;
}
if( !err ){
`<search 4hf replacement in htf-4hf`>
`<search 4hf replacement in charset`>
} }
>>>
\<htf replacement from htf-4hf\><<<
if( htf_4hf[mid].type1 == ch1 ){
ch1 = htf_4hf[mid].type2;
(IGNORED) strcpy((char *) str, (char *) htf_4hf[mid].str );
}
>>>
\<htf replacement from charset\><<<
if( charset[mid].type1 == ch1 ){
ch1 = charset[mid].type2;
(IGNORED) strcpy((char *) str, (char *) charset.str );
}
>>>
\<search 4hf replacement in htf-4hf\><<<
int bottom, mid, top;
BOOL found=FALSE;
bottom = 0; top = htf_4hf_n;
while( !found ){
mid = (bottom + top) / 2;
if( value == htf_4hf[mid].ch ){
`<htf replacement from htf-4hf`>
found = TRUE;
} else if( value < htf_4hf[mid].ch ){
if( bottom == top ){ break; }
top = mid;
}
else {
if ( bottom < mid ){ bottom = mid; }
else if ( bottom<top ){ bottom++; }
else{ break; }
}
}
>>>
\<htf replacement from charset\><<<
bottom = 0; top = charset_n;
while( !found ){
mid = (bottom + top) / 2;
if( value == charset[mid].ch ){
`<htf into 4hf`>
found = TRUE;
} else if( value < charset[mid].ch ){
if( bottom == top ){ break; }
top = mid;
}
else {
if ( bottom < mid ){ bottom = mid; }
else if ( bottom<top ){ bottom++; }
else{ break; }
}
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%
\Chapter{Utilities}
%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Output Character}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<header functions\><<<
static INTEGER insert_ch( ARG_I(int) );
>>>
\<functions\><<<
`[
static INTEGER insert_ch(ch) int ch
;{
if( !ignore_chs ){
BOOL flag;
`<flag = empty next-str?`>
if( !flag ){
try_new_line();
`<insert ch to text`>
text_on = TRUE;
} }
`<return char width`>
}
>>>
/* if (ch < 0) ch+=256; /*NEW */
\<flag = empty next-str?\><<<
flag = FALSE;
if( `<next-str`> ) {
if( eq_str(next_str, "") ) {
flag = TRUE;
free((void *) next_str);
next_str = (char *) 0;
} }
>>>
\<header functions\><<<
static void put_char( ARG_I(int) );
>>>
\<functions\><<<
`[
static void put_char( ch ) int ch
;{
if( !ignore_chs
&& !( ((ch==' ') || (ch=='\n')) && no_root_file )
){
`<open output file`>
if( ch_map_flag ){
if( special_on || ((ch != '\n') && (ch != ' ')) ){
`<insert ch to ch-map`> }
}else {
if ( ch == '\n' ){ `<insert eoln ch`> }
else if ( ch == ' ' ){ `<insert space ch`> }
else { (IGNORED) put_4ht_ch( ch, cur_o_file ); }
} } }
>>>
\<vars\><<<
static BOOL special_on = FALSE;
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Output String}
%%%%%%%%%%%%%%%%%%%%%%%%%
\<header functions\><<<
static void print_f( ARG_I(const char*) );
>>>
\<functions\><<<
`[
static void print_f(str) const char* str
;{
`<open output file`>
if( ch_map_flag ){
while( *str ){ put_char( *str ); str++; }
}else {
(IGNORED) print_f_4ht( str );
} }
>>>
% fprintf(cur_o_file, "%s", str);
\<header functions\><<<
static void print_f_4ht( ARG_I(const char*) );
>>>
\<functions\><<<
`[
static void print_f_4ht(str) const char* str
;{
`<open output file`>
if( ch_map_flag ){
while( *str ){ put_char( *str ); str++; }
} else {
while( *str ){
(IGNORED) put_4ht_ch( *str, cur_o_file );
str++;
} } }
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Read Character}
%%%%%%%%%%%%%%%%%%%%%%%%%
\<header functions\><<<
static int get_char( ARG_I(void) );
>>>
\<functions\><<<
static int get_char(MYVOID)
{
return (int) getc(dvi_file);
}
>>>
\<header functions\><<<
static int get_noop( ARG_I(void) );
>>>
\<functions\><<<
static int get_noop(MYVOID)
{ int ch;
while( (ch = get_char()) == `<no op`> ){;}
return ch;
}
>>>
\Section{Read Strings}
\<header functions\><<<
static char* get_str( ARG_I(int) );
>>>
\<functions\><<<
`[
static char* get_str(n) int n
;{ U_CHAR *q, *p;
p = q = m_alloc(char,n+1);
while( n-- ) *q++ = get_char();
*q = '\0';
return p;
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Read Unsigned Integer}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<defines\><<<
#define get_unt(n) fget_unt(dvi_file,n)
>>>
\<header functions\><<<
static long fget_unt( ARG_II(FILE*, int) );
>>>
\<functions\><<<
`[
static long fget_unt( file, n )
FILE* file`;
register int n
;{ register long val = 0;
while( n-- ){ val = (val << 8) + (unsigned INTEGER) getc(file) ; }
return val;
}
>>>
\Section{Read Signed Integer}
\<defines\><<<
#define get_int(n) fget_int(dvi_file,n)
>>>
\<header functions\><<<
static long fget_int( ARG_II(FILE *, int) );
>>>
\<functions\><<<
`[
static long fget_int( file, n )
FILE *file`;
int n
;{ register long val;
val = (unsigned INTEGER) getc(file);
if( val & 0x80 ) val -= 0x100;
while( --n ){ val = (val << 8) + (unsigned INTEGER) getc(file); }
return val;
}
>>>
The implementation in dvi2ps takes the following form with the comment
``This code assumes that the right-shift is an arithmetic, rather than
logical, shift which will propagate the sign bit right. According to
Kernighan and Ritchie, this is compiler dependent!''.
\Verbatim
val = getc(fp);
n1 = n--;
while (n--) { val <<= 8;
val |= getc(fp); }
val<<=32-8*n1;
val>>=32-8*n1; /* sign extend */
\EndVerbatim
\<header functions\><<<
static long cond_int( ARG_I(register INTEGER) );
>>>
\<functions\><<<
`[
static long cond_int( n )
register INTEGER n
;{ register long val;
int ch;
val = (unsigned int) (ch = get_char());
cond_idv_char( ch );
if( val & 0x80 ) val -= 0x100;
while( --n ){
val = (val << 8) + (unsigned int) (ch = get_char());
cond_idv_char( ch );
}
return val;
}
>>>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Strings Equality}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<defines\><<<
#define eq_str(x,y) (!strcmp(x,y))
#define gt_str(x,y) (strcmp(x,y)>0)
>>>
\Section{Warnings and Errors}
\SubSection{Messages}
\<warn and err messages\><<<
`<command line options`>, `%0`%
"Can't find/open file ``%s'\n", `%1`%
"Can't open output file for ``%s'\n", `%2`%
"Can't close file ``%s' (file is not open)\n", `%3`%
"Insufficient memory\n", `%4`%
"Bad character code: %d\n", `%5`%
"Can't find font number %d\n", `%6`%
"Improper dvi file\n", `%7`%
"Improper op while scanning font defs in postamble\n",`%8`%
"Problem with command line\n", `%9`%
"Font definition repeated in postamble\n", `%10`%
"Empty entry in font environment variable\n", `%11`%
"Can't access directory ``%s\n'", `%12`%
"Too many directories in font environment variable\n",`%13`%
"Missing fonts, can't proceed\n", `%14`%
"Invalid header in file ``%s'\n", `%15`%
"Checksum inconsistent\n", `%16`%
"MAXFONTS too small: %d\n", `%17`%
"Improper signature at end of file ``%s.htf'\n", `%18`%
"Improper signature at start of file ``%s.htf'\n", `%19`%
"Improper file ``%s.htf'\n", `%20`%
"Couldn't find font ``%s.htf' (char codes: ", `%21`%
"File ``%s.htf' starts/ends with character code %d (instead of %d)\n",`%22`%
"Implementation problem\n", `%23`%
"Improper groups in \\special{t4ht+}... idv[%d]\n", `%24`%
"Too many characters (> %d) for map line: ``%c'\n", `%25`%
"Extra characters in \\special{t4ht%c...", `%26`%
"Page break within a ch map/picture\n", `%27`%
"Char code >255 in htf file: %d\n", `%28`%
"Improper char for code in htf file: %c\n", `%29`%
`<signals messages: 30-32`> `%30-32`%
#ifdef DOS_WIN32
"%c-script too long in tex4ht.env \n", `%33`%
#else
"%c-script too long in tex4ht.env (.tex4ht)\n", `%33`%
#endif
"Too many rows (> %d) for map: ``%c'\n", `%34`%
"More than 256 strings in font\n", `%35`%
"\\special{t4ht;%c...}?\n", `%36`%
"\\special{t4ht;|%s}?\n", `%37`%
"\\special{t4ht~!%s}?\n", `%38`%
"\\special{t4ht\"...%s}?\n", `%39`%
"System error 40\n", `%40`%
"``%c' in \\special{t4ht@...} or \\special{t4ht@-...}?\n", `%41`%
"\\special{t4ht~...} without \\special{t4ht~}\n", `%42`%
"Ignoring \\special{t4ht.%s}\n", `%43`%
"PUSH for \\special{t4ht<...%s}?\n", `%44`%
"Bad character code (%d) in \\special{t4h~}...\n", `%45`%
"Page break in \\special{t4h~}...\n", `%46`%
"tex4ht.fls: Couldn't find file ``%s'\n", `%47`%
"Improper entry (line %d)\n", `%48`%
"Improper environment variable %s: ``%s'\n", `%49`%
"Missing %s\n", `%50`%
"Can't back from file ``%s\n'", `%51`%
"\\special{t4ht%s}?\n", `%52`%
"Improper -v option\n", `%53`%
>>>
\<vars\><<<
static const U_CHAR *warn_err_mssg[]={ `<warn and err messages`> "" };
>>>
\SubSection{Warnings Commands}
\<header functions\><<<
static void warn_i( ARG_I(int) );
>>>
\<functions\><<<
`[
static void warn_i(n) int n
;{ (IGNORED) fprintf(stderr,"--- warning --- ");
(IGNORED) fprintf(stderr, "%s", warn_err_mssg[n]);
show_err_context();
}
>>>
\<warning(22\><<<
(IGNORED) fprintf(stderr,"--- warning --- ");
(IGNORED) fprintf(stderr, warn_err_mssg[22]
>>>
% \<vars\><<<
% static U_CHAR warning[] = "--- warning --- ";
% >>>
\<header functions\><<<
static void warn_i_int( ARG_II(int,int) );
>>>
\<functions\><<<
`[
static void warn_i_int(n,i) int n`; int i
;{ (IGNORED) fprintf(stderr,"--- warning --- ");
(IGNORED) fprintf(stderr, warn_err_mssg[n], i);
show_err_context();
}
>>>
\<header functions\><<<
static void warn_i_int_2( ARG_III(int,int,int) );
>>>
\<functions\><<<
`[
static void warn_i_int_2(n,i,j) int n`; int i`; int j
;{ (IGNORED) fprintf(stderr,"--- warning --- ");
(IGNORED) fprintf(stderr, warn_err_mssg[n], i, j);
show_err_context();
}
>>>
\<header functions\><<<
static void warn_i_str( ARG_II(int,const char *) );
>>>
\<functions\><<<
`[
static void warn_i_str(n,str)
int n`;
const char *str
;{
(IGNORED) fprintf(stderr,"--- warning --- ");
(IGNORED) fprintf(stderr,warn_err_mssg[n], str);
show_err_context();
}
>>>
\<header functions\><<<
static void warn_i_str2( ARG_III(int,const char *,const char *) );
>>>
\<functions\><<<
`[
static void warn_i_str2(n,str1,str2)
int n`;
const char *str1`;
const char *str2
;{ (IGNORED) fprintf(stderr,"--- warning --- ");
(IGNORED) fprintf(stderr,warn_err_mssg[n], str1,str2);
show_err_context();
}
>>>
\SubSection{Error Commands}
\<defines\><<<
#define bad_arg err_i(0)
#define bad_in_file(name) err_i_str(1,name)
#define bad_out_file(name) err_i_str(2,name)
#define bad_special(name) warn_i_str(3,name)
#define bad_mem err_i(4)
#define bad_char(chr) warn_i_int(5,chr)
#define bad_dvi err_i(7)
>>>
\<header functions\><<<
static void err_i( ARG_I(int) );
>>>
\<functions\><<<
`[
static void err_i(n) int n
;{ (IGNORED) fprintf(stderr,"--- error --- ");
(IGNORED) fprintf(stderr, "%s", warn_err_mssg[n]);
show_err_context();
exit(EXIT_FAILURE);
}
>>>
REPLACE EXIT with somthing that DOS also accept.
\<header functions\><<<
static void err_i_int( ARG_II(int,int) );
>>>
\<functions\><<<
`[
static void err_i_int(n,i) int n`; int i
;{ (IGNORED) fprintf(stderr,"--- error --- ");
(IGNORED) fprintf(stderr, warn_err_mssg[n], i);
show_err_context();
exit(EXIT_FAILURE);
}
>>>
\<header functions\><<<
static void err_i_str( ARG_II(int,char *) );
>>>
\<functions\><<<
`[
static void err_i_str(n,str)
int n`;
U_CHAR *str
;{ (IGNORED) fprintf(stderr,"--- error --- ");
(IGNORED) fprintf(stderr, warn_err_mssg[n], str);
show_err_context();
exit(EXIT_FAILURE);
}
>>>
\SubSection{Extra Characters in Special}
\<check for extra special chars\><<<
if( special_n > 0 ){
warn_i_int( 26, sv);
while( special_n-- ) (IGNORED) putc( get_char(), stderr);
}
>>>
\<special resolution err\><<<
warn_i_int( 26, '!');
(IGNORED) putc( ch, stderr);
while( special_n-- ) (IGNORED) putc( get_char(), stderr);
>>>
\<consume unused specials\><<<
while( special_n-- ) (void) get_char();
>>>
\<unused special\><<<
;
>>>
\<header functions\><<<
static void show_err_context( ARG_I(void) );
>>>
\<functions\><<<
static void show_err_context(MYVOID)
{ long curr_pos;
int n, i;
U_CHAR ch;
if( err_context ){
curr_pos = ftell(dvi_file);
for(n=6; n--;){
(IGNORED) putc( '\n', stderr );
for(i=70; i--;){
ch = get_char();
(IGNORED) putc(( (ch>31) && (ch<127))? ch : ' ', stderr);
}
}
(IGNORED) fseek(dvi_file, curr_pos, `<abs file addr`>);
(IGNORED) putc( '\n', stderr ); (IGNORED) putc( '\n', stderr );
if( err_mark ){ print_f( err_mark ); }
} }
>>>
A static informs that the function is defined just locally, preventing
possible errors when the linker detects name conflicts with definitions
that use the same names in other libraries.
\<static\><<<
static
>>>
\<vars\><<<
static BOOL err_context = FALSE;
>>>
\<context for warn and errs\><<<
err_context = TRUE;
>>>
\SubSection{Trace Errors}
\<vars\><<<
static U_CHAR *err_mark = (char *) 0;
>>>
\<get err str\><<<
if( err_mark ){ free((void *) err_mark); }
if( special_n ){
err_mark = get_str( (int) special_n ); special_n=0;
} else { err_mark = (char *) 0; }
>>>
\SubSection{Trace Specials}
\<trace specials in dvi\><<<
trace_special = TRUE;
>>>
\<vars\><<<
static BOOL trace_special = FALSE;
>>>
On off with \''\special{t4ht@/}'
\<on/off special trace\><<<
trace_special = !trace_special;
>>>
\<trace specials\><<<
{ long curr_pos;
int n, i;
U_CHAR ch;
curr_pos = ftell(dvi_file);
print_f("\nSPECIAL: "); ch = special_hd[8]; i=60;
for( n=*special_n - 3; n--;){
if( !i ){ (IGNORED) putc( '\n', cur_o_file ); i=70; }
else i--;
(IGNORED) putc(( (ch>31) && (ch<127))? ch : ' ', cur_o_file);
ch = get_char();
}
(IGNORED) putc( '\n', cur_o_file );
(IGNORED) fseek(dvi_file, curr_pos, `<abs file addr`>);
}
>>>
\Section{Dos}
\List{disc}
\item
\''open(file, ..._FLAGS)': All
text files should be opened with "r" or "w"; all binary files with "rb"
or "wb". This is the portable way and will work both under Unix and DOS;
as Unix doesn't distinguish between text and binary files, its compilers
will simply ignore the "b" part.
Unix doesn't differentiate between binary binary and text files.
In general, the rule of thumb is that DOS/Windows ports should recognize
both back- and forward-slashes in their input (such as command-line
arguments), but can construct file names by using forward slashes alone
(since DOS/Windows kernels support both styles).
On the subject of the .lg file: you open the tex4ht.env file in binary
mode ("rb"), which results in strange line endings in the .lg file
(CR/CR/LF). Changing the mode to "r" fixes this.
\''(WRITE_BIN_FLAGS, READ_BIN_FLAGS)': Use binary mode with DJGPP. ---captured with the \''__MSDOS__'.
\<defines\><<<
#if defined(DOS_WIN32) || defined(__MSDOS__)
#define READ_BIN_FLAGS "rb"
#define READ_TEXT_FLAGS "r"
#define WRITE_BIN_FLAGS "wb"
#ifdef WIN32
#define WRITE_TEXT_FLAGS "wb"
#else
#define WRITE_TEXT_FLAGS "w"
#endif
#else
#define READ_BIN_FLAGS "r"
#define READ_TEXT_FLAGS "r"
#define WRITE_BIN_FLAGS "w"
#define WRITE_TEXT_FLAGS "w"
#endif
>>>
\Verbatim
I've been held up a bit by a
bug in tex4ht.c that caused emTeX to complain that the created .idv file
was corrupt:
*** Fatal error 2106: corrupt DVI file (postamble not found)
I found the cause, though: you open it with mode "w", which is text
mode. Changing this to "wb" solved the problem.
\EndVerbatim
\item Argument types in declaration of functions
Function prototypes
\<tex4ht.h\><<<
#ifdef DOS
#define PROTOTYP
#endif
#ifdef ANSI
#define PROTOTYP
#endif
#ifdef KWIN32
#define PROTOTYP
#endif
>>>
\<defines\><<<
#ifdef PROTOTYP
#define MYVOID void
#define ARG_I(x) x
#define ARG_II(x,y) x,y
#define ARG_III(x,y,z) x,y,z
#define ARG_IV(x,y,z,w) x,y,z,w
#define ARG_V(x,y,z,w,v) x,y,z,w,v
#define ARG_VI(x,y,z,w,v,u) x,y,z,w,v,u
#define ARG_VII(x,y,z,w,v,u,t) x,y,z,w,v,u,t
#else
#define MYVOID
#define ARG_I(x)
#define ARG_II(x,y)
#define ARG_III(x,y,z)
#define ARG_IV(x,y,z,w)
#define ARG_V(x,y,z,w,v)
#define ARG_VI(x,y,z,w,v,u)
#define ARG_VII(x,y,z,w,v,u,t)
#endif
>>>
\EndList
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Op Codes of Dvi}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\<insert rule + move op\><<<
132 >>>
\<insert rule + nomove op\><<<
137 >>>
\<no op\><<<
138 >>>
\<start page op\><<<
139 >>>
\<end page op\><<<
140 >>>
\<sv loc op\><<<
141 >>>
\<retrieve loc op\><<<
142 >>>
\<mv hor 1-byte\><<<
143 >>>
\<mv hor 2-byte\><<<
144 >>>
\<mv hor 3-byte\><<<
145 >>>
\<mv hor 4-byte\><<<
146 >>>
\<mv hor dist dx.1\><<<
147 >>>
\<dx.1 store and mv hor 1-byte\><<<
148 >>>
\<dx.1 store and mv hor 2-byte\><<<
149 >>>
\<dx.1 store and mv hor 3-byte\><<<
150 >>>
\<dx.1 store and mv hor 4-byte\><<<
151 >>>
\<mv hor dist dx.2\><<<
152 >>>
\<dx.2 store and mv hor 1-byte\><<<
153 >>>
\<dx.2 store and mv hor 2-byte\><<<
154 >>>
\<dx.2 store and mv hor 3-byte\><<<
155 >>>
\<dx.2 store and mv hor 4-byte\><<<
156 >>>
\<mv ver 1-byte\><<<
157 >>>
\<mv ver 2-byte\><<<
158 >>>
\<mv ver 3-byte\><<<
159 >>>
\<mv ver 4-byte\><<<
160 >>>
\<mv ver dist dy.1\><<<
161 >>>
\<dy.1 store and mv ver 1-byte\><<<
162 >>>
\<dy.1 store and mv ver 2-byte\><<<
163 >>>
\<dy.1 store and mv ver 3-byte\><<<
164 >>>
\<dy.1 store and mv ver 4-byte\><<<
165 >>>
\<mv ver dist dy.2\><<<
166 >>>
\<dy.2 store and mv ver 1-byte\><<<
167 >>>
\<dy.2 store and mv ver 2-byte\><<<
168 >>>
\<dy.2 store and mv ver 3-byte\><<<
169 >>>
\<dy.2 store and mv ver 4-byte\><<<
170 >>>
\<font 0\><<<
171 >>>
\<eof op\><<<
223 >>>
\<font 63\><<<
234 >>>
\<number of direct fonts\><<<
63 >>>
\<font 1-byte\><<<
235 >>>
\<font 2-bytes\><<<
236 >>>
\<font 3-bytes\><<<
237 >>>
\<font int\><<<
238 >>>
\<special 1\><<<
239 >>>
\<special 2\><<<
240 >>>
\<special 3\><<<
241 >>>
\<special 4\><<<
242 >>>
\<def 1 byte font\><<<
243 >>>
\<def 2 byte font\><<<
244 >>>
\<def 3 byte font\><<<
245 >>>
\<def 4 byte font\><<<
246 >>>
\<start preamble op\><<<
247 >>>
\<begin-postamble op\><<<
248 >>>
\<end-postamble op\><<<
249 >>>
\<start of preamble op\><<<
247 >>>
The ops 250--255 have no meaning in dvi.
\<xdv-pic-file op\><<<
251
>>>
\<xdv-native-font-def op\><<<
252
>>>
\<xdv-glyph-array op\><<<
253
>>>
\<xdv-glyph-string op\><<<
254
>>>
%%%%%%%%%%%%%%%%%%
\Chapter{References}
%%%%%%%%%%%%%%%%%%
\Link[
http://www.eskimo.com/\string~scs/C-faq/top.html]{}{}comp.lang.c\EndLink
{ \tt --}
\Link[
http://www.cs.tut.fi/\string~leopold/Boar/AnsiLib]{}{}AnsiLib\EndLink
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%Line break needed for #defines !!!!
\Comment{
/*}{*/
}
\ifx \export\:UnDef \else
\Comment{
}{}
\fi
\OutputCodE\<tex4ht.c\>
\bye