% $Id: tex4ht-c.tex 952 2021-06-17 21:47:06Z karl $ % tex tex4ht-c % Copyright 2009-2018 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{}#1\HCode{}\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 (`version), generated from `jobname.tex Copyright 2009-2018 TeX Users Group Copyright `CopyYear.1996. Eitan M. Gurari ` */ ` >>> \<<< /* Compiler options (uncommented | command line), as comments: Classic C (CC) default #define ANSI ansi-c, c++ #define DOS_C #define HAVE_STRING_H #define HAVE_DIRENT_H #define HAVE_SYS_NDIR_H #define HAVE_SYS_DIR_H #define HAVE_NDIR_H #define HAVE_IO_H #define HAVE_UNISTD_H #define WIN32 #define KPATHSEA #define CDECL ....... #define BCC32 bordland c++ ************************************************* Tex4ht variables * (uncommented | command line) * ----------------------------------------------- */ ` /* ******************************************** */ ` ` ` ` #ifdef DOS_C #define DOS #endif #ifdef DOS #define DOS_WIN32 #define HAVE_STRING_H #endif #ifdef WIN32 #define DOS_WIN32 #ifndef KPATHSEA #define HAVE_STRING_H #endif #endif ` ` ` ` >>> % lint tex4ht.c -DHAVE_DIRENT_H -DHAVE_STRING_H \<<< #ifdef BCC32 #define DOS_WIN32 #define ANSI #define HAVE_DIRENT_H #define PLATFORM "ms-win32" #endif >>> The \`'-mh' below calls for `Huge Memory Model'. \<<< #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 \<<< #ifdef KPATHSEA #include #endif #include `% EOF, FILE `% #include `% EXIT_FAILURE `% >>> \<<< #ifndef EXIT_FAILURE #define EXIT_FAILURE 1 #endif >>> \<<< #ifdef HAVE_STRING_H #include #endif >>> Under ANSI C, all malloc stuff is declared in \`'' (which you also include), hence this non-standard header is redundant. \SubSection{MSVC-1-52-DOS} \<<< #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 >>> \<<< #ifndef MSVC_1_52_DOS >>> \<<< #endif >>> \SubSection{KPATHSEA Windows} % #define NOSUBDIR % MSVC_1_52_DOS -> DOS, NOSUBDIR, not % ANSI --> PROTOTYPE % DOS -> \<<< #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. \<<< #ifdef WIN32 #ifdef KPATHSEA #undef CDECL #define CDECL __cdecl #else #include #endif #else #ifdef KPATHSEA #define CDECL #endif #endif >>> \<<< #ifndef WIN32 >>> \<<< #endif >>> \
<<< #ifdef WIN32 static BOOL sigint_handler(ARG_I(DWORD)); #endif >>> \<<< #ifdef WIN32 `[ static BOOL sigint_handler(dwCtrlType) DWORD dwCtrlType ;{ ` err_i(32); return FALSE; `% return value obligatory `% } #endif >>> \<<< if( dwCtrlType ){ (IGNORED) printf(" "); } >>> \<<< #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. \<<< #ifdef WIN32 static U_CHAR dirname[PATH_MAX]; #endif >>> Recursive directory search relies on some Windows API similar to opendir/readdir \<<< { 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) { ` } } >>> \<<< 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. \<<< 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 \<<< #ifdef __DJGPP__ #define DOS_WIN #define ANSI #ifndef HAVE_STRING_H #define HAVE_STRING_H 1 #endif #endif >>> \<<< 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. \<<< #if !defined(DOS_GIF_FILE) && !defined(WIN32) && defined(__DJGPP__) dos_file_names = !_use_lfn(NULL); #endif >>> \<<< 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 \<<< #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 (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 \<<< #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. \<<< #include `% INT_MAX `% >>> \<<< #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 \<<< #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} \<<< #include >>> \
<<< static void ` sig_err(ARG_I(int)); >>> \<<< `[ static void ` 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:(sig\_err): If got fatal signal other than SIGINT or SIGQUIT, don't return to caller, since the program will be aborted otherwise. \<<< #ifdef SIGSEGV (void) signal(SIGSEGV,sig_err); #endif (void) signal(SIGFPE,sig_err); #ifdef WIN32 ` #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. \<<< "Illegal storage address\n", `%30 segmentation`% "Floating-point\n", `%31 `% "Interrupt with Cntr-C\n", `%32 `% >>> \Section{Kpathsea} \<<< #ifdef KPATHSEA #include #include #include #include #include #include #include #include #include #if !defined(_AMIGA) && !defined(WIN32) #include #endif #include #include #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} %%%%%%%%%%%%%%%%%% \<<< ` ` ` `
` `[ int ` main(argc, argv) int argc`; U_CHAR **argv ;{ ` ` ` ` ` ` ` 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? \<<< (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; i1)?"\n " : "", argv[i]); } (IGNORED) printf("\n"); >>> \SubSection{Main's Body} \<<< { long file_len; ` ` ` ` ` ` ` (IGNORED) fclose(dot_file); } ` ` put_char('\n');put_char('\n'); ` { ` ` } >>> \<<< static FILE* dot_file; >>> \<<< register INTEGER i; int ch; >>> \SubSection{Utilities} \<<< #define m_alloc(typ,n) (typ *) malloc_chk((int) ((n) * sizeof(typ))) >>> \
<<< static void* malloc_chk(ARG_I(int)); >>> \<<< `[ static void* malloc_chk( n ) int n ;{ void* p; if((p = (void *) malloc( (size_t) n)) == NULL ) bad_mem; return p; } >>> \
<<< static void* r_alloc(ARG_II(void *, size_t)); >>> \<<< `[ 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, * \<<< { int i; U_CHAR *p; const U_CHAR *in_name = "", *out_name = ""; ` ` if( argc == 1 ){ bad_arg; } for(i=1; i } else in_name = argv[i]; } ` ` { `
 }
}
>>>


If \`'strlen()  argv[i] ) == 2' the switch is followed by a
space to be deleted when more input is awaited.





\<<<
if( (int) strlen((char *)  argv[i] ) == 2 ){
   if( ++i == argc ) bad_arg;  
} 
switch( *(p+1) ){
  case 'b':{ `     break; }
  case 'c':{ `  break;}   
  case 'e':{ `break; }
  case 'f':{ `break; }
  case 'F':{ `             break; }
  case 'g':{ `      break; }
  case 'h':{ `  break; }
  case 'i':{ `  break; }
  case 'l':{ `  break; }
  case 'P':{ `  break; }
  case 'S':{ `    break; }
  case 's':{ `    break; }
  case 't':{ `  break; }
  case 'u':{ `  break; }
  case 'v':{ `  break; }
  case 'x':{ `  break; }
  case '.':{ `  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.

\<<<
p = p + 2;
in_name = p + (int) strlen((char *)  p );
while( *in_name != *p ){ in_name--; }
in_name++;
>>>


Suboptions for \`'-h'.

\<<<
{
  char trace = *(p+2);
  if (trace == 'A' || trace == 'e') { ` }
  if (trace == 'A' || trace == 'f') { ` }
  if (trace == 'A' || trace == 'F') { ` }
  if (trace == 'A' || trace == 's') { ` }
  if (trace == 'A' || trace == 'g') { ` }
  if (trace == 'A' || trace == 'v') { ` }
  if (trace == 'A' || trace == 'V') { ` }
  else { bad_arg; }
}
>>>

The following is incomplete!!!


\<<<
"improper command line\ntex4ht [-f]in-file[.dvi]\n"
"   [-.]            replacement to default file extension name .dvi\n"
"   [-c]       choose named segment in env file\n"
"   [-e]\n"
"   [-f]        remove path from the file name\n"
"   [-F]        replacement for missing font characters; 0--255; default 0\n"
"   [-g]\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]\n"
"   [-l]\n"
"   [-P(*|)]     permission for system calls: *-always, filter\n"
"   [-S]\n"
"   [-s]   default: -s4cs; multiple entries allowed\n"
"   [-t]\n"
"   [-u10]               base 10 for unicode characters\n"
"   [-utf8]              utf-8 encoding for unicode characters\n"
"   [-v]    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}

\<<<
#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}

\<<<
static FILE* dvi_file;
>>>

\<<<
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.

\<<<
if( *in_name != '\0' ){ ` }
#ifdef KWIN32
   else if (!isatty(fileno(stdin))) SET_BINARY(fileno(stdin));
#endif
>>>


\<<<
      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-`,`);
}
if( tag ){
   job_name_n+=`; (IGNORED) strct(job_name, `);  
}
if( (dvi_file = fopen(job_name, READ_BIN_FLAGS)) == NULL )
   { ` }
`
>>>


\<<<
(
  (ext==NULL)? 4 : (int) strlen((char *) ext) 
)
>>>

\<<<
(
  (ext==NULL)? ".dvi" : ext 
)
>>>




\<<<
static U_CHAR *ext = NULL;
>>>


\<<<
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.

\<<<
{                             int i;
   for(i=job_name_n-5; i; i--){ 
     if( job_name[i] == '.' ){
       job_name[i] = '\0';
       job_name_n = i + `;
       (IGNORED) strct(job_name, `);  
       break; 
   } }
   if( (dvi_file = fopen(job_name, READ_BIN_FLAGS)) == NULL ){
      warn_i_str(1, job_name); bad_in_file(job_name);
}  }
>>>


\<<<
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 *) );'.

\
<<< static void strct( ARG_II(char *, const U_CHAR *) ); >>> \<<< `[ 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} \
<<<
   U_CHAR *name=0;
if( *out_name == '\0' ) 
  { if( *in_name == '\0' ){ ` }
    else                  { ` }
  }
else{ ` }
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.

\<<<
if( no_root_file ){  open_o_file(); }
>>>


\
<<< static void open_o_file( ARG_I(void) ); >>> \<<< static void open_o_file(MYVOID) { ` cur_o_file = root_file = open_html_file(no_root_file); no_root_file = (char *) 0; } >>> % free((void *) no_root_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 = >>> \<<< if( !no_root_file ){ ` if( !` ){ put_char('\n'); } } >>> \<<< 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); } >>> \<<< static U_CHAR *no_root_file; >>> \<<< bad_arg; >>> \<<< 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 >>> \<<< 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 >>> \
<<< static FILE* open_html_file( ARG_I(char*) ); >>> \<<< `[ 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; } >>> \<<< static FILE *out_file = (FILE *) 0, *root_file = (FILE *) 0, *cur_o_file = (FILE *) 0; >>> \SubSection{Close Output} \<<< 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: \<<< { 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, `); 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 #include #include #include // 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: try.htex 000163: POP 000164: Y3: 786432 000168: PUSH 000169: PUSH 000170: RIGHT3: 1310720 000174: Char:

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: 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 \EndVerbatim \egroup %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \SubSection{No Op's at the End of File} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Verify \LinkPort\{}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. \<<< i=0; do{ i++; file_len -= 1; (IGNORED) fseek(dvi_file, file_len, `); } while( (ch=get_char()) == ` ); eof_op_n = file_len; if( (i<4) || ((ch != `) && (ch > `)) ) bad_dvi; version_id = ch; >>> \<<< static int version_id; >>> The function \`'(IGNORED) fseek' enables random access to file. \<<< 0>>> \<<< 1>>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \SubSection{Find Start of Postamble} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< file_len -= 5; (IGNORED) fseek(dvi_file, file_len, `); if( get_char() != ` ) bad_dvi; eof_op_n -= begin_postamble = get_unt(4); (IGNORED) fseek(dvi_file, begin_postamble, `); >>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \SubSection{Find Stack Size and Number of Pages} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< if( get_char() != ` ) bad_dvi; (IGNORED) fseek(dvi_file, 16L, `); ` if( (stack_len = (int) get_unt(2)) < 1) bad_dvi; ` unread_pages = (int) get_unt(2); >>> \<<< int unread_pages; >>> \<<< static int stack_len; >>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \SubSection{Font Definitions} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< { ` BOOL missing_fonts; #ifndef KPATHSEA ` ` ` #endif ` missing_fonts = FALSE; ` ` ` while( (ch = get_char()) != ` ){ ` } ` if( missing_fonts ) err_i(14); #ifndef KPATHSEA ` ` #endif ` } >>> %%%%%%%%%%%%%%%%%%%%%%%%%%%% \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 \<<< (IGNORED) fseek(dvi_file, 0L, `); ch = get_noop(); if( ch != ` ) 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(); >>> \<<< 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. \<<< 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 \<<< { dis_pages = unread_pages; while( unread_pages-- ){ (IGNORED) printf("[%d", dis_pages - unread_pages); ` if( get_noop() != ` ) bad_dvi; for( i = 1; i<45; i++ ) if( get_char() == EOF ) bad_dvi; while( (ch = get_char()) != ` ){ ` } ` (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 \`'

' elements




\<<<
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 
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  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:
> 

> 
> 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 

) and second, each piece of LaTeX math > is converted into a GIF graphic and inserted into the HTML document > with an 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. > \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 \`'', 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 \<<< if( special_n>1 ) { special_n--; if ( get_char() == '%' ) { if( special_n>2 ) { ` } else { ` } } else { ` } } 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. \<<< if( span_on && (default_font != font_tbl[cur_fnt].num) ){ if( !ch_map_flag && start_span ){ if( span_name_on ){ ` 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...`> \<<< static BOOL start_span = FALSE, in_span_ch = FALSE; >>> \<<< if( span_on && !in_span_ch && !ignore_chs && !in_accenting && (default_font != font_tbl[cur_fnt].num) ){ if( (ch < 137) && (ch != `) ){ in_span_ch = TRUE; start_span = TRUE; } } else if ( in_span_ch ){ if( !span_on || (ch == `) || ((136 < ch) && (ch < `)) || (ch > `) ){ in_span_ch = FALSE; if( *end_span[0] ){ ` (IGNORED) fprintf(cur_o_file, "%s", end_span[0]); } } } >>> \<<< if( span_on && in_span_ch ){ if( *end_span[0] ){ in_span_ch = FALSE; ` (IGNORED) fprintf(cur_o_file, "%s", end_span[0]); } } >>> \<<< >>> \<<< if( trace_dvi_C && !in_trace_char ){ if( (ch < 137) && (ch != `) ){ in_trace_char = TRUE; block_start = TRUE; } } else if ( in_trace_char ){ if( !trace_dvi_C || (ch > 136) || (ch == `) ){ 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 ....'. \<<< 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'; >>> \<<< 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 ); >>> \<<< 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); >>> \<<< 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]; >>> \<<< 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, "]" ); } >>> \<<< 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); >>> \
<<< static void set_del( ARG_II(char **, U_CHAR **) ); >>> \<<< `[ 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} \<<< 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); } >>> \<<< 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); } >>> \<<< 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); } >>> \<<< 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); } >>> \<<< if( push_depth<256 ) { push_st[push_depth] = push_id++; } if( trace_dvi_P && !ch_map_flag ){ ` 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++; >>> \<<< push_depth--; if( trace_dvi_P && !ch_map_flag ){ ` 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); } >>> \<<< if( trace_dvi_C ){ if( !ch_map_flag ){ ` 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; } >>> \<<< if( trace_dvi_C && !ch_map_flag ){ ` (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} \<<< { register int ch_1; ch_1 = ch; ` ` ` ` ` `
   if( ch < 132 )  {  
      x_val += math_class_on? ` 
                            : 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?  ` : insert_ch(ch_1);
         max_x_val = ( x_val + w > max_x_val )?  x_val + w : max_x_val; 
         break;
      }
      ` 
   } 
}
>>>

