diff options
Diffstat (limited to 'Build/source/texk/xdvik/dvi-draw.c')
-rw-r--r-- | Build/source/texk/xdvik/dvi-draw.c | 6794 |
1 files changed, 6794 insertions, 0 deletions
diff --git a/Build/source/texk/xdvik/dvi-draw.c b/Build/source/texk/xdvik/dvi-draw.c new file mode 100644 index 00000000000..12e4cabca73 --- /dev/null +++ b/Build/source/texk/xdvik/dvi-draw.c @@ -0,0 +1,6794 @@ +/* The rest of the code has the following copyright: + +Copyright (c) 1990-2004 Paul Vojta and others + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +NOTE: + xdvi is based on prior work, as noted in the modification history + in xdvi.c. + +\*========================================================================*/ + +#include "xdvi-config.h" +#include "xdvi.h" + +#include <stdarg.h> + +#include <stdlib.h> +#include <ctype.h> + +#include <setjmp.h> + +#define USE_HASH + +#include <ctype.h> +#include "kpathsea/c-fopen.h" +#include "kpathsea/c-stat.h" +#include "kpathsea/magstep.h" +#include "kpathsea/tex-file.h" +#include "kpathsea/c-vararg.h" + +#include <string.h> + +#include "dvi.h" +#include "string-utils.h" +#include "util.h" +#include "x_util.h" +#include "events.h" +#include "dvi-init.h" +#include "statusline.h" +#include "hypertex.h" +#include "special.h" +#include "tfmload.h" +#include "read-mapfile.h" +#include "my-snprintf.h" +#include "kpathsea/tex-file.h" +#include "mag.h" +#include "message-window.h" + +#include "dvi-draw.h" +#include "search-internal.h" +#include "encodings.h" +#include "pagesel.h" +#include "pagehist.h" + +#ifdef RGB_ANTI_ALIASING +#define TRACE_AA 0 +#define TRACE_AA1 1 +#define TRACE_AA3 0 +#else +#define TRACE_AA 0 +#define TRACE_AA1 0 +#define TRACE_AA3 0 +#endif + +#define MY_DEBUG 1 + +#if MY_DEBUG +# define TRACE_FIND_VERBOSE(x) TRACE_FIND(x) +#else +# define TRACE_FIND_VERBOSE(x) /* as nothing */ +#endif + +#if PS && PS_GS +#include "psgs.h" +#endif /* PS && PS_GS */ + +#ifdef DOPRNT /* define this if vfprintf gives you trouble */ +# define vfprintf(stream, message, args) _doprnt(message, args, stream) +#endif + +#define BUF_SIZE 1024 + +/* + * All calculations are done with shrink factor = 1, so we re-do some + * macros accordingly. Many of these are also defined in special.c. + */ + +#define xpixel_conv(x) ((int) ((x) >> 16)) +#define xpixel_round(x) ((int) ROUNDUP(x, 1 << 16)) + +#define G_PXL_H xpixel_conv(currinf.data.dvi_h) +#define G_OFFSET_X (resource.xoffset_int << 16) + (3 << 15) +#define G_OFFSET_Y (resource.yoffset_int << 16) + (3 << 15) + +#ifdef T1LIB +/* hashing stuff to speed up checking whether a font or tfminfo file + has already been loaded. +*/ +#define T1FONTS_INITIAL_HASHTABLE_SIZE 1031 +static hashTableT t1fonts_hash; +static hashTableT tfminfo_hash; +static hashTableT fontmaps_hash; +static hashTableT font_warn_hash; + +typedef enum { FAILURE_BLANK = -2, FAILURE_PK = -1, SUCCESS = 0 } t1FontLoadStatusT; +#endif + +#if PS +int scanned_page_ps; +int scanned_page_ps_bak; +#endif + +#if COLOR +int scanned_page_color; +#endif + +int scanned_page_reset, scanned_page; + +struct drawinf currinf; +Boolean htex_inside_href = False; /* whether we're inside a href */ + +Boolean drawing_mag = False; + +#ifdef T1LIB + +# include "t1lib.h" + +/* Datastructures: + + encodings[n]: Loaded from map file + [0] = 8r, <vector> + [1] = 8c, <vector> + + The vectors are demand-loaded in 'load_encoded_font' +*/ + +struct encoding { + char *enc; /* This may be NULL. From dvips maps we get + encodings loaded by filename, and no name given */ + char *file; + char **vector; +}; + +static struct encoding *encodings = NULL; /* Dynamic array = realloc when it gets full */ +static size_t enclidx = 0; /* global, for communication with find_T1_font */ + +/* + + t1fonts[n]: Built as xdvi loads t1 fonts + idx file short t1id loaded + [0] = /path/cmr10.pfb, cmr10 0 1 + [1] = /path/prmr.pfa, prmr 1 0 + [2] = /path/pcrr8a.pfa, pcrr8a 2 0 + + This array enumerates the loaded t1 fonts by full path name. + The integer `t1id' is the t1lib font id. The font will not be + extended/slanted/encoded. The `loaded' field indicates if + T1_LoadFont has been called for the font yet. Texfonts that need a + modified version of the t1 font must use T1_CopyFont to copy the + raw font and obtain the id of a new t1 font which can then be + modified. As described in the t1 docs a font must be loaded before + it can be copied. + + Fonts that are copies of other fonts have NULL filenames. If any + font files are to be reused based on the file name, the raw font + must be reused, not a copy. + + The `short' field contains the font as it was identified in the + fontmap. This is used to save searching; if two fonts are + identified the same way in the fontmap, they are the same font. + +*/ + +struct t1font_info { + char *file; + char *shortname; + int t1id; + int loaded; +}; + +static struct t1font_info *t1fonts = NULL; /* Dynamic array */ + +/* + fontmaps[n]: Loaded from map file, and extended with 'implied' + fonts as needed. + idx texname enc exten. slant filena. t1libid pathn. tfmidx + [0] = pcrr8rn 0 750 0 pcrr8a 2 ... 2 + [1] = putro8r 0 0 0 putro8r 3 ... 3 + + The first 5 fields of this table are loaded from the font map + files, or filled out with default values when implied fonts are + set-up. The t1libid is -1 until the font is loaded or copied as + described under t1fonts. Once the dvi file reveals that the font + is needed, it is located on disk to ensure it exists, and the + pathname field is filled out. The t1 font is not loaded or + copied before it's needed because set_t1_char has been called to + draw a glyph from the font. The late loading is necessiated by the + high cost of loading fonts. If the font loading is concentrated at + startup of the program the startup-time becomes ecessively high due + to the high number of fonts used by the average LaTeX document. + + A value of 0 for `extension'/`slant' means no extension/slant. If + the input values are decimal (less than 10) they're multiplied by + 1000 and 10000 respectively to obtain fixed-point integer values. + (This means that xdvi will translate entries like `0.81' to 810.) + Integer values have the advantage that they can be tested for + exact equality on all architectures. +*/ + +struct fontmap { + char *texname; + int enc; /* Index in encoding array */ + int extension; /* Fixed point, *1000 */ + int slant; /* Fixed point, *10000, some font slantings have 4 + significant digits, all after the decimalpoint */ + char *filename; /* Name of the t1 font file as given in map/dvi */ + + int t1libid; /* The t1lib id of this font, or -1 if not set up */ + char *pathname; /* Full path of the font, once needed and located */ + int tfmidx; /* Index in tfminfo array, or -1 */ + Boolean warned_about; /* whether a warning message about this font has been displayed yet */ + /* + if the font file was not readable for some reason (e.g. corrupt, + or encrypted for proprietry distribution), load_font_now() will + try to load a PK version via load_font() instead. If this is + successful, it will set force_pk to true, which will tell + get_t1_glyph() to return immediately, and just use the standard + set_char() routine to set the PK glyph. If even that fails, + get_t1_glyph() returns an error status in a special flag that's + evaluated by set_t1_char(). + */ + Boolean force_pk; +}; + +static struct fontmap *fontmaps = NULL; +static size_t g_maplidx = 0; /* global, for communication with find_texfont */ + +/* + widthinfo[n]: TFM width info, loaded on demand as the fonts are used. + idx texname widths + [0] = cmr10 ... + [1] = ptmr8r ... + [2] = pcrr8rn ... + [3] = putro8r ... + + The widthinfo is loaded from the font tfm file because the type1 + width information isn't precise enough to avoid accumulating + rounding errors that make things visibly misaligned. This is + esp. noticeable in tables (for the lines separating the columns). + + For other than "design" sizes the widths are scaled up or down. + +*/ + +struct tfminfos { + const char *texname; + long designsize; + long widths[256]; + long fontdimen2; /* width of word spacing in this font */ +}; + +static struct tfminfos *tfminfo = NULL; + +/* For file reading */ +# define BUFFER_SIZE 1024 + +/* This is the conversion factor from TeXs pt to TeXs bp (big point). + Maximum machine precision is needed to keep it accurate. */ +/* UNUSED */ +/* static double bp_fac=72.0/72.27; */ + +/* Try to convert from PS charspace units to DVI units with minimal + loss of significant digits */ +# define t1_dvi_conv(x) ((((int) (x)) << 16)/1000) +/* Convert from DVI units to TeX pt */ +# define dvi_pt_conv(x) (((long) ((x)*dimconv)) >> 19) + +/* Convert from TFM units to DVI units */ +# define tfm_dvi_conv(x) (((int) (x)) >> 1) + +static Boolean padMismatch = False; /* Does t1lib padding match xdvi required padding? */ +static int archpad; +# define T1PAD(bits, pad) (((bits) + (pad) - 1) & -(pad)) + +static struct glyph *get_t1_glyph( +# ifdef TEXXET + wide_ubyte cmd, +# endif + wide_ubyte ch, t1FontLoadStatusT *flag, + Boolean is_geom_scan); + +# ifdef WORDS_BIGENDIAN +unsigned char rbits[] = { + 0x00, /* 00000000 -> 00000000 */ + 0x80, /* 00000001 -> 10000000 */ + 0x40, /* 00000010 -> 01000000 */ + 0xC0, /* 00000011 -> 11000000 */ + 0x20, /* 00000100 -> 00100000 */ + 0xA0, /* 00000101 -> 10100000 */ + 0x60, /* 00000110 -> 01100000 */ + 0xE0, /* 00000111 -> 11100000 */ + 0x10, /* 00001000 -> 00010000 */ + 0x90, /* 00001001 -> 10010000 */ + 0x50, /* 00001010 -> 01010000 */ + 0xD0, /* 00001011 -> 11010000 */ + 0x30, /* 00001100 -> 00110000 */ + 0xB0, /* 00001101 -> 10110000 */ + 0x70, /* 00001110 -> 01110000 */ + 0xF0, /* 00001111 -> 11110000 */ + 0x08, /* 00010000 -> 00001000 */ + 0x88, /* 00010001 -> 10001000 */ + 0x48, /* 00010010 -> 01001000 */ + 0xC8, /* 00010011 -> 11001000 */ + 0x28, /* 00010100 -> 00101000 */ + 0xA8, /* 00010101 -> 10101000 */ + 0x68, /* 00010110 -> 01101000 */ + 0xE8, /* 00010111 -> 11101000 */ + 0x18, /* 00011000 -> 00011000 */ + 0x98, /* 00011001 -> 10011000 */ + 0x58, /* 00011010 -> 01011000 */ + 0xD8, /* 00011011 -> 11011000 */ + 0x38, /* 00011100 -> 00111000 */ + 0xB8, /* 00011101 -> 10111000 */ + 0x78, /* 00011110 -> 01111000 */ + 0xF8, /* 00011111 -> 11111000 */ + 0x04, /* 00100000 -> 00000100 */ + 0x84, /* 00100001 -> 10000100 */ + 0x44, /* 00100010 -> 01000100 */ + 0xC4, /* 00100011 -> 11000100 */ + 0x24, /* 00100100 -> 00100100 */ + 0xA4, /* 00100101 -> 10100100 */ + 0x64, /* 00100110 -> 01100100 */ + 0xE4, /* 00100111 -> 11100100 */ + 0x14, /* 00101000 -> 00010100 */ + 0x94, /* 00101001 -> 10010100 */ + 0x54, /* 00101010 -> 01010100 */ + 0xD4, /* 00101011 -> 11010100 */ + 0x34, /* 00101100 -> 00110100 */ + 0xB4, /* 00101101 -> 10110100 */ + 0x74, /* 00101110 -> 01110100 */ + 0xF4, /* 00101111 -> 11110100 */ + 0x0C, /* 00110000 -> 00001100 */ + 0x8C, /* 00110001 -> 10001100 */ + 0x4C, /* 00110010 -> 01001100 */ + 0xCC, /* 00110011 -> 11001100 */ + 0x2C, /* 00110100 -> 00101100 */ + 0xAC, /* 00110101 -> 10101100 */ + 0x6C, /* 00110110 -> 01101100 */ + 0xEC, /* 00110111 -> 11101100 */ + 0x1C, /* 00111000 -> 00011100 */ + 0x9C, /* 00111001 -> 10011100 */ + 0x5C, /* 00111010 -> 01011100 */ + 0xDC, /* 00111011 -> 11011100 */ + 0x3C, /* 00111100 -> 00111100 */ + 0xBC, /* 00111101 -> 10111100 */ + 0x7C, /* 00111110 -> 01111100 */ + 0xFC, /* 00111111 -> 11111100 */ + 0x02, /* 01000000 -> 00000010 */ + 0x82, /* 01000001 -> 10000010 */ + 0x42, /* 01000010 -> 01000010 */ + 0xC2, /* 01000011 -> 11000010 */ + 0x22, /* 01000100 -> 00100010 */ + 0xA2, /* 01000101 -> 10100010 */ + 0x62, /* 01000110 -> 01100010 */ + 0xE2, /* 01000111 -> 11100010 */ + 0x12, /* 01001000 -> 00010010 */ + 0x92, /* 01001001 -> 10010010 */ + 0x52, /* 01001010 -> 01010010 */ + 0xD2, /* 01001011 -> 11010010 */ + 0x32, /* 01001100 -> 00110010 */ + 0xB2, /* 01001101 -> 10110010 */ + 0x72, /* 01001110 -> 01110010 */ + 0xF2, /* 01001111 -> 11110010 */ + 0x0A, /* 01010000 -> 00001010 */ + 0x8A, /* 01010001 -> 10001010 */ + 0x4A, /* 01010010 -> 01001010 */ + 0xCA, /* 01010011 -> 11001010 */ + 0x2A, /* 01010100 -> 00101010 */ + 0xAA, /* 01010101 -> 10101010 */ + 0x6A, /* 01010110 -> 01101010 */ + 0xEA, /* 01010111 -> 11101010 */ + 0x1A, /* 01011000 -> 00011010 */ + 0x9A, /* 01011001 -> 10011010 */ + 0x5A, /* 01011010 -> 01011010 */ + 0xDA, /* 01011011 -> 11011010 */ + 0x3A, /* 01011100 -> 00111010 */ + 0xBA, /* 01011101 -> 10111010 */ + 0x7A, /* 01011110 -> 01111010 */ + 0xFA, /* 01011111 -> 11111010 */ + 0x06, /* 01100000 -> 00000110 */ + 0x86, /* 01100001 -> 10000110 */ + 0x46, /* 01100010 -> 01000110 */ + 0xC6, /* 01100011 -> 11000110 */ + 0x26, /* 01100100 -> 00100110 */ + 0xA6, /* 01100101 -> 10100110 */ + 0x66, /* 01100110 -> 01100110 */ + 0xE6, /* 01100111 -> 11100110 */ + 0x16, /* 01101000 -> 00010110 */ + 0x96, /* 01101001 -> 10010110 */ + 0x56, /* 01101010 -> 01010110 */ + 0xD6, /* 01101011 -> 11010110 */ + 0x36, /* 01101100 -> 00110110 */ + 0xB6, /* 01101101 -> 10110110 */ + 0x76, /* 01101110 -> 01110110 */ + 0xF6, /* 01101111 -> 11110110 */ + 0x0E, /* 01110000 -> 00001110 */ + 0x8E, /* 01110001 -> 10001110 */ + 0x4E, /* 01110010 -> 01001110 */ + 0xCE, /* 01110011 -> 11001110 */ + 0x2E, /* 01110100 -> 00101110 */ + 0xAE, /* 01110101 -> 10101110 */ + 0x6E, /* 01110110 -> 01101110 */ + 0xEE, /* 01110111 -> 11101110 */ + 0x1E, /* 01111000 -> 00011110 */ + 0x9E, /* 01111001 -> 10011110 */ + 0x5E, /* 01111010 -> 01011110 */ + 0xDE, /* 01111011 -> 11011110 */ + 0x3E, /* 01111100 -> 00111110 */ + 0xBE, /* 01111101 -> 10111110 */ + 0x7E, /* 01111110 -> 01111110 */ + 0xFE, /* 01111111 -> 11111110 */ + 0x01, /* 10000000 -> 00000001 */ + 0x81, /* 10000001 -> 10000001 */ + 0x41, /* 10000010 -> 01000001 */ + 0xC1, /* 10000011 -> 11000001 */ + 0x21, /* 10000100 -> 00100001 */ + 0xA1, /* 10000101 -> 10100001 */ + 0x61, /* 10000110 -> 01100001 */ + 0xE1, /* 10000111 -> 11100001 */ + 0x11, /* 10001000 -> 00010001 */ + 0x91, /* 10001001 -> 10010001 */ + 0x51, /* 10001010 -> 01010001 */ + 0xD1, /* 10001011 -> 11010001 */ + 0x31, /* 10001100 -> 00110001 */ + 0xB1, /* 10001101 -> 10110001 */ + 0x71, /* 10001110 -> 01110001 */ + 0xF1, /* 10001111 -> 11110001 */ + 0x09, /* 10010000 -> 00001001 */ + 0x89, /* 10010001 -> 10001001 */ + 0x49, /* 10010010 -> 01001001 */ + 0xC9, /* 10010011 -> 11001001 */ + 0x29, /* 10010100 -> 00101001 */ + 0xA9, /* 10010101 -> 10101001 */ + 0x69, /* 10010110 -> 01101001 */ + 0xE9, /* 10010111 -> 11101001 */ + 0x19, /* 10011000 -> 00011001 */ + 0x99, /* 10011001 -> 10011001 */ + 0x59, /* 10011010 -> 01011001 */ + 0xD9, /* 10011011 -> 11011001 */ + 0x39, /* 10011100 -> 00111001 */ + 0xB9, /* 10011101 -> 10111001 */ + 0x79, /* 10011110 -> 01111001 */ + 0xF9, /* 10011111 -> 11111001 */ + 0x05, /* 10100000 -> 00000101 */ + 0x85, /* 10100001 -> 10000101 */ + 0x45, /* 10100010 -> 01000101 */ + 0xC5, /* 10100011 -> 11000101 */ + 0x25, /* 10100100 -> 00100101 */ + 0xA5, /* 10100101 -> 10100101 */ + 0x65, /* 10100110 -> 01100101 */ + 0xE5, /* 10100111 -> 11100101 */ + 0x15, /* 10101000 -> 00010101 */ + 0x95, /* 10101001 -> 10010101 */ + 0x55, /* 10101010 -> 01010101 */ + 0xD5, /* 10101011 -> 11010101 */ + 0x35, /* 10101100 -> 00110101 */ + 0xB5, /* 10101101 -> 10110101 */ + 0x75, /* 10101110 -> 01110101 */ + 0xF5, /* 10101111 -> 11110101 */ + 0x0D, /* 10110000 -> 00001101 */ + 0x8D, /* 10110001 -> 10001101 */ + 0x4D, /* 10110010 -> 01001101 */ + 0xCD, /* 10110011 -> 11001101 */ + 0x2D, /* 10110100 -> 00101101 */ + 0xAD, /* 10110101 -> 10101101 */ + 0x6D, /* 10110110 -> 01101101 */ + 0xED, /* 10110111 -> 11101101 */ + 0x1D, /* 10111000 -> 00011101 */ + 0x9D, /* 10111001 -> 10011101 */ + 0x5D, /* 10111010 -> 01011101 */ + 0xDD, /* 10111011 -> 11011101 */ + 0x3D, /* 10111100 -> 00111101 */ + 0xBD, /* 10111101 -> 10111101 */ + 0x7D, /* 10111110 -> 01111101 */ + 0xFD, /* 10111111 -> 11111101 */ + 0x03, /* 11000000 -> 00000011 */ + 0x83, /* 11000001 -> 10000011 */ + 0x43, /* 11000010 -> 01000011 */ + 0xC3, /* 11000011 -> 11000011 */ + 0x23, /* 11000100 -> 00100011 */ + 0xA3, /* 11000101 -> 10100011 */ + 0x63, /* 11000110 -> 01100011 */ + 0xE3, /* 11000111 -> 11100011 */ + 0x13, /* 11001000 -> 00010011 */ + 0x93, /* 11001001 -> 10010011 */ + 0x53, /* 11001010 -> 01010011 */ + 0xD3, /* 11001011 -> 11010011 */ + 0x33, /* 11001100 -> 00110011 */ + 0xB3, /* 11001101 -> 10110011 */ + 0x73, /* 11001110 -> 01110011 */ + 0xF3, /* 11001111 -> 11110011 */ + 0x0B, /* 11010000 -> 00001011 */ + 0x8B, /* 11010001 -> 10001011 */ + 0x4B, /* 11010010 -> 01001011 */ + 0xCB, /* 11010011 -> 11001011 */ + 0x2B, /* 11010100 -> 00101011 */ + 0xAB, /* 11010101 -> 10101011 */ + 0x6B, /* 11010110 -> 01101011 */ + 0xEB, /* 11010111 -> 11101011 */ + 0x1B, /* 11011000 -> 00011011 */ + 0x9B, /* 11011001 -> 10011011 */ + 0x5B, /* 11011010 -> 01011011 */ + 0xDB, /* 11011011 -> 11011011 */ + 0x3B, /* 11011100 -> 00111011 */ + 0xBB, /* 11011101 -> 10111011 */ + 0x7B, /* 11011110 -> 01111011 */ + 0xFB, /* 11011111 -> 11111011 */ + 0x07, /* 11100000 -> 00000111 */ + 0x87, /* 11100001 -> 10000111 */ + 0x47, /* 11100010 -> 01000111 */ + 0xC7, /* 11100011 -> 11000111 */ + 0x27, /* 11100100 -> 00100111 */ + 0xA7, /* 11100101 -> 10100111 */ + 0x67, /* 11100110 -> 01100111 */ + 0xE7, /* 11100111 -> 11100111 */ + 0x17, /* 11101000 -> 00010111 */ + 0x97, /* 11101001 -> 10010111 */ + 0x57, /* 11101010 -> 01010111 */ + 0xD7, /* 11101011 -> 11010111 */ + 0x37, /* 11101100 -> 00110111 */ + 0xB7, /* 11101101 -> 10110111 */ + 0x77, /* 11101110 -> 01110111 */ + 0xF7, /* 11101111 -> 11110111 */ + 0x0F, /* 11110000 -> 00001111 */ + 0x8F, /* 11110001 -> 10001111 */ + 0x4F, /* 11110010 -> 01001111 */ + 0xCF, /* 11110011 -> 11001111 */ + 0x2F, /* 11110100 -> 00101111 */ + 0xAF, /* 11110101 -> 10101111 */ + 0x6F, /* 11110110 -> 01101111 */ + 0xEF, /* 11110111 -> 11101111 */ + 0x1F, /* 11111000 -> 00011111 */ + 0x9F, /* 11111001 -> 10011111 */ + 0x5F, /* 11111010 -> 01011111 */ + 0xDF, /* 11111011 -> 11011111 */ + 0x3F, /* 11111100 -> 00111111 */ + 0xBF, /* 11111101 -> 10111111 */ + 0x7F, /* 11111110 -> 01111111 */ + 0xFF /* 11111111 -> 11111111 */ +}; +# endif /* WORDS_BIGENDIAN */ +#endif /* T1LIB */ + +static struct frame frame0; /* dummy head of list */ +#ifdef TEXXET +static struct frame *scan_frame; /* head frame for scanning */ +#endif + +static const char *const reverse_search_helptext = +"Forward/reverse search allows you to jump from a point " +"in the DVI file to the corresponding location in the .tex source file, and vice versa. " +"To make this possible, the .tex file needs to be compiled with source special support. " +"This can be done by using either a package like \"srcltx.sty\" or \"srctex.sty\", " +"or a command-line switch like \"-src\" for the TeX executable. " +"See the xdvi man page (section SOURCE SPECIALS) " +"for more information about this."; + +static ubyte dvi_buffer[DVI_BUFFER_LEN]; +ubyte *G_dvi_buf_ptr = dvi_buffer; +static struct frame *current_frame; + +/* Points to drawinf record containing current dvi file location (for update by + geom_scan). */ +static struct drawinf *dvi_pointer_frame = NULL; + +#ifdef TEXXET +#define DIR currinf.dir +#else +#define DIR 1 +#endif + +/* + * Explanation of the following constant: + * offset_[xy] << 16: margin (defaults to one inch) + * currwin.shrinkfactor << 16: one pixel page border + * currwin.shrinkfactor << 15: rounding for pixel_conv + */ +#define OFFSET_X (resource.xoffset_int << 16) + (currwin.shrinkfactor * 3 << 15) +#define OFFSET_Y (resource.yoffset_int << 16) + (currwin.shrinkfactor * 3 << 15) + +#if (BMBYTES == 1) +bmUnitT bit_masks[] = { + 0x0, 0x1, 0x3, 0x7, + 0xf, 0x1f, 0x3f, 0x7f, + 0xff +}; +#else +#if (BMBYTES == 2) +bmUnitT bit_masks[] = { + 0x0, 0x1, 0x3, 0x7, + 0xf, 0x1f, 0x3f, 0x7f, + 0xff, 0x1ff, 0x3ff, 0x7ff, + 0xfff, 0x1fff, 0x3fff, 0x7fff, + 0xffff +}; +#else /* BMBYTES == 4 */ +bmUnitT bit_masks[] = { + 0x0, 0x1, 0x3, 0x7, + 0xf, 0x1f, 0x3f, 0x7f, + 0xff, 0x1ff, 0x3ff, 0x7ff, + 0xfff, 0x1fff, 0x3fff, 0x7fff, + 0xffff, 0x1ffff, 0x3ffff, 0x7ffff, + 0xfffff, 0x1fffff, 0x3fffff, 0x7fffff, + 0xffffff, 0x1ffffff, 0x3ffffff, 0x7ffffff, + 0xfffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, + 0xffffffff +}; +#endif +#endif + +#ifdef VMS +#define off_t int +#endif +extern off_t lseek(); + +#ifndef SEEK_SET /* if <unistd.h> is not provided (or for <X11R5) */ +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 +#endif + +static void draw_part(FILE *fp, struct frame *minframe, double current_dimconv); + +static void source_fwd_draw_box(void); + + + +/* + * X routines. + */ + +/* + * Put a rectangle on the screen. + */ +static void +put_rule(int x, int y, unsigned int w, unsigned int h) +{ + if (htex_inside_href) + htex_record_position(x, y, w, h); + if (x < globals.win_expose.max_x && x + (int)w >= globals.win_expose.min_x && y < globals.win_expose.max_y && y + (int)h >= globals.win_expose.min_y) { + if (--globals.ev.ctr == 0) { + if (read_events(EV_NOWAIT) & EV_GE_MAG_GONE) { +/* fprintf(stderr, "longjmp1!\n"); */ + longjmp(globals.ev.canit, 1); + } + } +#if COLOR + if (fg_active != fg_current) + do_color_change(); +#endif + XFillRectangle(DISP, currwin.win, + globals.gc.rule, + x - currwin.base_x, + y - currwin.base_y, + w ? w : 1, + h ? h : 1); + } +} + +static void +put_bitmap(struct bitmap *bitmap, int x, int y) +{ + if (globals.debug & DBG_BITMAP) + printf("X(%d,%d)\n", x - currwin.base_x, y - currwin.base_y); + if (htex_inside_href) + htex_record_position(x, y, bitmap->w, bitmap->h); + if (x < globals.win_expose.max_x && x + (int)bitmap->w >= globals.win_expose.min_x && + y < globals.win_expose.max_y && y + (int)bitmap->h >= globals.win_expose.min_y) { + if (--globals.ev.ctr == 0) + if (read_events(EV_NOWAIT) & EV_GE_MAG_GONE) { +/* fprintf(stderr, "longjmp2!\n"); */ + longjmp(globals.ev.canit, 1); + } +#if COLOR + if (fg_active != fg_current) + do_color_change(); +#endif + G_image->width = bitmap->w; + G_image->height = bitmap->h; + G_image->data = bitmap->bits; + G_image->bytes_per_line = bitmap->bytes_wide; + XPutImage(DISP, currwin.win, globals.gc.fore, G_image, + 0, 0, + x - currwin.base_x, y - currwin.base_y, bitmap->w, bitmap->h); + if (globals.gc.fore2) { + XPutImage(DISP, currwin.win, globals.gc.fore2, G_image, + 0, 0, + x - currwin.base_x, y - currwin.base_y, + bitmap->w, bitmap->h); + } + } +} + +#if GREY + +/* Anti-aliasing stuff. + The method used here is supersampling of the unshrunk glyph (this + also means that no anti-aliasing happens at shrink 1). A sample + of the number of bits that are `on' in the unsrunk glyph determines + the grey level of the shrunk image. +*/ + +/* Pixel lookup tables for anti-aliasing: These store all possible + supersampling values (i.e. number of bits set in the unshrunk + image) for the given shrink factor. E.g. at shrink level 2, the + size of the pixel table is 4; in other words, 1 pixel in the shrunk + image corresponds to 4 pixels in the unshrunk image. Thus, the possible + values for shink level 2 are: black, 1/4 (0x404040), 1/2 (0x808080), + 3/4 (0xc0c0c0) and white (0xffffff). +*/ +static Pixel *pixeltbl; +static Pixel *pixeltbl_gc2; /* drawing to globals.gc.fore2 (compare pixmap2_gc2) */ + +static void shrink_glyph_grey(struct glyph *); + +static void +put_image(struct glyph *g, int x, int y) +{ + XImage *img = g->image2; + + if (htex_inside_href) + htex_record_position(x, y, img->width, img->height); + if (x < globals.win_expose.max_x && x + img->width >= globals.win_expose.min_x && + y < globals.win_expose.max_y && y + img->height >= globals.win_expose.min_y) { + if (--globals.ev.ctr == 0) + if (read_events(EV_NOWAIT) & EV_GE_MAG_GONE) { +/* fprintf(stderr, "longjmp3!\n"); */ + longjmp(globals.ev.canit, 1); + } + +#if COLOR + if (g->fg != fg_current) /* if color change since last use */ + shrink_glyph_grey(g); + else if (fg_active != fg_current) /* if GCs need updating */ + do_color_change(); +#endif + /* TODO: Can we increase gamma locally to make the inverted text more readable? + + and to draw the background, so something like this: + XFillRectangle(DISP, currwin.win, globals.gc.fore, x - currwin.base_x, y - currwin.base_y, + (unsigned int)img->width * 2, (unsigned int)img->height * 2); + + test this with color changes!! + */ + /*TEST_DELAY("check point 1 ...")*/ + XPutImage(DISP, currwin.win, globals.gc.fore, img, + 0, 0, + x - currwin.base_x, y - currwin.base_y, + (unsigned int)img->width, (unsigned int)img->height); + /*TEST_DELAY("check point 2 ...")*/ + + if (globals.gc.fore2 != NULL) { + img->data = g->pixmap2_gc2; + XPutImage(DISP, currwin.win, globals.gc.fore2, img, + 0, 0, + x - currwin.base_x, y - currwin.base_y, + (unsigned int)img->width, (unsigned int)img->height); + img->data = g->pixmap2; + } + /*TEST_DELAY("check point 3 ...")*/ + } +} +#endif /* GREY */ + +/* + * Draw the border of a rectangle on the screen. + * This should be replaced by a grey background both in Xaw and Motif + * as soon as #470325 is fixed (see also FIXED_FLUSHING_PAGING). + */ +static void +draw_border(int x, int y, unsigned int width, unsigned int height, GC ourGC) +{ + --width; + --height; + XDrawRectangle(DISP, currwin.win, ourGC, x, y, width, height); +} + + +/* + * Byte reading routines for dvi file. + */ + +#define xtell(fp, pos) (lseek(fileno(fp), 0L, SEEK_CUR) - \ + (currinf.end - (pos))) + +static ubyte +xxone(FILE *fp) +{ + if (currinf.virtual) { + ++currinf.pos; + return EOP; + } + + currinf.end = G_dvi_buf_ptr + + read(fileno(fp), (char *)(currinf.pos = G_dvi_buf_ptr), DVI_BUFFER_LEN); + return currinf.end > G_dvi_buf_ptr ? *(currinf.pos)++ : EOF; +} + +#define xone(fp) (currinf.pos < currinf.end ? *(currinf.pos)++ : xxone(fp)) + +static unsigned long +xnum(FILE *fp, ubyte size) +{ + long x = 0; + + while (size--) + x = (x << 8) | xone(fp); + return x; +} + +static long +xsnum(FILE *fp, ubyte size) +{ + long x; + +#if __STDC__ + x = (signed char)xone(fp); +#else + x = xone(fp); + if (x & 0x80) + x -= 0x100; +#endif + while (--size) + x = (x << 8) | xone(fp); + return x; +} + +#define xsfour(fp) xsnum(fp, 4) + +static void +xskip(FILE *fp, long offset) +{ + currinf.pos += offset; + if (!currinf.virtual && currinf.pos > currinf.end) + (void)lseek(fileno(fp), (long)(currinf.pos - currinf.end), SEEK_CUR); +} + +void +dvi_fmt_error(const char *message, ...) +{ + + va_list args; + va_start(args, message); + fprintf(stderr, "%s: ", globals.program_name); + (void)vfprintf(stderr, message, args); + va_end(args); + if (currinf.virtual) + fprintf(stderr, " in virtual font %s\n", currinf.virtual->fontname); + else + fprintf(stderr, ", offset %ld\n", (long)xtell(globals.dvi_file.bak_fp, currinf.pos - 1)); + /* #ifndef NDEBUG */ + /* xdvi_exit(EXIT_FAILURE); */ + /* #else */ + XDVI_ABORT((stderr, "I'll abort now, to help you debugging this.")); + /* #endif */ +} + + +/* + * Code for debugging options. + */ + +static void +print_bitmap(struct bitmap *bitmap) +{ + bmUnitT *ptr = (bmUnitT *)bitmap->bits; + int x, y, i; + + ASSERT(ptr != NULL, "Invalid bitmap bits"); + + printf("w = %d, h = %d, bytes wide = %d\n", + bitmap->w, bitmap->h, bitmap->bytes_wide); + for (y = 0; y < (int)bitmap->h; ++y) { + for (x = bitmap->bytes_wide; x > 0; x -= BMBYTES) { +#ifdef WORDS_BIGENDIAN + for (i = BMBITS - 1; i >= 0; --i) + putchar((*ptr & (1 << i)) ? '@' : '.'); +#else + for (i = 0; i < BMBITS; ++i) + putchar((*ptr & (1 << i)) ? '@' : '.'); +#endif + ++ptr; + } + putchar('\n'); + } +} + +static void +print_char(ubyte ch, struct glyph *g) +{ + printf("char %d", ch); + if (isprint(ch)) + printf(" (%c)", ch); + putchar('\n'); + printf("x = %d, y = %d, dvi = %ld\n", g->x, g->y, g->dvi_adv); + print_bitmap(&g->bitmap); +} + +static const char *dvi_table1[] = { + "SET1", "SET2", NULL, NULL, "SETRULE", "PUT1", "PUT2", NULL, + NULL, "PUTRULE", "NOP", "BOP", "EOP", "PUSH", "POP", "RIGHT1", + "RIGHT2", "RIGHT3", "RIGHT4", "W0", "W1", "W2", "W3", "W4", + "X0", "X1", "X2", "X3", "X4", "DOWN1", "DOWN2", "DOWN3", + "DOWN4", "Y0", "Y1", "Y2", "Y3", "Y4", "Z0", "Z1", + "Z2", "Z3", "Z4" +}; + +static const char *dvi_table2[] = { + "FNT1", "FNT2", "FNT3", "FNT4", "XXX1", "XXX2", "XXX3", "XXX4", + "FNTDEF1", "FNTDEF2", "FNTDEF3", "FNTDEF4", "PRE", "POST", "POSTPOST", + "SREFL", "EREFL", NULL, NULL, NULL, NULL +}; + +static void +print_dvi(ubyte ch) +{ + const char *s; + + fprintf(stderr, "%4d %4d ", PXL_H, PXL_V); + if (ch <= (ubyte) (SETCHAR0 + 127)) { + fprintf(stderr, "SETCHAR%-3d", ch - SETCHAR0); + if (isprint(ch)) + fprintf(stderr, " (%c)", ch); + fputc('\n', stderr); + return; + } + else if (ch < FNTNUM0) + s = dvi_table1[ch - 128]; + else if (ch <= (ubyte) (FNTNUM0 + 63)) { + fprintf(stderr, "FNTNUM%d\n", ch - FNTNUM0); + return; + } + else + s = dvi_table2[ch - (FNTNUM0 + 64)]; + if (s) { + fputs(s, stderr); + fputc('\n', stderr); + } + else + XDVI_FATAL((stderr, "print_dvi: unknown op-code %d", ch)); +} + + +/* + * Count the number of set bits in a 4x4-region of the bitmap + */ + +static char sample_count[] = { + 0, 1, 1, 2, + 1, 2, 2, 3, + 1, 2, 2, 3, + 2, 3, 3, 4 +}; + +/* + * For greyscaling, return the number of bits that are set in a given region + * of width w and height h of the bitmap `bits', starting horizontally after + * `bit_skip' bits, where `bytes_wide' is the same as the `bytes_wide' field + * in the bitmap struct (scan line width in bytes). + * Note that `bits' is really a one-dimensional array, i.e. all the rows + * are put in sequence into one single row. + */ +static int +sample(bmUnitT *bits, int bytes_wide, int bit_skip, int w, int h) +{ + bmUnitT *beg_ptr, *end_ptr, *curr_ptr; + int bits_left; + int n, bit_shift, wid; + +#if TRACE_AA + fprintf(stderr, "sample: %d bytes wide, %d skip, %d w, %d h\n", bytes_wide, bit_skip, w, h); +#endif + + beg_ptr = bits + bit_skip / BMBITS; + end_ptr = ADD(bits, h * bytes_wide); + +#if TRACE_AA + fprintf(stderr, "beg_ptr: %p, end: %p\n", (void *)beg_ptr, (void *)end_ptr); +#endif + + bits_left = w; +#ifdef WORDS_BIGENDIAN + bit_shift = BMBITS - bit_skip % BMBITS; +#else + bit_shift = bit_skip % BMBITS; +#endif +#if TRACE_AA + fprintf(stderr, "shift: %d\n", bit_shift); +#endif + n = 0; + + while (bits_left) { +#ifdef WORDS_BIGENDIAN + wid = bit_shift; +#else + wid = BMBITS - bit_shift; +#endif + if (wid > bits_left) + wid = bits_left; + if (wid > 4) /* why? */ + wid = 4; +#ifdef WORDS_BIGENDIAN + bit_shift -= wid; +#endif + for (curr_ptr = beg_ptr; + curr_ptr < end_ptr; + curr_ptr = ADD(curr_ptr, bytes_wide)) { +#if TRACE_AA + int i; + for (i = 0; i < bytes_wide; i++) { + fprintf(stderr, "%d ", *(curr_ptr + i)); + } + fprintf(stderr, "\nMask: %d; count: %d\n", wid, + sample_count[ (*curr_ptr >> bit_shift) & bit_masks[wid] ]); +#endif + n += sample_count[ (*curr_ptr >> bit_shift) & bit_masks[wid] ]; + } +#ifdef WORDS_BIGENDIAN + if (bit_shift == 0) { + bit_shift = BMBITS; + ++beg_ptr; + } +#else + bit_shift += wid; + if (bit_shift == BMBITS) { + bit_shift = 0; + ++beg_ptr; + } +#endif + bits_left -= wid; + /* fprintf(stderr, "bits_left: %d\n", bits_left); */ + } + return n; +} + +static void +shrink_glyph(struct glyph *g) +{ + int shrunk_bytes_wide, shrunk_height; + int rows_left, rows, init_cols; + int cols_left; + int cols; + bmUnitT *unshrunk_ptr, *shrunk_ptr; + bmUnitT m, *cp; + /* threshold for which a bit will be set to `on' in the shrunken bitmap */ + int min_sample = currwin.shrinkfactor * currwin.shrinkfactor * resource.density / 100; + int row_num; + + /* These machinations ensure that the character is shrunk according to + its hot point, rather than its upper left-hand corner. */ + g->x2 = g->x / currwin.shrinkfactor; + init_cols = g->x - g->x2 * currwin.shrinkfactor; + if (init_cols <= 0) + init_cols += currwin.shrinkfactor; + else + ++g->x2; + g->bitmap2.w = g->x2 + ROUNDUP((int)g->bitmap.w - g->x, currwin.shrinkfactor); + /* include row zero with the positively numbered rows */ + row_num = g->y + 1; + g->y2 = row_num / currwin.shrinkfactor; + rows = row_num - g->y2 * currwin.shrinkfactor; + if (rows <= 0) { + rows += currwin.shrinkfactor; + --g->y2; + } + g->bitmap2.h = shrunk_height + = g->y2 + ROUNDUP((int)g->bitmap.h - row_num, currwin.shrinkfactor) + 1; + alloc_bitmap(&g->bitmap2); + + unshrunk_ptr = (bmUnitT *) g->bitmap.bits; + shrunk_ptr = (bmUnitT *) g->bitmap2.bits; + shrunk_bytes_wide = g->bitmap2.bytes_wide; + rows_left = g->bitmap.h; + memset((char *)shrunk_ptr, '\0', shrunk_bytes_wide * shrunk_height); + + while (rows_left) { + if (rows > rows_left) + rows = rows_left; + cols_left = g->bitmap.w; +#ifndef WORDS_BIGENDIAN + m = 1; /* XXX was (1 << 0) */ +#else + m = ((bmUnitT)1 << (BMBITS - 1)); +#endif + cp = shrunk_ptr; + cols = init_cols; + while (cols_left) { + /* ??? */ + if (cols > cols_left) + cols = cols_left; + /* set a bit to `on' if it's over the threshold */ + if (sample(unshrunk_ptr, g->bitmap.bytes_wide, + (int)g->bitmap.w - cols_left, cols, rows) + >= min_sample) + *cp |= m; +#ifndef WORDS_BIGENDIAN + if (m == ((bmUnitT)1 << (BMBITS - 1))) { + m = 1; /* XXX was (1 << 0) */ + ++cp; + } + else + m <<= 1; +#else + if (m == 1) { /* XXX was (1 << 0) */ + m = ((bmUnitT)1 << (BMBITS - 1)); + ++cp; + } + else + m >>= 1; +#endif + cols_left -= cols; + cols = currwin.shrinkfactor; + } + shrunk_ptr += shrunk_bytes_wide / sizeof(bmUnitT); + unshrunk_ptr += rows * g->bitmap.bytes_wide / sizeof(bmUnitT); +/* *((char **)&shrunk_ptr) += shrunk_bytes_wide; */ +/* *((char **)&unshrunk_ptr) += rows * g->bitmap.bytes_wide; */ + rows_left -= rows; + rows = currwin.shrinkfactor; + } + g->y2 = g->y / currwin.shrinkfactor; + if (globals.debug & DBG_BITMAP) + print_bitmap(&g->bitmap2); +} + +#ifdef GREY + +#ifdef RGB_ANTI_ALIASING +/* void */ +/* filter_colors(Pixel *p1, Pixel *p2) */ +/* { */ +/* Pixel res1, res2; */ + +/* int res1_r = *p1 & G_visual->red_mask; */ +/* int res1_g = *p1 & G_visual->green_mask; */ +/* int res1_b = *p1 & G_visual->blue_mask; */ + +/* int res2_r = *p2 & G_visual->red_mask; */ +/* int res2_g = *p2 & G_visual->green_mask; */ +/* int res2_b = *p2 & G_visual->blue_mask; */ +/* } */ + +static void +color_filter_image(struct glyph *g) +{ + int rows = g->bitmap2.h; + int cols = g->bitmap2.w; + int i, j, k; + int arr_size = rows * cols * 3; + unsigned char *arr = xmalloc(arr_size * sizeof *arr); + unsigned char *arr2 = xmalloc(arr_size * sizeof *arr2); + + fprintf(stderr, "total: %d\n", arr_size); + + for (i = 0, j = 0; j < rows; j++) { + for (k = 0; k < cols; k++) { + Pixel p = XGetPixel(g->image2, k, j); +#if TRACE_AA3 + fprintf(stderr, "k: %d, j: %d, idx: %d\n", k, j, i); +#endif + arr[i] = (p & G_visual->red_mask) >> 16; + arr[i + 1] = (p & G_visual->green_mask) >> 8; + arr[i + 2] = (p & G_visual->blue_mask); +#if TRACE_AA3 + fprintf(stderr, "0x%.6lX -> %d, %d, %d\n", p, arr[i], arr[i + 1], arr[i + 2]); +#endif + i += 3; + } + } + + for (i = 0; i < arr_size; i++) { + if (i == 0) { /* merge two elems */ + if (i == arr_size - 1) /* only one elem, do nothing */ + break; + arr2[i] = (int)(resource.subpixel_energy[0] * arr[i] + + resource.subpixel_energy[1] * arr[i + 1] + 0.5); + } + else if (i == arr_size - 1) { /* merge two elems */ + if (i == 0) /* only one elem, do nothing */ + break; + arr2[i] = (int)(resource.subpixel_energy[1] * arr[i - 1] + + resource.subpixel_energy[0] * arr[i] + 0.5); + } + else { /* merge three elems */ + arr2[i] = (int)(resource.subpixel_energy[1] * arr[i - 1] + + resource.subpixel_energy[0] * arr[i] + + resource.subpixel_energy[1] * arr[i + 1] + 0.5); +#if TRACE_AA3 + fprintf(stderr, "%d, %d, %d -> %d\n", arr[i - 1], arr[i], arr[i + 1], arr2[i]); +#endif + } + } + + for (i = 0, j = 0; j < rows; j++) { + for (k = 0; k < cols; k++) { + Pixel p = arr2[i] << 16 | (arr2[i + 1] << 8) | arr2[i + 2]; +#if TRACE_AA3 + fprintf(stderr, "%d, %d, %d -> 0x%.6lX\n", arr2[i], arr2[i + 1], arr2[i + 2], p); +#endif + XPutPixel(g->image2, k, j, p); + i += 3; + } + } + + free(arr); + free(arr2); +} + +#endif + +static void +shrink_glyph_grey(struct glyph *g) +{ + int rows_left, rows, init_cols; + int cols_left; + int cols; + int x, y; + long thesample; +/* int min_sample = currwin.shrinkfactor * currwin.shrinkfactor * resource.density / 100; */ + Pixel onoff, onoff2; + bmUnitT *unshrunk_ptr; + unsigned int size; + int row_num; + int actual_w; + +#if COLOR + if (fg_active != fg_current) { + do_color_change(); + } +#endif + + /* TODO: rounding errors causing color fringing (see HACK comment below): + + \documentclass{article} + \pagestyle{empty} + + \begin{document} + l + \end{document} + + With ./xdvi-xaw.bin -name xdvi -subpixel rgb -s 4 ./test.dvi: + + subpixel order: rgb = 1 + g->x2: 0, init_cols: -2 + AFTER: g->x2: 0, init_cols: 2 + + but with ./xdvi-xaw.bin -name xdvi -s 4 ./test.dvi: + + g->x2: 0, init_cols: -3 + AFTER: g->x2: 0, init_cols: 1 + */ + + /* These machinations ensure that the character is shrunk according to + its hot point, rather than its upper left-hand corner. */ +#ifdef RGB_ANTI_ALIASING + if (resource.subpixel_order == SUBPIXEL_NONE) + g->x2 = g->x / currwin.shrinkfactor; + else { + if (g->x < 0) + g->x2 = (int)(g->x / 3.0 - 0.5) / currwin.shrinkfactor; + else + g->x2 = (int)(g->x / 3.0 + 0.5) / currwin.shrinkfactor; + } + + if (resource.subpixel_order == SUBPIXEL_NONE) + init_cols = g->x - g->x2 * currwin.shrinkfactor; + else { + if (g->x < 0) + init_cols = (int)(g->x / 3.0 - 0.5) - g->x2 * currwin.shrinkfactor; + else + init_cols = (int)(g->x / 3.0 + 0.5) - g->x2 * currwin.shrinkfactor; + fprintf(stderr, "g->x: %d, g->x2: %d, init_cols: %d\n", g->x, g->x2, init_cols); + } +#else + g->x2 = g->x / currwin.shrinkfactor; + init_cols = g->x - g->x2 * currwin.shrinkfactor; +#endif + + if (init_cols <= 0) + init_cols += currwin.shrinkfactor; + else + ++(g->x2); + +#ifdef RGB_ANTI_ALIASING + + if (resource.subpixel_order == SUBPIXEL_NONE) + g->bitmap2.w = g->x2 + ROUNDUP((int)g->bitmap.w - g->x, currwin.shrinkfactor); + else { + fprintf(stderr, "AFTER: g->x2: %d, init_cols: %d\n", g->x2, init_cols); +/* fprintf(stderr, "g->bitmap.w / 3.0 + 0.5: %d; g->x / 3.0: %d; all: %d, %d\n", */ +/* (int)(g->bitmap.w / 3.0 + 0.5), */ +/* (int)(g->x / 3.0), */ +/* (int)((g->bitmap.w / 3.0 + 0.5) - g->x / 3.0), */ +/* g->x2 + (int)((((g->bitmap.w / 3.0 + 0.5) - g->x / 3.0) + currwin.shrinkfactor - 1) / currwin.shrinkfactor)); */ + g->bitmap2.w = g->x2 + ROUNDUP((int)((g->bitmap.w / 3.0 + 0.5) - g->x / 3.0), currwin.shrinkfactor); +/* fprintf(stderr, "g->bitmap.w: %d\n", g->bitmap2.w); */ + } +#else + g->bitmap2.w = g->x2 + ROUNDUP((int)g->bitmap.w - g->x, currwin.shrinkfactor); +#endif + + /* include row zero with the positively numbered rows */ + row_num = g->y + 1; +#ifdef DBG_AA + fprintf(stderr, "row_num: %d\n", row_num); +#endif /* DBG_AA */ + /* g->y2 is the new height of the image: */ + g->y2 = row_num / currwin.shrinkfactor; +#ifdef DBG_AA + fprintf(stderr, "g->y2: %d\n", g->y2); +#endif /* DBG_AA */ + /* in C89 and before, result of division can `truncate towards negative infinity' + (i.e., round to the larger digit) for negative quotients, hence we need + to test for rows < 0. OTOH, if rows = 0, use currwin.shrinkfactor instead + (why?) + */ + if ((rows = row_num - g->y2 * currwin.shrinkfactor) <= 0) { + rows += currwin.shrinkfactor; + --(g->y2); + } + g->bitmap2.h = g->y2 + ROUNDUP((int)g->bitmap.h - row_num, currwin.shrinkfactor) + 1; + +#if TRACE_AA1 + if (resource.subpixel_order != SUBPIXEL_NONE) { + fprintf(stderr, "\nbitmap.h: %d, bitmap.w: %d\n", g->bitmap2.h, g->bitmap2.w); + } +#endif /* DBG_AA */ + + /* allocate pixmap for antialiasing ... */ + if (g->pixmap2 == NULL) { + g->image2 = XCreateImage(DISP, G_visual, G_depth, ZPixmap, + 0, (char *)NULL, g->bitmap2.w, g->bitmap2.h, + BMBITS, 0); + size = g->image2->bytes_per_line * g->bitmap2.h; + g->pixmap2 = g->image2->data = xmalloc(size != 0 ? size : 1); + } + /* ... and the pixmap used for globals.gc.fore2: */ + if (globals.gc.fore2 != NULL && g->pixmap2_gc2 == NULL) { + size = g->image2->bytes_per_line * g->bitmap2.h; + g->pixmap2_gc2 = xmalloc(size != 0 ? size : 1); + } + +#if 0 + if (resource.subpixel_order != SUBPIXEL_NONE) { + fprintf(stderr, "\n============= BITMAP ==============\n"); + print_bitmap(&g->bitmap); + } +#endif + + unshrunk_ptr = (bmUnitT *)g->bitmap.bits; + actual_w = g->bitmap.w; + rows_left = g->bitmap.h; + y = 0; + /* the basic algorithm is the same as in the nogrey code, with the main + exception that the return value of sample() is used. */ + while (rows_left) { +#ifdef RGB_ANTI_ALIASING + Pixel pixel = 0; + Pixel pixel2 = 0; +#endif + x = 0; + if (rows > rows_left) /* why - extra safety? */ + rows = rows_left; + cols_left = g->bitmap.w; +/* fprintf(stderr, "init_cols: %d\n", init_cols); */ + cols = init_cols; + while (cols_left) { + if (cols > cols_left) /* why - extra safety? */ + cols = cols_left; + + thesample = sample(unshrunk_ptr, g->bitmap.bytes_wide, + (int)g->bitmap.w - cols_left, cols, rows); + +/* if (resource.subpixel_order != SUBPIXEL_NONE && resource.subpixel_energy[2] != 0) */ +/* onoff = thesample >= min_sample ? 0xffffff : 0; */ +/* else */ + onoff = pixeltbl[thesample]; + +#ifdef XSERVER_INFO + if (globals.debug & DBG_PK) { + int c; +/* fprintf(stderr, "onoff: %d\n", onoff); */ + if (onoff > 65536) + c = onoff / 65536; + else if (onoff > 256) + c = onoff / 256; + else + c = onoff; + if (c == 0) + fprintf(stdout, ",.."); + else + fprintf(stdout, ",%.2x", c); + } +#endif + +#ifdef RGB_ANTI_ALIASING + if (resource.subpixel_order != SUBPIXEL_NONE) { + int div = x / 3; + int rest = x % 3; + + if (resource.subpixel_order == SUBPIXEL_RGB) { + if (rest == 0) + pixel = onoff & G_visual->red_mask; + else if (rest == 1) + pixel |= onoff & G_visual->green_mask; + else + pixel |= onoff & G_visual->blue_mask; + } + else { /* SUBPIXEL_BGR */ + if (rest == 0) + pixel = onoff & G_visual->blue_mask; + else if (rest == 1) + pixel |= onoff & G_visual->green_mask; + else + pixel |= onoff & G_visual->red_mask; + } + +#if 0 && TRACE_AA1 + fprintf(stderr, "sample: %ld; row %d, col %d, left %d: 0x%.6lx; pixel: 0x%.6lx at pos %d,%d (x:%d)\n", + thesample, rows, cols, cols_left, onoff, pixel, div, y, x); +#endif /* TRACE_AA */ + fprintf(stderr, "pixel at %d\n", div); + XPutPixel(g->image2, div, y, pixel); + + /* HACK to fix color fringing problem */ + if (div + 1 < g->bitmap2.w) { + fprintf(stderr, "rest pixel at %d\n", div); + XPutPixel(g->image2, div + 1, y, 0); + } + } + else { +#if 0 && TRACE_AA1 + fprintf(stderr, "sample: %ld; row %d, col %d, left %d: 0x%.6lx at pos %d, %d\n", + thesample, rows, cols, cols_left, onoff, x, y); +#endif /* TRACE_AA */ + XPutPixel(g->image2, x, y, onoff); + } +#else + XPutPixel(g->image2, x, y, onoff); +#endif /* RGB_ANTI_ALIASING */ + if (globals.gc.fore2 != NULL) { + onoff2 = pixeltbl_gc2[thesample]; +#ifdef RGB_ANTI_ALIASING + if (resource.subpixel_order != SUBPIXEL_NONE) { + int div = x / 3; + int rest = x % 3; + + if (resource.subpixel_order == SUBPIXEL_RGB) { + if (rest == 0) + pixel2 = onoff2 & G_visual->red_mask; + else if (rest == 1) + pixel2 |= onoff2 & G_visual->green_mask; + else + pixel2 |= onoff2 & G_visual->blue_mask; + } + else { /* SUBPIXEL_BGR */ + if (rest == 0) + pixel2 = onoff2 & G_visual->blue_mask; + else if (rest == 1) + pixel2 |= onoff2 & G_visual->green_mask; + else + pixel2 |= onoff2 & G_visual->red_mask; + } + + g->image2->data = g->pixmap2_gc2; +#if TRACE_AA1 + fprintf(stderr, "fore2 at %d, %d\n", div, y); +#endif + XPutPixel(g->image2, div, y, pixel2); + g->image2->data = g->pixmap2; + } + else { +#else /* RGB_ANTI_ALIASING */ + g->image2->data = g->pixmap2_gc2; + XPutPixel(g->image2, x, y, onoff2); + g->image2->data = g->pixmap2; +#endif +#ifdef RGB_ANTI_ALIASING + } +#endif + } + +#if 0 && TRACE_AA1 + fprintf(stderr, "subtracting %d from %d: %d\n", cols, cols_left, cols_left - cols); +#endif + cols_left -= cols; + cols = currwin.shrinkfactor; + x++; + } + /* advance pointer by the number of rows covered */ +/* fprintf(stderr, "++: %d; %d, %d\n", rows * g->bitmap.bytes_wide, g->bitmap.bytes_wide, sizeof(bmUnitT)); */ + unshrunk_ptr += rows * g->bitmap.bytes_wide / sizeof(bmUnitT); +/* *((char **)&unshrunk_ptr) += rows * g->bitmap.bytes_wide; */ + rows_left -= rows; + rows = currwin.shrinkfactor; + y++; +#ifdef XSERVER_INFO + if (globals.debug & DBG_PK) + fprintf(stdout, "\n"); +#endif + } +#ifdef XSERVER_INFO + if (globals.debug & DBG_PK) + fprintf(stdout, "\n"); +#endif + +#if 0 + fprintf(stderr, "y: %d, bitmap.h: %d\n", y, (int)g->bitmap2.h); +#endif /* DBG_AA */ + + /* fill remaining rows not covered before (how?) */ + while (y < (int)g->bitmap2.h) { + for (x = 0; x < (int)g->bitmap2.w; x++) { +/* int c = *pixeltbl; */ +#if TRACE_AA1 + fprintf(stderr, "Remaining at %d, %d: 0x%.6lx\n", x, y, *pixeltbl); +#endif + XPutPixel(g->image2, x, y, *pixeltbl); +/* if (c == 0) */ +/* fprintf(stdout, ",.."); */ +/* else */ +/* fprintf(stdout, ",%.2x", c); */ + if (globals.gc.fore2 != NULL) { + g->image2->data = g->pixmap2_gc2; +#if TRACE_AA1 + fprintf(stderr, "image2 at %d, %d: 0x%.6lx\n", x, y, *pixeltbl_gc2); +#endif + XPutPixel(g->image2, x, y, *pixeltbl_gc2); + g->image2->data = g->pixmap2; + } + } + y++; + } + +#ifdef RGB_ANTI_ALIASING + if (resource.subpixel_order != SUBPIXEL_NONE && resource.subpixel_energy[1] != 0) + color_filter_image(g); + if (resource.subpixel_order != SUBPIXEL_NONE && resource.subpixel_energy[2] != 0) + color_filter_image(g); +#endif + + g->y2 = g->y / currwin.shrinkfactor; +#if COLOR + g->fg = fg_current; +#endif +} +#endif /* GREY */ + +/* + * Find font #n. + */ + +static void +change_font(unsigned long n) +{ + struct tn *tnp; + + if (n < currinf.tn_table_len) + currinf.fontp = currinf.tn_table[n]; + else { + currinf.fontp = NULL; + for (tnp = currinf.tn_head; tnp != NULL; tnp = tnp->next) + if (tnp->TeXnumber == n) { + currinf.fontp = tnp->fontp; + break; + } + } + if (currinf.fontp == NULL) + XDVI_FATAL((stderr, "Non-existent font number %ld", n)); + if (currinf.fontp->set_char_p == NULL) + XDVI_FATAL((stderr, "No procedure to set font %ld, %s", n, currinf.fontp->fontname)); + maxchar = currinf.fontp->maxchar; + currinf.set_char_p = currinf.fontp->set_char_p; +} + + +/* + * Open a font file. + */ + +static void +open_font_file(struct font *fontp) +{ + if (fontp->file == NULL) { + fontp->file = XFOPEN(fontp->filename, OPEN_MODE); + if (fontp->file == NULL) + XDVI_FATAL((stderr, "Couldn't re-locate font file `%s'", fontp->filename)); + } +} + +/* + * Read and return a 0-terminated special string allocated in static memory + * (i.e. contents will be overwritten by next call of this function). + */ + +static char * +read_special(FILE *fp, long nbytes) +{ + static char *spcl = NULL; + static long spcl_len = -1; + char *p; + + if (nbytes > spcl_len) { + spcl = xrealloc(spcl, (unsigned)nbytes + 1); + spcl_len = nbytes; + } + p = spcl; + for (;;) { + int i = currinf.end - currinf.pos; + + if (i > nbytes) + i = nbytes; + memcpy(p, (char *)currinf.pos, i); + currinf.pos += i; + p += i; + nbytes -= i; + if (nbytes == 0) + break; + (void)xxone(fp); + --currinf.pos; + } + *p = '\0'; + return spcl; +} + +/* + * Table used for scanning. If >= 0, then skip that many bytes. + * M1 means end of page, M2 means special, M3 means FNTDEF, + * M4 means unrecognizable, and M5 means doesn't belong here. + */ + +#define M1 255 +#define M2 254 +#define M3 253 +#define M4 252 +#define M5 251 +#define MM 251 + +static ubyte scantable[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* chars 0 - 127 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, /* SET1,SET2 (128,129) */ + /* -,-,SETRULE,PUT1,PUT2,-,-,PUTRULE,NOP,BOP (130-139) */ + M4, M4, 8, 1, 2, M4, M4, 8, 0, 44, + M1, 0, 0, 1, 2, 3, 4, 0, 1, 2, /* EOP,PUSH,POP,RIGHT1-4,W0M2 (140-149) */ + 3, 4, 0, 1, 2, 3, 4, 1, 2, 3, /* W3-4,X0-4,DOWN1-3 (150-159) */ + 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, /* DOWN4,Y0-4,Z0-3 (160-169) */ + 4, /* Z4 (170) */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* change font 171 - 234 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, M2, /* FNT1-4,XXX1 (235-239) */ + /* XXX2-4,FNTDEF1-4,PRE,POST,POSTPOST (240-249) */ + M2, M2, M2, M3, M3, M3, M3, M5, M5, M5, + 0, 0, M4, M4, M4, M4 +}; /* SREFL,EREFL,-,-,-,- (250-255) */ + +/* + * This is the generic scanning routine. It assumes that currinf, etc. + * are ready to go at the start of the page to be scanned. + */ + + + +Boolean +spcl_scan(Boolean (*spcl_proc)(char *str, int str_len, void *data), void *data, Boolean return_if_found, FILE *fp) +{ + ubyte ch; + ubyte n; + long a; + + for (;;) { + ch = xone(fp); +/* print_dvi(ch); */ + n = scantable[ch]; + if (n < MM) + while (n-- != 0) + (void)xone(fp); + else if (n == M1) + break; /* end of page */ + else + switch (n) { + case M2: /* special */ + a = xnum(fp, ch - XXX1 + 1); + if (a > 0) { + if (spcl_proc(read_special(fp, a), a, data) && return_if_found) { + return True; + } + } + break; + case M3: /* FNTDEF */ + xskip(fp, (long)(12 + ch - FNTDEF1 + 1)); + ch = xone(fp); + xskip(fp, (long)ch + (long)xone(fp)); + break; + case M4: /* unrecognizable */ + XDVI_FATAL((stderr, "unknown op-code %d", ch)); + break; + case M5: /* doesn't belong */ + dvi_fmt_error("spcl_scan: shouldn't happen: %s encountered", + dvi_table2[ch - (FNTNUM0 + 64)]); + break; + } + } + return False; +} + +#define xspell_conv(n) spell_conv0(n, current_dimconv) + +/* + * Prescanning routine for dvi file. This looks for specials like + * `header=' and `!'. + */ + +void +prescan(FILE *fp) +{ + if (fp == NULL) { + return; + } + + TRACE_FILES((stderr, "prescan on %p", (void *)fp)); + + (void)lseek(fileno(fp), pageinfo_get_offset(scanned_page + 1), SEEK_SET); + G_dvi_buf_ptr = dvi_buffer; + currinf.pos = currinf.end = G_dvi_buf_ptr; + for (;;) { + if (scanned_page == -1) { /* on first page */ + TRACE_FILES((stderr, "prescan on page 1")); + pageinfo_set_page_width(scanned_page + 1, pageinfo_get_page_width(total_pages)); + pageinfo_set_page_height(scanned_page + 1, pageinfo_get_page_height(total_pages)); + pageinfo_set_window_width(scanned_page + 1, pageinfo_get_window_width(total_pages)); + pageinfo_set_window_height(scanned_page + 1, pageinfo_get_window_height(total_pages)); + } + else { + TRACE_FILES((stderr, "prescan on page %d", scanned_page)); + pageinfo_set_page_width(scanned_page + 1, pageinfo_get_page_width(scanned_page)); + pageinfo_set_page_height(scanned_page + 1, pageinfo_get_page_height(scanned_page)); + pageinfo_set_window_width(scanned_page + 1, pageinfo_get_window_width(scanned_page)); + pageinfo_set_window_height(scanned_page + 1, pageinfo_get_window_height(scanned_page)); + } + if (read_events(EV_NOWAIT) & EV_GE_NEWPAGE) { + break; + } + /* NOTE: longjmp(globals.ev.canit) should not be done within + read_events(). */ + htex_prescan_save(); + htex_prescan_initpage(); + + if (!setjmp(globals.ev.canit)) { + struct htex_prescan_data data; + int pre_depth, depth; + data.pageno = scanned_page + 1; + data.scan_type = HTEX_ANCHOR_NUM; /* just for the anchor numbers */ + pre_depth = htex_prescan_get_depth(); + (void)spcl_scan(scan_special, &data, False, fp); + + depth = htex_prescan_get_depth(); + + if (depth > pre_depth) { + /* we have a mismatched anchor. We currently don't deal with + _nested_ mismatched anchors (ugh), so there's only one + anchor string that can be used as info on the next page. */ + int anchor_num = htex_prescan_get_mismatched_anchor_num(depth); + /* scan again to find the anchor string at anchor number `anchor_num' */ + (void)lseek(fileno(fp), pageinfo_get_offset(scanned_page + 1), SEEK_SET); + currinf.pos = currinf.end = G_dvi_buf_ptr; + data.anchor_num = anchor_num; + data.scan_type = HTEX_ANCHOR_STRING; + htex_prescan_reset_firstpass(); + (void)spcl_scan(scan_special, &data, False, fp); + depth = htex_prescan_get_depth(); + } + else if (depth > 0 && scanned_page >= 0) { /* mismatch was on a previous page */ + htex_prescan_carry_over(scanned_page, scanned_page + 1); + } + } + else { /* if interrupted */ + htex_prescan_restore(scanned_page + 1); +#if PS + psp.interrupt(); +#endif + break; + } + if (globals.ev.flags & EV_GE_NEWPAGE) + break; + ++scanned_page; +#if COLOR + if (scanned_page_color < scanned_page) { + scan_color_eop(); + scanned_page_color = scanned_page; + } +#endif +#if PS + if (scanned_page_ps < scanned_page) + scanned_page_ps = scanned_page; +#endif + if (scanned_page >= current_page) + break; + } + +#if PS + if (!(globals.ev.flags & EV_GE_NEWPAGE)) + psp.endheader(); +#endif +} + +/* + * Routines to print characters. + */ + +setcharRetvalT +set_char( +#ifdef TEXXET + wide_ubyte cmd, +#endif + wide_ubyte ch) +{ + struct glyph *g; +#ifdef TEXXET + long dvi_h_sav; +#endif + + if (ch > maxchar) + realloc_font(currinf.fontp, (wide_ubyte)ch); + if ((g = &currinf.fontp->glyph[ch])->bitmap.bits == NULL) { + if (g->addr == 0) { + if (!resource.hush_chars) + XDVI_WARNING((stderr, "Character %d not defined in font %s", ch, currinf.fontp->fontname)); + g->addr = -1; +#ifdef TEXXET + return; +#else + return 0L; +#endif + } + if (g->addr == -1) { +#ifdef TEXXET + return; +#else + return 0L; /* previously flagged missing char */ +#endif + } + open_font_file(currinf.fontp); + fseek(currinf.fontp->file, g->addr, SEEK_SET); + (*currinf.fontp->read_char) (currinf.fontp, ch); + if (globals.debug & DBG_BITMAP) + print_char((ubyte) ch, g); + currinf.fontp->timestamp = ++current_timestamp; + } + +#ifdef TEXXET + dvi_h_sav = DVI_H; + if (currinf.dir < 0) + DVI_H -= g->dvi_adv; + + if (scan_frame == NULL) { +#endif + +#ifdef RGB_ANTI_ALIASING + if (currwin.shrinkfactor == -1) { + put_bitmap(&g->bitmap, PXL_H - g->x, PXL_V - g->y); + } +#ifdef __GNUC__ +#warning TODO: implement horizontal AA at shrink 1 +#endif +#else + if (currwin.shrinkfactor == 1) { + put_bitmap(&g->bitmap, PXL_H - g->x, PXL_V - g->y); + } +#endif + else { +#ifdef GREY + if (resource.use_grey) { + if (g->pixmap2 == NULL) { +#ifdef DBG_AA + fprintf(stderr, "shinking the bitmap!\n"); +#endif /* DBG_AA */ +/* print_bitmap(&g->bitmap); */ + shrink_glyph_grey(g); + } + put_image(g, PXL_H - g->x2, PXL_V - g->y2); + } + else { + if (g->bitmap2.bits == NULL) { + shrink_glyph(g); + } + put_bitmap(&g->bitmap2, PXL_H - g->x2, PXL_V - g->y2); + } +#else + if (g->bitmap2.bits == NULL) { + shrink_glyph(g); + } + put_bitmap(&g->bitmap2, PXL_H - g->x2, PXL_V - g->y2); +#endif + } +#ifdef TEXXET + } + if (cmd == PUT1 || (resource.omega && cmd == PUT2)) + DVI_H = dvi_h_sav; + else if (currinf.dir > 0) + DVI_H += g->dvi_adv; + return; +#else + return g->dvi_adv; +#endif +} + +/* + * Routines to print characters. + */ + +static setcharRetvalT +set_empty_char( +#ifdef TEXXET + wide_ubyte cmd, +#endif + wide_ubyte ch) +{ +#ifdef TEXXET + UNUSED(cmd); +#endif + UNUSED(ch); +#ifdef TEXXET + return; +#else + return 0L; +#endif +} + +setcharRetvalT +load_n_set_char( +#ifdef TEXXET + wide_ubyte cmd, +#endif + wide_ubyte ch) +{ + if (!load_font(currinf.fontp, resource.t1lib)) { /* if not found */ + if (globals.ev.flags & EV_GE_NEWDOC) { /* if abort */ + longjmp(globals.ev.canit, 1); + } + + /* FIXME: does this need to be replaced by GUI warning, or has this case already been covered? */ + XDVI_WARNING((stderr, "load_n_set_char: Character(s) will be left blank.")); + currinf.set_char_p = currinf.fontp->set_char_p = set_empty_char; + +#ifdef TEXXET + return; +#else + return 0L; +#endif + } + maxchar = currinf.fontp->maxchar; + currinf.set_char_p = currinf.fontp->set_char_p; +#ifdef TEXXET + (*currinf.set_char_p) (cmd, ch); + return; +#else + return (*currinf.set_char_p) (ch); +#endif +} + +setcharRetvalT +set_vf_char( +#ifdef TEXXET + wide_ubyte cmd, +#endif + wide_ubyte ch) +{ + struct macro *m; + struct drawinf oldinfo; + wide_ubyte oldmaxchar; + static ubyte c; +#ifdef TEXXET + long dvi_h_sav; +#endif + + if (ch > maxchar) + realloc_virtual_font(currinf.fontp, ch); + if ((m = &currinf.fontp->macro[ch])->pos == NULL) { + if (!resource.hush_chars) + XDVI_WARNING((stderr, "Character %d not defined in font %s", ch, currinf.fontp->fontname)); + m->pos = m->end = &c; +#ifdef TEXXET + return; +#else + return 0L; +#endif + } +#ifdef TEXXET + dvi_h_sav = DVI_H; + if (currinf.dir < 0) + DVI_H -= m->dvi_adv; + if (scan_frame == NULL) { +#endif + oldinfo = currinf; + if (!currinf.virtual) + dvi_pointer_frame = &oldinfo; + oldmaxchar = maxchar; + WW = XX = YY = ZZ = 0; + currinf.tn_table_len = VFTABLELEN; + currinf.tn_table = currinf.fontp->vf_table; + currinf.tn_head = currinf.fontp->vf_chain; + currinf.pos = m->pos; + currinf.end = m->end; + currinf.virtual = currinf.fontp; + + draw_part(globals.dvi_file.bak_fp, current_frame, currinf.fontp->dimconv); + if (currinf.pos != currinf.end + 1) + dvi_fmt_error("virtual character macro does not end correctly"); + currinf = oldinfo; + if (!currinf.virtual) + dvi_pointer_frame = &currinf; + maxchar = oldmaxchar; +#ifdef TEXXET + } + if (cmd == PUT1 || (resource.omega && cmd == PUT2)) + DVI_H = dvi_h_sav; + else if (currinf.dir > 0) + DVI_H += m->dvi_adv; + return; +#else + return m->dvi_adv; +#endif +} + +static setcharRetvalT +set_no_char( +#ifdef TEXXET + wide_ubyte cmd, +#endif + wide_ubyte ch) +{ + if (currinf.virtual) { + currinf.fontp = currinf.virtual->first_font; + if (currinf.fontp != NULL) { + maxchar = currinf.fontp->maxchar; + currinf.set_char_p = currinf.fontp->set_char_p; +#ifdef TEXXET + (*currinf.set_char_p) (cmd, ch); + return; +#else + return (*currinf.set_char_p) (ch); +#endif + } + } + dvi_fmt_error("set_no_char: attempt to set character of unknown font"); + exit(0); + /* NOTREACHED */ +} + + +/* + * Set rule. Arguments are coordinates of lower left corner. + */ + +static void +set_rule(int h, int w) +{ +#ifdef TEXXET + put_rule(PXL_H - (currinf.dir < 0 ? w - 1 : 0), PXL_V - h + 1, + (unsigned int)w, (unsigned int)h); +#else + put_rule(PXL_H, PXL_V - h + 1, (unsigned int)w, (unsigned int)h); +#endif +} + + +/* + * Interpret a sequence of dvi bytes (either the page from the dvi file, + * or a character from a virtual font). + */ + +static void +draw_part(FILE *fp, struct frame *minframe, double current_dimconv) +{ + ubyte ch = 0; +#ifdef TEXXET + struct drawinf oldinfo; + ubyte oldmaxchar = 0; + off_t file_pos = 0; + int refl_count = 0; +#endif + int pause_cnt = 0; + + globals.pausing.flag = False; + + currinf.fontp = NULL; + currinf.set_char_p = set_no_char; +#ifdef TEXXET + currinf.dir = 1; + scan_frame = NULL; /* indicates we're not scanning */ +#endif + + for (;;) { + ch = xone(fp); + if (globals.debug & DBG_DVI) { + print_dvi(ch); + } + if (ch <= (ubyte)(SETCHAR0 + 127)) { +#ifdef TEXXET + (*currinf.set_char_p) (ch, ch); +#else + DVI_H += (*currinf.set_char_p) (ch); +#endif + } + else if (FNTNUM0 <= ch && ch <= (ubyte) (FNTNUM0 + 63)) { + change_font((unsigned long)(ch - FNTNUM0)); + } + else { + long a, b; + + switch (ch) { + case SET1: + case PUT1: +#ifdef TEXXET + (*currinf.set_char_p) (ch, xone(fp)); +#else + a = (*currinf.set_char_p) (xone(fp)); + if (ch != PUT1) + DVI_H += a; +#endif + break; + + case SET2: + case PUT2: + if (!resource.omega) + dvi_fmt_error("%s:%d: draw_part: op-code %d only works with the \"-omega\" option", + __FILE__, __LINE__, ch); + else { +#ifdef TEXXET + (*currinf.set_char_p) (ch, xnum(fp, 2)); +#else + a = (*currinf.set_char_p) (xnum(fp, 2)); + if (ch != PUT2) + DVI_H += a; +#endif + } + break; + + case SETRULE: + /* Be careful, dvicopy outputs rules with + height = 0x80000000. We don't want any + SIGFPE here. */ + a = xsfour(fp); + b = xspell_conv(xsfour(fp)); + if (a > 0 && b > 0 +#ifdef TEXXET + && scan_frame == NULL +#endif + ) { + set_rule(pixel_round(xspell_conv(a)), pixel_round(b)); + } + DVI_H += DIR * b; + break; + + case PUTRULE: + a = xspell_conv(xsfour(fp)); + b = xspell_conv(xsfour(fp)); + if (a > 0 && b > 0 +#ifdef TEXXET + && scan_frame == NULL +#endif + ) { + set_rule(pixel_round(a), pixel_round(b)); + } + break; + + case NOP: + break; + + case BOP: + xskip(fp, (long)11 * 4); + DVI_H = OFFSET_X; + DVI_V = OFFSET_Y; + PXL_V = pixel_conv(DVI_V); + WW = XX = YY = ZZ = 0; + break; + + case EOP: + if (current_frame != minframe) + dvi_fmt_error("%s:%d: draw_part: stack not empty at EOP", __FILE__, __LINE__); + return; + + case PUSH: + if (current_frame->next == NULL) { + struct frame *newp = xmalloc(sizeof *newp); + + current_frame->next = newp; + newp->prev = current_frame; + newp->next = NULL; + } + current_frame = current_frame->next; + current_frame->data = currinf.data; + break; + + case POP: + if (current_frame == minframe) + dvi_fmt_error("%s:%d: draw_part: more POPs than PUSHes", __FILE__, __LINE__); + currinf.data = current_frame->data; + current_frame = current_frame->prev; + break; + +#ifdef TEXXET + case SREFL: + if (scan_frame == NULL) { + /* we're not scanning: save some info. */ + oldinfo = currinf; + oldmaxchar = maxchar; + if (!currinf.virtual) + file_pos = xtell(fp, currinf.pos); + scan_frame = current_frame; /* now we're scanning */ + refl_count = 0; + break; + } + /* we are scanning */ + if (current_frame == scan_frame) + ++refl_count; + break; + + case EREFL: + if (scan_frame != NULL) { /* if we're scanning */ + if (current_frame == scan_frame && --refl_count < 0) { + /* we've hit the end of our scan */ + scan_frame = NULL; + /* first: push */ + if (current_frame->next == NULL) { + struct frame *newp = xmalloc(sizeof *newp); + + current_frame->next = newp; + newp->prev = current_frame; + newp->next = NULL; + } + current_frame = current_frame->next; + current_frame->data = currinf.data; + /* next: restore old file position, XX, etc. */ + if (!currinf.virtual) { + off_t bgn_pos = xtell(fp, G_dvi_buf_ptr); + + if (file_pos >= bgn_pos) { + oldinfo.pos = dvi_buffer + (file_pos - bgn_pos); + oldinfo.end = currinf.end; + } + else { + (void)lseek(fileno(fp), file_pos, SEEK_SET); + oldinfo.pos = oldinfo.end; + } + } + currinf = oldinfo; + maxchar = oldmaxchar; + /* and then: recover position info. */ + DVI_H = current_frame->data.dvi_h; + DVI_V = current_frame->data.dvi_v; + PXL_V = current_frame->data.pxl_v; + /* and finally, reverse direction */ + currinf.dir = -currinf.dir; + } + break; + } + /* we're not scanning, */ + /* so just reverse direction and then pop */ + currinf.dir = -currinf.dir; + currinf.data = current_frame->data; + current_frame = current_frame->prev; + break; +#endif /* TEXXET */ + + case RIGHT1: + case RIGHT2: + case RIGHT3: + case RIGHT4: + DVI_H += DIR * xspell_conv(xsnum(fp, ch - RIGHT1 + 1)); + break; + + case W1: + case W2: + case W3: + case W4: + WW = xspell_conv(xsnum(fp, ch - W0)); + case W0: + DVI_H += DIR * WW; + break; + + case X1: + case X2: + case X3: + case X4: + XX = xspell_conv(xsnum(fp, ch - X0)); + case X0: + DVI_H += DIR * XX; + break; + + case DOWN1: + case DOWN2: + case DOWN3: + case DOWN4: + DVI_V += xspell_conv(xsnum(fp, ch - DOWN1 + 1)); + PXL_V = pixel_conv(DVI_V); + break; + + case Y1: + case Y2: + case Y3: + case Y4: + YY = xspell_conv(xsnum(fp, ch - Y0)); + case Y0: + DVI_V += YY; + PXL_V = pixel_conv(DVI_V); + break; + + case Z1: + case Z2: + case Z3: + case Z4: + ZZ = xspell_conv(xsnum(fp, ch - Z0)); + case Z0: + DVI_V += ZZ; + PXL_V = pixel_conv(DVI_V); + break; + + case FNT1: + case FNT2: + case FNT3: + case FNT4: + change_font(xnum(fp, ch - FNT1 + 1)); + break; + + case XXX1: + case XXX2: + case XXX3: + case XXX4: + a = xnum(fp, ch - XXX1 + 1); + if (a > 0) { + char *p = read_special(fp, a); + if (resource.pause && strcmp(p, resource.pause_special) == 0) { + if (++pause_cnt > globals.pausing.num) { + globals.pausing.flag = True; + /* can't use longjmp(globals.ev.canit, 1); */ + return; + } + } + applicationDoSpecial(p, a); + } + break; + + case FNTDEF1: + case FNTDEF2: + case FNTDEF3: + case FNTDEF4: + xskip(fp, (long)(12 + ch - FNTDEF1 + 1)); + a = (long)xone(fp); + xskip(fp, a + (long)xone(fp)); + break; + +#ifndef TEXXET + case SREFL: + case EREFL: +#endif + case PRE: + case POST: + case POSTPOST: + dvi_fmt_error("%s:%d: draw_part: shouldn't happen: %s encountered", + __FILE__, __LINE__, dvi_table2[ch - (FNTNUM0 + 64)]); + break; + + default: + dvi_fmt_error("%s:%d: draw_part: unknown op-code %d", __FILE__, __LINE__, ch); + } /* end switch */ + } /* end else (ch not a SETCHAR or FNTNUM) */ + } /* end for */ +} + +extern int waiting_for_anchor; + +static void +warn_raw_postscript(void) +{ + static int *pagelist = NULL; + static int pagelist_size = 0; +#ifdef PS + if (!resource.hush_stdout) { + if (total_pages >= pagelist_size) { + pagelist_size = total_pages + 1; + pagelist = xrealloc(pagelist, pagelist_size * sizeof *pagelist); + memset(pagelist, 0, pagelist_size * sizeof *pagelist); + } + + ASSERT(pagelist_size > current_page, "pagelist_size too small"); + if (pagelist[current_page] == 0) { + XDVI_WARNING((stderr, "Raw Postscript commands on page %d may be rendered incorrectly.", + current_page + 1)); + pagelist[current_page] = 1; + } + + /* too likely to overdraw important information */ + /* statusline_print(STATUS_MEDIUM, */ + /* "Warning: Postscript commands on this page may not display correctly."); */ + } +#endif /* PS */ +} + +void +draw_page(void) +{ +#if 0 + volatile double save_gamma = 0.0; +#endif /* 0 */ + /* Check for changes in dvi file. */ + if (dvi_file_changed()) { + return; + } + + if (globals.dvi_file.bak_fp == NULL) { + return; + } + +#ifdef PS + have_raw_postscript = False; +#endif + +#if COLOR + color_bottom = &fg_initial; + color_bot_size = 1; + + if (page_colors.stack != NULL && current_page > 0) { + color_bottom = page_colors.stack[current_page - 1].colorstack; + color_bot_size = page_colors.stack[current_page - 1].stacksize; + } + rcs_top = NULL; + ASSERT(color_bot_size > 0, "color_bot_size mustn't become negative!"); + ASSERT(color_bottom != NULL, "color_bottom mustn't become negative!"); + set_fg_color(&color_bottom[color_bot_size - 1]); +#endif /* COLOR */ + +#if !FIXED_FLUSHING_PAGING + draw_border(-currwin.base_x, -currwin.base_y, + ROUNDUP(pageinfo_get_page_width(current_page), currwin.shrinkfactor) + 2, + ROUNDUP(pageinfo_get_page_height(current_page), currwin.shrinkfactor) + 2, globals.gc.high); +#endif /* MOTIF */ + + (void) lseek(fileno(globals.dvi_file.bak_fp), pageinfo_get_offset(current_page), SEEK_SET); + + memset((char *)&currinf.data, '\0', sizeof currinf.data); + currinf.tn_table_len = TNTABLELEN; + currinf.tn_table = tn_table; + currinf.tn_head = tn_head; + currinf.pos = currinf.end = dvi_buffer; + currinf.virtual = NULL; + dvi_pointer_frame = &currinf; + drawing_mag = (currwin.win == magnifier.win); + psfig_begun = False; + + htex_initpage(False, False, current_page); + + if (currwin.win == mane.win) { + XRectangle rect; + rect.x = globals.win_expose.min_x; + rect.y = globals.win_expose.min_y; + rect.width = globals.win_expose.max_x - globals.win_expose.min_x; + rect.height = globals.win_expose.max_y - globals.win_expose.min_y; +/* fprintf(stderr, "clip: %d, %d, %d, %d\n", */ +/* globals.win_expose.min_x, */ +/* globals.win_expose.min_y, */ +/* globals.win_expose.max_x - globals.win_expose.min_x, */ +/* globals.win_expose.max_y - globals.win_expose.min_y); */ +#define SET_CLIP(gc) if (gc != NULL) XSetClipRectangles(DISP, gc, 0, 0, &rect, 1, Unsorted) +#define CLEAR_CLIP(gc) if (gc != NULL) XSetClipMask(DISP, gc, None) + /* Set clip masks for all GC's */ + SET_CLIP(globals.gc.fore); + SET_CLIP(globals.gc.fore2); + SET_CLIP(globals.gc.fore2_bak); + SET_CLIP(globals.gc.fore2_bak1); + SET_CLIP(globals.gc.rule); + SET_CLIP(globals.gc.high); + SET_CLIP(globals.gc.linkcolor); + SET_CLIP(globals.gc.copy); + } + + if (!setjmp(globals.ev.canit)) { + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! BUG ALERT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + ALL GLYPH DRAWING/RULE SETTING COMMANDS THAT MIGHT INVOKE + longjmp(globals.ev.canit) + MUST GO INSIDE THIS IF CASE, AND MUST NOT BE INVOKED FROM + SOMEWHERE ELSE! + + Failure to check whether a command could (indirectly) invoke + such a drawing routine (like e.g. put_rule()) will result + in *really* strange bugs (see e.g. #616920, and probably also #471021). + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! BUG ALERT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + */ + /* generate an expose event */ + if (search_have_match(current_page)) { + search_erase_highlighting(False); + } + if (resource.mouse_mode == MOUSE_TEXT_MODE) { + text_change_region(TEXT_SEL_ERASE, NULL); + } + draw_part(globals.dvi_file.bak_fp, current_frame = &frame0, dimconv); + + if (have_raw_postscript) { + warn_raw_postscript(); + } + } + else { + /* If we were interrupted, put the expose event back, so that the + * region gets redrawn. The if statement is necessary because the + * magnifier may have been destroyed as part of the interrupt. */ + if (currwin.win == mane.win || currwin.win == magnifier.win) { + expose(currwin.win == mane.win ? &mane : &magnifier, + globals.win_expose.min_x - currwin.base_x, globals.win_expose.min_y - currwin.base_y, + globals.win_expose.max_x - globals.win_expose.min_x, globals.win_expose.max_y - globals.win_expose.min_y); + } + +#ifdef PS + psp.interrupt(); + /* reset this flag too, just to make sure ... */ +#if GS_PIXMAP_CLEARING_HACK + had_ps_specials = False; +#endif +#endif + globals.ev.flags &= ~EV_MAG_GONE; +#if 0 + if (search_have_match() && save_gamma != 0.0) { + resource.gamma = save_gamma; + do_color_change(); + reset_fonts(); + } +#endif /* 0 */ + } + + drawing_mag = False; + dvi_pointer_frame = NULL; + if (currwin.win == mane.win) { + if (globals.src.fwd_box_page >= 0) { + source_fwd_draw_box(); /* draw box showing found source line */ + } + htex_draw_anchormarkers(); + } +#ifdef PS + psp.endpage(); +#endif + if (currwin.win == mane.win && resource.postscript != 1) { + display_bboxes(); + } + if (search_have_match(current_page)) { + /* highlight search match */ + search_draw_inverted_regions(); + } + if (currwin.win == mane.win && resource.mouse_mode == MOUSE_TEXT_MODE) { + /* highlight selection */ + text_change_region(TEXT_SEL_REDRAW, NULL); + } + if (resource.mouse_mode == MOUSE_RULER_MODE) { + redraw_ruler(); + } + + clear_bboxes(); + + if (currwin.win == mane.win) { + CLEAR_CLIP(globals.gc.fore); + CLEAR_CLIP(globals.gc.fore2); + CLEAR_CLIP(globals.gc.fore2_bak); + CLEAR_CLIP(globals.gc.fore2_bak1); + CLEAR_CLIP(globals.gc.rule); + CLEAR_CLIP(globals.gc.high); + CLEAR_CLIP(globals.gc.linkcolor); + CLEAR_CLIP(globals.gc.copy); + } +#undef SET_CLIP +#undef CLEAR_CLIP + + currwin.win = (Window) 0; +} + +/* this sets the file-scope htex_anchor_type to the type of the anchor just scanned, + which is used by the drawing routines to determine whether current position is inside + an anchor. Called from special.c. + */ +void +htex_do_special(const char *str, size_t len) +{ + if (INSIDE_MANE_WIN) { + htex_inside_href = htex_scan_anchor(str, len); + } +} + +/* + * General dvi scanning routines. These are used for: + * o source special lookups and + * o finding the dimensions of links (if compiling with support for + * hypertext specials). + * This routine can be a bit slower than draw_page()/draw_part(), since + * it is not run that often; that is why it is a separate routine in + * spite of much duplication. + * + * Note that it does not use a separate copy of define_font(). + */ + +/* + * This set of routines can be called while draw_part() is active, + * so the global variables must be separate. + */ + +static struct frame geom_frame0; /* dummy head of list */ + +#ifdef TEXXET +static struct frame *geom_scan_frame; /* head frame for scanning */ +#endif + +static struct frame *geom_current_frame; + +static uint32_t +get_unicode_char(wide_ubyte ch, struct drawinf currinf, char *retbuf) +{ + /* attempt 1: if it's a Type1 font, get the Adobe character name + from T1lib's T1_GetCharName */ + if (currinf.set_char_p == set_t1_char) { + int id = currinf.fontp->t1id; + int t1libid = fontmaps[id].t1libid; + char *char_name; + ASSERT(t1libid != -1, "Font must have been loaded at this point!"); + char_name = T1_GetCharName(t1libid, ch); + TRACE_FIND_VERBOSE((stderr, "T1 char: %d = `%s' (id: %d; font: %s; enc: %d)", + ch, char_name, + currinf.fontp->t1id, fontmaps[currinf.fontp->t1id].texname, + fontmaps[currinf.fontp->t1id].enc)); + return adobe2unicode_name(char_name); + } + else { + /* attempt 2: try to derive the encoding from the font name, by + looking it up in a list of known font names. + */ + return guess_encoding(ch, currinf.fontp->fontname, retbuf); + } +} + +#define MAX_CHARS 16 /* maximum number of unicode characters that one do_char can produce (rounded up ...) */ +static const size_t ALLOC_STEP = 1024; + +/* create a page break at BOP, unless we're at the start of the file */ +static void +do_newpage(struct scan_info *info) +{ + struct word_info *w_info = (struct word_info *)info->data; + if (w_info->curr_buf_idx > 0) { /* not at start of file */ + /* resize buffer if needed */ + while (w_info->curr_buf_idx + MAX_CHARS >= w_info->txt_buf_size) { + w_info->txt_buf_size += ALLOC_STEP; + w_info->txt_buf = xrealloc(w_info->txt_buf, w_info->txt_buf_size); + } + w_info->txt_buf[w_info->curr_buf_idx++] = '\n'; + } +} + +static void +reset_bboxes(struct word_info *info) +{ + size_t i; + for (i = 0; i < info->bboxes_size; i++) { + info->bboxes[i].ulx = INT_MAX; + info->bboxes[i].uly = INT_MAX; + info->bboxes[i].lrx = 0; + info->bboxes[i].lry = 0; + } +} + +static void +delete_last_bbox(struct word_info *info) +{ + info->bboxes[info->bboxes_idx].ulx = INT_MAX; + info->bboxes[info->bboxes_idx].uly = INT_MAX; + info->bboxes[info->bboxes_idx].lrx = 0; + info->bboxes[info->bboxes_idx].lry = 0; +} + +static void +finish_bbox(struct word_info *info) +{ +/* if (info->bboxes_idx == 0) /\* nothing to do *\/ */ +/* return; */ + + info->bboxes_idx++; + while (info->bboxes_idx + 1 > info->bboxes_size) { + /* re-allocate info */ + info->bboxes_size += 32; + info->bboxes = xrealloc(info->bboxes, info->bboxes_size * sizeof *(info->bboxes)); + } + info->bboxes[info->bboxes_idx].ulx = INT_MAX; + info->bboxes[info->bboxes_idx].uly = INT_MAX; + info->bboxes[info->bboxes_idx].lrx = 0; + info->bboxes[info->bboxes_idx].lry = 0; + +#if 0 + fprintf(stderr, "========= finish_bbox: index=%d, boxes:\n", info->bboxes_idx); + { + int i; + for (i = 0; i < info->bboxes_idx; i++) { + fprintf(stderr, "%d: x %d, y %d, w %d, h %d\n", + i, info->bboxes[i].ulx, info->bboxes[i].uly, info->bboxes[i].lrx, info->bboxes[i].lry); + } + } +#endif /* 0 */ +} + +static void +create_bbox(struct word_info *info, int x, int y, int w, int h) +{ +#if 0 + fprintf(stderr, "++++++++++++++ inside match: %d,%d,%d,%d!\n", x,y,w,h); + XDrawRectangle(DISP, mane.win, globals.gc.high, + x / (double)currwin.shrinkfactor + 0.5, + y / (double)currwin.shrinkfactor + 0.5, + w / (double)currwin.shrinkfactor + 0.5, + h / (double)currwin.shrinkfactor + 0.5); +#endif + while (info->bboxes_idx + 1 > info->bboxes_size) { + /* re-allocate info */ + size_t old_size = info->bboxes_size; + size_t i; + info->bboxes_size += 32; + info->bboxes = xrealloc(info->bboxes, info->bboxes_size * sizeof *(info->bboxes)); + for (i = old_size; i < info->bboxes_size; i++) { + info->bboxes[i].ulx = INT_MAX; + info->bboxes[i].uly = INT_MAX; + info->bboxes[i].lrx = 0; + info->bboxes[i].lry = 0; + } + + } + /* adjust size of box */ + if (x < info->bboxes[info->bboxes_idx].ulx) + info->bboxes[info->bboxes_idx].ulx = x; + if (y < info->bboxes[info->bboxes_idx].uly) + info->bboxes[info->bboxes_idx].uly = y; + if (x + w > info->bboxes[info->bboxes_idx].lrx) + info->bboxes[info->bboxes_idx].lrx = x + w; + if (y + h > info->bboxes[info->bboxes_idx].lry) + info->bboxes[info->bboxes_idx].lry = y + h; +#if 0 + fprintf(stderr, "dimens: %d, %d, %d, %d\n", + info->bboxes[info->bboxes_idx].ulx, + info->bboxes[info->bboxes_idx].uly, + info->bboxes[info->bboxes_idx].lrx, + info->bboxes[info->bboxes_idx].lry); + fprintf(stderr, "============ create_bbox: index=%d, boxes:\n", info->bboxes_idx); + { + int i; + for (i = 0; i <= info->bboxes_idx; i++) { + fprintf(stderr, "%d: x %d, y %d, w %d, h %d\n", + i, info->bboxes[i].ulx, info->bboxes[i].uly, info->bboxes[i].lrx, info->bboxes[i].lry); + } + } +#endif /* 0 */ +} + +static void +map_index_positions(const struct search_info *searchinfo, + const struct page_mapping *page_mapping, + int *from, int *to) +{ + *from = searchinfo->from_pos; + *to = searchinfo->to_pos; + + if (*from >= page_mapping[0].offset) { /* on second page of scan */ +#if 0 + fprintf(stderr, "current_page: %d, from: %d, to: %d; subtracting %d for pageinfo at index 0\n", + current_page, *from, *to, page_mapping[0].offset); +#endif /* 0 */ + if (page_mapping[0].offset != -1) { + ASSERT(page_mapping[0].offset != -1, "page_mapping not properly initialized?"); + (*from) -= page_mapping[0].offset; + (*to) -= page_mapping[0].offset; + } + ASSERT(*from >= 0, "index must be > 0"); + ASSERT(*to >= 0, "index must be > 0"); + + } +} + +/* decrement curr_buf_idx so that the previous character in info + * (which might be a multi-byte character) is erased. + */ +static void +erase_prev_char(struct word_info *info) +{ + /* erase the hyphen, which might be a multibyte character */ + if ((unsigned char)(info->txt_buf[info->curr_buf_idx - 1]) < 0x80) { /* single-byte */ + TRACE_FIND_VERBOSE((stderr, "1 XXX: %d", + (unsigned char)(info->txt_buf[info->curr_buf_idx - 1]))); + info->txt_buf[--(info->curr_buf_idx)] = '\0'; + } + else { /* skip back over multi-byte sequence */ + while ((unsigned char)(info->txt_buf[info->curr_buf_idx - 1]) >= 0x80 + && (unsigned char)(info->txt_buf[info->curr_buf_idx - 1]) < 0xC0) { /* 10xxxxxx bytes */ + TRACE_FIND_VERBOSE((stderr, "2 XXX: %d", + (unsigned char)(info->txt_buf[info->curr_buf_idx - 1]))); + info->txt_buf[--(info->curr_buf_idx)] = '\0'; + } + /* first byte (TODO: sanity check?) */ + info->txt_buf[--(info->curr_buf_idx)] = '\0'; + } +} + + +static Boolean +inside_text_match(int curr_pos, int from, int to) +{ + return from < curr_pos && curr_pos <= to; +} + +static Boolean reinit_scan = False; + +void +reinit_text_scan(void) { + reinit_scan = True; +} + +static Boolean +inside_bbox(int x, int y, int w, int h, + struct bbox *bboxes) +{ + int mid_x, mid_y; +#if 0 + XDrawRectangle(DISP, mane.win, globals.gc.high, + x / (double)currwin.shrinkfactor + 0.5, + y / (double)currwin.shrinkfactor + 0.5, + w / (double)currwin.shrinkfactor + 0.5, + h / (double)currwin.shrinkfactor + 0.5); +#endif + /* already treat it as inside if there's an overlap of more than 50% */ + mid_x = x + w / 2.0; + mid_y = y + h / 2.0; +#if 0 + fprintf(stderr, "------- check:\n%d, %d\n%d, %d, %d, %d\n", + mid_x, mid_y, bboxes->ulx, bboxes->uly, bboxes->lrx, bboxes->lry); +#endif + if (bboxes->ulx <= mid_x && + bboxes->uly <= mid_y && + bboxes->lrx >= mid_x && + bboxes->lry >= mid_y) { +#if 0 + fprintf(stderr, "====== MATCH:\n%d, %d\n%d, %d, %d, %d\n", + mid_x, mid_y, bboxes->ulx, bboxes->uly, bboxes->lrx, bboxes->lry); +#endif + return True; + } + return False; +} + +static Boolean +inside_bbox_line(int y, int h, + struct bbox *bboxes) +{ + int mid_y = y + h / 2.0; + if (bboxes->uly <= mid_y && + bboxes->lry >= mid_y) { +#if 0 + fprintf(stderr, "MATCH:\n%d, %d, %d, %d\n%d, %d, %d, %d\n", + x, y, w, h, bboxes->ulx, bboxes->uly, bboxes->lrx, bboxes->lry); +#endif + return True; + } + return False; +} + +static void +do_char(wide_ubyte ch, + struct drawinf currinf, + struct scan_info *info, + long pxl_v1, long pxl_v2, + long x1, long x2, + struct glyph *g) +{ + struct word_info *w_info = (struct word_info *)info->data; + struct search_settings *settings = NULL; + const struct page_mapping *page_mapping = NULL; + uint32_t u_glyph = 0, new_glyph = 0; + float fsize = dvi_pt_conv(currinf.fontp->scale); /* font size in pt */ + Boolean convert_to_lowercase = False; + /* if set to true, ignore whitespace before/after character (for CJK characters) */ + Boolean ignore_whitespace = False; + + /* default min space between words, in DVI units */ + /* TODO: for T1 fonts, should we look at fontdimen2 / fontdimen4? + (e.g. tfminfo[fontmaps[currinf.fontp->t1id].tfmidx].fontdimen2) + */ + long min_delta = (int)(1.5 * fsize + 0.5) << 16; + + size_t buf_offset = w_info->buffer_offset; + + size_t i; + /* for delayed insertion of newlines in text selection mode */ + static Boolean had_newline = False; + /* for whitespace insertion in text selection mode */ + static Boolean had_chars_in_line = False; + static long last_dvi_h1 = 0, last_pxl_v = 0; + static long last_dvi_h2 = 0; + static long last_x = 0; + static uint32_t last_u_glyph = 0; + static int last_page = -1; + static int page_bak = -1; + const char *expanded_lig = NULL; + const uint32_t UNKNOWN_GLYPH_CODE = 0x003F; /* question mark */ + char retbuf[MAX_CHARS]; + retbuf[0] = '\0'; + + if (w_info != NULL && w_info->settings != NULL) { + settings = w_info->settings; + if (!settings->case_sensitive) + convert_to_lowercase = True; + } + + if (w_info->bbox_pass) { + ASSERT(w_info != NULL, ""); + ASSERT(w_info->page_mapping != NULL, ""); + page_mapping = w_info->page_mapping; + } + + if (reinit_scan || last_page != current_page) { /* reinit */ + last_dvi_h1 = last_pxl_v = last_dvi_h2 = last_x = 0; + last_page = current_page; + last_u_glyph = 0; + w_info->bboxes_idx = 0; + reset_bboxes(w_info); +/* w_info->curr_buf_idx = 0; */ + reinit_scan = False; + had_newline = False; + had_chars_in_line = False; + } + +/* TRACE_FIND((stderr, "\n--------- POSITIONS: %ld, %ld, %ld, %ld", x1, pxl_v2, x2, y2)); */ + + if ((u_glyph = get_unicode_char(ch, currinf, retbuf)) == 0) { + if (retbuf[0] == '\0') { + TRACE_FIND((stderr, "unknown glyph `%lu'\n", (unsigned long)ch)); + u_glyph = UNKNOWN_GLYPH_CODE; + } + else /* several characters in retbuf, will be evaluated later */ + u_glyph = 0; + } + + TRACE_FIND_VERBOSE((stderr, "UNICODE: 0x%.4X; idx: %ld", + (unsigned int)u_glyph, + (unsigned long)w_info->curr_buf_idx)); + + /* resize buffer if needed */ + while (w_info->curr_buf_idx + MAX_CHARS >= w_info->txt_buf_size) { + w_info->txt_buf_size += ALLOC_STEP; + w_info->txt_buf = xrealloc(w_info->txt_buf, w_info->txt_buf_size); + } + + /* Copy text into buffer, applying heuristics for special glyphs. + A `\0' is always appended, so that we have a valid C string; + if we have more text, this '\0' will be overwritten in the next call. */ + + /* apply accent/linebreak heuristics */ +/* fprintf(stderr, "Checking: %ld > 0, %ld > 0\n", last_dvi_h1, last_pxl_v); */ + if (last_dvi_h1 > 0 && last_pxl_v > 0) { /* had at least 1 character */ + TRACE_FIND_VERBOSE((stderr, "++++ dvi_h: %ld, last_dvi_h1: %ld, w_info->curr_buf_idx: %ld", + DVI_H, last_dvi_h1, (unsigned long)w_info->curr_buf_idx)); + + /* spaces after/before ideographic characters are ignored */ + if (is_ideograph(last_u_glyph) || is_ideograph(u_glyph)) + ignore_whitespace = true; + + /* first, check for linebreaks since accents are also triggered by negative hspace. + * Usually, a linebreak is signalled by vertical movement down. However, in multicolumn + * mode, it can also be a movement up, which we try to catch with the second condition. */ + if (pxl_v2 > last_pxl_v + (int)(1.2 * fsize + 0.5) + || (page_bak == current_page && pxl_v2 + (int)(6 * fsize + 0.5) < last_pxl_v)) { + TRACE_FIND_VERBOSE((stderr, "linebreak (%ld > %ld + %d || %ld < %ld)!\n", + pxl_v2, last_pxl_v, (int)(1.2 * fsize + 0.5), + pxl_v2 + (int)(6 * fsize + 0.5), last_pxl_v)); + + /* remove hyphen followed by newline if ignore_hyphens option is set, + and we're scanning for string search: */ + if ((w_info->search_scan_pass || w_info->bbox_pass) + && settings->ignore_hyphens && w_info->curr_buf_idx > 0 + && is_hyphenchar(last_u_glyph)) { + + erase_prev_char(w_info); + TRACE_FIND_VERBOSE((stderr, "%d > %d + %d? offset: %lu", + settings->searchinfo->from_pos, + (int)w_info->curr_buf_idx, + settings->hyphen_delta, (unsigned long)buf_offset)); + if (!w_info->bbox_pass + && settings->searchinfo->from_pos + > (int)(w_info->curr_buf_idx + buf_offset + settings->hyphen_delta)) { + settings->hyphen_delta += 2; + } + TRACE_FIND((stderr, "erasing hyphen %lu at pos %lu with char %c; hyphen_delta is: %d", + (unsigned long)last_u_glyph, (unsigned long)w_info->curr_buf_idx, ch, settings->hyphen_delta)); + /* If the position of this hyphen had been at the start of the bounding + box, this bounding box info is now invalid - delete it */ + if (w_info->bbox_pass) { + int from, to; + map_index_positions(settings->searchinfo, page_mapping, &from, &to); + if ((int)w_info->curr_buf_idx == from) { + delete_last_bbox(w_info); + } + } + } + else { /* ignore_hyphens not set, insert newline */ + /* Also save hyphen_delta, in case we need to update from_pos and to_pos + if user switched from ignore hyphens to don't ignore hyphens */ + if (w_info->search_scan_pass && !w_info->bbox_pass + && is_hyphenchar(last_u_glyph) + && settings->searchinfo->from_pos + settings->hyphen_delta + >= (int)(w_info->curr_buf_idx + buf_offset)) { + settings->hyphen_delta += 2; + TRACE_FIND((stderr, "updating delta: %d at pos %d, curr_idx %d, offset: %lu", + settings->hyphen_delta, settings->searchinfo->from_pos, + (int)w_info->curr_buf_idx, (unsigned long)buf_offset)); + } + if (w_info->text_selection_pass) { + if (inside_bbox(last_x, last_pxl_v, 5, 5, w_info->bboxes) + || (inside_bbox_line(last_pxl_v, 5, w_info->bboxes))) { + had_newline = True; + had_chars_in_line = False; + } + } + else if (w_info->search_scan_pass && settings->ignore_linebreaks && !ignore_whitespace) + w_info->txt_buf[w_info->curr_buf_idx++] = ' '; + else if (!ignore_whitespace) + w_info->txt_buf[w_info->curr_buf_idx++] = '\n'; + } + if (w_info->bbox_pass) { + int from, to; + map_index_positions(settings->searchinfo, page_mapping, &from, &to); + if (inside_text_match((int)w_info->curr_buf_idx, from, to)) { + finish_bbox(w_info); + } + } + } + else if (w_info->curr_buf_idx > 0 && last_u_glyph != 0 && last_x > 0 + && (!w_info->text_selection_pass + || (w_info->text_selection_pass + && inside_bbox(last_x, pxl_v2, x1 - last_x, pxl_v1 - pxl_v2, w_info->bboxes))) + && x1 < last_x /* overlapping glyphs: check for diacritics */ + && ((new_glyph = get_accented_glyph(last_u_glyph, u_glyph)) != 0)) { + erase_prev_char(w_info); + /* use new glyph for next writing operation: */ + u_glyph = new_glyph; + } + else if (!ignore_whitespace && last_dvi_h2 > 0 && DVI_H > last_dvi_h2 + min_delta) { + TRACE_FIND_VERBOSE((stderr, "space (%ld > %ld + %ld)!", DVI_H, last_dvi_h2, min_delta)); + if (!w_info->text_selection_pass + || (w_info->text_selection_pass + && had_chars_in_line + && inside_bbox(x1, pxl_v2, x2 - x1, g->bitmap.h, w_info->bboxes))) { + w_info->txt_buf[w_info->curr_buf_idx++] = ' '; + w_info->txt_buf[w_info->curr_buf_idx] = '\0'; + if (w_info->bbox_pass) { + int from, to; + map_index_positions(settings->searchinfo, page_mapping, &from, &to); + if (inside_text_match((int)w_info->curr_buf_idx, from, to)) { + TRACE_FIND_VERBOSE((stderr, "bounding box for space: %ld, %ld, %ld, %ld", + last_x, last_pxl_v, x1 - last_x, pxl_v1 - pxl_v2)); + create_bbox(w_info, last_x, pxl_v2, x1 - last_x, pxl_v1 - pxl_v2); + } + } + } + } + } + + last_page = current_page; + if (retbuf[0] != '\0' + || (expanded_lig = expand_ligature(u_glyph)) != NULL + /* when in search pass, normalize more characters */ + || ((w_info->bbox_pass || w_info->search_scan_pass) + && (expanded_lig = search_normalize_chars(u_glyph)) != NULL)) { + /* expanded ligature, which is always in 7-bit ASCII -> copy into buffer */ + size_t len; + if (retbuf[0] != '\0') + expanded_lig = retbuf; + len = strlen(expanded_lig); + TRACE_FIND_VERBOSE((stderr, "I: %lu to %lu", + (unsigned long)w_info->curr_buf_idx, + (unsigned long)(w_info->curr_buf_idx + len))); + + if (!w_info->text_selection_pass + || (w_info->text_selection_pass && inside_bbox(x1, pxl_v2, x2 - x1, g->bitmap.h, + w_info->bboxes))) { + +/* fprintf(stderr, "inserting1 %s\n", expanded_lig); */ + if (had_newline) { + w_info->txt_buf[w_info->curr_buf_idx++] = '\n'; + had_newline = False; + } + memcpy(w_info->txt_buf + w_info->curr_buf_idx, expanded_lig, len); + had_chars_in_line = True; + for (i = 0; i < len; i++) { + w_info->curr_buf_idx++; +/* fprintf(stderr, "setting index to: %d\n", w_info->curr_buf_idx); */ + if (w_info->bbox_pass) { + int from, to; + map_index_positions(settings->searchinfo, page_mapping, &from, &to); + if (inside_text_match((int)w_info->curr_buf_idx, from, to)) { + create_bbox(w_info, x1, pxl_v2, x2 - x1, g->bitmap.h); + } + } + } + w_info->txt_buf[w_info->curr_buf_idx] = '\0'; + } + } + else if (!w_info->text_selection_pass + || (w_info->text_selection_pass && inside_bbox(x1, pxl_v2, x2 - x1, g->bitmap.h, w_info->bboxes))) { + /* convert to utf8 */ + char utf8_buf[MAX_CHARS]; /* ample ... */ + size_t len; + /* convert to utf8, eventually lowercasing */ + ucs4_to_utf8(u_glyph, utf8_buf, &len, convert_to_lowercase); +/* fprintf(stderr, "inserting2 %lu\n", u_glyph); */ + if (had_newline) { + w_info->txt_buf[w_info->curr_buf_idx++] = '\n'; + had_newline = False; + } + memcpy(w_info->txt_buf + w_info->curr_buf_idx, utf8_buf, len); + had_chars_in_line = True; + for (i = 0; i < len; i++) { + w_info->curr_buf_idx++; +/* fprintf(stderr, "setting index2 to: %d\n", w_info->curr_buf_idx); */ + if (w_info->bbox_pass) { + int from, to; +#if 0 + int j; + fprintf(stderr, "current page_mapping:\n"); + for (j = 0; j <= total_pages; j++) { + fprintf(stderr, "%d: %d\n", j, page_mapping[j]); + } +#endif /* 0 */ +/* fprintf(stderr, "mapping!\n"); */ + map_index_positions(settings->searchinfo, page_mapping, &from, &to); + if (inside_text_match((int)w_info->curr_buf_idx, from, to)) { + create_bbox(w_info, x1, pxl_v2, x2 - x1, g->bitmap.h); + } + } + } + w_info->txt_buf[w_info->curr_buf_idx] = '\0'; /* ensure termination */ + } + + last_dvi_h1 = DVI_H; + last_u_glyph = u_glyph; + last_pxl_v = pxl_v1; + last_dvi_h2 = DVI_H + currinf.fontp->glyph[ch].dvi_adv; + last_x = x2; +} + +/* handle a character in the text scanning routine */ +long +text_do_char(FILE *fp, struct scan_info *info, wide_ubyte ch) +{ + if (currinf.set_char_p == set_no_char) { + if (currinf.virtual == NULL + || (currinf.fontp = currinf.virtual->first_font) == NULL) + return 0; /* error; we'll catch it later */ + maxchar = currinf.fontp->maxchar; + currinf.set_char_p = currinf.fontp->set_char_p; + } + + if (currinf.set_char_p == set_empty_char) + return 0; /* error; we'll catch it later */ + + if (currinf.set_char_p == load_n_set_char) { + if (globals.ev.flags & EV_GE_NEWDOC) /* if abort */ + return 0; + if (!load_font(currinf.fontp, resource.t1lib)) { /* if not found */ + if (globals.ev.flags & EV_GE_NEWDOC) /* if abort */ + return 0; + + /* FIXME: replace by GUI warning! */ + fputs("geom_do_char: Character(s) will be left blank.\n", stderr); + currinf.set_char_p = currinf.fontp->set_char_p = set_empty_char; + return 0; + } + maxchar = currinf.fontp->maxchar; + currinf.set_char_p = currinf.fontp->set_char_p; + } + + if (currinf.set_char_p == set_char) { + struct glyph *g; + long x, y; + + if (ch > maxchar) + return 0; /* catch the error later */ + if ((g = &currinf.fontp->glyph[ch])->bitmap.bits == NULL) { + if (g->addr == 0) + return 0; /* catch the error later */ + if (g->addr == -1) + return 0; /* previously flagged missing char */ + open_font_file(currinf.fontp); + fseek(currinf.fontp->file, g->addr, SEEK_SET); + (*currinf.fontp->read_char) (currinf.fontp, ch); + if (globals.debug & DBG_BITMAP) + print_char((ubyte) ch, g); + currinf.fontp->timestamp = ++current_timestamp; + } +#ifdef TEXXET + if (geom_scan_frame == NULL) { + long dvi_h_sav = DVI_H; + if (currinf.dir < 0) + DVI_H -= g->dvi_adv; +#endif + x = G_PXL_H - g->x; + y = PXL_V - g->y; + do_char(ch, currinf, info, PXL_V, y, x, x + g->bitmap.w - 1, g); +#ifdef TEXXET + DVI_H = dvi_h_sav; + } +#endif + return DIR * g->dvi_adv; + } + else if (currinf.set_char_p == set_vf_char) { + struct macro *m; + struct drawinf oldinfo; + ubyte oldmaxchar; +#ifdef TEXXET + long dvi_h_sav; +#endif + + if (ch > maxchar) + return 0; /* catch the error later */ + if ((m = &currinf.fontp->macro[ch])->pos == NULL) + return 0; /* catch the error later */ +#ifdef TEXXET + dvi_h_sav = DVI_H; + if (currinf.dir < 0) + DVI_H -= m->dvi_adv; + if (geom_scan_frame == NULL) { +#endif + oldinfo = currinf; + oldmaxchar = maxchar; + WW = XX = YY = ZZ = 0; + currinf.tn_table_len = VFTABLELEN; + currinf.tn_table = currinf.fontp->vf_table; + currinf.tn_head = currinf.fontp->vf_chain; + currinf.pos = m->pos; + currinf.end = m->end; + currinf.virtual = currinf.fontp; + geom_scan_part(text_do_char, fp, info, geom_current_frame, currinf.fontp->dimconv); + currinf = oldinfo; + maxchar = oldmaxchar; +#ifdef TEXXET + DVI_H = dvi_h_sav; + } +#endif + return DIR * m->dvi_adv; + } +#ifdef T1LIB + else if (currinf.set_char_p == set_t1_char) { + struct glyph *g ; + long x, y; + t1FontLoadStatusT status; + +#ifdef TEXXET + g = get_t1_glyph(0, ch, &status, True); + if (status == FAILURE_BLANK) + return 0; + if (geom_scan_frame == NULL) { + long dvi_h_sav = DVI_H; + if (currinf.dir < 0) + DVI_H -= g->dvi_adv; + x = G_PXL_H - g->x; + y = PXL_V - g->y; + do_char(ch, currinf, info, PXL_V, y, x, x + g->bitmap.w - 1, g); + DVI_H = dvi_h_sav; + } +#else + g = get_t1_glyph(ch, &status, True); + if (status == FAILURE_BLANK) + return 0; + x = G_PXL_H - g->x; + y = PXL_V - g->y; + do_char(ch, currinf, info, PXL_V, y, x, x + g->bitmap.w - 1, g); +#endif + return DIR * g->dvi_adv; + } +#endif /* T1LIB */ + else { + XDVI_FATAL((stderr, "currinf.set_char_p not a registered routine!")); + } + /* NOTREACHED */ + return 0; +} + +/* + * Handle a character in geometric scanning routine. + */ + +static long +geom_do_char(FILE *fp, struct scan_info *info, wide_ubyte ch) +{ + struct geom_info *g_info = info->data; + + if (currinf.set_char_p == set_no_char) { + if (currinf.virtual == NULL + || (currinf.fontp = currinf.virtual->first_font) == NULL) + return 0; /* error; we'll catch it later */ + maxchar = currinf.fontp->maxchar; + currinf.set_char_p = currinf.fontp->set_char_p; + } + + if (currinf.set_char_p == set_empty_char) + return 0; /* error; we'll catch it later */ + + if (currinf.set_char_p == load_n_set_char) { + if (globals.ev.flags & EV_GE_NEWDOC) /* if abort */ + return 0; + if (!load_font(currinf.fontp, resource.t1lib)) { /* if not found */ + if (globals.ev.flags & EV_GE_NEWDOC) /* if abort */ + return 0; + + /* FIXME: replace by GUI warning! */ + fputs("geom_do_char: Character(s) will be left blank.\n", stderr); + currinf.set_char_p = currinf.fontp->set_char_p = set_empty_char; + return 0; + } + maxchar = currinf.fontp->maxchar; + currinf.set_char_p = currinf.fontp->set_char_p; + } + + if (currinf.set_char_p == set_char) { + struct glyph *g; + long x, y; + + if (ch > maxchar) + return 0; /* catch the error later */ + if ((g = &currinf.fontp->glyph[ch])->bitmap.bits == NULL) { + if (g->addr == 0) + return 0; /* catch the error later */ + if (g->addr == -1) + return 0; /* previously flagged missing char */ + open_font_file(currinf.fontp); + fseek(currinf.fontp->file, g->addr, SEEK_SET); + (*currinf.fontp->read_char) (currinf.fontp, ch); + if (globals.debug & DBG_BITMAP) + print_char((ubyte) ch, g); + currinf.fontp->timestamp = ++current_timestamp; + } +#ifdef TEXXET + if (geom_scan_frame == NULL) { + long dvi_h_sav = DVI_H; + if (currinf.dir < 0) + DVI_H -= g->dvi_adv; +#endif + x = G_PXL_H - g->x; + y = PXL_V - g->y; + g_info->geom_box(info, x, y, + x + g->bitmap.w - 1, y + g->bitmap.h - 1); + +#ifdef TEXXET + DVI_H = dvi_h_sav; + } +#endif + return DIR * g->dvi_adv; + } + else if (currinf.set_char_p == set_vf_char) { + struct macro *m; + struct drawinf oldinfo; + ubyte oldmaxchar; +#ifdef TEXXET + long dvi_h_sav; +#endif + + if (ch > maxchar) + return 0; /* catch the error later */ + if ((m = &currinf.fontp->macro[ch])->pos == NULL) + return 0; /* catch the error later */ +#ifdef TEXXET + dvi_h_sav = DVI_H; + if (currinf.dir < 0) + DVI_H -= m->dvi_adv; + if (geom_scan_frame == NULL) { +#endif + oldinfo = currinf; + oldmaxchar = maxchar; + WW = XX = YY = ZZ = 0; + currinf.tn_table_len = VFTABLELEN; + currinf.tn_table = currinf.fontp->vf_table; + currinf.tn_head = currinf.fontp->vf_chain; + currinf.pos = m->pos; + currinf.end = m->end; + currinf.virtual = currinf.fontp; + geom_scan_part(geom_do_char, fp, info, geom_current_frame, currinf.fontp->dimconv); + currinf = oldinfo; + maxchar = oldmaxchar; +#ifdef TEXXET + DVI_H = dvi_h_sav; + } +#endif + return DIR * m->dvi_adv; + } +#ifdef T1LIB + else if (currinf.set_char_p == set_t1_char) { + struct glyph *g ; + long x, y; + t1FontLoadStatusT status; + +#ifdef TEXXET + g = get_t1_glyph(0, ch, &status, False); + if (status == FAILURE_BLANK) + return 0; + if (geom_scan_frame == NULL) { + long dvi_h_sav = DVI_H; + if (currinf.dir < 0) + DVI_H -= g->dvi_adv; + x = G_PXL_H - g->x; + y = PXL_V - g->y; + g_info->geom_box(info, x, y, + x + g->bitmap.w - 1, y + g->bitmap.h - 1); + DVI_H = dvi_h_sav; + } +#else + g = get_t1_glyph(ch, &status, False); + if (status == FAILURE_BLANK) + return 0; + x = G_PXL_H - g->x; + y = PXL_V - g->y; + g_info->geom_box(info, x, y, + x + g->bitmap.w - 1, y + g->bitmap.h - 1); +#endif + return DIR * g->dvi_adv; + } +#endif /* T1LIB */ + else { + XDVI_FATAL((stderr, "currinf.set_char_p not a registered routine!")); + } + /* NOTREACHED */ + return 0; +} + +/* + * Do a rule in the geometry-scanning routine. + */ + +static void +geom_do_rule(struct scan_info *info, long h, long w) +{ + long x, y; +#ifdef TEXXET + long dvi_h_save = DVI_H; +#endif + struct geom_info *g_info = info->data; + +#ifdef TEXXET + if (currinf.dir < 0) + DVI_H -= w - 1; +#endif + x = G_PXL_H; + y = PXL_V; + g_info->geom_box(info, x, y - xpixel_round(h) + 1, + x + xpixel_round(w) - 1, y); +#ifdef TEXXET + DVI_H = dvi_h_save; +#endif +} + +/* + * Geometric dvi scanner work routine. This does most of the work + * (a) reading from a page, and (b) executing vf macros. + */ + +void +geom_scan_part(long(*char_proc)(FILE *, struct scan_info *, wide_ubyte), + FILE *fp, struct scan_info *info, struct frame *minframe, double current_dimconv) +{ + ubyte ch; +#ifdef TEXXET + struct drawinf oldinfo; + ubyte oldmaxchar = 0; + off_t file_pos = 0; + int refl_count = 0; +#endif + + currinf.fontp = NULL; + currinf.set_char_p = set_no_char; +#ifdef TEXXET + currinf.dir = 1; + geom_scan_frame = NULL; /* indicates we're not scanning */ +#endif + for (;;) { + ch = xone(fp); + if (ch <= (ubyte)(SETCHAR0 + 127)) + DVI_H += char_proc(fp, info, ch); + else if (FNTNUM0 <= ch && ch <= (ubyte) (FNTNUM0 + 63)) { + change_font((unsigned long)(ch - FNTNUM0)); + } + else { + long a, b; + + switch (ch) { + case SET1: + case PUT1: + a = char_proc(fp, info, xone(fp)); + if (ch != PUT1) + DVI_H += a; + break; + + case SET2: + case PUT2: + if (!resource.omega) + dvi_fmt_error("%s:%d: draw_part: op-code %d only works with the \"-omega\" option", + __FILE__, __LINE__, ch); + else { +#ifdef TEXXET + char_proc(fp, info, xnum(fp, 2)); +#else + a = char_proc(fp, info, xnum(fp, 2)); + if (ch != PUT2) + DVI_H += a; +#endif + } + break; + + case SETRULE: + /* Be careful, dvicopy outputs rules with + height = 0x80000000. We don't want any + SIGFPE here. */ + a = xsfour(fp); + b = xspell_conv(xsfour(fp)); + if (a >= 0 && b >= 0 +#ifdef TEXXET + && geom_scan_frame == NULL +#endif + ) { + /* is this a geom scan? */ + if (info->geom_special != NULL) + geom_do_rule(info, xspell_conv(a), b); + } + DVI_H += DIR * b; + break; + + case PUTRULE: + a = xspell_conv(xsfour(fp)); + b = xspell_conv(xsfour(fp)); + if (a >= 0 && b >= 0 +#ifdef TEXXET + && geom_scan_frame == NULL +#endif + ) { + /* is this a geom scan? */ + if (info->geom_special != NULL) + geom_do_rule(info, a, b); + } + break; + + case NOP: + break; + + case BOP: + xskip(fp, (long)11 * 4); + DVI_H = G_OFFSET_X; + DVI_V = G_OFFSET_Y; + PXL_V = xpixel_conv(DVI_V); + WW = XX = YY = ZZ = 0; + /* create pagebreak in character scan */ + if (info->geom_special == NULL) { + do_newpage(info); + } + break; + + case PUSH: + if (geom_current_frame->next == NULL) { + struct frame *newp = xmalloc(sizeof *newp); + + geom_current_frame->next = newp; + newp->prev = geom_current_frame; + newp->next = NULL; + } + geom_current_frame = geom_current_frame->next; + geom_current_frame->data = currinf.data; + break; + + case POP: + if (geom_current_frame == minframe) + dvi_fmt_error("more POPs than PUSHes"); + currinf.data = geom_current_frame->data; + geom_current_frame = geom_current_frame->prev; + break; + +#ifdef TEXXET + case SREFL: + if (geom_scan_frame == NULL) { + /* we're not scanning: save some info. */ + oldinfo = currinf; + oldmaxchar = maxchar; + if (!currinf.virtual) + file_pos = xtell(fp, currinf.pos); + geom_scan_frame = geom_current_frame; /* start scanning */ + refl_count = 0; + break; + } + /* we are scanning */ + if (geom_current_frame == geom_scan_frame) + ++refl_count; + break; + + case EREFL: + if (geom_scan_frame != NULL) { /* if we're scanning */ + if (geom_current_frame == geom_scan_frame + && --refl_count < 0) { + /* we've hit the end of our scan */ + geom_scan_frame = NULL; + /* first: push */ + if (geom_current_frame->next == NULL) { + struct frame *newp = xmalloc(sizeof *newp); + + geom_current_frame->next = newp; + newp->prev = geom_current_frame; + newp->next = NULL; + } + geom_current_frame = geom_current_frame->next; + geom_current_frame->data = currinf.data; + /* next: restore old file position, XX, etc. */ + if (!currinf.virtual) { + off_t bgn_pos = xtell(fp, G_dvi_buf_ptr); + + if (file_pos >= bgn_pos) { + oldinfo.pos = dvi_buffer + (file_pos - bgn_pos); + oldinfo.end = currinf.end; + } + else { + (void)lseek(fileno(fp), file_pos, + SEEK_SET); + oldinfo.pos = oldinfo.end; + } + } + currinf = oldinfo; + maxchar = oldmaxchar; + /* and then: recover position info. */ + DVI_H = geom_current_frame->data.dvi_h; + DVI_V = geom_current_frame->data.dvi_v; + PXL_V = geom_current_frame->data.pxl_v; + /* and finally, reverse direction */ + currinf.dir = -currinf.dir; + } + break; + } + /* we're not scanning, */ + /* so just reverse direction and then pop */ + currinf.dir = -currinf.dir; + currinf.data = geom_current_frame->data; + geom_current_frame = geom_current_frame->prev; + break; +#endif /* TEXXET */ + + case RIGHT1: + case RIGHT2: + case RIGHT3: + case RIGHT4: + DVI_H += DIR * xspell_conv(xsnum(fp, ch - RIGHT1 + 1)); + break; + + case W1: + case W2: + case W3: + case W4: + WW = xspell_conv(xsnum(fp, ch - W0)); + case W0: + DVI_H += DIR * WW; + break; + + case X1: + case X2: + case X3: + case X4: + XX = xspell_conv(xsnum(fp, ch - X0)); + case X0: + DVI_H += DIR * XX; + break; + + case DOWN1: + case DOWN2: + case DOWN3: + case DOWN4: + DVI_V += xspell_conv(xsnum(fp, ch - DOWN1 + 1)); + PXL_V = xpixel_conv(DVI_V); + break; + + case Y1: + case Y2: + case Y3: + case Y4: + YY = xspell_conv(xsnum(fp, ch - Y0)); + case Y0: + DVI_V += YY; + PXL_V = xpixel_conv(DVI_V); + break; + + case Z1: + case Z2: + case Z3: + case Z4: + ZZ = xspell_conv(xsnum(fp, ch - Z0)); + case Z0: + DVI_V += ZZ; + PXL_V = xpixel_conv(DVI_V); + break; + + case FNT1: + case FNT2: + case FNT3: + case FNT4: + change_font(xnum(fp, ch - FNT1 + 1)); + break; + + case XXX1: + case XXX2: + case XXX3: + case XXX4: + a = xnum(fp, ch - XXX1 + 1); + if (a > 0) { + char *str = read_special(fp, a); + + /* is this a geom scan? */ + if (info->geom_special != NULL) { + /* process the bounding box */ + geom_do_special(info, str, current_dimconv); + /* process the specials we're looking for */ + info->geom_special(info, str, a); + } + } + break; + + case FNTDEF1: + case FNTDEF2: + case FNTDEF3: + case FNTDEF4: + xskip(fp, (long)(12 + ch - FNTDEF1 + 1)); + a = (long)xone(fp); + xskip(fp, a + (long)xone(fp)); + break; + +#ifndef TEXXET + case SREFL: + case EREFL: +#endif + case PRE: + case POST: + case POSTPOST: + case EOP: + default: + return; + + } /* end switch */ + } /* end else (ch not a SETCHAR or FNTNUM) */ + } /* end for */ +} + + +/* + * Main scanning routine. + */ + +void +geom_scan(long(*char_proc)(FILE *, struct scan_info *, wide_ubyte), + FILE *fp, struct scan_info *info, int pageno) +{ + volatile off_t pos_save = 0; + struct drawinf currinf_save; + ubyte maxchar_save; + +#if PS + if (info->geom_special != NULL && scanned_page < current_page) { + fprintf(stderr, "shouldn't happen: %d >= %d!\n", scanned_page, current_page); + return; /* should not happen */ + } +#endif + + if (dvi_pointer_frame != NULL) + pos_save = lseek(fileno(fp), 0L, SEEK_CUR) - (dvi_pointer_frame->end - dvi_pointer_frame->pos); + + (void) lseek(fileno(fp), pageinfo_get_offset(pageno), SEEK_SET); + + currinf_save = currinf; + maxchar_save = maxchar; + + memset((char *)&currinf.data, '\0', sizeof currinf.data); + currinf.tn_table_len = TNTABLELEN; + currinf.tn_table = tn_table; + currinf.tn_head = tn_head; + currinf.pos = currinf.end = dvi_buffer; + currinf.virtual = NULL; + + if (!setjmp(info->done_env)) { + geom_scan_part(char_proc, fp, info, geom_current_frame = &geom_frame0, dimconv); + } + + maxchar = maxchar_save; + currinf = currinf_save; + + if (dvi_pointer_frame != NULL) { + (void)lseek(fileno(fp), pos_save, SEEK_SET); + dvi_pointer_frame->pos = dvi_pointer_frame->end = dvi_buffer; + } +} + + +/* + * Routines for source special lookup + */ + +struct src_spec_data { + long x, y; /* coordinates we're looking for */ + unsigned long distance; /* best distance so far */ + Boolean recent_in_best; /* most recent string == XXX->best */ + struct src_parsed_special best; /* best special so far */ + struct src_parsed_special recent; /* most recent special */ +}; + +void +src_parse(const char *str, int str_len, struct src_parsed_special *parsed) +{ + const char *p = str; + + if (*p >= '0' && *p <= '9') { + parsed->line = atoi(p); + do { + ++p; + str_len--; + } + while (*p >= '0' && *p <= '9'); + } + + parsed->col = 0; + if (*p == ':') { + ++p; + str_len--; + parsed->col = atoi(p); + while (*p >= '0' && *p <= '9') { + ++p; + str_len--; + } + } + + if (*p == ' ') { + ++p; + str_len--; + } + + if (*p != '\0') { + size_t len = str_len + 1; + + if (len > parsed->filename_len) { + if (parsed->filename_len != 0) + free(parsed->filename); + parsed->filename_len = (len & -8) + 64; /* rounding? */ + parsed->filename = xmalloc(parsed->filename_len); + } + memcpy(parsed->filename, p, len); + } +} + +static void +src_spec_box(struct scan_info *info, long ulx, long uly, long lrx, long lry) +{ + struct geom_info *g_info = (struct geom_info *)info->data; + struct src_spec_data *data = g_info->geom_data; + unsigned long distance; + + distance = 0; + + if (data->x < ulx) + distance += (ulx - data->x) * (ulx - data->x); + else if (data->x > lrx) + distance += (data->x - lrx) * (data->x - lrx); + + if (data->y < uly) + distance += (uly - data->y) * (uly - data->y); + else if (data->y > lry) + distance += (data->y - lry) * (data->y - lry); + + if (distance < data->distance) { + data->distance = distance; + + /* Copy it over */ + if (!data->recent_in_best) { + data->best.line = data->recent.line; + data->best.col = data->recent.col; + if (data->recent.filename_len != 0) { + if (data->best.filename_len < data->recent.filename_len) { + if (data->best.filename_len != 0) + free(data->best.filename); + data->best.filename_len = data->recent.filename_len; + data->best.filename = xmalloc(data->best.filename_len); + } + memcpy(data->best.filename, data->recent.filename, + data->recent.filename_len); + } + + data->recent_in_best = True; + } + + /* Quit early if we've found our glyph. */ + if (distance == 0 && data->best.filename_len != 0) { + longjmp(info->done_env, 1); + } + } +} + +static void +src_spec_special(struct scan_info *info, const char *str, int str_len) +{ + struct geom_info *g_info = (struct geom_info *)info->data; + struct src_spec_data *data = g_info->geom_data; + + if (memcmp(str, "src:", 4) != 0) + return; + + src_parse(str + 4, str_len - 4, &data->recent); + + /* + * If this is the first special on the page, we may already have + * spotted the nearest box. + */ + + if (data->best.filename_len == 0) { + data->best.line = data->recent.line; + data->best.col = data->recent.col; + if (data->recent.filename_len != 0) { + if (data->best.filename_len < data->recent.filename_len) { + if (data->best.filename_len != 0) + free(data->best.filename); + data->best.filename_len = data->recent.filename_len; + data->best.filename = xmalloc(data->best.filename_len); + } + memcpy(data->best.filename, data->recent.filename, + data->recent.filename_len); + + data->recent_in_best = True; + } + + if (data->distance == 0) { + longjmp(info->done_env, 1); + } + } + else + data->recent_in_best = False; +} + +/* + * Routines for reverse searches on other pages. + */ + +static struct src_parsed_special found; +/* static jmp_buf scan_env; */ + +static Boolean +scan_first_src_spcl(char *str, int str_len, void *data) +{ + UNUSED(data); + if (memcmp(str, "src:", 4) != 0) + return False; + + src_parse(str + 4, str_len - 4, &found); + + return True; +} + +static Boolean +scan_last_src_spcl(char *str, int str_len, void *data) +{ + UNUSED(data); + if (memcmp(str, "src:", 4) != 0) + return False; + + src_parse(str + 4, str_len - 4, &found); + + return True; +} + + +/* Replace (pseudo-)format arguments in NULL-terminated argv list as follows: + * %f -> filename, %l -> linenumber, %c -> column number. + * If %f or %l are not specified, they are appended as %f and +%l. + * If colno == 0, no %c argument is provided. + */ +static char ** +src_format_arguments(char **argv, const char *filename, int lineno, int colno) +{ + size_t i; + Boolean found_filename = False; + Boolean found_lineno = False; + /* index of argv elem that contained a deleted column number specifier */ + int colno_deleted = -1; + + for (i = 0; argv[i] != NULL; i++) { + char *ptr, *curr = argv[i]; + while ((ptr = strchr(curr, '%')) != NULL) { + char *p1; + if ((p1 = strchr("flc", ptr[1])) != NULL) { /* we have a formatting char */ + char digit_arg[LENGTH_OF_INT]; + const char *new_elem = NULL; + /* remember offsets and lengths */ + size_t l_init = ptr - argv[i]; + size_t l_rest = strlen(ptr + 2) + 1; + size_t l_mid; + + if (*p1 == 'f') { + found_filename = True; + new_elem = filename; + } + else if (*p1 == 'l') { + found_lineno = True; + sprintf(digit_arg, "%d", lineno); + new_elem = digit_arg; + } + else if (*p1 == 'c') { + if (colno == 0) { /* if we have no column information, don't provide this argument */ + memmove(ptr, ptr + 2, l_rest); + curr = ptr; + colno_deleted = i; + continue; + } + sprintf(digit_arg, "%d", colno); + new_elem = digit_arg; + } + + l_mid = strlen(new_elem); + + argv[i] = xrealloc(argv[i], strlen(argv[i]) + l_mid + 1); + curr = argv[i] + l_init; /* need to reinitialize it because of realloc */ + memmove(curr + l_mid, curr + 2, l_rest); + memcpy(curr, new_elem, l_mid); + curr += l_mid; + } + else if (ptr[1] == '%') { /* escaped %, skip both */ + curr = ptr + 2; + } + else { + curr = ptr + 1; + } + } + } + + /* append line number and file name arguments if they were not specified */ + if (!found_lineno) { + i++; + argv = xrealloc(argv, (i + 1) * sizeof *argv); + argv[i - 1] = xmalloc(LENGTH_OF_INT + 2); + sprintf(argv[i - 1], "+%d", lineno); + argv[i] = NULL; + } + + if (!found_filename) { + i++; + argv = xrealloc(argv, (i + 1) * sizeof *argv); + argv[i - 1] = xstrdup(filename); + argv[i] = NULL; + } + + /* if we deleted %c, remove from argv if it's empty now */ + if (colno_deleted > -1 && strlen(argv[colno_deleted]) == 0) { + for (i = colno_deleted; argv[i] != NULL; i++) { + argv[i] = argv[i + 1]; + + } + } + return argv; +} + + +static void +src_spawn_editor(const struct src_parsed_special *parsed) +{ + char *expanded_filename; + char **argv; + const char *p; + char *q; + int i; + + struct stat buf; + + /* first, determine the editor if necessary */ + if (resource.editor == NULL || *resource.editor == '\0') { + p = getenv("XEDITOR"); + if (p != NULL) + resource.editor = xstrdup(p); + else { + + p = getenv("VISUAL"); + if (p == NULL) { + p = getenv("EDITOR"); + if (p == NULL) { + popup_message(globals.widgets.top_level, + MSG_WARN, + /* help text */ + "Use the \"-editor\" command-line opion, the X resource " + "\"xdvi.editor\" or one of the following environment variables " + "to select the editor for source specials: " + "\"XEDITOR\", \"VISUAL\" or \"EDITOR\".\n" + "See the xdvi man page for more information on source specials " + "and the editor options.", + /* message */ + "No custom editor set - using vi as default."); + p = "vi"; + } + } + q = xmalloc(strlen(p) + 10); + memcpy(q, "xterm -e ", 9); + strcpy(q + 9, p); + resource.editor = q; + } + } + + /* now try to open the file; find_file allocates space for expanded_filename */ + if ((expanded_filename = find_file(parsed->filename, &buf, kpse_tex_format)) == NULL) { + popup_message(globals.widgets.top_level, + MSG_ERR, NULL, + "File \"%s\" not found, couldn't jump to special\n\"%s:%d\"\n", + parsed->filename, parsed->filename, parsed->line); + } + else { + TRACE_SRC((stderr, "source file \"%s\" expanded to \"%s\"\n", parsed->filename, expanded_filename)); + if (buf.st_mtime > globals.dvi_file.time) { + statusline_print(STATUS_FOREVER, + "Warning: TeX file is newer than dvi file - " + "source special information might be wrong."); + } + + /* this allocates argv */ + argv = src_format_arguments(get_separated_list(resource.editor, " \t", True), + expanded_filename, parsed->line, parsed->col); + + fork_process(argv[0], False, NULL, NULL, NULL, argv); + + free(expanded_filename); + for (i = 0; argv[i] != NULL; i++) + free(argv[i]); + free(argv); + } +} + +off_t +save_file_status(FILE *fp, struct drawinf *currinf_save, ubyte *maxchar_save) +{ + off_t pos_save = 0; + if (dvi_pointer_frame != NULL) + pos_save = lseek(fileno(fp), 0L, SEEK_CUR) + - (dvi_pointer_frame->end - dvi_pointer_frame->pos); + + *currinf_save = currinf; + *maxchar_save = maxchar; + return pos_save; +} + +void +restore_file_status(FILE *fp, struct drawinf currinf_save, ubyte maxchar_save, off_t pos_save) +{ + maxchar = maxchar_save; + currinf = currinf_save; + + if (dvi_pointer_frame != NULL) { + (void)lseek(fileno(fp), pos_save, SEEK_SET); + dvi_pointer_frame->pos = dvi_pointer_frame->end = dvi_buffer; + } +} + + + +/* + * The main routine for source specials (reverse search). + */ +void +source_reverse_search(int x, int y, wide_bool call_editor) +{ + struct scan_info info; + struct geom_info g_info; + struct src_spec_data data; + struct src_parsed_special *foundp; + + info.geom_special = src_spec_special; + + g_info.geom_box = src_spec_box; + g_info.geom_data = &data; + + info.data = &g_info; + + data.x = x; + data.y = y; + data.distance = 0xffffffff; + data.recent_in_best = True; + data.best.filename_len = data.recent.filename_len = 0; + foundp = &data.best; + + geom_scan(geom_do_char, globals.dvi_file.bak_fp, &info, current_page); + + if (data.best.filename_len == 0) { + /* + * nothing found on current page; + * scan next and previous pages with increasing offset + */ + volatile int upper, lower; + volatile off_t pos_save; + struct drawinf currinf_save; + ubyte maxchar_save; + + /* Save file position */ + pos_save = save_file_status(globals.dvi_file.bak_fp, &currinf_save, &maxchar_save); + + upper = lower = current_page; + found.filename_len = 0; /* mark it as empty */ + for (;;) { + if (++upper < total_pages) { + (void)lseek(fileno(globals.dvi_file.bak_fp), pageinfo_get_offset(upper), SEEK_SET); + memset((char *)&currinf.data, '\0', sizeof currinf.data); + currinf.tn_table_len = TNTABLELEN; + currinf.tn_table = tn_table; + currinf.tn_head = tn_head; + currinf.pos = currinf.end = dvi_buffer; + currinf.virtual = NULL; + + if (spcl_scan(scan_first_src_spcl, NULL, True, globals.dvi_file.bak_fp)) { + lower = upper; + break; + } + } + else if (lower < 0) + break; + + if (--lower >= 0) { + (void)lseek(fileno(globals.dvi_file.bak_fp), pageinfo_get_offset(lower), SEEK_SET); + + memset((char *)&currinf.data, '\0', sizeof currinf.data); + currinf.tn_table_len = TNTABLELEN; + currinf.tn_table = tn_table; + currinf.tn_head = tn_head; + currinf.pos = currinf.end = dvi_buffer; + currinf.virtual = NULL; + + (void)spcl_scan(scan_last_src_spcl, NULL, False, globals.dvi_file.bak_fp); + if (found.filename_len != 0) + break; + } + } + + if (found.filename_len != 0) + statusline_print(STATUS_MEDIUM, + "No source specials on this page - nearest on page %d", + lower + globals.pageno_correct); + else { + /* nothing found at all; complain */ + XBell(DISP, 0); + popup_message(globals.widgets.top_level, + MSG_ERR, + /* helptext */ + reverse_search_helptext, + /* popup */ + "No source specials in this DVI file - couldn't do reverse search."); + } + + /* Restore file status. */ + restore_file_status(globals.dvi_file.bak_fp, currinf_save, maxchar_save, pos_save); + + foundp = &found; + } + + if (data.recent.filename_len != 0) + free(data.recent.filename); + + if (foundp->filename_len != 0) { + if (call_editor) { + src_spawn_editor(foundp); + } + else { + statusline_print(STATUS_MEDIUM, "nearest special at (%d,%d): \"%s:%d\"", + x / currwin.shrinkfactor, y / currwin.shrinkfactor, foundp->filename, foundp->line); + } + free(foundp->filename); + } +} + + +/* + * Debug routines for source special display (highlight the first box + * after each source special, or highlight each box). + */ + +struct src_spec_show_data { + Boolean do_this_one; /* flag set after source special */ + Boolean do_them_all; /* flag to reset the above to */ +}; + +static void +src_spec_show_box(struct scan_info *info, long ulx, long uly, long lrx, long lry) +{ + struct geom_info *g_info = (struct geom_info *)info->data; + struct src_spec_show_data *data = g_info->geom_data; + + if (data->do_this_one) { + long x = ulx / mane.shrinkfactor; + long y = uly / mane.shrinkfactor; + + XDrawRectangle(DISP, mane.win, globals.gc.high, + x - mane_base_x, y - mane_base_y, + lrx / mane.shrinkfactor - x, + lry / mane.shrinkfactor - y); + data->do_this_one = data->do_them_all; + } +} + +static void +src_spec_show_special(struct scan_info *info, const char *str, int str_len) +{ + /* if (memcmp(str, "src:", 4) != 0) */ + /* return; */ + struct geom_info *g_info = (struct geom_info *)info->data; + UNUSED(str_len); + XDVI_INFO((stdout, "special: %s", str)); + ((struct src_spec_show_data *)g_info->geom_data)->do_this_one = True; +} + +void +source_special_show(wide_bool do_them_all) +{ + struct scan_info info; + struct geom_info g_info; + struct src_spec_show_data src_data; + + info.geom_special = src_spec_show_special; + + g_info.geom_box = src_spec_show_box; + g_info.geom_data = &src_data; + + info.data = &g_info; + + src_data.do_this_one = src_data.do_them_all = do_them_all; + + geom_scan(geom_do_char, globals.dvi_file.bak_fp, &info, current_page); +} + + +/* + * Routines for forward search (look up a source line). + * + * The general procedure is: + * (1) Use spcl_scan() to find the page containing the line (or at + * least the closest line). This could be optimized further. + * (2) Use geom_scan_part() to find the exact location of the source + * special, and the box to highlight. + */ + +/* These variables are referenced by src_scan_special(). */ + +static int src_this_line; +static Boolean src_this_file_equal; +static int src_line; +static int src_col; +static const char *src_file; +static int src_page; +/* static jmp_buf src_env; */ +static Boolean found_src; +static unsigned long best_distance; +static unsigned long best_col_dist; +static int best_line; +static int best_page; +static off_t best_offset; +static off_t max_offset; + +/* Some of the above, plus these below, are used by geom_scan_part(). */ + +static Boolean src_fwd_active; + +#define BBOX_INFO_MAXIDX 8 /* maximum number of bounding boxes */ + +/* list of bounding boxes */ +struct bbox_info { + long min_x, max_x, min_y, max_y; + long spcl_min_x, spcl_max_x, spcl_min_y, spcl_max_y; +} g_bbox_info[BBOX_INFO_MAXIDX]; + +/* current index in g_bbox_info[] */ +static size_t bbox_info_idx = 0; + +static Boolean +src_scan_special(char *str, int str_len, void *data) +{ + char *p; + int col = 0; + unsigned long distance; + unsigned long col_dist; + + UNUSED(data); + if (memcmp(str, "src:", 4) != 0) + return False; + + found_src = True; + + p = str + 4; + str_len -= 4; + + if (*p >= '0' && *p <= '9') { + src_this_line = atoi(p); + do { + ++p; + str_len--; + } + while (*p >= '0' && *p <= '9'); + } + + if (*p == ':') { + ++p; + str_len--; + col = atoi(p); + while (*p >= '0' && *p <= '9') { + ++p; + str_len--; + } + } + + if (*p == ' ') { + ++p; + str_len--; + } + + /* src specials might omit the file name when it had already + been specified on the page; so skip the test when the + special ends right here: + */ + if (*p != '\0') { + ASSERT(globals.dvi_file.dirname != NULL, "DVI name should be set here"); + src_this_file_equal = src_compare(p, str_len, src_file, globals.dvi_file.dirname, globals.dvi_file.dirlen); + } + + if (!src_this_file_equal) + return False; + + distance = (src_line > src_this_line + ? src_line - src_this_line + : 2 * (src_this_line - src_line)); /* favor earlier lines */ + + if (distance < best_distance) { /* found a better line */ + best_distance = distance; + best_line = src_this_line; + best_page = src_page; + max_offset = best_offset = xtell(globals.dvi_file.bak_fp, currinf.pos); + } + else if (distance == best_distance) { /* still on a good line, remember diff */ + max_offset = xtell(globals.dvi_file.bak_fp, currinf.pos); + } + + if (distance == 0 && best_distance == 0) { /* found a better col */ + col_dist = (src_col > col ? src_col - col : 2 * (col - src_col)); + + if (col_dist < best_col_dist) { + best_col_dist = col_dist; + best_page = src_page; + max_offset = best_offset = xtell(globals.dvi_file.bak_fp, currinf.pos); + } + else if (col_dist == best_col_dist) { + max_offset = xtell(globals.dvi_file.bak_fp, currinf.pos); + } + } + return True; +} + +static Boolean +htex_scan_special(char *str, int str_len, void *data) +{ + UNUSED(data); + + if (g_anchor_pos == NULL) + return False; + + /* first, hypertex specials */ + if (memicmp(str, "html:", strlen("html:")) == 0) { + size_t offset = strlen("html:"); + str += offset; + str_len -= offset; + + while (*str == ' ' || *str == '\t') { + str++; + str_len--; + } + + if (memicmp(str, "<a name", strlen("<a name")) != 0) + return False; + + offset = strlen("<a name"); + str += offset; + str_len -= offset; + str_len--; /* for the closing > */ + + /* skip over spaces, = and open quotes */ + while (*str == ' ' || *str == '=') { + str++; + str_len--; + } + if (*str == '"') { + str++; + str_len -= 2; /* don't compare closing quote */ + } + + /* fprintf(stderr, "comparing: |%s|%s|%d\n", str, g_anchor_pos, MAX((size_t)str_len, g_anchor_len)); */ + if (memcmp(str, g_anchor_pos, MAX((size_t)str_len, g_anchor_len)) == 0) + return True; + else + return False; + } + /* then, hdvips specials */ + else if (memicmp(str, "ps:", 3) == 0 && g_anchor_pos != NULL) { + str += 3; + if (memicmp(str, "sdict begin ", strlen("sdict begin ")) == 0) { + char *ptr = NULL, *pptr = NULL; + str += strlen("sdict begin "); + if ((ptr = strstr(str, "/View")) != NULL + && (ptr = strchr(ptr, '(')) != NULL + && (pptr = strchr(ptr + 1, ')')) != NULL) { + if (memcmp(ptr + 1, g_anchor_pos, pptr - ptr - 1) == 0 + && g_anchor_pos[pptr - ptr - 1] == '\0') { + return True; + } + } + } + return False; + } + else { + return False; + } +} + +static void +htex_scan_special_noreturn(struct scan_info *info, const char *str, int str_len) +{ + struct geom_info *g_info = (struct geom_info *)info->data; + + /* first, hypertex specials */ + if (memcmp(str, "html:", strlen("html:")) == 0) { + size_t offset = strlen("html:"); + str += offset; + str_len -= offset; + + while (*str == ' ' || *str == '\t') { + str++; + str_len--; + } + + if (memicmp(str, "<a name", strlen("<a name")) != 0) + return; + + offset = strlen("<a name"); + str += offset; + str_len -= offset; + str_len--; /* for the closing > */ + + /* skip over spaces, = and open quotes */ + while (*str == ' ' || *str == '=') { + str++; + str_len--; + } + if (*str == '"') { + str++; + str_len -= 2; /* don't compare closing quote */ + } + + if (g_anchor_pos != NULL + && memcmp(str, g_anchor_pos, MAX((size_t)str_len, g_anchor_len)) == 0) { + *((int *)g_info->geom_data) = pixel_conv(DVI_V); + longjmp(info->done_env, 1); + } + } + /* then, hdvips specials */ + else if (memicmp(str, "ps:", 3) == 0 && g_anchor_pos != NULL) { + str += 3; + if (memicmp(str, "sdict begin ", strlen("sdict begin ")) == 0) { + char *ptr = NULL, *pptr = NULL; + str += strlen("sdict begin "); + if ((ptr = strstr(str, "/View")) != NULL + && (ptr = strchr(ptr, '(')) != NULL + && (pptr = strchr(ptr + 1, ')')) != NULL) { + if (memcmp(ptr + 1, g_anchor_pos, pptr - ptr - 1) == 0 + && g_anchor_pos[pptr - ptr - 1] == '\0') { + *((int *)g_info->geom_data) = pixel_conv(DVI_V); + longjmp(info->done_env, 1); + } + } + } + } +} + +static void +src_spec_fwd_box(struct scan_info *info, long ulx, long uly, long lrx, long lry) +{ + /* Heuristic value to detect column breaks: A negative vertical + offset to the next glyph larger than BBOX_OFF will create a new + bounding box. The amount is in unshrunken pixels, i.e. 120 + \approx 1 Line of 12pt Text. */ + static const int BBOX_OFF = 360; + + UNUSED(info); + if (src_fwd_active) { + /* probably a column break, create new box */ + if (lry < g_bbox_info[bbox_info_idx].max_y - BBOX_OFF + && lrx > g_bbox_info[bbox_info_idx].max_x + 50) { + if (bbox_info_idx < BBOX_INFO_MAXIDX - 1) { + bbox_info_idx++; + + g_bbox_info[bbox_info_idx].min_y = g_bbox_info[bbox_info_idx].min_x = LONG_MAX; + g_bbox_info[bbox_info_idx].max_x = g_bbox_info[bbox_info_idx].max_y = 0; + } + } + if (ulx < g_bbox_info[bbox_info_idx].min_x) { + g_bbox_info[bbox_info_idx].min_x = ulx; + } + if (uly < g_bbox_info[bbox_info_idx].min_y) { + g_bbox_info[bbox_info_idx].min_y = uly; + } + if (lrx > g_bbox_info[bbox_info_idx].max_x) + g_bbox_info[bbox_info_idx].max_x = lrx; + if (lry > g_bbox_info[bbox_info_idx].max_y) { + g_bbox_info[bbox_info_idx].max_y = lry; + } + } +} + +/* dummy procedure for hyperlinks; we don't need geometry info for these. */ +static void +htex_dummy_box(struct scan_info *info, long ulx, long uly, long lrx, long lry) +{ + UNUSED(info); + UNUSED(ulx); + UNUSED(uly); + UNUSED(lrx); + UNUSED(lry); + return; +} + +static void +src_spec_fwd_special(struct scan_info *info, const char *str, int str_len) +{ + long pos; + UNUSED(str_len); + + if (memcmp(str, "src:", 4) != 0) /* if no source special */ + return; + + pos = xtell(globals.dvi_file.bak_fp, currinf.pos); + if (pos >= best_offset) + src_fwd_active = True; + + if (src_fwd_active) { + if (pos > max_offset) + longjmp(info->done_env, 1); + + if (G_PXL_H < g_bbox_info[bbox_info_idx].spcl_min_x) + g_bbox_info[bbox_info_idx].spcl_min_x = G_PXL_H; + if (G_PXL_H > g_bbox_info[bbox_info_idx].spcl_max_x) + g_bbox_info[bbox_info_idx].spcl_max_x = G_PXL_H; + if (PXL_V < g_bbox_info[bbox_info_idx].spcl_min_y) + g_bbox_info[bbox_info_idx].spcl_min_y = PXL_V; + if (PXL_V > g_bbox_info[bbox_info_idx].spcl_max_y) + g_bbox_info[bbox_info_idx].spcl_max_y = PXL_V; + + } +} + +/* + * Routine to actually draw the box. + */ + +static void +source_fwd_draw_box(void) +{ + if (globals.src.fwd_box_page != current_page) { + globals.src.fwd_box_page = -1; /* different page---clear it */ + } + else { + size_t i; + static const int padding = 15; + + /* fix oversplitting by heuristics, by merging box n with box n+1 if they overlap */ + for (i = 0; i < bbox_info_idx; i++) { + if (g_bbox_info[i].min_x == LONG_MAX) + continue; + if ((g_bbox_info[i+1].min_x < g_bbox_info[i].max_x + && g_bbox_info[i+1].min_y < g_bbox_info[i].max_y) || + (g_bbox_info[i].min_x < g_bbox_info[i+1].max_x + && g_bbox_info[i].min_y < g_bbox_info[i+1].max_y)) { /* overlap */ + g_bbox_info[i].min_x = MIN(g_bbox_info[i].min_x, g_bbox_info[i+1].min_x); + g_bbox_info[i].min_y = MIN(g_bbox_info[i].min_y, g_bbox_info[i+1].min_y); + g_bbox_info[i].max_x = MAX(g_bbox_info[i].max_x, g_bbox_info[i+1].max_x); + g_bbox_info[i].max_y = MAX(g_bbox_info[i].max_y, g_bbox_info[i+1].max_y); + bbox_info_idx--; + } + } + + for (i = 0; i <= bbox_info_idx; i++) { + int min_x, min_y, max_x, max_y; + + volatile XPoint ul, ur, ll, lr; + + if (g_bbox_info[i].min_x == LONG_MAX) { + /* If no glyphs or rules, use hot point of special instead. */ + g_bbox_info[i].min_x = g_bbox_info[i].spcl_min_x; + g_bbox_info[i].min_y = g_bbox_info[i].spcl_min_y; + g_bbox_info[i].max_x = g_bbox_info[i].spcl_max_x; + g_bbox_info[i].max_y = g_bbox_info[i].spcl_max_y; + } + + min_x = (g_bbox_info[i].min_x - padding) / mane.shrinkfactor - mane_base_x; + min_y = (g_bbox_info[i].min_y - padding) / mane.shrinkfactor - mane_base_y; + max_x = (g_bbox_info[i].max_x + padding) / mane.shrinkfactor - mane_base_x; + max_y = (g_bbox_info[i].max_y + padding) / mane.shrinkfactor - mane_base_y; + + ul.x = min_x; + ul.y = min_y; + + ur.x = max_x; + ur.y = min_y; + + ll.x = min_x; + ll.y = max_y; + + lr.x = max_x; + lr.y = max_y; + + if (i == 0 && bbox_info_idx == 0) { /* only 1 bounding box */ + XDrawRectangle(DISP, mane.win, globals.gc.high, + min_x, min_y, max_x - min_x, max_y - min_y); + } + else if (i == 0) { /* draw first bbox with open bottom */ + XPoint points[4]; + points[0] = ll; + points[1] = ul; + points[2] = ur; + points[3] = lr; + XDrawLines(DISP, mane.win, globals.gc.high, points, 4, CoordModeOrigin); + } + else if (i > 0 && i < bbox_info_idx) { /* draw middle box with open top and open bottom */ + XPoint points[2]; + points[0] = ul; + points[1] = ll; + XDrawLines(DISP, mane.win, globals.gc.high, points, 2, CoordModeOrigin); + points[0] = ur; + points[1] = lr; + XDrawLines(DISP, mane.win, globals.gc.high, points, 2, CoordModeOrigin); + } + else { /* draw last box with open top */ + XPoint points[4]; + points[0] = ul; + points[1] = ll; + points[2] = lr; + points[3] = ur; + XDrawLines(DISP, mane.win, globals.gc.high, points, 4, CoordModeOrigin); + } + } + } +} + +#if 0 +#include<asm/msr.h> +unsigned long time_start=0, time_end=0; +#endif + +void +source_forward_search(const char *str) +{ + volatile off_t pos_save = 0; + struct drawinf currinf_save; + ubyte maxchar_save; + struct scan_info info; + struct geom_info g_info; + + TRACE_CLIENT((stderr, "Entering source_forward_search(%s)", str)); + + max_offset = 0; + src_file = str; + while (*src_file == '0') + ++src_file; + if (*src_file < '1' || *src_file > '9') { + XDVI_WARNING((stderr, "Ignoring malformed source special \"%s\"", str)); + return; + } + src_line = atoi(src_file); + while (*src_file >= '0' && *src_file <= '9') + ++src_file; + + src_col = 0; + if (*src_file == ':') { + ++src_file; + src_col = atoi(src_file); + while (*src_file >= '0' && *src_file <= '9') + ++src_file; + } + + if (*src_file == ' ') + ++src_file; + + TRACE_CLIENT((stderr, "File = \"%s\", line = %d, col = %d", src_file, src_line, src_col)); + + /* Save status of dvi_file reading (in case we hit an error and resume + drawing). */ + + if (dvi_pointer_frame != NULL) + pos_save = lseek(fileno(globals.dvi_file.bak_fp), 0L, SEEK_CUR) - (dvi_pointer_frame->end - dvi_pointer_frame->pos); + (void)lseek(fileno(globals.dvi_file.bak_fp), pageinfo_get_offset(0), SEEK_SET); + + currinf_save = currinf; + maxchar_save = maxchar; + + memset((char *)&currinf.data, '\0', sizeof currinf.data); + currinf.tn_table_len = TNTABLELEN; + currinf.tn_table = tn_table; + currinf.tn_head = tn_head; + currinf.pos = currinf.end = dvi_buffer; + currinf.virtual = NULL; + + /* Start search over pages */ +#if 0 + rdtscl(time_start); +#endif + + found_src = False; + best_distance = best_col_dist = ULONG_MAX; + src_this_line = 0; /* initialize things that are kept as defaults */ + src_this_file_equal = False; + + /* These two lines do the actual scanning (first pass). */ + for (src_page = 0; src_page < total_pages; ++src_page) + (void)spcl_scan(src_scan_special, NULL, False, globals.dvi_file.bak_fp); + + if (best_distance == ULONG_MAX) { + if (!found_src) { + popup_message(globals.widgets.top_level, + MSG_WARN, + /* helptext */ + reverse_search_helptext, + /* popup */ + "No source specials in this DVI file - couldn't do reverse search."); + } + else { + popup_message(globals.widgets.top_level, + MSG_WARN, + /* helptext */ + "To enable reverse search, the TeX file has to be compiled with source specials. " + "See the xdvi man page (section SOURCE SPECIALS) for details.", + /* popup */ + "No references to source file \"%s\" in DVI file.", + src_file); + } + + /* Restore file position. */ + maxchar = maxchar_save; + currinf = currinf_save; + + if (dvi_pointer_frame != NULL) { + (void)lseek(fileno(globals.dvi_file.bak_fp), pos_save, SEEK_SET); + dvi_pointer_frame->pos = dvi_pointer_frame->end = dvi_buffer; + } + + return; + } + TRACE_CLIENT((stderr, "Match: line %d on page %d, offset %lu", + best_line, best_page + globals.pageno_correct, (unsigned long)best_offset)); + + /* + * In this case we don't need to restore maxchar and currinf, since + * we won't resume drawing -- we'll jump to a new page instead. + */ + + /* Move to that page. */ + goto_page(best_page, resource.keep_flag ? NULL : home, False); + page_history_insert(best_page); + globals.ev.flags |= EV_NEWPAGE; /* so that existing mark gets erased */ + /* Now search that particular page. */ + + info.geom_special = src_spec_fwd_special; + + g_info.geom_box = src_spec_fwd_box; + g_info.geom_data = NULL; + + src_fwd_active = False; + bbox_info_idx = 0; + + g_bbox_info[bbox_info_idx].min_x = + g_bbox_info[bbox_info_idx].min_y = + g_bbox_info[bbox_info_idx].spcl_min_x = + g_bbox_info[bbox_info_idx].spcl_min_y = LONG_MAX; + g_bbox_info[bbox_info_idx].max_x = + g_bbox_info[bbox_info_idx].max_y = + g_bbox_info[bbox_info_idx].spcl_max_x = + g_bbox_info[bbox_info_idx].spcl_max_y = 0; + globals.src.fwd_box_page = -1; /* in case of error, suppress box */ + + (void)lseek(fileno(globals.dvi_file.bak_fp), pageinfo_get_offset(best_page), SEEK_SET); + currinf.tn_table_len = TNTABLELEN; + currinf.tn_table = tn_table; + currinf.tn_head = tn_head; + currinf.pos = currinf.end = dvi_buffer; + currinf.virtual = NULL; + + info.data = &g_info; + + if (!setjmp(info.done_env)) + geom_scan_part(geom_do_char, globals.dvi_file.bak_fp, &info, geom_current_frame = &geom_frame0, dimconv); + + if (!src_fwd_active) { + XDVI_ERROR((stderr, "%s:%d: shouldn't happen: geom_scan_part() failed to re-find the special.", __FILE__, __LINE__)); + } + else { + long x_min = g_bbox_info[bbox_info_idx].min_x; + long y_min = g_bbox_info[bbox_info_idx].min_y; + long x_max = g_bbox_info[bbox_info_idx].max_x; + long y_max = g_bbox_info[bbox_info_idx].max_y; + do_autoscroll = True; + globals.src.fwd_box_page = current_page; + if (x_min == LONG_MAX || x_max == LONG_MAX) { + /* If no glyphs or rules, use hot point of special instead. */ + x_min = g_bbox_info[bbox_info_idx].spcl_min_x; + y_min = g_bbox_info[bbox_info_idx].spcl_min_y; + x_max = g_bbox_info[bbox_info_idx].spcl_max_x; + y_max = g_bbox_info[bbox_info_idx].spcl_max_y; + } + scroll_page_if_needed((int)(x_min / currwin.shrinkfactor) + 2, (int)(x_max / currwin.shrinkfactor) - 2, + (int)(y_min / currwin.shrinkfactor) + 10, (int)(y_max / currwin.shrinkfactor) - 10); + } +#if 0 + rdtscl(time_end); + printf("time search: %lu\n", time_end - time_start); +#endif +} + + +void +anchor_search(const char *str) +{ + off_t pos_save = 0; + struct drawinf currinf_save; + ubyte maxchar_save; + volatile int test_page = 0; + Boolean found_anchor = False; + int y_pos = -1; + struct scan_info info; + struct geom_info g_info; + + ASSERT(str != NULL, "Argument to anchor_search() musn't be NULL"); + TRACE_HTEX((stderr, "Entering anchor_search(%s)", str)); + + /* Save status of dvi_file reading (in case we hit an error and resume drawing). */ + if (dvi_pointer_frame != NULL) + pos_save = lseek(fileno(globals.dvi_file.bak_fp), 0L, SEEK_CUR) - (dvi_pointer_frame->end - dvi_pointer_frame->pos); + (void)lseek(fileno(globals.dvi_file.bak_fp), pageinfo_get_offset(0), SEEK_SET); + + currinf_save = currinf; + maxchar_save = maxchar; + + memset((char *)&currinf.data, '\0', sizeof currinf.data); + currinf.tn_table_len = TNTABLELEN; + currinf.tn_table = tn_table; + currinf.tn_head = tn_head; + currinf.pos = currinf.end = dvi_buffer; + currinf.virtual = NULL; + + /* Start search over pages */ + for (test_page = 0; test_page < total_pages; test_page++) { + if (spcl_scan(htex_scan_special, NULL, True, globals.dvi_file.bak_fp)) { + found_anchor = True; + break; + } + } + + if (!found_anchor) { + /* Restore file position. */ + maxchar = maxchar_save; + currinf = currinf_save; + + if (dvi_pointer_frame != NULL) { + (void)lseek(fileno(globals.dvi_file.bak_fp), pos_save, SEEK_SET); + dvi_pointer_frame->pos = dvi_pointer_frame->end = dvi_buffer; + } + XBell(DISP, 0); + statusline_print(STATUS_MEDIUM, "Error: Anchor \"%s\" not found.", str); + return; + } + + /* + * In this case we don't need to restore maxchar and currinf, since + * we won't resume drawing -- we'll jump to a new page instead. + */ + + /* Move to that page. */ + (void)lseek(fileno(globals.dvi_file.bak_fp), pageinfo_get_offset(test_page), SEEK_SET); + currinf.tn_table_len = TNTABLELEN; + currinf.tn_table = tn_table; + currinf.tn_head = tn_head; + currinf.pos = currinf.end = dvi_buffer; + currinf.virtual = NULL; + + info.geom_special = htex_scan_special_noreturn; + + g_info.geom_box = htex_dummy_box; + g_info.geom_data = &y_pos; + + info.data = &g_info; + + if (!setjmp(info.done_env)) + geom_scan_part(geom_do_char, globals.dvi_file.bak_fp, &info, geom_current_frame = &geom_frame0, dimconv); + + if (y_pos == -1) { /* not found */ + XDVI_ERROR((stderr, "%s:%d: shouldn't happen: geom_scan_part() failed to re-find the link.", __FILE__, __LINE__)); + } + else { + goto_page(test_page, resource.keep_flag ? NULL : home, False); + page_history_insert(test_page); + do_autoscroll = True; + htex_set_anchormarker(y_pos); + } + /* reset info */ + free(g_anchor_pos); + g_anchor_pos = NULL; + g_anchor_len = 0; +} + + + +#ifdef T1LIB + +/* ********************** FONT AND ENCODING LOADING ******************** */ + +static char ** +load_vector(const char *enc) +{ + char *filename; + + TRACE_T1((stderr, "loading vector: |%s|", enc)); + /* + * With TDS 1.0/kpathsea 3.5.2(?), encoding files are in texmf/fonts/enc and accessed + * via kpse_enc_format; see e.g.: + * http://tug.org/mailman/htdig/tex-live/2004-January/004734.html + * + * The lookups under kpse_program_text_format and kpse_tex_ps_header_format + * are kept for backwards compatibility. + */ + do { + filename = kpse_find_file(enc, kpse_enc_format, 0); + if (filename != NULL) + break; + filename = kpse_find_file(enc, kpse_program_text_format, 0); + if (filename != NULL) + break; + /* If that fails we try the standard place: postscript headers */ + filename = kpse_find_file(enc, kpse_tex_ps_header_format, 1); + } while (0); + + if (filename != NULL) { + return T1_LoadEncoding(filename); + } + return NULL; +} + + + +static int +add_tfm(const char *texname) +{ + /* Load font metrics if not already loaded. Return index into + fontmetrics array. If it fails, fallback metrics must be + loaded instead; if that fails as well, exit. */ + + size_t i; + int idx; + static size_t wlidx = 0; + static size_t wlidx_max = 0; + static const size_t wlidx_step = 1024; + +#ifdef USE_HASH + if (tfminfo_hash.size == 0) + tfminfo_hash = hash_create(T1FONTS_INITIAL_HASHTABLE_SIZE); + + if (find_str_int_hash(&tfminfo_hash, texname, &i)) { + return i; + } +#else + for (i = 0; i <= wlidx; i++) + if (strcmp(tfminfo[i].texname, texname) == 0) + return i; +#endif /* USE_HASH */ + + while (wlidx >= wlidx_max) { + wlidx_max += wlidx_step; + tfminfo = xrealloc(tfminfo, wlidx_max * sizeof *tfminfo); + } + + tfminfo[wlidx].texname = texname; +#ifdef USE_HASH + put_str_int_hash(&tfminfo_hash, tfminfo[wlidx].texname, wlidx); +#endif /* USE_HASH */ + + if (!tfmload(texname, &tfminfo[wlidx].designsize, tfminfo[wlidx].widths, + &(tfminfo[wlidx].fontdimen2))) { + XDVI_FATAL((stderr, "Cannot find font metrics file %s.tfm, " + "and fallback metrics cmr10.tfm is missing as well - exiting.\n", + texname)); + } + + idx = wlidx++; + + if (fallbacktfm == 1) { + fallbacktfm = 2; + popup_message(globals.widgets.top_level, + MSG_ERR, + /* helptext */ + "A font metrics file is missing and cmr10.tfm is used instead. Either " + "you don't have the file, or your TeX system cannot find it. Try to " + "invoke \"kpsewhich\" with the filename mentioned in the error " + "message and the \"-debug 2\" option to find out where Xdvik is " + "searching for your files.\n" + "To enable automatic creation of .tfm files for " + "Metafont fonts, " + /* FIXME: how about Type1 fonts?? */ + "set the environment variable MKTEXTFM " + "to 1 and re-start xdvi. See the file texmf.cnf for the default settings " + "of MKTEXTFM and related variables.", + /* message */ + "Could not find font metrics file %s.tfm; using cmr10.tfm instead. " + "Expect ugly output.", + texname); + } + + return idx; +} + +static int +add_t1font(const char *fontname, const char *filename) +{ + /* Add t1 font to list if it's not already there; return the + (newly created, or already existing) t1lib font id. The + filename argument is optional, but is assumed to be a full path + if it is given. + */ + + int id, idx; + size_t i; + char *path = NULL; + + static size_t t1lidx = 0; + static size_t t1lidx_max = 0; + static const size_t t1lidx_step = 1024; + + +#ifdef USE_HASH + /* Already set up by that name? */ + if (t1fonts_hash.size == 0) + t1fonts_hash = hash_create(T1FONTS_INITIAL_HASHTABLE_SIZE); + + if (find_str_int_hash(&t1fonts_hash, fontname, &i)) { + return i; + } +#else + for (i = 0; i <= t1lidx; i++) { + if (strcmp(t1fonts[i].shortname, fontname) == 0 || + strcmp(t1fonts[i].file, fontname) == 0) { + TRACE_T1((stderr, "Type1 font %s already loaded from %s", fontname, filename)); + return i; + } + } +#endif /* USE_HASH */ + + /* Insert and set up new t1 font */ + while (t1lidx >= t1lidx_max) { + t1lidx_max += t1lidx_step; + t1fonts = xrealloc(t1fonts, t1lidx_max * sizeof *t1fonts); + + TRACE_T1((stderr, "Enlarged t1 table from %lu to %lu entries", + (unsigned long)t1lidx, (unsigned long)t1lidx_max)); + } + + if (filename == NULL) { + /* We don't know the full path name yet, find it */ + path = kpse_find_file(fontname, kpse_type1_format, 0); + if (path == NULL) + return -1; /* xdvi will use substitution font */ + } + else { + path = xstrdup(filename); + } + + t1fonts[t1lidx].file = xstrdup(path); + t1fonts[t1lidx].t1id = id = T1_AddFont(path); + t1fonts[t1lidx].shortname = xstrdup(fontname); + t1fonts[t1lidx].loaded = 0; + + TRACE_T1((stderr, "Loaded font %lu/%s (%d) from %s", (unsigned long)t1lidx, fontname, id, path)); + +#ifdef USE_HASH + /* also save the info in the hashtable (note: no duplication of the string here!) */ + put_str_int_hash(&t1fonts_hash, t1fonts[t1lidx].shortname, t1lidx); +#endif /* USE_HASH */ + + idx = t1lidx++; + + free(path); + +#if USE_AFM + /* BZZT! AFM files are useless to us, their metric information is + too inacurate for TeX use. Never define USE_AFM */ + /* Set the afm filename. Always before loading. If no afm file + found t1lib will use fallback code. In fact the fallback code + has been deactivated entierly, it's dog slow. */ + path = kpse_find_file(fontname, kpse_afm_format, 0); + if (path != NULL) { + TRACE_T1((stderr, "found afm file: %s", path)); + T1_SetAfmFileName(id, path); + free(path); + } +#endif + + return idx; +} + +static int +find_texfont(const char *texname) +{ + /* Find fontmap index of texfont */ + size_t i; + +#ifdef USE_HASH + if (fontmaps_hash.size == 0) + fontmaps_hash = hash_create(T1FONTS_INITIAL_HASHTABLE_SIZE); + + if (find_str_int_hash(&fontmaps_hash, texname, &i)) { + return i; + } +#else + for (i = 0; i < g_maplidx; i++) { + if (strcmp(fontmaps[i].texname, texname) == 0) { + TRACE_T1((stderr, "Type1 font already loaded at index %d: %s\n", i, texname)); + return i; + } + } +#endif /* USE_HASH */ + return -1; +} + + +static int +setup_encoded_T1_font(const char *mapfile, + int lineno, + const char *texname, + const char *alias, + const char *filename, + int enc, + int ext, + int sl) +{ + /* xdvi T1 Font loading is done in two steps: + + 1. At xdvi startup two things happen: + + a. The fontmaps are read. + + b. the dvi file is (partialy) scanned, and fonts are set up. + In the case of t1 fonts we only set up the data structures, + they are not actually loaded until they are used. + + 2. At the time a T1 font is used it is loaded, encoded, extended + and slanted as prescribed. + + This procedure takes care of step 1a. It locates the font and + sets up the data-structures so it may be assumed, by xdvi, to be + loaded. Return the fontmaps array index. + + The 'texname' param should be the texname of the font, such as + ptmr8r or cmr10. + + The 'alias' param should be the file name of the font if it is + different from the texname. This is often the case with fonts + defined in fontmaps. + + If, for some reason, the full filename of the font has already + been looked up before we get here it is passed in the filename + param so we don't have to look it up again. + + Implied encodings are not handled here. + + REMEMBER: THIS PROC IS CALLED FOR EACH FONT IN THE FONTMAPS, WE + CANNOT DO ANY EXPENSIVE OPERATIONS HERE!!! + + */ + + int test_idx; + size_t curr_idx; + static size_t maplidx_max = 0; + static const size_t maplidx_step = 1024; + + /* fprintf(stderr, "setup font: |%s|, %s\n", texname, mapfile); */ + + /* Already setup by that name? */ + test_idx = find_texfont(texname); + ASSERT(test_idx == -1 || (test_idx > -1 && test_idx < (int)g_maplidx), "Result of find_texfont() out of range"); + + if (mapfile != NULL && test_idx != -1) { + /* font is already set up, and we're scanning the map file: replace existing font with new one */ + curr_idx = test_idx; + XDVI_INFO((stdout, "%s: Entry for font \"%s\" on line %d overrides previous entry.", + mapfile, texname, lineno)); + + free(fontmaps[curr_idx].filename); + free(fontmaps[curr_idx].pathname); + } + else if (test_idx != -1) { + /* font is already set up, but we're not scanning the map file - return font index */ + TRACE_T1_VERBOSE((stderr, "font |%s| already set up at index %d", texname, test_idx)); + return test_idx; + } + else { /* Not set up yet, do it. */ + curr_idx = g_maplidx++; /* increment global index count */ + while (curr_idx >= maplidx_max) { + maplidx_max += maplidx_step; + fontmaps = xrealloc(fontmaps, maplidx_max * sizeof *fontmaps); + + TRACE_T1((stderr, "Enlarged the fontmap from %lu to %lu entries", + (unsigned long)curr_idx, (unsigned long)maplidx_max)); + } + fontmaps[curr_idx].texname = xstrdup(texname); + } + + if (alias == NULL) + alias = texname; + +#ifdef USE_HASH + if (test_idx == -1) { + if (fontmaps_hash.size == 0) + fontmaps_hash = hash_create(T1FONTS_INITIAL_HASHTABLE_SIZE); + put_str_int_hash(&fontmaps_hash, fontmaps[curr_idx].texname, curr_idx); + } +#endif /* USE_HASH */ + + fontmaps[curr_idx].enc = enc; + fontmaps[curr_idx].extension = ext; + fontmaps[curr_idx].slant = sl; + + fontmaps[curr_idx].filename = xstrdup(alias); + fontmaps[curr_idx].t1libid = -1; + TRACE_T1((stderr, + "fontmaps[%lu]: \"%s\", enc=%d, ext=%d, sl=%d, alias \"%s\"", + (unsigned long)curr_idx, texname, enc, ext, sl, alias)); + + if (filename != NULL) { + fontmaps[curr_idx].pathname = xstrdup(filename); + } + else { + fontmaps[curr_idx].pathname = NULL; + } + fontmaps[curr_idx].tfmidx = -1; + fontmaps[curr_idx].warned_about = False; + fontmaps[curr_idx].force_pk = False; + + return curr_idx; +} + + +static t1FontLoadStatusT +try_pk_fallback(int idx, struct font *fontp) +{ + Boolean success; + + success = load_font(fontp, False); /* loading non-t1 version of font */ + fontmaps[idx].force_pk = True; + TRACE_T1((stderr, + "setting force_pk for %d to true; success for PK fallback: %d\n", + idx, success)); + + if (!success) { + /* this is probably serious enough for a GUI warning */ + popup_message(globals.widgets.top_level, + MSG_ERR, + /* helptext */ + "Xdvi tries all of the following possibilities in turn, and all of them have failed:\n\n" + " (1) If the resource t1lib is set, try a Postscript Type1 version of a font.\n\n" + " (2) Otherwise, or if the Type1 version hasn't been found, try to " + "locate, or generate via mktexpk, a TeX Pixel (PK) version of the font.\n\n" + " (3) Use the fallback font defined via the \"altfont\" resource (cmr10 by default), " + "both as Type1 and as PK version, at various resolutions.\n\n" + "It seems that your font setup is defective.\n", + /* errmsg */ + "Error loading font %s: Neither a Type1 version nor " + "a pixel version could be found. The character(s) " + "will be left blank.", + fontmaps[idx].texname); + currinf.set_char_p = currinf.fontp->set_char_p = set_empty_char; + return FAILURE_BLANK; + } + /* free unneeded resources. We mustn't free + fontmaps[idx].texname, + else find_T1_font will try to load the font all over again. + */ + free(fontmaps[idx].filename); + free(fontmaps[idx].pathname); + currinf.set_char_p = currinf.fontp->set_char_p = set_char; + return FAILURE_PK; +} + +static t1FontLoadStatusT +load_font_now(int idx, struct font *fontp) +{ + /* At this point xdvi needs to draw a glyph from this font. But + first it must be loaded/copied and modified if needed. */ + + int t1idx; /* The fontmap entry number */ + int t1id; /* The id of the unmodified font */ + int cid; /* The id of the copied font */ + int enc, sl, ext; + +/* statusline_print(STATUS_SHORT, "Loading T1 font %s", fontmaps[idx].filename); */ + TRACE_T1((stderr, "adding %s %s", + fontmaps[idx].filename, fontmaps[idx].pathname)); + + t1idx = add_t1font(fontmaps[idx].filename, fontmaps[idx].pathname); + + t1id = fontmaps[idx].t1libid = t1fonts[t1idx].t1id; + + errno = 0; + if (!t1fonts[t1idx].loaded && T1_LoadFont(t1id) == -1) { + /* this error is somehow better kept at stderr instead of + a popup (too annoying) or the statusline (might disappear + too fast) ... */ + XDVI_ERROR((stderr, "Could not load Type1 font %s from %s: %s %s (T1_errno = %d); " + "will try pixel version instead.\nPlease see the T1lib documentation for details about this.\n", + fontmaps[idx].texname, fontmaps[idx].pathname, + T1_StrError(T1_errno), + errno != 0 ? strerror(errno) : "", + T1_errno)); + return try_pk_fallback(idx, fontp); + } + + fontmaps[idx].tfmidx = add_tfm(fontmaps[idx].texname); + + t1fonts[t1idx].loaded = 1; + + /* If there is nothing further to do, just return */ + enc = fontmaps[idx].enc; + ext = fontmaps[idx].extension; + sl = fontmaps[idx].slant; + TRACE_T1((stderr, "lookup at idx[%d] yields: %d, %d, %d", idx, enc, ext, sl)); + + if (enc == -1 && ext == 0 && sl == 0) + return SUCCESS; + + /* The fontmap entry speaks of a modified font. Copy it first. */ + cid = T1_CopyFont(t1id); + + fontmaps[idx].t1libid = cid; + + if (enc != -1) { + TRACE_T1_VERBOSE((stderr, "trying to load vector from encodings index %d", enc)); + /* Demand load vector */ + if (encodings[enc].vector == NULL) + encodings[enc].vector = load_vector(encodings[enc].file); + + if (encodings[enc].vector == NULL) { + /* this error is somehow better kept at stderr instead of + a popup (too annoying) or the statusline (might disappear + too fast) ... */ + XDVI_ERROR((stderr, "Could not load load encoding file %s for vector %s (font %s): %s (T1_errno = %d); " + "will try pixel version instead.\n" + "Please see the T1lib documentation for details about this.\n", + encodings[enc].file, encodings[enc].enc, fontmaps[idx].texname, + T1_StrError(T1_errno), T1_errno)); + return try_pk_fallback(idx, fontp); + } + else { + if (T1_ReencodeFont(cid, encodings[enc].vector) != 0) { + XDVI_ERROR((stderr, "Re-encoding of %s failed: %s (T1_errno = %d); " + "will try pixel version instead.\n" + "Please see the T1lib documentation for details about this.\n", + fontmaps[idx].texname, + T1_StrError(T1_errno), T1_errno)); + return try_pk_fallback(idx, fontp); + } + } + } + + if (ext) + T1_ExtendFont(cid, ext / 1000.0); + + if (sl) + T1_SlantFont(cid, sl / 10000.0); + + return SUCCESS; +} + + +int +find_T1_font(const char *texname) +{ + /* Step 1b in the scenario above. xdvi knows that this font is + needed. Check if it is available, but do not load it yet. + Return the fontmap index at which the font was found */ + + int idx; /* Iterator, t1 font id */ + char *filename; + int fl, el; /* Fontname length, encoding name length */ + int encoded = -1; + char *mname; /* Modified font name */ + size_t i; + + encoded = 0; + + /* First: Check the maps */ + idx = find_texfont(texname); + + if (idx != -1) { + if (fontmaps[idx].force_pk) { + return idx; + } + if (fontmaps[idx].pathname == NULL) { + filename = kpse_find_file(fontmaps[idx].filename, kpse_type1_format, 0); + /* It should have been on disk according to the map, but never + fear, xdvi will try to find it other ways. */ + if (filename == NULL) { + if (!fontmaps[idx].warned_about) { + fontmaps[idx].warned_about = True; + XDVI_WARNING((stderr, "Font map calls for %s, but it was not found (will try PK version instead).", + fontmaps[idx].filename)); + } + return -1; + } + fontmaps[idx].pathname = filename; + } + TRACE_T1((stderr, "find_T1_font map: %s, entered as #%d",texname,idx)); + return idx; + } + +#if 0 + /* Second: the bare name */ + filename = kpse_find_file(texname, kpse_type1_format, 0); + + if (filename != NULL) { + idx = setup_encoded_T1_font(NULL, 0, texname, NULL, filename, -1, 0, 0); + TRACE_T1((stderr, "find_T1_font bare enc: %s, entered as #%d", texname, idx)); + return idx; + } +#endif + + /* Third: Implied encoding? */ + fl = strlen(texname); + + for (i = 0; i < enclidx; i++) { + if (encodings[i].enc == NULL) + continue; + + el = strlen(encodings[i].enc); + + /* Note: the encodings map is short, so the strcmp doesn't hurt here */ + if (strcmp(texname + (fl - el), encodings[i].enc) == 0) { + /* Found a matching encoding */ + TRACE_T1((stderr, "Encoding match: %s - %s", texname, + encodings[i].enc)); + + mname = malloc(fl + 2); + strcpy(mname, texname); + mname[fl - el] = '\0'; + /* If we had 'ptmr8r' the we now look for 'ptmr' */ + filename = kpse_find_file(mname, kpse_type1_format, 0); + if (filename == NULL) { + /* No? Look for ptmr8a. 8a postfix is oft used on raw fonts */ + strcat(mname, "8a"); + filename = kpse_find_file(mname, kpse_type1_format, 0); + } + if (filename != NULL) { + idx = setup_encoded_T1_font(NULL, 0, texname, mname, filename, i, 0, 0); + TRACE_T1((stderr, "find_T1_font implied enc: %s, is #%d", texname, idx)); + return idx; + } + + free(mname); + /* Now we have tried everything. Time to give up. */ + return -1; + + } /* If (strcmp...) */ + } /* for */ + return -1; +} + +/* ************************* CONFIG FILE READING ************************ */ + + +static int +new_encoding(const char *enc, const char *file) +{ + /* (Possibly) new encoding entered from .cfg file, or from dvips + map. When entered from dvips map the enc is null/anonymous */ + + size_t i; + static size_t enclidx_max = 0; + static const size_t enclidx_step = 16; + + /* Shirley! You're jesting! */ + if (file == NULL) + return -1; + + if (enc == NULL) { + /* Enter by file name. First check if the file has been entered + already. */ + for (i = 0; i < enclidx; i++) { + if (strcmp(encodings[i].file, file) == 0) { + return i; + } + } + } + else { + /* Enter by encoding name. Check if already loaded first. */ + for (i = 0; i < enclidx; i++) { + if (encodings[i].enc != NULL && strcmp(encodings[i].enc, enc) == 0) + return i; + } + } + + /* Bonafide new encoding */ + + while (enclidx >= enclidx_max) { + enclidx_max += enclidx_step; + encodings = xrealloc(encodings, enclidx_max * sizeof *encodings); + + TRACE_T1((stderr, "Enlarged encoding map from %lu to %lu entries", + (unsigned long)enclidx, (unsigned long)enclidx_max)); + } + + TRACE_T1((stderr, "New encoding #%lu: '%s' -> '%s'", (unsigned long)enclidx, enc ? enc : "<NULL>", file)); + + /* The encoding name is optional */ + encodings[enclidx].enc = NULL; + if (enc != NULL) + encodings[enclidx].enc = xstrdup(enc); + + /* The file name is required */ + encodings[enclidx].file = xstrdup(file); + encodings[enclidx].vector = NULL; /* Demand load later */ + + return enclidx++; +} + + +void +add_T1_mapentry(int lineno, + const char *mapfile, + const char *name, + const char *file, + const char *vec, + char *spec) +{ + /* This is called from read_map_file, once for each fontmap line `lineno'. We + will dutifully enter the information into our fontmap table */ + + static char delim[] = "\t "; + char *last, *current; + float number; + int extend = 0; + int slant = 0; + + TRACE_T1_VERBOSE((stderr, "%s:%d: %s -> %s Enc: %s Spec: %s", + mapfile, lineno, + name, file, + vec == NULL ? "<none>" : vec, + spec == NULL ? "<none>" : spec)); + + if (spec != NULL) { + /* Try to analyze the postscript string. We recognize two things: + "n ExtendFont" and "m SlantFont". n can be a decimal number in + which case it's an extension factor, or a integer, in which + case it's a charspace unit? In any case 850 = .85 */ + + last = strtok(spec, delim); + current = strtok(NULL, delim); + while (current != NULL) { + if (strcmp(current, "ExtendFont") == 0) { + sscanf(last, "%f", &number); + if (number < 10.0) + extend = number * 1000.0; + else + extend = number; + } + else if (strcmp(current, "SlantFont") == 0) { + sscanf(last, "%f", &number); + slant = number * 10000.0; + } + last = current; + current = strtok(NULL, delim); + } + } + + setup_encoded_T1_font(mapfile, lineno, + name, file, NULL, new_encoding(NULL, vec), extend, + slant); +} + +static void +read_cfg_file(const char *file) +{ + char *filename; + FILE *fp; + int len; + char *keyword; + char *ptr; + char *buffer; + char *enc; + char *name; + int i; + static const char delim[] = "\t \n\r"; + Boolean found_no_map_files = True; + + if (file == NULL) + file = "xdvi.cfg"; + + filename = kpse_find_file(file, kpse_program_text_format, 1); + if (filename == NULL) { + statusline_print(STATUS_MEDIUM, "Warning: Unable to find \"%s\"!", file); + return; + } + + if ((fp = XFOPEN(filename, "r")) == NULL) { + XDVI_ERROR((stderr, "Cannot open config file `%s' for reading: %s", filename, strerror(errno))); + return; + } + + TRACE_T1((stderr, "Reading cfg file %s", filename)); + + buffer = xmalloc(BUFFER_SIZE); + + while (fgets(buffer, BUFFER_SIZE, fp) != NULL) { + len = strlen(buffer); + if (buffer[len - 1] != '\n') { + int c; + /* this really shouldn't happen ... */ + XDVI_WARNING((stderr, "Skipping overlong line (> %d characters) in config file `%s':\n%s ...", + BUFFER_SIZE, filename, buffer)); + /* read until end of this line */ + while((c = fgetc(fp)) != '\n' && c != EOF) { ; } + continue; + } + + keyword = buffer; + + /* Skip leading whitespace */ + while (keyword[0] != '\0' && (keyword[0] == ' ' || keyword[0] == '\t')) + keyword++; + + /* % in first column is a correct comment */ + if (keyword[0] == '%' || keyword[0] == '\0' || keyword[0] == '\n') + continue; + + keyword = strtok(keyword, delim); + + if (strcmp(keyword, "dvipsmap") == 0) { + if ((ptr = strtok(NULL, delim)) == NULL) { + XDVI_WARNING((stderr, "Syntax error in entry \"%s\"", buffer)); + continue; + } + TRACE_T1((stderr, "DVIPSMAP: '%s'", ptr)); + + if (read_map_file(ptr)) { + found_no_map_files = False; + } + else { + TRACE_T1((stderr, "Read map file: '%s'", ptr)); + } + } + else if (strcmp(keyword, "encmap") == 0) { + popup_message(globals.widgets.top_level, + MSG_ERR, + "Your xdvi.cfg file is for a previous version of xdvik. Please replace " + "it by the xdvi.cfg file in the current xdvik distribution.", + "Keyword \"encmap\" in xdvi.cfg is no longer supported, " + "please update the config file %s.", + filename); + } + else if (strcmp(keyword, "enc") == 0) { + enc = strtok(NULL, delim); + name = strtok(NULL, delim); + if ((ptr = strtok(NULL, delim)) == NULL) { + XDVI_WARNING((stderr, "Syntax error in entry \"%s\" (skipping line)", buffer)); + continue; + } + i = new_encoding(enc, ptr); + TRACE_T1((stderr, "Encoding[%d]: '%s' = '%s' -> '%s'", i, enc, name, ptr)); + } else { + /* again, nag them with a popup so that they'll do something about this ... */ + popup_message(globals.widgets.top_level, + MSG_ERR, + "Please check the syntax of your config file. " + "Valid keywords are: \"enc\" and \"dvipsmap\".", + "Skipping unknown keyword \"%s\" in config file %s.", + keyword, filename); + } + } + + if (found_no_map_files) { + /* nag 'em with a popup so that they'll do something about this ... */ + popup_message(globals.widgets.top_level, + MSG_ERR, + "Direct Type 1 font rendering via T1lib gives you many benefits, such as:\n" + " - quicker startup time, since no bitmap fonts need to be generated;\n" + " - saving disk space for storing the bitmap fonts.\n" + "To fix this error, check that the file `ps2pk.map' is located somewhere " + "in your XDVIINPUTS path. Have a look at the xdvi wrapper shell script " + "(type \"which xdvi\" to locate that shell script) for the current setting " + "of XDVIINPUTS.", + "Could not load any of the map files listed in xdvi.cfg - disabling T1lib."); + resource.t1lib = False; + } + fclose(fp); + + free(buffer); + free(filename); +} + + +/* **************************** GLYPH DRAWING *************************** */ + + +/* Set character# ch */ + +static struct glyph * +get_t1_glyph( +#ifdef TEXXET + wide_ubyte cmd, +#endif + wide_ubyte ch, t1FontLoadStatusT *flag, + Boolean is_geom_scan) +{ + + /* + A problem is that there are some gaps in math modes tall braces '{', + (see for example the amstex users guide). These gaps are seen less if a + small size factor is applied, 1.03 seems nice. This does not seem like + the Right Thing though. Also gaps do not show up in the fullsize + window. + + All in all the factor has been dropped. Despite the beauty flaw. + */ + + float size = dvi_pt_conv(currinf.fontp->scale); + + int id = currinf.fontp->t1id; + int t1libid = fontmaps[id].t1libid; + + GLYPH *G; /* t1lib glyph */ + struct glyph *g; /* xdvi glyph */ + +#ifdef TEXXET + UNUSED(cmd); +#endif + *flag = SUCCESS; + +#if COLOR + if (!is_geom_scan && fg_active != fg_current) + do_color_change(); +#endif + + /* return immediately if we need to use the fallback PK */ + if (fontmaps[id].force_pk) + return NULL; + + TRACE_T1((stderr, "scale: %ld, ppi %d, sf: %d, size: %f", + currinf.fontp->scale, resource.pixels_per_inch, + currwin.shrinkfactor, size)); + + if (t1libid == -1) { + TRACE_T1((stderr, "trying to load font %d", id)); + *flag = load_font_now(id, currinf.fontp); + if (*flag < 0) { + TRACE_T1((stderr, "load_font_now failed for T1 font %d", id)); + return NULL; + } + t1libid = fontmaps[id].t1libid; + } + + TRACE_T1((stderr, "Setting 0x%x `%c' of %d, at %ld(%.2fpt), shrinkage is %d", + ch, + isprint(ch) ? ch : '?', + t1libid, currinf.fontp->scale, + size, currwin.shrinkfactor)); + + /* Check if the glyph already has been rendered */ + if ((g = &currinf.fontp->glyph[ch])->bitmap.bits == NULL) { + int bitmapbytes; + int h; + unsigned char *f; + unsigned char *t; + /* Not rendered: Generate xdvi glyph from t1font */ + + g->addr = 1; /* Dummy, should not provoke errors other places in xdvi */ + + /* Use the width from the tfm file */ + g->dvi_adv = tfminfo[fontmaps[id].tfmidx].widths[ch] * currinf.fontp->dimconv; + TRACE_T1((stderr, "0x%x, dvi_adv = %ld; dimconv: %f", + ch, + tfminfo[fontmaps[id].tfmidx].widths[ch], + currinf.fontp->dimconv)); + + /* Render the glyph. Size here should be postscript bigpoints */ + G = T1_SetChar(t1libid, ch, size, NULL); + if (G == NULL) { + /* This can happen e.g. if font is too small; example with plain TeX: + \magnification=50 Hello, world!\end + */ + statusline_print(STATUS_FOREVER, + "Error rendering character 0x%x `%c' - replacing by whitespace", + ch, + isprint(ch) ? ch : '?'); + XDVI_ERROR((stderr, "T1lib failed for character 0x%x `%c': %s. Replacing by whitespace.", + ch, isprint(ch) ? ch : '?', + T1_StrError(T1_errno))); + g->bitmap.w = 1; + g->bitmap.h = 1; + g->bitmap.bytes_wide = T1PAD(g->bitmap.w, archpad) / 8; + alloc_bitmap(&g->bitmap); + memset(g->bitmap.bits, 0, g->bitmap.h * g->bitmap.bytes_wide); + g->x = 0; + g->y = 0; +#if COLOR + g->fg = fg_current; +#endif + return g; + } + + if (G->bits == NULL) { + if (ch != ' ') { + /* -- janl: + Blank glyph != .notdef (e.g., it could be a space char). + Drop message until I find out how .notdef can be detected. + -- SU 2003/02/24: + I changed this, since dropped characters is an important + thing to warn users about. We try to catch the space char + case by checking for space above (rather lame, since it won't + catch all fonts, but ...) + The only case in which TeX actually uses a space character seems + to be the output of `tex testfont', or in some CJK fonts + (e.g. gbksong25). + */ + char index[128]; + char *fontname = xstrdup(currinf.fontp->fontname); + size_t dummy = 0; + + SNPRINTF(index, sizeof index, "%d", ch); + fontname = xstrcat(fontname, index); + if (font_warn_hash.size == 0) { + /* TODO: print to statusline */ + /* popup_message(MSG_WARN, */ + /* NULL, */ + /* "Unexpected blank character (.notdef) in font %s, " */ + /* "replacing with whitespace (see output to stderr for details). " */ + /* "This might indicate that the font %s is missing " */ + /* "from the font map, or that it has been specified " */ + /* "with a wrong encoding.", */ + /* currinf.fontp->fontname, currinf.fontp->fontname); */ + font_warn_hash = hash_create(T1FONTS_INITIAL_HASHTABLE_SIZE); + } + + if (!find_str_int_hash(&font_warn_hash, fontname, &dummy)) { + if (!resource.hush_chars) + XDVI_WARNING((stderr, "Character %d is mapped to .notdef in font %s (page %d), " + "replacing by whitespace.", + ch, currinf.fontp->fontname, current_page + 1)); + put_str_int_hash(&font_warn_hash, fontname, dummy); + } + else { + free(fontname); + } + } + /* *flag = FAILURE_BLANK; */ + /* g->addr = -1; */ + /* return NULL; */ + + /* FIXME: g->dvi_adv is only used when invoking set_char(), not with + set_empty_char(), and the former apparently needs a valid bitmap. + Check how this is done without t1lib. + */ + TRACE_T1((stderr, "replacing .notdef char 0x%02x by space.\n", ch)); + g->bitmap.w = 1; + g->bitmap.h = 1; + g->bitmap.bytes_wide = T1PAD(g->bitmap.w, archpad) / 8; + alloc_bitmap(&g->bitmap); + memset(g->bitmap.bits, 0, g->bitmap.h * g->bitmap.bytes_wide); + g->x = 0; + g->y = 0; +#if COLOR + g->fg = fg_current; +#endif + return g; + } + + g->bitmap.w = abs(G->metrics.rightSideBearing - G->metrics.leftSideBearing); + g->bitmap.h = abs(G->metrics.descent - G->metrics.ascent); + g->bitmap.bytes_wide = T1PAD(g->bitmap.w, archpad) / 8; + alloc_bitmap(&g->bitmap); + + bitmapbytes = g->bitmap.bytes_wide * g->bitmap.h; + + if (padMismatch) { + /* Fix alignment mismatch */ + int from_bytes = T1PAD(g->bitmap.w, 8) / 8; + + f = (unsigned char *)G->bits; + t = (unsigned char *)g->bitmap.bits; + + memset(t, 0, bitmapbytes); /* OR ELSE! you get garbage */ + + for (h = 0 ; h < g->bitmap.h; h++) { + memcpy(t, f, from_bytes); + f += from_bytes; + t += g->bitmap.bytes_wide; + } + + + } else { + /* t1lib and arch alignment matches */ + memcpy(g->bitmap.bits, G->bits, bitmapbytes); + } + +#ifdef WORDS_BIGENDIAN + /* xdvi expects the bits to be reversed according to + endianness. t1lib expects no such thing. This loop reverses + the bit order in all the bytes. -janl 18/5/2001 */ + t = (unsigned char *)g->bitmap.bits; + + for (h = 0; h < bitmapbytes; h++) { + *t = rbits[*t]; + t++; + } +#endif + + g->x = -G->metrics.leftSideBearing; /* Opposed x-axis */ + g->y = G->metrics.ascent; +#if COLOR + g->fg = fg_current; +#endif + } + return g; +} + +setcharRetvalT +set_t1_char( +#ifdef TEXXET + wide_ubyte cmd, +#endif + wide_ubyte ch) +{ + t1FontLoadStatusT flag; + + /* + get_t1_glyph() will itself set currinf.fontp->set_char_p + to set_empty_char() or set_char() if it failed to load the + T1 version, but for the *current* call of set_t1_char, + we still need to know whether (1) or (2) holds: + (1) Type1 font hasn't been found but PK is working, + use set_char() to set the PK version + (2) neither Type1 nor PK have been found (this is the + FAILURE_BLANK case), use set_empty_char(). + So we need the flag to pass this additional information. + */ +#ifdef TEXXET + (void)get_t1_glyph(cmd, ch, &flag, False); + if (flag == FAILURE_BLANK) + set_empty_char(cmd, ch); + else + set_char(cmd, ch); + return; +#else + (void)get_t1_glyph(ch, &flag, False); + if (flag == FAILURE_BLANK) + return set_empty_char(ch); + else + return set_char(ch); +#endif +} + +void read_T1_char(struct font *fontp, wide_ubyte ch) +{ + UNUSED(fontp); + UNUSED(ch); + /* Should never be called */ + XDVI_ABORT((stderr, "%s:%d: asked to load %c", __FILE__, __LINE__, ch)); +} + +void init_t1(void) +{ + int i; + void *success; + /* Attempt to set needed padding. + + FIXME: This is not really the required alignment/padding. On + ix86 the requirement for int * is 8 bits, on sparc on the other + hand it's 32 bits. Even so, some operations will be faster if + the bitmaps lines are alligned "better". But on the other hand + this requires a more complex glyph copying operation... + + - janl 16/5/2001 + */ + archpad = BMBYTES * 8; + i = T1_SetBitmapPad(archpad); + if (i == -1) { + /* Failed, revert to 8 bits and compilicated bitmap copying */ + padMismatch = True; + T1_SetBitmapPad(8); + } else { + padMismatch = False; + } + + /* Initialize t1lib, use LOGFILE to get logging, NO_LOGFILE otherwise */ + /* Under NO circumstances remove T1_NO_AFM, it slows us down a LOT */ + + if (globals.debug & DBG_T1) { + XDVI_INFO((stdout, "Additional t1lib messages may be found in \"t1lib.log\".")); + success = T1_InitLib(LOGFILE | T1_NO_AFM | IGNORE_CONFIGFILE | IGNORE_FONTDATABASE); + } + else + success = T1_InitLib(NO_LOGFILE | T1_NO_AFM | IGNORE_CONFIGFILE | IGNORE_FONTDATABASE); + if (success == NULL) { + XDVI_FATAL((stderr, "Initialization of t1lib failed!")); + } + T1_SetLogLevel(T1LOG_DEBUG); + + if (resource.subpixel_order == SUBPIXEL_NONE) + T1_SetDeviceResolutions(BDPI, BDPI); + else + T1_SetDeviceResolutions(3 * BDPI, BDPI); + + read_cfg_file(NULL); +} + +#endif /* T1LIB */ + + +#if GREY + +extern double pow(); + +static void +mask_shifts(Pixel mask, int *pshift1, int *pshift2) +{ + int k, l; + + for (k = 0; (mask & 1) == 0; ++k) + mask >>= 1; + for (l = 0; (mask & 1) == 1; ++l) + mask >>= 1; + *pshift1 = sizeof(short) * 8 - l; + *pshift2 = k; +} + +/* + * Try to allocate 4 color planes for 16 colors (for GXor drawing). + * Not called if the visual type is TrueColor. + * When color specials are active, this is called exactly once. + * It is used for the first foreground/background pair displayed in + * the document. For other documents, we act as if this call had failed. + */ + + +void +init_plane_masks(void) +{ + Pixel pixel; + + if (globals.gc.do_copy || plane_masks[0] != 0) + return; + + if (XAllocColorCells(DISP, G_colormap, False, plane_masks, 4, &pixel, 1)) { + /* Make sure fore_Pixel and back_Pixel are a part of the palette */ + resource.back_Pixel = pixel; + resource.fore_Pixel = pixel | plane_masks[0] | plane_masks[1] + | plane_masks[2] | plane_masks[3]; + if (mane.win != (Window) 0) + XSetWindowBackground(DISP, mane.win, resource.back_Pixel); + } + else { + globals.gc.do_copy = True; + warn_overstrike(); + } +} + +#endif /* GREY */ + +#if COLOR + +/* + * Insert into list of colors to be freed upon opening new document. + */ + +static void +color_list_insert(Pixel pixel) +{ + if (color_list_len >= color_list_max) { + if (color_list_max == 0) + color_list = xmalloc((color_list_max += 16) * sizeof *color_list); + else + color_list = xrealloc(color_list, + (color_list_max += 16) * sizeof *color_list); + } + color_list[color_list_len++] = pixel; +} + + +/* + * Warn about colors not being correct. + */ + +static void +color_warn(void) +{ + if (!color_warned) { + color_warned = True; + popup_message(globals.widgets.top_level, + MSG_WARN, + /* helptext */ + "Either this document is using too many " + "colors, or some other application is. " + "In the latter case, try to close that " + "application and re-read the DVI file.", + /* msg */ + "Cannot allocate colormap entry, " + "displayed colors will not be exact."); + } +} + + +/* + * Allocate a color and add it to our list of pixels to be returned + * upon opening a new document. + */ + +#define SHIFTIFY(x, shift1, shift2) ((((Pixel)(x)) >> (shift1)) << (shift2)) + +static int shift1_r, shift1_g, shift1_b; +static int shift2_r, shift2_g, shift2_b; +static Boolean shifts_good = False; + +Pixel +alloc_color(const struct rgb *colorp, Pixel fallback_pixel) +{ + XColor xcol; + + if (G_visual->class == TrueColor) { + if (!shifts_good) { + mask_shifts(G_visual->red_mask, &shift1_r, &shift2_r); + mask_shifts(G_visual->green_mask, &shift1_g, &shift2_g); + mask_shifts(G_visual->blue_mask, &shift1_b, &shift2_b); + shifts_good = True; + } + return SHIFTIFY(colorp->r, shift1_r, shift2_r) + | SHIFTIFY(colorp->g, shift1_g, shift2_g) + | SHIFTIFY(colorp->b, shift1_b, shift2_b); + } + else { + xcol.red = colorp->r; + xcol.green = colorp->g; + xcol.blue = colorp->b; + xcol.flags = DoRed | DoGreen | DoBlue; + if (XAllocColor(DISP, G_colormap, &xcol)) { + color_list_insert(xcol.pixel); + if (globals.debug & DBG_DVI) + printf("alloc_color%6d%6d%6d --> %ld\n", + xcol.red, xcol.green, xcol.blue, xcol.pixel); + return xcol.pixel; + } + else { + if (globals.debug & DBG_DVI) + printf("alloc_color%6d%6d%6d --> failed\n", + xcol.red, xcol.green, xcol.blue); + color_warn(); + return fallback_pixel; + } + } +} + +#undef SHIFTIFY + + +/* + * Switch colors of GCs, etc. Called when we're about to use a GC. + */ + +void +do_color_change(void) +{ + static int shrink_allocated_for = 0; + Pixel set_bits; + Pixel clr_bits; + + ASSERT(fg_current != NULL, "Current foreground mustn't be NULL"); + ASSERT(bg_current != NULL, "Current background mustn't be NULL"); +#if GREY + if (resource.use_grey) { + if (G_visual->class == TrueColor) { + if (!fg_current->pixel_good) { + fg_current->pixel = alloc_color(&fg_current->color, + color_data[0].pixel); + fg_current->pixel_good = True; + } + + set_bits = fg_current->pixel & ~bg_current->pixel; + clr_bits = bg_current->pixel & ~fg_current->pixel; + + if (set_bits & G_visual->red_mask) + set_bits |= G_visual->red_mask; + if (clr_bits & G_visual->red_mask) + clr_bits |= G_visual->red_mask; + if (set_bits & G_visual->green_mask) + set_bits |= G_visual->green_mask; + if (clr_bits & G_visual->green_mask) + clr_bits |= G_visual->green_mask; + if (set_bits & G_visual->blue_mask) + set_bits |= G_visual->blue_mask; + if (clr_bits & G_visual->blue_mask) + clr_bits |= G_visual->blue_mask; + + /* + * Make the GCs + */ + + globals.gc.rule = set_or_make_gc(globals.gc.rule, GXcopy, fg_current->pixel, bg_current->pixel); + globals.gc.fore2 = NULL; + + if (resource.copy + || (set_bits && clr_bits && !resource.thorough)) { + if (!resource.copy) { + warn_overstrike(); + } + globals.gc.fore = set_or_make_gc(globals.gc.fore, GXcopy, fg_current->pixel, bg_current->pixel); + } + else { + if (set_bits) { + globals.gc.fore = set_or_make_gc(globals.gc.fore, GXor, fg_current->pixel & set_bits, 0); + if (clr_bits) { + globals.gc.fore2 = globals.gc.fore2_bak = set_or_make_gc(globals.gc.fore2_bak, GXandInverted, + ~fg_current->pixel & clr_bits, 0); + } + } + else { + globals.gc.fore = set_or_make_gc(globals.gc.fore, GXandInverted, + ~fg_current->pixel & clr_bits, 0); + } + } + + if (globals.debug & DBG_DVI) + printf("do_color_change: fg = %lx, bg = %lx, with%s globals.gc.fore2\n", + fg_current->pixel, bg_current->pixel, + globals.gc.fore2 == NULL ? "out" : ""); + + if (mane.shrinkfactor > 1) { + unsigned int i; + unsigned int sf_squared; + + sf_squared = mane.shrinkfactor * mane.shrinkfactor; + + if (shrink_allocated_for < mane.shrinkfactor) { + if (pixeltbl != NULL) { + free((char *) pixeltbl); + if (pixeltbl_gc2 != NULL) { + free((char *) pixeltbl_gc2); + pixeltbl_gc2 = NULL; + } + } + pixeltbl = xmalloc((sf_squared + 1) * sizeof *pixeltbl); + shrink_allocated_for = mane.shrinkfactor; + } + if (globals.gc.fore2 != NULL && pixeltbl_gc2 == NULL) { + /* Can't use sf_squared (or mane.shrinkfactor) here */ + pixeltbl_gc2 = xmalloc((shrink_allocated_for * shrink_allocated_for + 1) * sizeof *pixeltbl_gc2); + } + + /* + * Initialize the pixel lookup table according to the gamma values. + */ +#define SHIFTIFY(x, shift1, shift2) ((((Pixel)(x)) >> (shift1)) << (shift2)) + + for (i = 0; i <= sf_squared; ++i) { + double frac = resource.gamma > 0 + ? pow((double)i / sf_squared, 1 / resource.gamma) + : 1 - pow((double) (sf_squared - i) / sf_squared, -resource.gamma); + unsigned int red, green, blue; + Pixel pixel; + /* fprintf(stderr, "frac: %f\n", frac); */ +/* fprintf(stderr, "fg_current: %d, bg_current: %d\n", fg_current->color.r, bg_current->color.r); */ + red = frac + * ((double) fg_current->color.r - bg_current->color.r) + + bg_current->color.r; + green = frac + * ((double) fg_current->color.g - bg_current->color.g) + + bg_current->color.g; + blue = frac + * ((double) fg_current->color.b - bg_current->color.b) + + bg_current->color.b; + + pixel = SHIFTIFY(red, shift1_r, shift2_r) + | SHIFTIFY(green, shift1_g, shift2_g) + | SHIFTIFY(blue, shift1_b, shift2_b); + + if (globals.gc.fore2 != NULL) { /* if thorough */ + /* fprintf(stderr, "++++++ pixeltable at %d: %ld\n", i, pixel & ~bg_current->pixel); */ + pixeltbl[i] = pixel & ~bg_current->pixel; + pixeltbl_gc2[i] = ~pixel & bg_current->pixel; + } + else if (resource.copy || (set_bits && clr_bits)) { + /* fprintf(stderr, "++++++ pixeltable2 at %d: %ld\n", i, pixel); */ + pixeltbl[i] = pixel; + } + else { + /* fprintf(stderr, "++++++ pixeltable3 at %d: 0x%lx\n", i, ~pixel & clr_bits); */ + /* fprintf(stderr, "++++++ pixeltable3 at %d: %ld\n", i, pixel & set_bits); */ + pixeltbl[i] = set_bits ? pixel & set_bits : ~pixel & clr_bits; + } + } +#undef SHIFTIFY + } + + } + else { /* not TrueColor */ + int i; + Boolean using_planes; + + using_planes = (fg_current == bg_head->fg_head && !globals.gc.do_copy); + if (!fg_current->palette_good) { + XColor color; + + /* + * Initialize the pixel lookup table according to the gamma values. + */ + for (i = 0; i < 16; ++i) { + double frac; + + frac = resource.gamma > 0 + ? pow((double) i / 15, 1 / resource.gamma) + : 1 - pow((double) (15 - i) / 15, -resource.gamma); + color.red = frac + * ((double) fg_current->color.r - bg_current->color.r) + + bg_current->color.r; + color.green = frac + * ((double) fg_current->color.g - bg_current->color.g) + + bg_current->color.g; + color.blue = frac + * ((double) fg_current->color.b - bg_current->color.b) + + bg_current->color.b; + + color.flags = DoRed | DoGreen | DoBlue; + + if (using_planes) { + color.pixel = resource.back_Pixel; /* start of block */ + if (i & 1) color.pixel |= plane_masks[0]; + if (i & 2) color.pixel |= plane_masks[1]; + if (i & 4) color.pixel |= plane_masks[2]; + if (i & 8) color.pixel |= plane_masks[3]; + XStoreColor(DISP, G_colormap, &color); + fg_current->palette[i] = color.pixel; + } + else { + if (XAllocColor(DISP, G_colormap, &color)) { + fg_current->palette[i] = color.pixel; + color_list_insert(color.pixel); + } + else { + color_warn(); + fg_current->palette[i] = + (i * 100 >= resource.density * 15) + ? resource.fore_Pixel : bg_current->pixel; + } + } + } + + if (using_planes && bg_current->pixel != resource.back_Pixel) { + bg_current->pixel = resource.back_Pixel; +/* XSetWindowBackground(DISP, mane.win, bg_current->pixel); */ +#if MOTIF + fprintf(stderr, "setting window background!\n"); + XSetWindowBackground(DISP, XtWindow(globals.widgets.main_window), bg_current->pixel); + XtVaSetValues(globals.widgets.main_window, XmNbackground, bg_current->pixel, NULL); +#else + XSetWindowBackground(DISP, mane.win, bg_current->pixel); +#endif + XClearWindow(DISP, mane.win); + } + + fg_current->palette_good = True; + } + + if (globals.debug & DBG_DVI) + printf("do_color_change: fg = %ld, bg = %ld, using_planes = %d\n", + fg_current->palette[15], bg_current->pixel, using_planes); + + if (using_planes) { + globals.gc.rule = set_or_make_gc(globals.gc.rule, GXor, fg_current->palette[15], + bg_current->pixel); + globals.gc.fore = set_or_make_gc(globals.gc.fore, GXor, fg_current->palette[15], + bg_current->pixel); + } + else { + globals.gc.rule = set_or_make_gc(globals.gc.rule, GXcopy, fg_current->palette[15], + bg_current->pixel); + globals.gc.fore = set_or_make_gc(globals.gc.fore, GXcopy, fg_current->palette[15], + bg_current->pixel); + } + + globals.gc.fore2 = NULL; + + if (mane.shrinkfactor > 1) { + if (shrink_allocated_for < mane.shrinkfactor) { + if (pixeltbl != NULL) + free((char *) pixeltbl); + pixeltbl = xmalloc((unsigned)(mane.shrinkfactor * mane.shrinkfactor + 1) + * sizeof *pixeltbl); + shrink_allocated_for = mane.shrinkfactor; + } + + for (i = 0; i <= mane.shrinkfactor * mane.shrinkfactor; ++i) { + pixeltbl[i] = fg_current->palette[(i * 30 + mane.shrinkfactor * mane.shrinkfactor) + / (2 * mane.shrinkfactor * mane.shrinkfactor)]; + } + } + } + } /* end if resource.use_grey */ + else +#endif /* GREY */ + { + if (!fg_current->pixel_good) { + fg_current->pixel = alloc_color(&fg_current->color, + color_data[0].pixel); + fg_current->pixel_good = True; + } + + if (globals.debug & DBG_DVI) + printf("do_color_change: fg = %lx, bg = %lx\n", + fg_current->pixel, bg_current->pixel); + + globals.gc.rule = set_or_make_gc(globals.gc.rule, GXcopy, fg_current->pixel, bg_current->pixel); + + set_bits = (Pixel) (fg_current->pixel & ~bg_current->pixel); + clr_bits = (Pixel) (bg_current->pixel & ~fg_current->pixel); + globals.gc.fore2 = NULL; + + if (resource.copy + || (set_bits && clr_bits && !resource.thorough)) { + if (!resource.copy) { + /* I used to get a warning here which I didn't get for + xdvi-22.64/events.c, l.1330, but I can't reproduce + it any more ... + */ + warn_overstrike(); + } + globals.gc.fore = set_or_make_gc(globals.gc.fore, GXcopy, fg_current->pixel, bg_current->pixel); + } + else { + if (set_bits) { + globals.gc.fore = set_or_make_gc(globals.gc.fore, GXor, set_bits, 0); + if (clr_bits) { + globals.gc.fore2 = globals.gc.fore2_bak1 = set_or_make_gc(globals.gc.fore2_bak1, GXandInverted, clr_bits, 0); + } + } + else + globals.gc.fore = set_or_make_gc(globals.gc.fore, GXandInverted, clr_bits, 0); + } + } + + fg_active = fg_current; +} + +#elif GREY /* if COLOR */ + +void +init_pix(void) +{ + static int shrink_allocated_for = 0; + static float oldgamma = 0.0; + static Pixel palette[17]; + int i; + + if (G_visual->class == TrueColor) { + /* This mirrors the non-grey code in xdvi.c */ + static int shift1_r, shift1_g, shift1_b; + static int shift2_r, shift2_g, shift2_b; + static Pixel set_bits; + static Pixel clr_bits; + unsigned int sf_squared; + + if (oldgamma == 0.0) { + mask_shifts(G_visual->red_mask, &shift1_r, &shift2_r); + mask_shifts(G_visual->green_mask, &shift1_g, &shift2_g); + mask_shifts(G_visual->blue_mask, &shift1_b, &shift2_b); + + set_bits = color_data[0].pixel & ~(color_data[1].pixel); + clr_bits = color_data[1].pixel & ~(color_data[0].pixel); + + if (set_bits & G_visual->red_mask) + set_bits |= G_visual->red_mask; + if (clr_bits & G_visual->red_mask) + clr_bits |= G_visual->red_mask; + if (set_bits & G_visual->green_mask) + set_bits |= G_visual->green_mask; + if (clr_bits & G_visual->green_mask) + clr_bits |= G_visual->green_mask; + if (set_bits & G_visual->blue_mask) + set_bits |= G_visual->blue_mask; + if (clr_bits & G_visual->blue_mask) + clr_bits |= G_visual->blue_mask; + + /* + * Make the GCs + */ + + globals.gc.fore = globals.gc.fore2 = globals.gc.rule = 0; + globals.gc.copy = set_or_make_gc(NULL, GXcopy, resource.fore_Pixel, resource.back_Pixel); + if (globals.gc.do_copy || (set_bits && clr_bits)) { + globals.gc.rule = globals.gc.copy; + if (!resource.thorough) + globals.gc.do_copy = True; + } + if (globals.gc.do_copy) { + globals.gc.fore = globals.gc.rule; + if (!resource.copy) { + warn_overstrike(); + } + } + else { + if (set_bits) { + globals.gc.fore = set_or_make_gc(NULL, GXor, set_bits & color_data[0].pixel, 0); + } + if (clr_bits || !set_bits) { +/* fprintf(stderr, "using GXandInverted!\n"); */ + *(globals.gc.fore ? &globals.gc.fore2 : &globals.gc.fore) = + set_or_make_gc(NULL, GXandInverted, clr_bits & ~(color_data[0].pixel), 0); + } + if (!globals.gc.rule) + globals.gc.rule = globals.gc.fore; + } + + oldgamma = resource.gamma; + } + + if (mane.shrinkfactor == 1) + return; + sf_squared = mane.shrinkfactor * mane.shrinkfactor; + + if (shrink_allocated_for < mane.shrinkfactor) { + if (pixeltbl != NULL) { + free((char *)pixeltbl); + if (pixeltbl_gc2 != NULL) + free((char *)pixeltbl_gc2); + } + pixeltbl = xmalloc((sf_squared + 1) * sizeof *pixeltbl); + shrink_allocated_for = mane.shrinkfactor; + if (globals.gc.fore2 != NULL) { + pixeltbl_gc2 = xmalloc((sf_squared + 1) * sizeof *pixeltbl_gc2); + } + } + + /* + * Initialize the pixel lookup table according to the gamma values. + */ +#define SHIFTIFY(x, shift1, shift2) ((((Pixel)(x)) >> (shift1)) << (shift2)) + + for (i = 0; i <= sf_squared; ++i) { + double frac = resource.gamma > 0 + ? pow((double)i / sf_squared, 1 / resource.gamma) + : 1 - pow((double)(sf_squared - i) / sf_squared, -resource.gamma); + unsigned int red, green, blue; + Pixel pixel; + + red = frac * ((double)color_data[0].red - color_data[1].red) + + color_data[1].red; + green = frac + * ((double)color_data[0].green - color_data[1].green) + + color_data[1].green; + blue = frac * ((double)color_data[0].blue - color_data[1].blue) + + color_data[1].blue; + + pixel = SHIFTIFY(red, shift1_r, shift2_r) | + SHIFTIFY(green, shift1_g, shift2_g) | + SHIFTIFY(blue, shift1_b, shift2_b); + + if (globals.gc.do_copy) { + pixeltbl[i] = pixel; + } + else if (globals.gc.fore2 != NULL) { /* if thorough */ + pixeltbl[i] = pixel & ~(color_data[1].pixel); + pixeltbl_gc2[i] = ~pixel & color_data[1].pixel; + } + else { + pixeltbl[i] = set_bits ? pixel & set_bits : ~pixel & clr_bits; + } + } + +#undef SHIFTIFY + + return; + } + + /* if not TrueColor ... */ + + if (resource.gamma != oldgamma) { + XColor color; + + for (i = 0; i < 16; ++i) { + double frac = resource.gamma > 0 + ? pow((double)i / 15, 1 / resource.gamma) + : 1 - pow((double)(15 - i) / 15, -resource.gamma); + + color.red = frac + * ((double)color_data[0].red - color_data[1].red) + + color_data[1].red; + color.green = frac + * ((double)color_data[0].green - color_data[1].green) + + color_data[1].green; + color.blue = frac + * ((double)color_data[0].blue - color_data[1].blue) + + color_data[1].blue; + + color.pixel = resource.back_Pixel; + color.flags = DoRed | DoGreen | DoBlue; + + if (!globals.gc.do_copy) { + if (i & 1) + color.pixel |= plane_masks[0]; + if (i & 2) + color.pixel |= plane_masks[1]; + if (i & 4) + color.pixel |= plane_masks[2]; + if (i & 8) + color.pixel |= plane_masks[3]; + XStoreColor(DISP, G_colormap, &color); + palette[i] = color.pixel; + } + else { + if (XAllocColor(DISP, G_colormap, &color)) + palette[i] = color.pixel; + else + palette[i] = (i * 100 >= resource.density * 15) + ? resource.fore_Pixel : resource.back_Pixel; + } + } + + globals.gc.copy = set_or_make_gc(NULL, GXcopy, resource.fore_Pixel, resource.back_Pixel); + globals.gc.rule = globals.gc.do_copy + ? globals.gc.copy + : set_or_make_gc(NULL, GXor, resource.fore_Pixel, resource.back_Pixel); + globals.gc.fore = globals.gc.rule; + globals.gc.fore2 = NULL; + oldgamma = resource.gamma; + } + + if (mane.shrinkfactor == 1) + return; + + if (shrink_allocated_for < mane.shrinkfactor) { + if (pixeltbl != NULL) + free((char *)pixeltbl); + pixeltbl = xmalloc((unsigned) + (mane.shrinkfactor * mane.shrinkfactor + 1) * sizeof *pixeltbl); + shrink_allocated_for = mane.shrinkfactor; + } + + for (i = 0; i <= mane.shrinkfactor * mane.shrinkfactor; ++i) { + pixeltbl[i] = palette[(i * 30 + mane.shrinkfactor * mane.shrinkfactor) + / (2 * mane.shrinkfactor * mane.shrinkfactor)]; + } +} + +#endif /* COLOR */ |