Characters are inserted into the html file as they are
encountered. Spaces are inserted when \`'x_val' is larger than
\`'max_x_val'. 

\<<<
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.

\
<<< static void try_new_line( ARG_I(void) ); >>> \<<< static void try_new_line(MYVOID) { long int v; double dy; dy = (cur_fnt == -1)? 0.0 : (` * `) ; v = y_val - prev_y_val; if( !text_on && (y_val > max_y_val) ){ if( v > dy/2.5 ){ ` max_x_val = -10000; prev_y_val = max_y_val = stack_n? y_val : 0; } }else{ if( v > dy ){ ` 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}}$+. \<<< (` < 0? -1 : 1) >>> \`++ 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} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< case `: {;} case `: {;} case `: {;} case `: { try_new_line(); (void) move_x((INTEGER) get_int(ch - ` + 1 )); break; } >>> \<<< case `: { (void) move_x( dx_1 ); break; } case `: {;} case `: {;} case `: {;} case `: { try_new_line(); dx_1 = move_x((INTEGER) get_int(ch - ` )); break; } >>> \<<< case `: { (void) move_x( dx_2 ); break; } case `: {;} case `: {;} case `: {;} case `: { try_new_line(); dx_2 = move_x((INTEGER) get_int(ch - ` )); break; } >>> \<<< static INTEGER dx_1 = 0, dx_2 = 0; >>> \
<<< static INTEGER move_x( ARG_I(register INTEGER) ); >>> \<<< `[ 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; ` } else if( d && text_on && (x_val != max_x_val) ){ ` } 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. \<<< 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} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< case `: {;} case `: {;} case `: {;} case `: { (void) move_y( (INTEGER) get_int(ch - ` + 1 )); break; } >>> \<<< case `: { (void) move_y( dy_1 ); break; } case `: {;} case `: {;} case `: {;} case `: { dy_1 = move_y( (INTEGER) get_int(ch - ` )); break; } >>> \<<< case `: { (void) move_y( dy_2 ); break; } case `: {;} case `: {;} case `: {;} case `: { dy_2 = move_y( (INTEGER) get_int(ch - ` )); break; } >>> \<<< static INTEGER dy_1 = 0, dy_2 = 0; static long int y_val = 0; >>> \
<<< static INTEGER move_y( ARG_I(register INTEGER) ); >>> \<<< `[ static INTEGER move_y( d ) register INTEGER d ;{ y_val += d; ` 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). \<<< if( (ch > 127) && (ch < 137) && (ch != `) ){ 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'. \<<< i = (INTEGER) ( (double) (dx = x_val - max_x_val) / (text_on? word_sp : margin_sp) + 0.5 ); ` if( i<0 ) i=0; if( i==0 ){ ` } if( i ){ ` } if( !ignore_spaces ){ ` while( i-- ) { text_on=TRUE; put_char(' '); } } else { recover_spaces = (int) i; } max_x_val = x_val; >>> \<<< if( i==0 ){ i = (INTEGER) ( (double) dx / word_sp + 0.5 ); } >>> \<<< while( recover_spaces-- ){ text_on=TRUE; put_char(' '); } recover_spaces = 0; >>> \<<< ignore_spaces++; >>> \<<< ignore_spaces--; >>> \<<< 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. \<<< if( special_n ){ ` cr_fnt = cur_fnt; unskip_depth = 0; unhskip_mark = get_str( (int) special_n ); special_n=0; retract_addr = ftell(dvi_file); ` cur_fnt = cr_fnt; free((void *) unhskip_mark); } else { ignore_chs++;; } >>> \<<< unhskip = TRUE; while( unhskip ){ if( (ch = get_char()) >= 128 ) { switch( ch ){ ` ` ` ` ` ` } } } ` >>> \<<< do{ long int i; char *mark; ch = get_char(); if( ( ch==`) || ( ch==`) || ( ch==`) || ( ch==`) ) { 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, `); } while(FALSE); >>> \<<< 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; } >>> \<<< case `: case `: { break; } >>> \<<< case `: case `: case `: case `: { 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{ ` } break; } >>> \<<< if( special_n ){ while( special_n-- > 0 ){ (void) get_char(); } } else { ignore_chs--; } >>> \<<< while( special_n-- > 0 ){ (void) get_char(); } >>> \<<< 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; } >>> \<<< if( eoln_str ){ print_f(eoln_str); } else { (IGNORED) put_4ht_ch( ch, cur_o_file ); } recover_spaces = 0; >>> \<<< static U_CHAR *eoln_str = (char *)0; >>> \<<< 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; } >>> \<<< if( space_str ){ print_f(space_str); } else { (IGNORED) put_4ht_ch( ch, cur_o_file ); } >>> \<<< static U_CHAR *space_str = (char *)0; >>> \<<< static int ignore_chs=0, ignore_spaces=0, recover_spaces=0; >>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \SubSection{Out of Place Spaces} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< long curr_pos; BOOL done; int ch, cr_fnt; curr_pos = ftell(dvi_file); done = FALSE; while( !done ){ ch = get_char(); switch( ch ){ ` ` ` case `: case `: { break; } default: { if( (ch < `) || (ch > `) ){ done = TRUE; } else { ` } } } } (IGNORED) fseek(dvi_file, curr_pos, `); >>> \<<< double word_sp; cr_fnt = ch - `; 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 ); ` if( i>0 ){ i =1; } >>> \<<< case `: case `: case `: case `: { INTEGER n; int ch; n = ch - ` + 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 \`++ 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} %%%%%%%%%%%%%%%%%%%%%%%%%% \<<< case `: { (void) rule_x( TRUE ); break; } case `: { (void) rule_x( FALSE ); break; } >>> \<<< 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 \`'', 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++. \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 \
<<< static void rule_x( ARG_I(BOOL) ); >>> \<<< `[ 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 ){ ` } else if( pos_dvi ){ ` if( tag ) x_val += right; } else if( (up>0) && (right>0) ) { ` if( tag ) x_val += right; } else { ` if( tag ) x_val += right; } } >>> \<<< static U_CHAR rule_ch = '_'; static BOOL ` = FALSE; >>> \<<< if( !special_n ){ rule_ch = '\0'; } else { while( special_n-- > 0 ){ rule_ch = get_char(); } } >>> \<<< 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; } } >>> \<<< 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 ); ` if( i && !text_on ) try_new_line(); ` while( i-- ) { text_on=TRUE; if( rule_ch && !` ){ put_char(rule_ch); } } ` max_x_val = x_val + right; } >>> \<<< if( i==0 ){ i = (INTEGER) ( (double) (x_val + right - max_x_val) / word_sp + 0.5 ); } >>> \<<< rule_ch_off >>> \<<< ` ` >>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \Section{Command Characters} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \SubSection{Fonts} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< ` >>> \<<< case `: (void) get_char(); case `: (void) get_char(); case `: (void) get_char(); case `: { for( i=0; i<14; i++ ){ ch = get_char(); } for( i=ch + get_char(); i>0; i--) (void) get_char(); break; } >>> \<<< case `: if( version_id == ` ){ ` break; } case `: if( version_id == ` ){ ` break; } case `: if( version_id == ` ){ ` break; } >>> \<<< default: { if( (ch < `) || (ch > `) ) { bad_char(ch); } else { cur_fnt = ch - `; ` } break; } >>> \<<< case ` : case `: case `: case ` : { INTEGER n; n = ch - ` + 1; cur_fnt = (int) ((n==4)? get_int(4) : get_unt((int) n)); ` break; } >>> \<<< static int cur_fnt = -1; `% should be INTEGER for DOS`% >>> \Section{Specials: Extensions to dvi Primitives} \<<< case `: {;} case `: {;} case `: {;} case `: { ` break; } >>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \Chapter{Specials of TeX4ht} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \Section{The Options} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< long int special_n; ` if( tex4ht_special( &ch, &special_n) ) { int sv; sv = ch; special_on = TRUE; ` special_on = FALSE; ` } else { ` } >>> \<<< while( special_n-- ) (void) get_char(); >>> \
<<< static BOOL tex4ht_special( ARG_II( int*, long int*) ); >>> \<<< `[ static BOOL tex4ht_special( chr, special_n) int *chr`; long int *special_n ;{ BOOL tex4ht; int i; long unsigned N; tex4ht = FALSE; ` if( *special_n > (long int) 4 ){ for(i=4; i<9; i++) special_hd[i]=get_char(); special_hd[9]='\0'; ` *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!!!!!!!!! \<<< 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 ){ ` } >>> \<<< *special_n = (long int) (N = get_unt(*chr - ` + 1)); for(i=4; i--; ){ special_hd[i] = (unsigned char) (N & 0xFF); N = N >> 8; } >>> \<<< static U_CHAR special_hd[10]; >>> \<<< try_new_line(); switch( ch ){ case '*': { ` break; } case '@': { ` break; } case '+': { ` break; } case '=': { ` break; } case '<': case '>': { ` break; } case '!': { ` break; } case '|': { gif_ch = !gif_ch; break; } case ':': { ` break; } case ';': { ` break; } case '"': { ` break; } case '~': { ` break; } case '.': { ` break; } case '^': { ` break; } } >>> The \LinkPort\{}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{t4ht@...integer...} 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{t4ht@-...integer...} 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{t4ht+@...message...} 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*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=
...} does not work.) Try the following. 1. Prepare a file x.tex having the following content. \special{t4ht=} Just trying. \special{t4ht=} \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)} %%%%%%%%%%%%%%%%%%%%%%%%%% \<<< 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 \<<< 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; ` ` } else { ` } >>> \<<< while( hcode_repl != (struct hcode_repl_typ*) 0 ){ p = hcode_repl; hcode_repl = hcode_repl->next; free((void *) p->str); free((void *) p); } >>> \<<< 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; } } } >>> \<<< 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; } >>> \<<< struct hcode_repl_typ { char *str; struct hcode_repl_typ *next; }; >>> \<<< 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 \`'\'. \<<< int code, digit; special_n--; switch ( code = get_char() ){ case '%': { ` break; } case '@': { verb_ch = !verb_ch; break; } case '/': { ` break; } case 'e': { ` break; } case '!': { ` break; } case '(': { ` break; } case ')': { ` break; } case '[': { ` break; } case ']': { ` break; } case '?': { ` break; } case '-': { if( special_n ) { code = 0; ` put_char( code ); } else { nomargin = TRUE; } break; } case '*': { ` } case '+': { ` break; } case '.': { ` break; } case ',': { ` break; } case '_': { ` break; } case 'D': { ` break; } case 'u': { ` break; } default: { ` } } >>> \<<< static BOOL nomargin = FALSE; static int next_char = -1; static U_CHAR *next_str = (char *) 0; >>> \<<< #define IGNORED void >>> \<<< 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,'?') ; } >>> \<<< code -= '0'; ` next_char = code; if( ` ){ 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. \<<< if( ` != -1 ) { ` (IGNORED) put_4ht_ch( ` , cur_o_file ); ` = -1; } if( ` ){ print_f(next_str); free((void *) next_str); next_str = (char *) 0; } next_str = get_str( (int) special_n ); special_n = 0; ` >>> Indirect characters are expressed by character codes enclosed within braces. For instance, `\Verb!\special{t4ht@+\string&{35}x0142;}x!'. \<<< { 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_str >>> \<<< keepChar=1; >>> \<<< if( keepChar ){ keepChar=FALSE; { ` } } >>> % (IGNORED) put_4ht_ch( ch, cur_o_file ); \<<< 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}'. \<<< ` ` `

if( ch == '>' ){ ` } else { ` } cur_o_file = ( out_file == (FILE *) 0 )? root_file : out_file; >>> \<<< if( special_n > 0 ){ ` } else { ` } >>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \SubSection{Open 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 { ` } } >>> \<<< 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} \<<< if( p == (struct files_rec *) 0 ) bad_special( name ); else { /* if p is null, do nothing more */ ` 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 ); } >>> \<<< 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} \<<< while( opened_files != (struct files_rec*) 0 ) { (IGNORED) fclose( opened_files->file ); opened_files = opened_files->next; } >>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \SubSection{Backup to Previous 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 ){ ` cur_o_file = p->prev_file; p->prev_file = (FILE *) 0; break; } } >>> \<<< 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} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< static struct files_rec *p, *q; U_CHAR name[256]; int i; ` if( p != (struct files_rec*) 0 ){ ` } >>> \<<< 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; } } >>> \<<< 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} %%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< 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} \

<<< for( p = opened_files; p != (struct files_rec*) 0; p = p->next ) { if( eq_str(p->name, name) ) break; } >>> \<<< static struct files_rec *opened_files = (struct files_rec *) 0, *p; >>> \<<< struct files_rec{ FILE *file, *prev_file; char* name; struct files_rec *next, *prev; }; >>> \Section{Import Files, System Calls} \<<< if( special_n ){ special_n--; switch ( get_char() ){ case '<': { ` break; } case '>': { ` break; } case '!': { ` break; } case '^': { ` break; } case '@': { ` break; } case '=': { ` break; } default: { ` } } } else { ` } >>> The library \`'' 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}%' \<<< 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); ` if( flag ){ (IGNORED) printf("System return: %d\n", system_yes? (int) system(name) : -1 ); } else { (IGNORED) printf("No permission for system call\n"); } >>> \<<< (IGNORED) fseek(dot_file, 0L, `); 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); } >>> \<<< { 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 = 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; } >>> \<<< struct sys_call_rec{ char* filter; struct sys_call_rec *next; }; >>> \<<< static BOOL system_yes; static struct sys_call_rec *system_calls = (struct sys_call_rec *) 0; >>> \<<< { U_CHAR *yes = NULL; system_yes = (system( yes ) != 0); } >>> \<<< 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 ) { ` 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 \<<< 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'; ` ` } >>> \<<< struct count_rec{ char* str; int i, depth, max; int* stack; struct count_rec* next; }; >>> \<<< static struct count_rec *counter = (struct count_rec *) 0; >>> \<<< 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); } >>> \<<< 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; >>> \<<< 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; } >>> \<<< switch ( code ){ case '+': { (q->i)++; break; } case '-': { (q->i)--; break; } case '>': { ` break; } case '<': { if( q->depth ){ ` } break; } case '!': { ` (IGNORED) fprintf(cur_o_file, "%d", q->i); break; } case '|': { if( q->depth ){ ` (IGNORED) fprintf(cur_o_file, "%d", q->stack[q->depth - 1] ); } break; } default: { ; } } >>> \Chapter{Character Maps} \Section{Requests} \<<< ch_map_flag = !ch_map_flag; if( ch_map_flag ){ ` } else { ` } >>> \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. \<<< struct ch_map_rec{ char* line; int max, chars; }; >>> \<<< 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. \<<< #define HEIGHT 120 >>> \<<< 1>>> \<<< 2>>> \<<< 3>>> \<<< 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. \<<< static BOOL ch_map_flag = FALSE; >>> \<<< #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 \<<< 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 { ` } } 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. \<<< xresolution = yresolution = 0; ` >>> \<<< static INTEGER xresolution, yresolution; >>> \<<< #define XRESOLUTION MARGINSP #ifdef LONG #define YRESOLUTION 786432L #else #define YRESOLUTION 786432 #endif >>> \

<<>> \<<< static void init_ch_map(MYVOID) { int i; for( i=0; i>> \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_map((char) ch, TRUE); >>> \<<< tag>>> \
<<< static void insert_ch_map( ARG_II(char,BOOL) ); >>> \<<< `[ static void insert_ch_map( ch, ` ) U_CHAR ch`; BOOL ` ;{ int row, col; ` 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{ ` if( row < min_map_line ) min_map_line = row; if( row > max_map_line ) max_map_line = row; if( ch_map[row].max ){ ` } else { ` } } } } >>> Leave the following to the user responsibility of getting the char mappings rightly in the `.htf' fonts. \Verbatim \<<< switch( ch ){ case '>': { ch = 'x'; break; } case '&': { ch = ''; break; } case '"': { ch = ''; break; } } >>> \EndVerbatim \<<< static U_CHAR ok_map = TRUE; >>> \<<< { 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. \<<< static int prevcol = -1, prevrow; static double prev_x; >>> \<<< #define MAX_MAP_LINE 500 >>> \<<< int n; char* p; ch_map[row].chars = (n = (col + 2 + 5) / 5 * 5) - `; 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 \<<< int n; char* p; ch_map[row].chars = (n = (col + 2 + 5) / 5 * 5) - `; 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 \<<< int n; char* p; if( ch_map[row].chars > col ){ ` } else{ ` } >>> Below: 8 = 1 (col starts at 0) + 2 (new ch=ch+bound) + 5 (rounding) The rounding to 5 to (hopefully) help garbage collection. \<<< n = (col - ch_map[row].chars + 8) / 5 * 5; ch_map[row].chars += n - `; 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; >>> \Verbatim \<<< n = (col - ch_map[row].chars + 8) / 5 * 5; ch_map[row].chars += n - `; 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) + !` ) = filter_bound_ch(ch); >>> \EndVerbatim \<<< if( ` ){ if( *(ch_map[row].line + ch_map[row].max - 1) || (ch_map[row].chars - col == 1) ){ ` } 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)) < ` ) 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)) < ` ) col--; p--; } } *(++p) = ch; >>> \Verbatim \<<< if( ` ){ if( *(ch_map[row].line + ch_map[row].max - 1) || (ch_map[row].chars - col == 1) ){ ` } col = (ch_map[row].chars--) - col; p = ch_map[row].line + ch_map[row].max; while( col ){ if( ((unsigned char) (*p)) < ` ) 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)) < ` ) col--; p--; } } *(++p) = filter_bound_ch(ch); >>> \EndVerbatim \<<< 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 \
<<< INTEGER filter_bound_ch( ARG_I(INTEGER) ); >>> \EndVerbatim %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \SubSection{With Rulers} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< 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) ) ? ` : ( ( right > up )? ` : ` ); 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( ` ) x_val += sv_right; >>> \Section{Dump Map} \<<< dump_ch_map(); >>> We need memory of order 2 times the number of characters. \<<< if( ch_map_flag ){ warn_i(27); `% dump_ch_map();`% init_ch_map(); } >>> \`'dump_ch_map();' creates overflow problems here. \
<<< static void dump_ch_map( ARG_I(void) ); >>> % if( (min_map_line < 0) || (max_map_line >= HEIGHT) ){ return; } \<<< static void dump_ch_map(MYVOID) { int n, i, min, k, extra_sp; U_CHAR *p; `% struct map_line_type *q; `% ` 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; ` while( 1 + n-- ){ if( --k < 0 ){ if( extra_sp && (((unsigned char) *p) < `) && (((unsigned char) *(p+1)) < `) ) { extra_sp--; } else { switch( *p ){ ` } } } p++; } free((void *) ch_map[i].line ); } if( i>> \<<< { 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; } >>> \<<< { U_CHAR *s; s = p + n; while( n && !(*s) && !(*(s-1)) ){ n--; s--; } if( n && !(*s) && (((unsigned char) *(s-1)) < `) ) n--; } >>> \<<< case 0: { put_char(' '); break; } case `: { put_char('-'); break; } case `: { put_char('|'); break; } case `: { put_char('#'); break; } case ' ': { extra_sp++; } default: { ` break; } >>> \<<< 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)) >= ` ); 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. \<<< static BOOL dvi_flag = FALSE, dvi_page = FALSE; static FILE *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';`% >>> \<<< long int eof_op_n, begin_postamble; int dis_pages; >>> \<<< job_name[job_name_n-3] = '\0'; ` page_n = 0; ` while( dis_pages ){ ` } ` ` ` >>> \<<< if( errCode > 0 ){ (IGNORED) fprintf(log_file, "tex4ht.c error: %d\n", errCode); } >>> \<<< 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. \<<< while( special_n-- > 0 ) (void) get_char(); >>> %%%%%%%%%%%%%%%%%%%%%%%% \Section{Preamble} %%%%%%%%%%%%%%%%%%%%%%%% \<<< file_n = 14; (IGNORED) fseek(dvi_file, 0L, `); do{ ch = get_char(); idv_char( ch ); file_n++; }while( ch == ` ); ` for( i=12; i ; i-- ){ idv_char( get_char() ); } i = get_char(); idv_char( (int) i ); while( i-- ) idv_copy(); >>> \<<< ch = get_char(); if( id_version != -1 ){ ch = id_version; } idv_char( ch ); >>> \<<< { 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++; } } >>> \<<< static int id_version = -1; >>> %%%%%%%%%%%%%%%%%%%% \Section{Postamble} %%%%%%%%%%%%%%%%%%%% \<<< idv_char(`); file_n += 2; idv_char( ` ); (IGNORED) fseek(dvi_file, begin_postamble, `); begin_postamble = file_n; idv_char( ` ); file_n += 5; idv_int( bop_addr ); (IGNORED) fseek(dvi_file, 5L, `); for( i = 20; i; i-- ) idv_copy(); >>> \<<< 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, `); >>> \<<< eof_op_n -= 32; `%fonts`% while( --eof_op_n ) idv_copy(); idv_int(begin_postamble); `%start postamble`% (IGNORED) fseek(dvi_file, 4L, `); file_n += 4; ` for( i = 8 - file_n % 4; i; i-- ) idv_char( ` ); >>> %%%%%%%%%%%%%%%%%%%%%%%% \Section{Body} %%%%%%%%%%%%%%%%%%%%%%%% \<<< if( (ch = get_char()) < 128 ) { visible_cnt = TRUE; cond_idv_char( ch );} else switch( ch ){ ` } >>> \<<< 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? \<<< case `: { x_val = 0; y_val = 0; stack_n = 0; (IGNORED) fseek(dvi_file, 44L, `); break; } case `: { dis_pages--; } case `: { break; } >>> \<<< case `: { cond_idv_char( ch ); x_val += dx_1; break; } case `: { cond_idv_char( ch ); x_val += dx_2; break; } case `: { cond_idv_char( ch ); y_val += dy_1; break; } case `: { cond_idv_char( ch ); y_val += dy_2; break; } >>> \<<< case `: case `: case `: case `: { cond_idv_char( ch ); x_val += cond_int( ch - ` + 1 ); break; } case `: case `: case `: case `: { cond_idv_char( ch ); dx_1 = (INTEGER) cond_int( ch - ` + 1); x_val += dx_1; break; } case `: case `: case `: case `: { cond_idv_char( ch ); dx_2 = (INTEGER) cond_int( ch - ` + 1); x_val += dx_2; break; } >>> \<<< case `: case `: case `: case `: { cond_idv_char( ch ); y_val += cond_int( ch - ` + 1); break; } case `: case `: case `: case `: { cond_idv_char( ch ); dy_1 = (INTEGER) cond_int( ch - ` + 1); y_val += dy_1; break; } case `: case `: case `: case `: { cond_idv_char( ch ); dy_2 = (INTEGER) cond_int( ch - ` + 1); y_val += dy_2; break; } >>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \SubSection{Rules} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< case `:{ visible_cnt = TRUE; cond_string( ch,4 ); x_val += cond_int(4); break; } case `:{ 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. \<<< case `: case `: case `: case `: { long int i; int special_nr; special_nr = ch; if( tex4ht_special( &ch, &i ) ){ if( ch == '+' ){ ` } else while( i-- ) (void) get_char(); }else if( dvi_flag ){ ` }else { ` } break; } >>> \<<< if( dvi_page || !page_n ){ dvi_page = FALSE; ` } dvi_flag = TRUE; ` dvi_flag = FALSE; >>> \<<< while( i-- ) (IGNORED) get_char(); >>> Replace the last piece with a seek! (IGNORED) fseek(dvi_file, (long)i, `); \<<< visible_cnt = TRUE; ` >>> \<<< { U_CHAR *ch; int j; ch = special_hd; (IGNORED) putc( (unsigned) `, 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 ); } >>> \<<< if( i==0 ){ if( dvi_flag ){ dvi_flag = 0; ` } } else{ if( dvi_flag ){ ` } else switch( get_char() ){ case '+': { ` dvi_flag = TRUE; dvi_page = TRUE; ` break; } case '@': { ` break; } default: { while( --i ) (void) get_char(); break; } } } >>> \<<< while( --i ) (void) putc( get_char(), log_file ); (IGNORED) putc( '\n', log_file ); >>> \Verbatim \<<< if( i==0 ){ if( dvi_flag ){ dvi_flag = 0; ` } } else{ if( dvi_flag ){ ` } else if( '+'==get_char() ){ ` dvi_flag = 1; ` } else while( --i ) (void) get_char(); } >>> \EndVerbatim \<<< { 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 \<<< if( i ){ ` } else{ dvi_flag = !dvi_flag; if( dvi_flag ){ ` } else { ` } } >>> \EndVerbatim \<<< cond_idv_char( special_nr ); cond_idv_int( i, special_nr - ` + 1 ); while( i-- ) cond_idv_char( get_char() ); visible_cnt = TRUE; >>> \<<< visible_cnt = FALSE; bop_addr = advance_idv_page( bop_addr, cur_font ); stack_depth = 0; set_loc( `, x_val ); set_loc( `, y_val ); >>> \<<< INTEGER bop_addr; >>> \
<<< static void set_loc( ARG_II(int, long int) ); >>> \<<< `[ 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} \<<< case `: { push_stack(); stack_depth++; cond_idv_char( ch ); break; } case `: { 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 ); ` } cond_idv_char( ch ); } break; } >>> \<<< cond_idv_char( ` ); idv_int( x_val - cur_x - dx_1 - dx_2 ); cond_idv_char( ` ); idv_int( dx_1 ); cond_idv_char( ` ); idv_int( dx_2 ); cond_idv_char( ` ); idv_int( y_val - cur_y - dy_1 - dy_2 ); cond_idv_char( ` ); idv_int( dy_1 ); cond_idv_char( ` ); idv_int( dy_2 ); cond_idv_char( ` ); file_n += 24; >>> \<<< 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(`); file_n++; } >>> \<<< int stack_depth=0; >>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \SubSection{Fonts Definition} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< case `: case `: case `: case `: { 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++ ){'. \<<< case `: case `: case `: case `: { int i; idv_char( ch ); file_n++; cur_font[0] = ch - ` + 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; } >>> \<<< default: { if( (ch < `) || (ch > `) ){ if( ` ){ ` } else { err_i(23); } } else { idv_char( ch ); file_n++; cur_font[0] = 1; cur_font[1] = ch; } break; } >>> \<<< char cur_font[6]; BOOL visible_cnt=FALSE; >>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \Section{Symbols from Fonts} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< { 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--; ){ ` 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( `, (long int) mid_page_x ); set_loc( `, (long int) mid_page_y ); ` ` } } (IGNORED) printf("Execute script ``%slg'\n", job_name); (IGNORED) fclose( log_file ); } >>> \<<< 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 ); ` script(font_gif, job_name ,page_n, str); >>> \`'printf("%s", str);' --- too mach info \<<< { 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); } >>> \<<< static FILE* log_file; >>> \<<< mid_page_y = (INTEGER) get_unt(4) / 2; mid_page_x = (INTEGER) get_unt(4) / 2; >>> \<<< static INTEGER mid_page_y, mid_page_x; >>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \SubSection{Activate Font} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< { INTEGER num; num = font_tbl[cur_fnt].num; if( num <= ` ) cond_idv_char( (int) (num + `) ); else if( dvi_flag ){ if( (num < 0) || (num > 16777215L) ) idv_int(`); else if( num < 256 ) { idv_char(`); file_n++; } else if( num < 65536L ) int_to_dvi((long int) `,2); else int_to_dvi((long int) `,3); cond_idv_char( (int) num ); } } >>> The above seems to be incorrect for big numbers!!!!!!!!!!!!!! \Section{Utilities} \
<<< static void idv_char( ARG_I(int) ); >>> \<<< `[ static void idv_char( n ) int n ;{ (IGNORED) putc( n, idv_file ); } >>> \
<<< static void cond_idv_char( ARG_I(int) ); >>> \<<< `[ static void cond_idv_char( n ) int n ;{ if( dvi_flag ){ (IGNORED) putc( n, idv_file ); file_n++; } } >>> \
<<< static void idv_copy( ARG_I(void) ); >>> \<<< static void idv_copy( MYVOID ) { idv_char( get_char() ); file_n++; } >>> \<<< #define idv_int(val) int_to_dvi((long int) val,4) >>> \
<<< static void cond_idv_int( ARG_II(long int, int) ); >>> \<<< `[ 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; } } >>> \
<<< static void int_to_dvi( ARG_II(long int, int) ); >>> \<<< `[ 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;`% } >>> \
<<< static void cond_string( ARG_II(int, int) ); >>> \<<< `[ static void cond_string(ch, n) int ch`; int n ;{ cond_idv_char( ch ); while( n-- ) cond_idv_char( get_char() ); } >>> \SubSection{Advance Page} \<<< x_val = 0; y_val = 0; stack_n = 0; idv_char( ` ); idv_int( page_n + 1 ); for( i=36; i--; ) idv_char( 0); idv_int( -1 ); bop_addr = file_n; file_n += 45; idv_char(`); file_n++; >>> \<<< static int page_n, file_n; >>> \
<<< static INTEGER advance_idv_page( ARG_II( INTEGER,char*) ); >>> \<<< `[ static INTEGER advance_idv_page( bop_addr, cur_font ) INTEGER bop_addr`; char* cur_font ;{ int i; if( page_n++ ){ idv_char(`); file_n++; idv_char(`); file_n++; idv_char( ` ); idv_int( page_n ); for( i=36; i--; ) idv_char( 0); idv_int( bop_addr ); bop_addr = file_n; file_n += 45; idv_char(`); file_n++; for( i=1; i<=cur_font[0]; i++ ){ idv_char( cur_font[i] ); file_n++; } } ` 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. \<<< store_mv(`, dx_1); store_mv(`, dx_2); store_mv(`, dy_1); store_mv(`, dy_2); >>> \
<<< static void store_mv( ARG_II( int, INTEGER) ); >>> \<<< `[ 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); } >>> \<<< `[ 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 ... ... ... ... ... ... \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 \<<< 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': { ` break; } case '/': { if( special_n ){ ` } else { ` } break; } case '&': { i++; } case '@': { i++; if( special_n ){ ` } else { ` } break; } default: { ` } } >>> \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. \<<< static struct halign_rec *halign[8]; >>> \<<< struct halign_rec{ char * str; int refs; }; >>> \<<< { 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} \<<< 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 halign_rec *halign[8]; BOOL halign_on, halign_info, row_no, col_no; >>> \<<< 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. \<<< new_halign = i * TRUE; >>> \<<< static BOOL new_halign = FALSE; >>> \<<< 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. \<<< if( new_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} \<<< ` ` ` 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. \<<< if( stack[stack_n].halign_on ) { switch( ch ){ case `: { ; } case `: { ; } case `: { ; } case `: { ; } case `: { ; } case `: { ; } case `: { ; } case `: { ; } case `: { ; } case `: { ; } case `: { ; } case `: { ; } case `: { ; } case `: { ; } case `: { ; } case `: { ; } case `: { ; } case `: { ; } case `: { ; } case `: { ; } case `: { break; } default: { ` } } } >>> \<<< if( stack[stack_n].halign_on ) { ` } >>> \<<< print_f( stack[stack_n].halign[1]->str ); stack[stack_n].halign_on = FALSE; >>> \<<< if( stack_n ){ if( stack[stack_n-1].halign_on ) { print_f( stack[stack_n-1].halign[3]->str ); } } >>> \<<< 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...}' --- 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 \<<< if( special_n ){ `
} else if( (group_dvi = !group_dvi) == TRUE ){ long curr_pos; int ch, sv_stack_n; ` ` stack_id = 0; curr_pos = ftell(dvi_file); sv_stack_n = stack_n; ` ` (IGNORED) fseek(dvi_file, curr_pos, `); group_dvi = TRUE; stack_n = sv_stack_n; stack_id = 0; } else { ` } >>> % SEEK_SET); \<<< static BOOL group_dvi = FALSE; >>> \<<< int stack_id=0; >>> \SubSection{Main Pass} \
<<< U_CHAR in_ch; if( (in_ch = get_char()) == '>' ) { ` } else if( in_ch == '!' ) { ` } else { if( !group_dvi ){ warn_i(42); } (IGNORED) fseek(dvi_file, (long) --special_n, `); special_n = 0; } >>> \ifHtml[\HPage{example}\Verbatim \def\:temp{\special{t4ht\string~<}\HCode{}\o:over: \special{t4ht\string~>}} \Let\over=\:temp aa \special{t4ht\string~}$ {A \over B} \over C $\special{t4ht\string~} \EndVerbatim\EndHPage{}]\fi %%%%%%%%%%%%%%%%%%%%% \SubSection{Memory} %%%%%%%%%%%%%%%%%%%%% \<<< static int stack_n = 0; static struct stack_entry* stack; >>> \<<< struct stack_entry{ long int x_val, y_val; INTEGER dx_1, dx_2, dy_1, dy_2; BOOL text_on; BOOL `; ` }; >>> \<<< stack = m_alloc(struct stack_entry,`); ` >>> \<<< ((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); \<<< int stack_id; struct group_info * begin; struct stack_end_entry * end; >>> \<<< struct group_info{ int stack_id; U_CHAR *info; struct group_info* next; }; >>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \SubSection{Initialization} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< { int i; for( i=`-1; i>=0; i--){ stack[i].begin = (struct group_info *) 0; stack[i].end = (struct stack_end_entry *) 0; stack[i].stack_id = -1; ` } } >>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \Section{Submissions and Retievals} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \SubSection{``Forward'' to End of Group} \<<< if( special_n == 1 ){ special_n--; switch( get_char() ){ case '[': { ignore_end_group++; break; } case ']': { ignore_end_group--; break; } default: { ` } } } 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. \<<< struct stack_end_entry{ struct stack_end_entry *next; U_CHAR *send; }; >>> \<<< 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); \<<< while( stack[stack_n-1].end ){ ` } >>> \<<< 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); \<<< { int stack_n; for( stack_n=`; stack_n>0; stack_n--){ group_dvi = TRUE; ` group_dvi =FALSE; ` } } >>> \<<< 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. \<<< 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; \<<< case `: { ` stack[stack_n].stack_id = stack_id++; ` stack_n++; if( stack_n > ` ){ warn_i(40); } break; } case `: { stack_n--; ` 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. \<<< { struct group_info *p, *last; if( (last = p = stack[ stack_n ].begin) != (struct group_info *)0 ) if( p->stack_id == -1 ){ ` ` } } >>> \<<< while( p ){ if( p->stack_id != -1 ){ break; } p->stack_id = stack_id; last = p; p = p->next; } >>> \<<< 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; } >>> \<<< { 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. \<<< { 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. \<<< while( group_dvi ){ ` } >>> \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. \<<< case `: case `: case `: case `: { long int i; if( tex4ht_special( &ch, &i ) ){ if( ch == '~' ){ ` } else { (IGNORED) fseek(dvi_file, (long) i, `); } }else{ ` } break; } >>> \<<< U_CHAR *ch; ch = special_hd + 4; while( *ch ){ ch++; } (IGNORED) fseek(dvi_file, (long) i, `); >>> % while( i-- ) (IGNORED) get_char(); % change to % (IGNORED) fseek(dvi_file, (long) i, `); % also in \ \<<< if( i==0 ){ group_dvi = FALSE ; }else{ switch( get_char() ){ case '<': { if( i-- ){ U_CHAR ch; if( (ch = get_char()) == '*' ) { ` } else if( (ch == '[') && (i==1) ){ i--; ` } else if( (ch == ']') && (i==1) ){ i--; ` } else if( (ch == '-') && (i==1) ){ i--; ` } else if( (ch == '+') && (i==1) ){ i--; ` } else if( (ch == '(') && (i==1) ){ i--; ` } else if( (ch == ')') && (i==1) ){ i--; ` } else { ` } } break; } default: { (IGNORED) fseek(dvi_file, (long) --i, `); break; } } } >>> % sv_stack_n' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \SubSection{Fonts and Default} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Fonts Definition: \<<< case `: (void) get_char(); case `: (void) get_char(); case `: (void) get_char(); case `: { int i; for( i=14; i; i-- ){ ch = get_char(); } i = ch + get_char(); (IGNORED) fseek(dvi_file, (long) i, `); break; } >>> Fonts Activation: \<<< case `: case `: case `: case `: { INTEGER n; n = ch - ` + 1; cr_fnt = (int) ((n==4)? get_int(4) : get_unt((int) n)); cr_fnt = search_font_tbl( cr_fnt ); break; } default: { if( (ch < `) || (ch > `) ) { if( ch == ` ) { warn_i(46); } else { warn_i_int(45,ch); } } else { cr_fnt = ch - `; cr_fnt = search_font_tbl( cr_fnt ); } break; } >>> \<<< int cr_fnt; >>> \<<< cr_fnt = cur_fnt; >>> \SubSection{Skipped Content} \<<< if( (ch = get_char()) >= 128 ) { switch( ch ){ ` ` ` ` ` ` } } else { ` } >>> \<<< 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); ` break; } >>> \<<< case `: case `:{ (IGNORED) fseek(dvi_file, 8L, `); break; } >>> \<<< case `: { (IGNORED) fseek(dvi_file, 44L, `); break; } >>> \<<< case `: case `: case `: case `: { (IGNORED) (get_int( ch - ` + 1 )); break; } case `: case `: case `: case `: { (IGNORED) (get_int( ch - ` + 1)); break; } case `: case `: case `: case `: { (IGNORED) (get_int( ch - ` + 1)); break; } case `: case `: case `: case `: { (IGNORED) (get_int( ch - ` + 1)); break; } case `: case `: case `: case `: { (IGNORED) (get_int( ch - ` + 1)); break; } case `: case `: case `: case `: { (IGNORED) (get_int( ch - ` + 1)); break; } >>> \<<< case `: case `: case `: case `: { break; } >>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \Section{Push/Pop Locations} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< case `: { ` break; } case `: { ` break; } >>> The dvi stacking code refers to the information \`'x_val', \`'y_val', \`'dx_1', \`'dx_2', \`'dy_1', and \`'dy_2'. \<<< ` ` stack[stack_n].text_on = text_on; push_stack(); ` ` ` ` >>> \
<<< static void push_stack( ARG_I(void) ); >>> \<<< 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; ` stack_n++; if( stack_n > ` ){ warn_i(40); } ` } >>> \<<< 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; >>> \<<< 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_stack(); if( ((x_val+0.6*word_sp) < stack[stack_n].x_val) ) put_char(' '); text_on = stack[stack_n].text_on; >>> \
<<< static void pop_stack( ARG_I(void) ); >>> \<<< static void pop_stack(MYVOID) { ` ` --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.'. \<<< 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; } } >>> \<<< 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++; if(!back_id_off ){ if( !id_hide ){ ch_token = TRUE; sv_id = ch_id; } switch( math_class_of( ch, cr_fnt ) ){ case `: { del_stack = push_del( (char) ch, cr_fnt); break; } case `: { del_stack = pop_del( (char) ch, id_hide, cr_fnt); break; } default:{ ; } } } >>> \<<< 5 >>> \<<< 4 >>> Assumption made: closing del is in the same font as the opening one. \
<<< static struct del_stack_entry* push_del( ARG_II(char, int) ); >>> \<<< `[ 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; } >>> \
<<< static struct del_stack_entry* pop_del( ARG_III(char,int,int) ); >>> \<<< `[ 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? \<<< static struct del_stack_entry *del_stack; >>> \<<< del_stack = (struct del_stack_entry *) 0; >>> \<<< struct del_stack_entry{ struct del_stack_entry *next; U_CHAR ch; int fnt, id; }; >>> \<<< static int ch_id, sv_id, id_latex, back_id_off; >>> \<<< back_id_off = 1; id_latex = 0; >>> \<<< ch_id = 0; >>> \<<< BOOL ch_token; int id_hide; >>> \<<< sv_id = 0; ` 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 ); } >>> \<<< id_hide++; >>> \<<< id_hide--; >>> \<<< id_latex++; >>> \<<< id_latex--; >>> \<<< back_id_off++; >>> \<<< back_id_off--; >>> \SubSection{Memory for Linked Lists} \<<< struct send_back_entry{ struct send_back_entry *next; U_CHAR *send; int id; }; >>> \<<< static struct send_back_entry *back_token, *back_group; >>> \<<< back_token = back_group = m_alloc(struct send_back_entry,1); back_token->id = -1; >>> \SubSection{Scan Backward Submissions} \<<< 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 ){ ` } 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 \<<< 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. \<<< back_group = rev_list( back_group ); back_token = rev_list( back_token ); back_token = back_insert ( back_token, 0); ` >>> \
<<< static struct send_back_entry * rev_list( ARG_I(struct send_back_entry *) ); >>> \<<< `[ 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} \<<< if( group_dvi ) { back_group = back_insert ( back_group, push_id); } >>> \<<< if( group_dvi ){ if( ( ch < 132 ) || ( (ch > 127) && (ch < 137) && (ch != ` ) ) ){ ch_id++; back_token = back_insert ( back_token, ch_id); } } >>> \
<<< static struct send_back_entry * back_insert( ARG_II(struct send_back_entry *, int) ); >>> \<<< `[ 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=} %%%%%%%% \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=}% %%%%%%%% \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 group_path * path_start, * path_end; >>> \<<< struct group_path{ U_CHAR action; U_CHAR *path; U_CHAR *info; struct group_path * next; }; >>> \<<< stack[i].path_start = (struct group_path *) 0; stack[i].path_end = (struct group_path *) 0; >>> \<<< struct group_path *p, *t; U_CHAR *q, str[256]; int n; p = m_alloc(struct group_path,1); ` ` 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; } } >>> \<<< 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); >>> \<<< 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} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< { ` if( ` ){ ` ` = FALSE; } if( stack_n > 1 ){ p = stack[stack_n - 2].path_start; if( p != (struct group_path *) 0 ){ ` } p = stack[stack_n - 2].path_end; if( p != (struct group_path *) 0 ){ ` } ` } } >>> \<<< 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; >>> \<<< 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; >>> \<<< while( p != (struct group_path *) 0 ){ ` q = p; p = p->next; q->next = (struct group_path *) 0; ` } >>> \<<< while( p != (struct group_path *) 0 ){ ` q = p; p = p->next; q->next = (struct group_path *) 0; ` } >>> \<<< switch( place ){ case `: 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 `: if( start_head == (struct group_path *) 0 ){ start_head = start_tail = q; } else { start_tail = start_tail->next = q; } break; case `: ` break; } >>> \<<< switch( place ){ case `: 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 `: if( end_head == (struct group_path *) 0 ){ end_head = end_tail = q; } else { end_tail = end_tail->next = q; } break; case `: ` break; } >>> \<<< 0 >>> \<<< 1 >>> \<<< 2 >>> \<<< 3 >>> \<<< 4 >>> \<<< 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. \<<< 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 = `; break; case '/': ignore_chs++; place = `; break; case '-': ` = TRUE; ` place = `; break; } } else { place = `; } } else { if( *(p->path ) == 's' ) { (IGNORED) strcpy((char *) p->path, (char *) p->path+1); } place = `; } >>> 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. \<<< if( *(p->path ) == 'e' ) { (IGNORED) strcpy((char *) p->path, (char *) p->path+1); place = `; } else { if( *(p->path ) == 's' ) { (IGNORED) strcpy((char *) p->path, (char *) p->path+1); } place = `; } >>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \SubSection{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 ){ ` } p = stack[stack_n - 1].path_end; if( p != (struct group_path *) 0 ){ ` } } } >>> \<<< while( p != (struct group_path *) 0 ){ ` q = p; p = p->next; ` } >>> \<<< while( p != (struct group_path *) 0 ){ ` q = p; p = p->next; ` } >>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \SubSection{Process Individual Records at Exit from Group} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< if( *(p->path) != '\0' ) { ` } else { switch( p->action ){ case '/': ignore_chs--; break; default: { ` break; } } } >>> \<<< if( *(p->path) != '\0' ) { ` } else { switch( p->action ){ case '>': print_f( p->info ); break; default: { ` break; } } } >>> \<<< 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} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< if( pos_dvi ){ long int d; if( *pos_text ){ ` (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 + `) > max_pos_x ) max_pos_x = d; if( (d = y_val - `) < min_pos_y ) min_pos_y = d; if( (d = y_val + `) > max_pos_y ) max_pos_y = d; } >>> % pos_y_D * (`<(double) char_height( ch )`> - base_pos_h) + \<<< 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{}} \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"**
*****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
(/\/\/\/\/\)
........................
\EndVerbatim\EndHPage{}]\fi). \ifHtml[\HPage{?}\Verbatim Positioning of characters? The attached source tries three renderings for `Ax'. 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 aa.html

Ax

Ax

Ax

y \EndVerbatim\EndHPage{}]\fi \Section{Compute Ruler Position} \<<< long int d; if( (up > 0) && (right > 0) ){ if( *pos_line ){ double from_x, from_y; ` 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} %%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< if( special_n ){ ` } 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; ` p = end_pos_body; while( *p ){ dim_on = TRUE; switch( *p ){ ` 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'. \<<< 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; } >>> \<<< 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. \<<< pos_text = pos_line = end_pos_body = end_pos_text = pos_body = m_alloc(char, (int) 1); (IGNORED) strcpy((char *) pos_text, "" ); >>> \<<< { 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; ` ` if( (i != 10) || special_n ){ warn_i_str(39,pos_text); *(pos_text = end_pos_body = pos_line = end_pos_text = pos_body) = '\0'; } } >>> \<<< { 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++; } } >>> \<<< { long int v=0; double w[5]; int j; U_CHAR ch, sign; BOOL done; for(j=0;j<5;j++){ ` 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';} >>> \<<< 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; } } >>> \

<<< static double pos_dbl( ARG_I(long int *) ); >>> \<<< `[ 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; } >>> \<<< 123454321 >>> %%%%%%%%%%%%%%%%%%%%%%%%%%% \Chapter{Font Information from TeX Font Metric (tfm) Files} %%%%%%%%%%%%%%%%%%%%%%%%%%% Managed by load-font (\LinkPort\font 0\EndLink --\LinkPort\font 63\EndLink, \LinkPort\font 1-byte\EndLink --\LinkPort\font int\EndLink) and activate-font (\LinkPort\def 1-byte font\EndLink --\LinkPort\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 \<<< struct font_entry { INTEGER num; INTEGER scale; INTEGER design_sz; ` ` ` ` ` ` }; >>> \<<< #define design_size_to_pt(n) ((double)n / (double)(1L<<20)) >>> \<<< 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`% \<<< #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 == `) && (ch == `) ){ ` } else { ` } ` ` free((void *) new_font_name); font_tbl_size++; } #ifdef MAXFONTS else err_i_int(17, MAXFONTS); #endif >>> \<<< ` new_font_checksum = (INTEGER) get_int(4); new_font.scale = (INTEGER) get_unt(4); new_font.design_sz = (INTEGER) get_unt(4); ` ` >>> \<<< static char* new_font_name; >>> \<<< #define new_font font_tbl[font_tbl_size] >>> %%%%%%%%%%%%%%%%%%%%%%%%%% \SubSection{Get Data} %%%%%%%%%%%%%%%%%%%%%%%%%% \<<< switch( ch ){ case `: case `: case `: { new_font.num = (INTEGER) get_unt(ch - ` + 1); break; } case `: { new_font.num = (INTEGER) get_int(4); break; } default: err_i(8); } >>> \<<< { 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'; } >>> \<<< ` ` 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 ); ` ` >>> The following is computed from \`'scale' and \`'design_sz'. We take it here relative to 100. \<<< new_font.mag = new_font.scale / (new_font.design_sz / 100); >>> \<<< INTEGER mag; >>> \<<< char *family_name, *font_size; >>> \<<< { int n, i; for( n=0; n 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>> \<<< { int m; for( m=n; m '9' )){ n=font_name_n; break; } } } >>> When the size is not zero, n+2 can be reduced to n+1. \<<< (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: \<<< 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. \<<< #ifndef LGFNT #define LGFNT "Font(\"%s\",\"%s\",\"%d\",\"%d\")\n" #endif >>> \<<< static U_CHAR *lg_font_fmt = NULL; >>> \<<< 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} %%%%%%%%%%%%% \<<< 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); ` new_font.layout_dir = (flags & `) ? 1 : 0; new_font.rgba_color = (flags & `)? (unsigned long) get_unt(4) : 0xffffffff; if( flags & ` ){ 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); ` >>> \<<< { 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!! \<<< INTEGER layout_dir; unsigned long rgba_color; >>> % char *fam_name, *sty_name; % INTEGER used; new_font.used = 0; \<<< 0x0100 >>> \<<< 0x0200 >>> \<<< 0x0800 >>> %%%%%%%%%%%%% \SubSection{Within IVD} %%%%%%%%%%%%% \<<< (version_id == `) && ( (ch == `) || (ch == `) || (ch == `) || (ch == `) ) >>> \<<< 0x0002 >>> \<<< 0x0001 >>> \<<< switch( ch ){ case `: ` break; case `: ` break; default: printf(" ===> ---------- xdv's idv ------------ <==== %d\n" , ch); } >>> \<<< { 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); } } >>> \<<< { int i, flags; (void) get_unt(4); `%tex_id`% (void) get_unt(4); `%scale`% flags = (INTEGER) get_unt(2); if ((flags & `) || (flags & `)) { 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 & ` ){ (void) get_unt(4); } if( flags & ` ){ 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{????????????????} %%%%%%%%%%%%% \<<< ` ` 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 ); ` ` >>> \<<< new_font.mag = new_font.scale / (new_font.design_sz / 100); >>> \<<< { `% FILE *font_file; U_CHAR file_name[256]; `% ` /* 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 { */ ` /* (IGNORED) fclose(font_file); } */ } >>> \<<< { ` ` ` ` ` ` ` ` ` ` ` ` } >>> \<<< ` `%all need design_size_to_pt(n)`% ` ` ` ` ` ` >>> \<<< new_font.it = 0; >>> \<<< new_font.word_sp = 350000; ` >>> \<<< new_font.ex = 450000; >>> \<<< 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; \<<< int it_correction_table_length, lig_kern_table_length, kern_table_length, extensible_char_table_length, `%<= 256`% num_font_parameters; >>> \<<< { 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; } } >>> \<<< { 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; } } >>> \<<< { 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; } } >>> \<<< 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 & ` ){ (void) get_unt(4); } if( flags & ` ){ 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); } } >>> \<<< doGlyphArray(TRUE); >>> \<<< doGlyphArray(FALSE); >>> \
<<< static void doGlyphArray( ARG_I(BOOL) ); >>> \<<< `[ 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} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< { 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} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< cur_fnt = search_font_tbl( cur_fnt ); word_sp = `; >>> \
<<< static int search_font_tbl( ARG_I(int) ); >>> \<<< `[ static int search_font_tbl( cur_fnt ) int cur_fnt ;{ int i; for( i=0; i>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \Section{Opening a Font File} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< { FILE *font_file; U_CHAR file_name[256]; ` 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 { ` (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. \<<< { ` ` ` ` ` ` ` ` ` ` ` ` } >>> % (IGNORED) fseek(G_fontfp,(long)0,FROM_START); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \SubSection{Breakdown} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< 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? \<<< 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; >>> \<<< 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} \<<< ` ` (IGNORED) fseek(font_file, (long) ((header_length - 2) * 4), `); >>> Incompatible fonts shouldn't agree in their check sum. A zero check sum should be disregarded. \<<< { 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$. \<<< 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} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< { 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, `); } } >>> 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} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< { 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} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< { 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} \<<< { 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); } } >>> \<<< `%needs design_size_to_pt(n)`% (IGNORED) fseek(font_file, (long) (depth_table_length * 4), `); >>> \SubSection{Italic Correction Table} Used for italic correction explicitly requested with \`'\/', and for isolated character in math mode (MetaFont 315--316). \<<< `%needs design_size_to_pt(n)`% (IGNORED) fseek(font_file, (long) (it_correction_table_length * 4), `); >>> \SubSection{Ligature Table} Substitutions for specified runs of chrarcters \<<< (IGNORED) fseek(font_file, (long) (lig_kern_table_length * 4), `); >>> \SubSection{kern table} Space to add between specified pairs of characters. \<<< `%needs design_size_to_pt(n)`% (IGNORED) fseek(font_file, (long) (kern_table_length * 4), `); >>> \SubSection{Extensible Table} \<<< (IGNORED) fseek(font_file, (long) (extensible_char_table_length * 4), `); >>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \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'. \<<< ` `%all need design_size_to_pt(n)`% ` ` ` ` ` ` >>> \List{$\circ$} \item The slant per point of a font, used for determining the height of accent characters. \<<< 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. \<<< new_font.word_sp = ( INTEGER) fget_int(font_file,4); ` >>> The following variable needs initialization that is used before the loading of any font. Without it, the program will enter an infinite loop. \<<< #ifdef LONG #define MARGINSP 344061L `%tt at 10pt`% #else #define MARGINSP 344061 #endif >>> \<<< static double word_sp = 999999.0, margin_sp; >>> Turbo C didn't let me to do casting within initialization. \<<< margin_sp = (double) MARGINSP; `%/ (double)(1L<<20) * (double) 655360;`% >>> \<<< 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>> We took for space size an approximated character width. \<<< (IGNORED) fseek(font_file, 4L, `); >>> \<<< (IGNORED) fseek(font_file, 4L, `); >>> \item The design height of letters. \<<< new_font.ex = (INTEGER) fget_int(font_file,4); >>> \item The size of one em in the font. \<<< >>> \item Extra interword space at the end of sentences. \<<< >>> \EndList \Section{Computing Font Info} \SubSection{Characters' Width} \<<< return (INTEGER)( `<(double) char_width( ch )`> ); >>> \<(double) char_width( chr )\><<< design_size_to_pt( *(font_tbl[cur_fnt].wtbl + (int) `) ) * (double) font_tbl[cur_fnt].scale >>> \<<< *(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) (`)) ) * (double) font_tbl[cur_fnt].scale >>> \<<< *(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) (`) ) ) * (double) font_tbl[cur_fnt].scale >>> \<<< *(font_tbl[cur_fnt].char_wi + (int) ( ch - font_tbl[cur_fnt].char_f)% 256) >>> \<<< (int)(`<(double) char_width( ch )`>) >>> \SubSection{Characters' Height} \<(double) char_height( ch )\><<< design_size_to_pt( *(font_tbl[cur_fnt].htbl + (int) (`)) ) * (double) font_tbl[cur_fnt].scale >>> \<<< ( *(font_tbl[cur_fnt].char_hidp + (int) ( ch - font_tbl[cur_fnt].char_f)% 256) >> 4 ) & 0x0F >>> \<<< (int)(`<(double) char_height( ch )`>) >>> \SubSection{Characters' Depth} \<(double) char_depth( ch )\><<< design_size_to_pt( *(font_tbl[cur_fnt].dtbl + (int) (`)) ) * (double) font_tbl[cur_fnt].scale >>> \<<< ( *(font_tbl[cur_fnt].char_hidp + (int) ( ch - font_tbl[cur_fnt].char_f)% 256) ) & 0x0F >>> \<<< (int)(`<(double) char_depth( ch )`>) >>> \SubSection{Other Sizes} \<<< design_size_to_pt( 1.7 * (double) font_tbl[cur_fnt].ex ) * (double) font_tbl[cur_fnt].scale >>> \<<< 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. \<<< 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. \<<< 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++; } >>> \<<< 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. \<<< { U_CHAR str[256]; int i, design_n, n_gif; ` n_gif = new_font.char_l - new_font.char_f + 1; new_font.ch255 = 0; ` ` new_font.ch = m_alloc(unsigned char, n_gif ); ` new_font.str = m_alloc(unsigned char*, n_gif); new_font.str[0] = &null_str; design_n = 0; ` new_font.str = (unsigned U_CHAR **) r_alloc((void *) new_font.str, (size_t) ( (design_n?design_n:1) * sizeof(char *)) ); ` } >>> 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 \<<< static unsigned U_CHAR null_str = '\0'; `% /*NEW*/ `% >>> \<<< { 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 ); ` for( i=n_gif_bytes; i--; ) { ` 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--; ) { ` new_font.gif1[i] = 0; } } >>> \<<< for( i = new_font.char_f; i <= new_font.char_l ; i++ ){ new_font.ch[i - new_font.char_f] = (char) (((31>> The first iteration searches for the font directly. The second iteration searches for aliased font. \<<< { char search_font_name [256]; (IGNORED) strcpy((char *) search_font_name, (char *) new_font.name); while( 1 ){ BOOL flag; ` flag = TRUE; for( ; font_name_n; font_name_n-- ){ FILE* file; int char_f, char_l; new_font_name[font_name_n] = '\0'; ` if( file != NULL){ INTEGER x_char_l; ` if( x_char_l == HTF_ALIAS) { ` (IGNORED) fclose(file); flag = FALSE; break; } ` ` htf_to_lg(html_font, new_font_name, fonts_n, file); ` (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 { ` } } >>> \<<< int loopBound = 0; U_CHAR loopName[256]; loopName[0] = '\0'; >>> \<<< 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( ( name == new_font_name ) && (n == 19) && (ch=='.') ){ return HTF_ALIAS; } >>> \<<< #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 \<<< { 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); } >>> \<<< if( dot_file ){ BOOL tag; (IGNORED) fseek(dot_file, 0L, `); tag = TRUE; while( tag ){ switch( getc(dot_file) ){ case EOF: { j++; tag = FALSE; break; } `%no replacement`% case 'a': { ` } default: { int ch; do ch = getc(dot_file); while( (ch != '\n') && (ch != EOF) ); break; } } } } >>> \<<< 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? \<<< 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; ` break; } if( i < 0 ){ ` } >>> \<<< 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++; >>> \<<< int fonts_n; struct html_font_rec *html_font=0; >>> \<<< fonts_n = 0; >>> \<<< if( html_font ){ while( fonts_n-- ) free((void *) html_font[fonts_n].name); free((void *) html_font ); } >>> EXPLAIN THE CONTENT OF THE FOLLOWING \<<< struct html_font_rec{ char* name; int i; }; >>> %%%%%%%%%%%%%%%%%%%%%%%%% \Section{Scan Characters} %%%%%%%%%%%%%%%%%%%%%%%%% \<<< if( char_f <= new_font.char_l ){ U_CHAR del; int j, n; ` 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++ ){ ` ` } ` } >>> \<<< while( char_f < new_font.char_f ){ while( get_html_ch(file) != '\n' ); char_f++; } >>> \<<< 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\'. \<<< { 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 ) { ` }; 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'; } ` new_font.gif1[i] = ch1 % 256; do{ if( (ch = get_html_ch(file)) == del ){ ` break; } } while( ch != '\n' ); if( ch != '\n' ){ do{ if( (ch = get_html_ch(file)) == del ){ ` break; } } while( ch != '\n' ); } if( ch != '\n' ) { while( get_html_ch(file) != '\n' ); } } >>> \<<< 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. \<<< 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`% } } >>> \
<<< static int get_html_ch( ARG_I(FILE*) ); >>> \<<< `[ 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; } >>> \
<<< static FILE* f_open( ARG_II(const char*,const char*) ); >>> \<<< `[ 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; } >>> \
<<< static void dump_htf( ARG_I(FILE*) ); >>> \<<< `[ 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; } } } >>> \<<< static short dump_htf_files = 0; static BOOL dump_env_files = FALSE; >>> \<<< dump_htf_files = 1; >>> \<<< dump_env_files = TRUE; >>> \<<< if( dump_htf_files ){ dump_htf_files++; dump_htf( file ); dump_htf_files--; } >>> \<<< if( dump_env_files ){ dump_env(); } >>> \
<<< static void dump_env( ARG_I(void) ); >>> \<<< static void dump_env( MYVOID ) { int ch; if( !dumped_env ){ dumped_env = TRUE; (IGNORED) fseek(dot_file, 0L, `); (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"); } } >>> \<<< static BOOL dumped_env = FALSE; >>> \
<<< static void htf_to_lg( ARG_IV(struct html_font_rec*,char*,int,FILE*)); >>> \<<< `[ 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>> \<<< 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; } >>> \<<< (void) get_html_file_id(file, new_font.char_f, new_font.char_l, 18); >>> \
<<< static INTEGER get_html_file_id( ARG_IV(FILE*, int, int, int) ); >>> \<<< `[ 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( (*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 ){ `, 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; } >>> \<<< #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. \<<< ` ` ` ` ` ` if( ` != -1 ) { ` (IGNORED) put_4ht_ch( `, cur_o_file ); ` = -1; } else if( ` ) { ` print_f_4ht(next_str); free((void *) next_str); next_str = (char *) 0; } else { if( verb_ch ){ ` (IGNORED) put_4ht_ch( ch, cur_o_file ); } else { ` } } ` ` ` ` ` ` >>> Does \`'verb_ch' correspond to \''\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; { ` } design_ch = 0; } else { ` } >>> \<<< static BOOL verb_ch = FALSE; >>> A design character is either a picture (\''gif_flag') or a string character (\''ch_str_flag'). \<<< U_CHAR str[256], *p; BOOL sv; int mag; sv = special_on; special_on = TRUE; if( gif_ch && (gif_flag % 2) ){ ` if( !gif_open[gif_flag] ){ ` } else if( !get_bit( class_on, gif_flag ) ) { notify_class_info(gif_flag); store_bit_I( class_on, gif_flag ); } `<...`>
   `<...`> } else { ` } special_on = sv; >>> \
<<< static void notify_class_info( ARG_I(int) ); >>> \<<< `[ 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...}' \<<< (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); >>> \<<< gif_open[gif_flag] = m_alloc(char, `<1 + strlen((char *) ++++++)`>); (IGNORED) strcpy((char *) gif_open[gif_flag], `<++++++`>); 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'; >>> \<++++++\><<< "\"+++++\"+" >>> \<1 + strlen((char *) ++++++)\><<< 29 >>> % `<..."IMG OPTIONS="...NOT`> What is the difference between the following two cases? \<<< if( !gif_flag || (gif_flag % 2) || ch_map_flag ) { put_char(chr); } else{ ` } >>> 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?) \<<< if( !gif_flag || (gif_flag % 2) || ch_map_flag ) { put_alt_ch(chr,ch_str_flag); } else{ ` } >>> \SubSection{Script class-fmt} Font information, e.g., for style files. \<<< #ifndef LGCLS #define LGCLS "Font_Class(%d,\"%s\"): %s\n" #endif >>> \<<< static U_CHAR *class_fmt = NULL; >>> \<<< 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. \<<< #define GIF_I "-%x%s" #define GIF_II "-%x-%x%s" #define GIF_VII "%s" >>> \<<< #ifdef DOS #define HTM #endif >>> \<<< #ifdef HTM #define DOS_GIF_FILE #endif >>> \<<< 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); } >>> \<<< 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. \<<< `% #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 `% >>> \<<< `% #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 \`'%'. \
<<< static void script( ARG_IV(char *, U_CHAR *, int, U_CHAR *) ); >>> \<<< `[ 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 == '%' ){ ` ` 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++; } } >>> \<<< *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. \<<< 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} \<<< #ifndef LGPIC #define LGPIC "--- needs --- %%1.idv[%%2] ==> %%3 ---\n%" #endif >>> \<<< static U_CHAR *font_gif = NULL; >>> \<<< font_gif = p+2; >>> \<<< font_gif = (char *) get_script(font_gif,LGPIC,'s'); ` >>> \<<< { 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. \<<< #ifndef LGSEP #define LGSEP "--- characters ---\n" #endif >>> \<<< static U_CHAR *begin_char_gif = NULL; >>> \<<< begin_char_gif = p+2; >>> \<<< begin_char_gif = (char *) get_script(begin_char_gif,LGSEP,'b'); >>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \SubSection{Default Format for Pictures} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< #ifndef LGTYP #define LGTYP ".png" #endif >>> \<<< static U_CHAR *gif = NULL; >>> \<<< gif = p+2; >>> In the extension name we don't want the line break at the end. \<<< 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. \
<<< `% #ifdef DOS_GIF_FILE `% static void dos_gif_file( ARG_III(char *, int, int) ); `% #endif `% >>> \<<< `% #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 ){ `

if( p == NULL ){ `

} 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 \<<< `% #ifdef DOS_GIF_FILE `% struct gif_file_rec{ U_CHAR code[4]; U_CHAR *name; struct gif_file_rec *next; }; `% #endif `% >>> \<<< `% #ifdef DOS_GIF_FILE `% static struct gif_file_rec * gif_file = (struct gif_file_rec *) 0; `% #endif `% >>> \

<<< if( (p = gif_file) != NULL ){ while( TRUE ){ if( eq_str( str, p->name ) ) break; if( (p = p->next) == gif_file ){ p = NULL; break; } } } >>> \

<<< p = m_alloc(struct gif_file_rec, 1); ` ` (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. \<<< 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!!! \<<< 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. \<<< #define BASE 36 >>> \<<< `% #ifdef DOS_GIF_FILE `% static U_CHAR xeh[]="0123456789abcdefghijklmnopqrstuvxyz"; `% #endif `% >>> \SubSection{Other} \`'<<' is NOT a safe operation, beyond char size, for transporting across platforms. \<<< #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)) \

<<< static void put_alt_ch( ARG_II( int, BOOL) ); >>> \<<< `[ 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 ){ ` } } >>> 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 \`'"'. \<<< 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); } %\<<< %static int alt_ch_length = 0; %>>> \<<< static BOOL gif_ch = TRUE; static int design_ch = 0; >>> \Section{Css Trace on Characters} \`'\Configure{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 '%': { ` break; } case '=': { ` break; } case '|': { ` break; } case ',': { ` 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. \<<< 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} \<<< q = img_src; img_src = p; >>> \<<< static U_CHAR * img_src; >>> \<<< img_src = m_alloc(char, (int) 12); (IGNORED) strcpy((char *) img_src, ">> \<>> \<..."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} \<<< { int bad_str, m; U_CHAR ch, *t[`], err_str[256]; bad_str=`; (IGNORED) strcpy((char *) err_str, (char *) p); if( n>` ){ 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[` - bad_str] = p+1; } p++; } } if( !bad_str ){ if( m==0 ){ span_name_on = n>`; } 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); } } >>> \<<< not_notify = TRUE; >>> \<<< static BOOL not_notify = FALSE; >>> \<<< 8>>> \<<< 7>>> \<<< 10>>> \<<< 11>>> \<<< 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]; >>> \<<< { 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 ); } } } >>> \<<< #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 >>> %\<<< %{ 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. \<<< ` ` if( span_on ){ ` ` ` ` ` ` } put_char(chr); if( span_on ){ ` } >>> \<<< ` if( span_on ){ ` ` ` ` ` ` } put_alt_ch(chr,ch_str_flag); if( span_on ){ ` } >>> \<<< if( gif_flag && !get_bit( class_on, gif_flag ) ) { notify_class_info(gif_flag); store_bit_I( class_on, gif_flag ); } >>> \<<< if( span_open[gif_flag] ) if( *span_open[gif_flag] ){ print_f( span_open[gif_flag] ); } >>> \<<< if( end_span[gif_flag] ) if( *end_span[gif_flag] ){ print_f( end_span[gif_flag] ); } >>> \<<< if( span_ch[gif_flag] ) if( *span_ch[gif_flag] ){ print_f( span_ch[gif_flag] ); } >>> \<<< 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); } >>> \<<< 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); } >>> \<<< if( span_mag[gif_flag] ) if( *span_mag[gif_flag] ){ (IGNORED) fprintf(cur_o_file, span_mag[gif_flag], font_tbl[cur_fnt].mag); } >>> \<<< if( span_ord[gif_flag] ) if( *span_ord[gif_flag] ){ (IGNORED) fprintf(cur_o_file, span_ord[gif_flag], chr); } >>> \SubSection{Font Properties} \<<< int f; f = 0; while( *p ){ f = 10*f + *(p++) - '0'; } ` (IGNORED) fprintf(cur_o_file, "%d", (font_tbl[cur_fnt].scale / base_font_size - 100) * f / 100 +100 ); >>> \<<< ` (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} %%%%%%%%%%%%%%%%%% \<<< struct css_ext_rec * css = m_alloc(struct css_ext_rec, 1);; css->name = p + 2; css->next = css_ext; css_ext = css; >>> \<<< 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; } >>> \<<< static struct css_ext_rec * css_ext = (struct css_ext_rec *) 0; static char css_default[] = "4cs"; >>> \<<< struct css_ext_rec{ char* name; struct css_ext_rec *next; }; >>> %%%%%%%%%%%%%%%%%% \Section{Retrieve Data} %%%%%%%%%%%%%%%%%% \<<< { 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'; ` } free((void *) css_file_name); } } >>> \<<< { U_CHAR name[256]; (IGNORED) sprintf(name, "%s.%s", css_file_name, search_css_ext->name); ` if( file != NULL ){ ` (IGNORED) fclose(file); break; } } >>> %%%%%%%%%%%%% \Section{Copy CSS File} %%%%%%%%%%%%% \<<< ` if( !is_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} %%%%%%%%%%%%% \<<< struct visited_file_rec * visited_file = (struct visited_file_rec *) 0; >>> \<<< 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); } >>> \<<< 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; >>> \<<< 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; } >>> \<<< 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. \<<< unsigned U_CHAR *accent, *accented; unsigned int *accent_array, *accented_array, accent_N, accented_N; >>> \<<< 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; } >>> \<<< 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; >>> \<<< 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} \<<< special_n--; switch ( get_char() ){ case 't': { if( special_n ){ ` } else { ` } break; } case 'm': { if( special_n ){ ` } else { ` } break; } case 'a': { ` break; } case 'i': { ` break; } default: { ` } } >>> 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. \<<< needs_accent_sym = TRUE * 2; >>> \<<< needs_accent_sym = 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. \
<<<
if( in_accenting ){
  `
} else if( ` ){
  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;
  }
}
>>>


\<<<
                                               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 )
{  ` }
else if( m_accent_template )
{  `  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;
>>>

\<<<
BOOL in_accenting;
>>>

\<<<
in_accenting = FALSE;
>>>

The following needs to be fixed for cases that ch is greater than 127.

\<<<
needs_accent_sym && (ch < 128)
>>>



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Closure for Text Accent}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



\<<<
if( needs_end_accent && t_accent_template ){
   `
   (IGNORED) fprintf(cur_o_file, "%s", t_accent_fifth);
   needs_end_accent = FALSE; `
}
>>>







The following in needed for cases of text-accents on empty content,
immeddiately followed by a character (e.g., \`'\^{}Xx').

\<<<
if( x_val > needs_end_accent ){
  `
}
>>>


The following takes care of closing a text accent after a character.

\<<<
`
>>>

%%%%%%%%%%%%%%%%%%%%%%
\SubSection{Tailoring}
%%%%%%%%%%%%%%%%%%%%%%

before-family-name, between-family-char-number, after char-number

\<<<
(IGNORED) get_open_accent(&t_accent_template,
            &t_accent_first, &t_accent_second,
            &t_accent_third, &t_accent_fourth,
            &t_accent_fifth, &special_n);
>>>

\<<<
(IGNORED) get_open_accent(&m_accent_template,
            &m_accent_first, &m_accent_second,  
            &m_accent_third, &m_accent_fourth,
            &m_accent_fifth, &special_n);
>>>




\<<<
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;
>>>




\
<<< static void get_open_accent( ARG_VII(char**, char**, char**, char**, char**, char**, long int*)); >>> \<<< `[ 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} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< 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); } >>> \<<< 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. \<<< needs_accented_sym--; >>> \<<< needs_accented_sym--; >>> \<<< (IGNORED) get_open_accent(&a_accent_template, &a_accent_first, &a_accent_second, &a_accent_third, &a_accent_fourth, &a_accent_fifth, &special_n); >>> \<<< 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; >>> \<<< (IGNORED) get_open_accent(&i_accent_template, &i_accent_first, &i_accent_second, &i_accent_third, &i_accent_fourth, &i_accent_fifth, &special_n); >>> \<<< 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. \<<< accented >>> \<<< if( stack[stack_n].accented ){ ` stack[stack_n].accented=FALSE; } >>> \<<< 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 \<<< switch( special_n ){ case 0:{ if( math_class_on ){ open_del = 256; pause_class = ignore_subclass_del = 0; math_class_on = FALSE; ` } else { show_class = !show_class; } break; } case 1:{ ` special_n--; if( (math_class = scan_class(1)) == ` ) { math_class = 0; } else math_class_on = TRUE; break; } case 2:{ ` break; } default:{ ` } } >>> \<<< 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. \<<< static int sv_group_dvi, sv_trace_dvi_C, sv_in_trace_char, sv_span_on, sv_in_span_ch; >>> \<<< 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; >>> \<<< 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; >>> \
<<< static int scan_class( ARG_I(int) ); >>> \<<< `[ static int scan_class( flag ) int flag ;{ int math_class; math_class = get_char(); if( (math_class >= '0' ) && (math_class < '0' + `) ){ { math_class -= '0'; } } else { if( flag== 1 ) { switch( math_class ){ case '-': { math_class = `; pause_class++; break; } case '+': { math_class = `; pause_class--; break; } default: { math_class = 0; } } } else if( flag== 2 ) { switch( math_class ){ case `: { math_class = `; break; } case `: { math_class = `; break; } default: { math_class = 0; } } } else { math_class = 0; } } return math_class; } >>> \Section{Setting Requests} \
<<< static INTEGER set_ch_class( ARG_I(int) ); >>> \<<< `[ static INTEGER set_ch_class(ch) int ch ;{ int r_ch; r_ch = ch - font_tbl[cur_fnt].char_f; if( math_class == ` ){ 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 == ` )? ch : 256; ` } >>> \<<< set_ch_class(ch_1) >>> \<<< { 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 > `)? &(`) : &(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 > `) ? &(`) : &(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 > `){ ` } } >>> 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. \
<<< static int math_class_of( ARG_II(int,int) ); >>> \<<< `[ 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_class + font_tbl[cur_fnt].math)); } >>> \Section{Memory and Initialization} \<<< static U_CHAR *open_class[`], *close_class[`]; >>> There are 79 characters in the range between the digit `0' and the character \`'~'. \<<< 78 >>> \<<< 79 >>> The following should be characters below the digit `0' \<<< ')' >>> \<<< '(' >>> \<<< (` + 1) >>> \<<< (` + 2) >>> \<<< 82 `%one extra is needed?`% >>> \<<< for( math_class=0; math_class<`; 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'; } >>> \<<< char *math, *math_closing; >>> \<<< new_font.math_closing = m_alloc(char, n_gif_bytes ); new_font.math = m_alloc(char, n_gif ); >>> \<<< new_font.math_closing[i] = >>> \<<< new_font.math[i] = >>> \<<< 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. \<<< special_n -= 2; math_class = scan_class(0); stack[stack_n+1].ignore_subclass_del = (` == 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. \<<< stack[stack_n+1].ignore_subclass_del = (math_class == `); stack[stack_n+1].temp_class_del = TRUE; stack[stack_n+1].active_class_del = TRUE; >>> \<<< stack[stack_n+1].temp_class_close >>> \<<< stack[stack_n+1].temp_class_open >>> \SubSection{Insertions} \<<< if( stack[stack_n].active_class_del ){ if( show_class && !pause_class && !ignore_subclass_del ){ ` } ignore_subclass_del = ignore_subclass_del + stack[stack_n].ignore_subclass_del; stack[stack_n+1].no_left_del= FALSE; } >>> \<<< 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 ){ ` } stack[stack_n].active_class_del = FALSE; } >>> \SubSection{Memory and Initialization} \<<< 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. \<<< 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} \<<< if( show_class && !pause_class && !ignore_subclass_del){ if(stack[stack_n+1].no_left_del && stack[stack_n+1].active_class_del ){ ` } else { math_class = math_class_of( ch, cur_fnt ); (IGNORED) print_f( open_class[math_class]); } } >>> \<<< 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 ){ ` } } else { ` } } >>> \<<< 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. \<<< if( show_class && !pause_class && !ignore_subclass_del ){ if(stack[stack_n+1].no_left_del && stack[stack_n+1].active_class_del ){ ` stack[stack_n+1].active_class_del = FALSE; } else { (IGNORED) print_f( close_class[math_class]); } } >>> \<<< 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 ){ ` } ` stack[stack_n+1].active_class_del = FALSE; } else { ` stack[stack_n+1].active_class_del = FALSE; `%%%%%%%%NEW%%%%`% } } >>> \<<< (IGNORED) print_f( close_class[math_class]); >>> \<<< (IGNORED) print_f( (stack[stack_n].temp_class_del)? stack[stack_n].temp_class_close : stack[stack_n].class_close); >>> \<<< (IGNORED) print_f( (stack[stack_n+1].temp_class_del)? stack[stack_n+1].temp_class_close : stack[stack_n+1].class_close); >>> \<<< (IGNORED) print_f( (stack[stack_n].temp_class_del)? stack[stack_n].temp_class_open : stack[stack_n].class_open); >>> \<<< (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. \<<< tex4ht_env_file = p+2; >>> % if( !access(p+2,F_OK) ) tex4ht_env_file = p+2; % else warn_i_str(12,p+2); \<<< char* tex4ht_env_file = (char *) 0; char* dos_env_file = #if defined(__MSDOS__) `; #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} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< static BOOL dump_env_search = FALSE; >>> \<<< dump_env_search = TRUE; >>> \<<< { U_CHAR str[PATH_MAX], *TEX4HTENV; ` ` if( !dot_file ){ ` } ` ` ` ` if( !dot_file ) { bad_in_file(`); } /* give up if no tex4ht.env */ } >>> \<<< 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; >>> \<<< 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 >>> \<<< 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 ); } >>> \<<< 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 >>> \<<< #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 = 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"); } } >>> \<<< #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 >>> \<<< #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_env_dir(argv[0]) >>> \<<< #if defined(__MSDOS__) ` #endif >>> \<<< `[ 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 \
<<< static char* get_script( ARG_III(char *, const U_CHAR *, int) ); >>> \<<< `[ 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, `); if( search_dot_file( x ) ){ ` } 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. \<<< 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} %%%%%%%%%%%%%%%%%%%%%%%%% \
<<< static BOOL search_dot_file( ARG_I( int) ); >>> \<<< `[ static BOOL search_dot_file( ch ) int ch ;{ int chr; while( TRUE ){ chr = getc(dot_file); if( chr == ch ){ return TRUE; } if( chr == '<' ) { ` continue; } if( chr == '\n' ){ continue; } do if( chr == EOF ) return FALSE; while( (chr = getc(dot_file)) != '\n' ); } } >>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% *p, \<<< 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'; } ` if( env_skip ){ ` } } >>> \<<< 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; } } >>> \<<< struct env_c_rec{ char *option; struct env_c_rec *next; }; >>> \<<< static struct env_c_rec *envChoice = (struct env_c_rec*) 0; >>> \<<< 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; >>> \<<< 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 \<<< com_dir(p); fontdir[fontdir_count++] = p+2; >>> The \`'access' function returns 0 if the path is fine; -1 for error. \<<< static U_CHAR *fontdir[MAXFDIRS]; static int fontdir_count = 0; >>> What the use of the following? Do we wants to make it h-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; '? \<<< { `% int i; `% font_file = NULL; (IGNORED) sprintf(file_name, "%s.tfm", new_font_name); ` ` 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); ` ` } >>> 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. \<<< #ifdef TFMDIR if( !font_file ) font_file = search_file_base(file_name, TFMDIR, READ_BIN_FLAGS, tfm_dirs); #endif >>> \<<< #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. \<<< #ifndef KPATHSEA tfm_dirs = get_env_var("TEX4HTTFM"); #endif htf_dirs = get_env_var("TEX4HTHTF"); >>> \
<<< static struct env_var_rec * get_env_var( ARG_I(const char *) ); >>> \<<< `[ 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; ` if( base ){ ` } } } 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. \<<< p = m_alloc(struct env_var_rec, 1); p->next = tfm_dirs; p->base = base; tfm_dirs = p; >>> \<<< { 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; } } } >>> \<<< #ifndef KPATHSEA struct env_var_rec *tfm_dirs; #endif struct env_var_rec *htf_dirs; >>> \<<< struct env_var_rec{ char* base; struct env_var_rec *next; }; >>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \SubSection{Cache Directories} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< 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; >>> \<<< { 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); ` 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; } } } >>> \<<< 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 ); } >>> \<<< static struct cache_font_rec *cache_font, *cur_cache_font; >>> \<<< cache_font = (struct cache_font_rec *) 0; cur_cache_font = (struct cache_font_rec *) 0; >>> \<<< 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 \''' \<<< #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 #endif #ifndef KPATHSEA #ifdef HAVE_UNISTD_H #include #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 \<<< { U_CHAR name[256]; (IGNORED) sprintf(name, "%s.htf", new_font_name); ` if( file ){ ` } } >>> \<<< file = NULL; ` 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 ` } >>> \<<< struct htf_com_rec{ char* name; struct htf_com_rec* next; }; >>> \<<< struct htf_com_rec* htf_font_dir = (struct htf_com_rec *) 0; >>> \<<< 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; } } >>> \
<<< static void com_dir( ARG_I(char*) ); >>> \<<< `[ 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; \<<< { 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; } } >>> \<<< #ifndef HTFDIR #endif >>> Example \`' #define HTFDIR "/n/gold/5/gurari/tex4ht-fonts.dir/"' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \Section{External File Cache} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< cur_cache_font->cache_file = (struct cache_file_rec *) 0; >>> \<<< { 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 ) { ` } } } >>> \<<< 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. \<<< U_CHAR cache_dir[256], dot_dir[256], *p, *q; BOOL flag; int n, ch; (IGNORED) fseek(cache_files, 0L, `); ch = (int) getc(cache_files); while ( ch != EOF ){ if ( ch == ' ' ) { ` } else { ` if( flag ){ ` if( flag ){ ` } } } if ( ch != EOF ){ ch = (int) getc(cache_files); } } >>> \<<< q = cache_dir; do *(q++) = ch = (int) getc(cache_files); while( (ch !='\n') && (ch != EOF) ); *(q-1 - (*(q-2) == `) #ifdef DOS_WIN32 - (*(q-2) == '/') #endif ) = '\0'; >>> \<<< 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 = FALSE; (IGNORED) fseek(dot_file, 0L, `); 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) == `) #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; } } >>> \<<< 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); >>> \<<< if( (file = f_open(cache_dir,flags)) == NULL ) { warn_i_str( 47, cache_dir); } else { return file; } >>> \SubSection{Store New Auxiliary Cache} \<<< if( tex4ht_fls ){ FILE *in_file, *out_file; U_CHAR temp_file[256]; ` ` 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 ){ ` (IGNORED) fclose(in_file); } (IGNORED) fclose(out_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'; >>> \<<< #ifndef KPATHSEA static BOOL tex4ht_fls = FALSE; static char *tex4ht_fls_name = (char *) 0; #endif >>> \<<< #if defined(DOS_WIN32) || defined(__DJGPP__) (is_forward_slash(files_cache)? "/" : "\\" ) #else "/" #endif >>> \<<< { U_CHAR *p; if( !tex4ht_fls_name ){ tex4ht_fls_name = p = files_cache; (IGNORED) fseek(dot_file, 0L, `); 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"); } } ` } >>> \<<< if( *tex4ht_fls_name == '~' ){ tex4ht_fls_name = abs_addr(tex4ht_fls_name,getenv("TEX4HTWR")); } >>> \<<< if( *tex4ht_fls_name == '~' ){ ` 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; } >>> \<<< U_CHAR files_cache[PATH_MAX]; >>> \<<< U_CHAR p[PATH_MAX]; >>> \<<< static U_CHAR *HOME_DIR; >>> \<<< HOME_DIR = getenv("HOME"); >>> We might want more than one version of \`'tex4ht.fls', for different combinations of subtrees of htf fonts. \<<< #ifndef KPATHSEA tex4ht_fls_name = p+2; #endif >>> \<<< 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); } >>> \<<< 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') ){ ` (IGNORED) fprintf(out_file," %s\n", dir); (IGNORED) strcpy((char *) prev_dir, (char *) dir); } else if( !is_dir && (file[0] != '\0') ){ ` (IGNORED) fprintf(out_file,"%s\n", file); } } ` >>> \<<< while( cur_cache_font != (struct cache_font_rec *)0 ){ if( gt_str(dir,cur_cache_font->dir) ){ ` } else break; cur_cache_font = cur_cache_font->next; } >>> \<<< 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 ); } } >>> \<<< 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; } } >>> \<<< while( cur_cache_font != (struct cache_font_rec *)0 ){ ` cur_cache_font = cur_cache_font->next; } >>> \<<< #ifndef KPATHSEA static FILE* cache_files; #endif >>> \<<< cache_files = f_open(tex4ht_fls_name, READ_BIN_FLAGS); >>> \<<< 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} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< #ifdef KPATHSEA { char str [256], *export_str, *postfix; export_str = m_alloc(char, 1); *export_str = '\0'; ` 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 ){ ` ` } } ` free((void *) export_str); ` } #endif >>> \<<< { 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>> An entry similar to the following one is assumed in texmf.cnf. \Verbatim TEX4HTINPUTS=.;$TEXMF/tex4ht/base//;$TEXMF/tex4ht/ht-fonts/{$TEX4HTFONTSET}// \EndVerbatim \<<< { U_CHAR * q; if( dump_htf_search ) { U_CHAR *p, *q; ` } 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 \<<< { 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, `); 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 `
>>>



\
<<<
{                    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}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


\
<<< #ifdef KPATHSEA static void export_htf( ARG_II(char**, char[]) ); #endif >>> \<<< #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. \<<< #ifdef KPATHSEA static char * export_str_chars = (char *) 0; #endif >>> \<<< #ifdef KPATHSEA ` #endif >>> \<<< { 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); } } >>> \<<< int cardinality=0; char ** fontset=0; >>> \<<< #ifdef KPATHSEA if( export_str_chars ){ ` } #endif >>> \<<< #ifdef KPATHSEA if( export_str_chars ){ free((void *) export_str_chars); free((void *) fontset); } #endif >>> \<<< { 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++; } } >>> \<<< { U_CHAR * head, * tail, *p; int n; ` htfname = (U_CHAR *) 0; ` } >>> \<<< 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--; } >>> \<<< 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} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< if( dump_htf_search || dump_env_search ) { U_CHAR *p, *q; ` } >>> 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. \<<< 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" ); } >>> \<<< 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 \<<< ` >>> \<<< #ifdef KPATHSEA kpse_set_program_name (argv[0], "tex4ht"); #endif >>> \<<< #ifdef KPATHSEA if( !dot_file ) { U_CHAR * envfile; char *arch, *p, str[256]; ` envfile = (char *) 0; ` if ( !envfile ){ ` } if ( !envfile ){ ` } 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 \<<< p = arch = (char *) kpse_var_value( "SELFAUTOLOC" ); while( *p != '\0' ){ if( (*p == '/') || (*p == '\\') ){ arch = p; } p++; } >>> \<<< 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); } >>> \<<< if( dump_env_search ){ (IGNORED) printf("kpse_open_file (\"tex4ht.env\", ...)?\n"); } envfile= kpse_find_file ("tex4ht.env", kpse_program_text_format, 0); >>> \<<< #ifdef KPATHSEA { U_CHAR * tfmfile; tfmfile = kpse_find_file (file_name, kpse_tfm_format, 0); if( !tfmfile ){ ` } 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 >>> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \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'. \<<< #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 ){ ` 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"); } } >>> \<<< char s [256]; (IGNORED) strcpy(s, "kpsewhich "); (IGNORED) strcat(s, file_name); (IGNORED) strcat(s, " > tex4ht.tmp "); if( system(s) == 0 ){ ` 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+>&+. \<<< 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} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< static BOOL dump_htf_search = FALSE; >>> \<<< dump_htf_search = TRUE; >>> \<<< #ifdef KPATHSEA if( !file ){ U_CHAR * htfname; htfname= kpse_find_file (name, kpse_program_text_format, 0); if ( 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 , > * 11 March 1999 > */ 94a99,102 > #ifdef KPATHSEA > #include > #include > #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. \
<<< static FILE* search_in_dot_file( ARG_IV( int, const U_CHAR *, const U_CHAR *, struct env_var_rec *) ); >>> \<<< `[ 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 ){ ` } #endif (IGNORED) fseek(dot_file, 0L, `); 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; } >>> \
<<< static FILE* search_file_base( ARG_IV( const U_CHAR *, const U_CHAR *, const U_CHAR *, struct env_var_rec *) ); >>> \<<< `[ 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; } >>> \
<<< static char * abs_addr( ARG_II( const U_CHAR *, const U_CHAR *) ); >>> \<<< `[ 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). \
<<< static FILE* search_file( ARG_III(const char *, const U_CHAR *, const U_CHAR *) ); >>> \<<< `[ 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; ` (IGNORED) strcpy((char *) str, dir); i = (int) strlen((char *) str) - 1; subs = str[i] == '!'; if( subs ) str[i] = '\0'; else i++; ` ` str[i] = '\0'; return subs? search_file_ext( name, str, flags): NULL; } >>> \<<< (IGNORED) strct(str, #if defined(__DJGPP__) (( dir[i-1] == '/') || ( dir[i-1] == '\\')) ? "" : (is_forward_slash(dir)? "/" : "\\" ) #else (dir[i-1] == '/')? "" : "/" #endif ); >>> \<<< if( (file = f_open(name, flags)) != NULL ){ return file; } >>> \<<< (IGNORED) strct(str,name); if( (file = f_open(str, flags)) != NULL ){ str[i] = '\0'; add_to_cache(str,name,i); return file; } >>> \
<<< static void add_to_cache( ARG_III(const char*,const char*,int) ); >>> \<<< `[ static void add_to_cache(dir,name,n) const char* dir`; const char* name`; int n ;{ struct cache_font_rec *cur_cache_font; ` ` } >>> \SubSection{Search in SubDirectories} \
<<< static FILE* search_file_ext( ARG_III(const char *, const U_CHAR *, const U_CHAR *) ); >>> \<<< `[ 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] == `) #ifdef DOS_WIN32 || (str[n] == '/' ) #endif ) n++; str[n-1] = '\0'; #ifndef NOSUBDIR #ifdef WIN32 ` #else ` #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 (\''/'). \<<< #if defined(__DJGPP__) '\\' #else '/' #endif >>> \<<< #if defined(__DJGPP__) #define dir_path_slash(str) (is_forward_slash(str)? '/' : '\\') #else #define dir_path_slash(str) '/' #endif >>> \
<<< #if defined(__DJGPP__) static BOOL is_forward_slash( ARG_I(const char*) ); #endif >>> \<<< #if defined(__DJGPP__) `[ static BOOL is_forward_slash(str) const char* str ;{ while( *str ){ if( *(str++) == '/' ) { return TRUE; } } return FALSE; } #endif >>> \<<< { DIR *dp; ` *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, "..") ) { ` } } (void) closedir(dp); } } >>> \<<< (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; } >>> \<<< #ifdef DOS_WIN32 #include #endif >>> \<<< #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 } defines it as 1 (Peter Breitenlohner). \Verbatim 1998-10-22 Eli Zaretskii DJGPP: (LSTAT): Define to stat for every system that doesn't define S_ISLNK. \EndVerbatim \<<< #if defined(DOS_WIN32) || !defined(S_ISLNK) #define LSTAT stat #else #define LSTAT lstat #endif #define STSTAT stat >>> \<<< #include `% stat _IF_DIR `% >>> \SubSection{dirent} readdir, opendir, closedir \<<< #ifdef DOS_WIN32 #define STRUCT_DIRENT #endif >>> \<<< #ifdef HAVE_DIRENT_H ` #else #ifndef STRUCT_DIRENT #define STRUCT_DIRECT #endif ` #endif >>> \<<< #include >>> \<<< #ifdef HAVE_SYS_NDIR_H #include #endif #ifdef HAVE_SYS_DIR_H #include #endif #ifdef HAVE_NDIR_H #include #endif >>> \<<< #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 , 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 +#ifdef HAVE_DIRENT_H +#include +typedef struct dirent struct_dirent; +#else /* no */ +typedef struct direct struct_dirent; +#ifdef HAVE_SYS_NDIR_H +#include +#endif +#ifdef HAVE_SYS_DIR_H +#include +#endif +#ifdef HAVE_NDIR_H +#include +#endif +#endif /* no */ #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} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \
<<< static FILE* f_open_pathed_filename( ARG_II(const char*,const char*) ); >>> \<<< `[ 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} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \
<<< static INTEGER put_4ht_ch( ARG_II(int,FILE *) ); >>> \<<< `[ static INTEGER put_4ht_ch(ch,htFile) int ch `; FILE* htFile ;{ int c; c = ch; if( ch=='&' ){ ` 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 == ';' ){ ` 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') ) ) { ` c = putc( ch, htFile ); } else { uni_code[ uni_code_p++ ] = ch; } } else { c = putc( ch, htFile ); } return c; } >>> \<<< 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; >>> \<<< #define MAX_UNI_CODE 20 >>> \<<< special_n--; switch ( code = get_char() ){ case '+': { put_4ht_off++; ` break; } case '-': { if( put_4ht_off>0 ){ put_4ht_off--; } else { warn_i_str(52, "@u-"); } break; } } >>> %%%%%%%%%%%%% \Section{Flash Unrecognized Codes} %%%%%%%%%%%%% \<<< flush_uni(); >>> \
<<< static void flush_uni( ARG_I(void) ); >>> \<<< static void flush_uni( MYVOID ) { int i; for( i=0; i>> %%%%%%%%%%%%% \Section{Load unicode.4hf Table} %%%%%%%%%%%%% \<<< { U_CHAR name[256]; FILE* file; (IGNORED) sprintf(name, "%s.4hf", "unicode"); ` if( file ){ ` ` ` put_4ht_off = 0; } else{ put_4ht_off = 1; ` } } >>> \<<< 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; >>> \<<< err = FALSE; line_no = 0; while( TRUE ){ line_no++; chr = (int) getc(file); if( chr == EOF ){ break; } if( (chr>32) && (chr<127) ){ ` if( delimiter_n == 8 ){ if( *in != '?' ) { if( ` ){ err = TRUE; } else { ` if( start[3] == (in_p-1) ){ if( !err ){ ` } } else { ` } } } } else { err = TRUE; } ` } while( (chr != EOF) && (chr!='\n') ){ chr = (int) getc(file); } if( chr == EOF ){ break; } } >>> \<<< (*in != '&') || (*(in+1) != '#') || ( (*(in+2) != 'x') && (*(in+2) != 'X')) || (*(start[1] - 2) != ';') >>> \<<< 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; } } >>> \<<< >>> \<<< 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} %%%%%%%%%%%%% \<<< struct charset_rec{ int ch; char* str; }; >>> \<<< static int charset_n = 0, max_charset_n; static struct charset_rec *charset; >>> \<<< max_charset_n = 256; charset = m_alloc(struct charset_rec, 256); >>> \<<< max_charset_n = 0; >>> \<<< 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; } >>> \<<< #define BASE_A 55 #define BASE_a 87 >>> \<<< ` 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. \<<< 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} %%%%%%%%%%%%% \<<< if( uni_code[1] != '#' ){ ` (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='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 ){ ` (IGNORED) putc( ch, htFile ); } else { ` } } >>> \<<< int bottom, mid, top; BOOL found=FALSE; bottom = 0; top = charset_n; while( !found ){ mid = (bottom + top) / 2; if( value == charset[mid].ch ){ ` found = TRUE; } else if( value < charset[mid].ch ){ if( bottom == top ){ break; } top = mid; } else { if ( bottom < mid ){ bottom = mid; } else if ( bottom } ` if( !utf8 ){ (IGNORED) putc( ch, htFile ); } } >>> \<<< { 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 ){ ` } } p++; } } >>> The utf8 option don't apply for unicode symbols coming from a 4hf fonts. %%%%%%%%%%%%% \Section{Base 10 for Entity Codes} %%%%%%%%%%%%% \<<< static BOOL u10 = FALSE; >>> \<<< if( eq_str(p+2, "10") ){ u10 = TRUE; } ` else{ bad_arg;} >>> \<<< 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 '9')? ( 10 + ((ch > 'Z')? (ch-'a') : (ch-'A')) ) : (ch-'0')); } if( u10 ){ ` } else { ` } } >>> \<<< 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 ]; } } >>> \<<< 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 == ';' ){ ` p=q-2; } break; } digit = *(q++); } } } >>> \<<< 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 \<<< 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; } >>> \<<< static BOOL utf8 = FALSE; >>> \<<< else if( eq_str(p+2, "tf8") ){ utf8 = TRUE; } >>> \<<< ` 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} %%%%%%%%%%%%% \<<< ` (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; ` ` >>> \<<< for( i = 0; i>> \<<< 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]) ); >>> \<<< 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; >>> \<<< 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; >>> \<<< struct htf_4hf_rec { int ch, type1, type2; char* str; }; >>> \<<< static int htf_4hf_n = 0, max_htf_4hf_n; static struct htf_4hf_rec *htf_4hf; >>> \<<< max_htf_4hf_n = 256; htf_4hf = m_alloc(struct htf_4hf_rec, 256); >>> \<<< max_htf_4hf_n = 0; >>> %%%%%%%%%%%%% \Section{Propagate Changes into the HTF Fonts} %%%%%%%%%%%%% \<<< 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 ){ ` ` } } >>> \<<< if( htf_4hf[mid].type1 == ch1 ){ ch1 = htf_4hf[mid].type2; (IGNORED) strcpy((char *) str, (char *) htf_4hf[mid].str ); } >>> \<<< if( charset[mid].type1 == ch1 ){ ch1 = charset[mid].type2; (IGNORED) strcpy((char *) str, (char *) charset.str ); } >>> \<<< 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 ){ ` found = TRUE; } else if( value < htf_4hf[mid].ch ){ if( bottom == top ){ break; } top = mid; } else { if ( bottom < mid ){ bottom = mid; } else if ( bottom>> \<<< bottom = 0; top = charset_n; while( !found ){ mid = (bottom + top) / 2; if( value == charset[mid].ch ){ ` found = TRUE; } else if( value < charset[mid].ch ){ if( bottom == top ){ break; } top = mid; } else { if ( bottom < mid ){ bottom = mid; } else if ( bottom>> %%%%%%%%%%%%%%%%%%%%%%% \Chapter{Utilities} %%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%% \Section{Output Character} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \
<<< static INTEGER insert_ch( ARG_I(int) ); >>> \<<< `[ static INTEGER insert_ch(ch) int ch ;{ if( !ignore_chs ){ BOOL flag; ` if( !flag ){ try_new_line(); ` text_on = TRUE; } } ` } >>> /* if (ch < 0) ch+=256; /*NEW */ \<<< flag = FALSE; if( ` ) { if( eq_str(next_str, "") ) { flag = TRUE; free((void *) next_str); next_str = (char *) 0; } } >>> \
<<< static void put_char( ARG_I(int) ); >>> \<<< `[ static void put_char( ch ) int ch ;{ if( !ignore_chs && !( ((ch==' ') || (ch=='\n')) && no_root_file ) ){ ` if( ch_map_flag ){ if( special_on || ((ch != '\n') && (ch != ' ')) ){ ` } }else { if ( ch == '\n' ){ ` } else if ( ch == ' ' ){ ` } else { (IGNORED) put_4ht_ch( ch, cur_o_file ); } } } } >>> \<<< static BOOL special_on = FALSE; >>> %%%%%%%%%%%%%%%%%%%%%%%%% \Section{Output String} %%%%%%%%%%%%%%%%%%%%%%%%% \
<<< static void print_f( ARG_I(const char*) ); >>> \<<< `[ static void print_f(str) const char* str ;{ ` if( ch_map_flag ){ while( *str ){ put_char( *str ); str++; } }else { (IGNORED) print_f_4ht( str ); } } >>> % fprintf(cur_o_file, "%s", str); \
<<< static void print_f_4ht( ARG_I(const char*) ); >>> \<<< `[ static void print_f_4ht(str) const char* str ;{ ` 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} %%%%%%%%%%%%%%%%%%%%%%%%% \
<<< static int get_char( ARG_I(void) ); >>> \<<< static int get_char(MYVOID) { return (int) getc(dvi_file); } >>> \
<<< static int get_noop( ARG_I(void) ); >>> \<<< static int get_noop(MYVOID) { int ch; while( (ch = get_char()) == ` ){;} return ch; } >>> \Section{Read Strings} \
<<< static char* get_str( ARG_I(int) ); >>> \<<< `[ 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} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< #define get_unt(n) fget_unt(dvi_file,n) >>> \
<<< static long fget_unt( ARG_II(FILE*, int) ); >>> \<<< `[ 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} \<<< #define get_int(n) fget_int(dvi_file,n) >>> \
<<< static long fget_int( ARG_II(FILE *, int) ); >>> \<<< `[ 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 \
<<< static long cond_int( ARG_I(register INTEGER) ); >>> \<<< `[ 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} %%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< #define eq_str(x,y) (!strcmp(x,y)) #define gt_str(x,y) (strcmp(x,y)>0) >>> \Section{Warnings and Errors} \SubSection{Messages} \<<< `, `%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`% ` `%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`% >>> \<<< static const U_CHAR *warn_err_mssg[]={ ` "" }; >>> \SubSection{Warnings Commands} \
<<< static void warn_i( ARG_I(int) ); >>> \<<< `[ static void warn_i(n) int n ;{ (IGNORED) fprintf(stderr,"--- warning --- "); (IGNORED) fprintf(stderr, "%s", warn_err_mssg[n]); show_err_context(); } >>> \<<< (IGNORED) fprintf(stderr,"--- warning --- "); (IGNORED) fprintf(stderr, warn_err_mssg[22] >>> % \<<< % static U_CHAR warning[] = "--- warning --- "; % >>> \
<<< static void warn_i_int( ARG_II(int,int) ); >>> \<<< `[ 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(); } >>> \
<<< static void warn_i_int_2( ARG_III(int,int,int) ); >>> \<<< `[ 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(); } >>> \
<<< static void warn_i_str( ARG_II(int,const char *) ); >>> \<<< `[ 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(); } >>> \
<<< static void warn_i_str2( ARG_III(int,const char *,const char *) ); >>> \<<< `[ 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} \<<< #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) >>> \
<<< static void err_i( ARG_I(int) ); >>> \<<< `[ 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. \
<<< static void err_i_int( ARG_II(int,int) ); >>> \<<< `[ 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); } >>> \
<<< static void err_i_str( ARG_II(int,char *) ); >>> \<<< `[ 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} \<<< if( special_n > 0 ){ warn_i_int( 26, sv); while( special_n-- ) (IGNORED) putc( get_char(), stderr); } >>> \<<< warn_i_int( 26, '!'); (IGNORED) putc( ch, stderr); while( special_n-- ) (IGNORED) putc( get_char(), stderr); >>> \<<< while( special_n-- ) (void) get_char(); >>> \<<< ; >>> \
<<< static void show_err_context( ARG_I(void) ); >>> \<<< 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, `); (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 BOOL err_context = FALSE; >>> \<<< err_context = TRUE; >>> \SubSection{Trace Errors} \<<< static U_CHAR *err_mark = (char *) 0; >>> \<<< 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_special = TRUE; >>> \<<< static BOOL trace_special = FALSE; >>> On off with \''\special{t4ht@/}' \<<< trace_special = !trace_special; >>> \<<< { 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, `); } >>> \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__'. \<<< #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 \<<< #ifdef DOS #define PROTOTYP #endif #ifdef ANSI #define PROTOTYP #endif #ifdef KWIN32 #define PROTOTYP #endif >>> \<<< #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} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \<<< 132 >>> \<<< 137 >>> \<<< 138 >>> \<<< 139 >>> \<<< 140 >>> \<<< 141 >>> \<<< 142 >>> \<<< 143 >>> \<<< 144 >>> \<<< 145 >>> \<<< 146 >>> \<<< 147 >>> \<<< 148 >>> \<<< 149 >>> \<<< 150 >>> \<<< 151 >>> \<<< 152 >>> \<<< 153 >>> \<<< 154 >>> \<<< 155 >>> \<<< 156 >>> \<<< 157 >>> \<<< 158 >>> \<<< 159 >>> \<<< 160 >>> \<<< 161 >>> \<<< 162 >>> \<<< 163 >>> \<<< 164 >>> \<<< 165 >>> \<<< 166 >>> \<<< 167 >>> \<<< 168 >>> \<<< 169 >>> \<<< 170 >>> \<<< 171 >>> \<<< 223 >>> \<<< 234 >>> \<<< 63 >>> \<<< 235 >>> \<<< 236 >>> \<<< 237 >>> \<<< 238 >>> \<<< 239 >>> \<<< 240 >>> \<<< 241 >>> \<<< 242 >>> \<<< 243 >>> \<<< 244 >>> \<<< 245 >>> \<<< 246 >>> \<<< 247 >>> \<<< 248 >>> \<<< 249 >>> \<<< 247 >>> The ops 250--255 have no meaning in dvi. \<<< 251 >>> \<<< 252 >>> \<<< 253 >>> \<<< 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\ \bye