/* $Header$ This is dvipdfm, a DVI to PDF translator. Copyright (C) 1998, 1999 by Mark A. Wicks This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA The author may be contacted via the e-mail address mwicks@kettering.edu */ #include #include #include #include #include "system.h" #include "mem.h" #include "error.h" #include "mfileio.h" #include "numbers.h" #include "dvi.h" #include "dvicodes.h" #include "pdflimits.h" #include "pdfdev.h" #include "pdfdoc.h" #include "tfm.h" #include "vf.h" /* Interal Variables */ static FILE *dvi_file; static char debug = 0; static unsigned num_loaded_fonts = 0, max_loaded_fonts = 0, stackdepth; static unsigned long *page_loc = NULL; static unsigned long post_location, dvi_file_size; static UNSIGNED_PAIR num_pages = 0; static UNSIGNED_QUAD media_width, media_height; static UNSIGNED_QUAD dvi_unit_num, dvi_unit_den, dvi_mag; #define PHYSICAL 1 #define VIRTUAL 2 #define DVI 1 #define VF 2 struct loaded_font { int type; /* Type is physical or virtual */ int font_id; /* id returned by dev (for PHYSICAL fonts) or by vf module for (VIRTUAL fonts) */ int tfm_id; spt_t size; int source; /* Source is either DVI or VF */ } *loaded_fonts = NULL; static void need_more_fonts (unsigned n) { if (num_loaded_fonts+n > max_loaded_fonts) { max_loaded_fonts += MAX_FONTS; loaded_fonts = RENEW (loaded_fonts, max_loaded_fonts, struct loaded_font); } } struct font_def { spt_t size; char *name; signed long tex_id; int font_id; /* index of _loaded_ font in loaded_fonts array */ int used; } *def_fonts = NULL; static unsigned num_def_fonts = 0, max_def_fonts = 0; static unsigned char verbose = 0; static unsigned char compute_boxes = 0; void dvi_set_verbose(void) { if (verbose < 255) { verbose += 1; } } void dvi_compute_boxes (unsigned char boxes) { compute_boxes = boxes; } void dvi_set_debug(void) { debug = 1; } unsigned dvi_npages (void) { return num_pages; } static void invalid_signature() { ERROR ("dvi_init: Something is wrong. Are you sure this is a DVI file?\n"); } #define range_check_loc(loc) {if ((loc) > dvi_file_size) invalid_signature();} static void find_post (void) { long current; int read_byte; /* First find end of file */ dvi_file_size = file_size (dvi_file); current = dvi_file_size; /* Scan backwards through PADDING */ do { current -= 1; seek_absolute (dvi_file, current); } while ((read_byte = fgetc(dvi_file)) == PADDING && current > 0); /* file_position now points to last non padding character or beginning of file */ if (dvi_file_size - current < 4 || current == 0 || read_byte != DVI_ID) { fprintf (stderr, "DVI ID = %d\n", read_byte); invalid_signature(); } /* Make sure post_post is really there */ current = current - 5; seek_absolute (dvi_file, current); if ((read_byte = fgetc(dvi_file)) != POST_POST) { fprintf (stderr, "Found %d where post_post opcode should be\n", read_byte); invalid_signature(); } current = get_signed_quad (dvi_file); seek_absolute (dvi_file, current); if ((read_byte = fgetc(dvi_file)) != POST) { fprintf (stderr, "Found %d where post_post opcode should be\n", read_byte); invalid_signature(); } post_location = current; } static void get_page_info (void) { int i; seek_absolute (dvi_file, post_location+27); num_pages = get_unsigned_pair (dvi_file); if (verbose > 2) { fprintf (stderr, "Page count:\t %4d\n", num_pages); } if (num_pages == 0) { ERROR ("dvi_init: Page count is 0!"); } page_loc = NEW (num_pages, unsigned long); seek_absolute (dvi_file, post_location+1); page_loc[num_pages-1] = get_unsigned_quad(dvi_file); range_check_loc(page_loc[num_pages-1]+41); for (i=num_pages-2; i>=0; i--) { seek_absolute (dvi_file, page_loc[i+1]+41); page_loc[i] = get_unsigned_quad(dvi_file); range_check_loc(page_loc[num_pages-1]+41); } } /* Following are computed "constants" used for unit conversion */ static double dvi2pts = 0.0, total_mag = 1.0; double dvi_tell_mag (void) { return total_mag; } static void do_scales (double mag) { total_mag = (double) dvi_mag / 1000.0 * mag; dvi2pts = (double) dvi_unit_num / (double) dvi_unit_den; dvi2pts *= (72.0)/(254000.0); } static void get_dvi_info (void) { seek_absolute (dvi_file, post_location+5); dvi_unit_num = get_unsigned_quad(dvi_file); dvi_unit_den = get_unsigned_quad(dvi_file); dvi_mag = get_unsigned_quad(dvi_file); media_height = get_unsigned_quad(dvi_file); media_width = get_unsigned_quad(dvi_file); stackdepth = get_unsigned_pair(dvi_file); if (stackdepth > DVI_MAX_STACK_DEPTH) { fprintf (stderr, "DVI needs stack depth of %d,", stackdepth); fprintf (stderr, "but MAX_DVI_STACK_DEPTH is %d", DVI_MAX_STACK_DEPTH); ERROR ("Capacity exceeded."); } if (verbose > 2) { fprintf (stderr, "DVI File Info\n"); fprintf (stderr, "Unit: %ld / %ld\n", dvi_unit_num, dvi_unit_den); fprintf (stderr, "Mag: %ld\n", dvi_mag); fprintf (stderr, "Media Height: %ld\n", media_height); fprintf (stderr, "Media Width: %ld\n", media_width); fprintf (stderr, "Stack Depth: %d\n", stackdepth); } } static void dump_font_info (void) { unsigned i; fprintf (stderr, "\nDVI file font info\n"); for (i=0; i= max_def_fonts) { max_def_fonts += MAX_FONTS; def_fonts = RENEW (def_fonts, max_def_fonts, struct font_def); } checksum = get_unsigned_quad (dvi_file); size = get_unsigned_quad (dvi_file); design_size = get_unsigned_quad (dvi_file); dir_length = get_unsigned_byte (dvi_file); name_length = get_unsigned_byte (dvi_file); directory = NEW (dir_length+1, char); if (fread (directory, 1, dir_length, dvi_file) != dir_length) { invalid_signature(); } name = NEW (name_length+1, char); if (fread (name, 1, name_length, dvi_file) != name_length) { invalid_signature(); } directory[dir_length] = 0; RELEASE (directory); name[name_length] = 0; def_fonts[num_def_fonts].name = name; def_fonts[num_def_fonts].size = size; def_fonts[num_def_fonts].tex_id = tex_id; def_fonts[num_def_fonts].used = 0; num_def_fonts +=1; return; } static void get_dvi_fonts (void) { UNSIGNED_BYTE code; SIGNED_QUAD tex_id; seek_absolute (dvi_file, post_location+29); while ((code = get_unsigned_byte(dvi_file)) != POST_POST) { switch (code) { case FNT_DEF1: tex_id = get_unsigned_byte (dvi_file); break; case FNT_DEF2: tex_id = get_unsigned_pair (dvi_file); break; case FNT_DEF3: tex_id = get_unsigned_triple (dvi_file); break; case FNT_DEF4: tex_id = get_signed_quad (dvi_file); break; default: fprintf (stderr, "Unexpected op code: %3d\n", code); invalid_signature(); } get_a_font_record(tex_id); } if (verbose>2) { dump_font_info(); } } void get_comment(void) { UNSIGNED_BYTE length; static char dvi_comment[257]; seek_absolute (dvi_file, 14); length = get_unsigned_byte(dvi_file); if (fread (dvi_comment, 1, length, dvi_file) != length) { invalid_signature(); } dvi_comment[length] = 0; if (verbose) { fprintf (stderr, "DVI Comment: %s\n", dvi_comment); } dev_add_comment (dvi_comment); } /* The section below this line deals with the actual processing of the dvi file. The dvi file processor state is contained in the following variables: */ struct dvi_registers { SIGNED_QUAD h, v, w, x, y, z; }; static struct dvi_registers dvi_state; static struct dvi_registers dvi_stack[DVI_MAX_STACK_DEPTH]; static int current_font; static unsigned dvi_stack_depth = 0; static int processing_page = 0; static void clear_state (void) { dvi_state.h = 0; dvi_state.v = 0; dvi_state.w = 0; dvi_state.x = 0; dvi_state.y = 0; dvi_state.z = 0; dvi_stack_depth = 0; current_font = -1; } double dvi_unit_size(void) { return dvi2pts; } int dvi_locate_font (char *tex_name, spt_t ptsize) { int thisfont; int font_id; if (debug) { fprintf (stderr, "dvi_locate_font: fontname: (%s) ptsize: %ld, dvi_id: %d\n", tex_name, ptsize, num_loaded_fonts); } if (verbose) fprintf (stderr, "<%s@%.2fpt", tex_name, ptsize*dvi2pts); need_more_fonts (1); /* This routine needs to be recursive/reentrant. Load current high water mark into an automatic variable */ thisfont = num_loaded_fonts++; loaded_fonts[thisfont].tfm_id = tfm_open (tex_name); loaded_fonts[thisfont].source = VF; /* This will be reset later if it was really generated by the dvi file */ font_id = vf_locate_font (tex_name, ptsize); loaded_fonts[thisfont].font_id = font_id; if (loaded_fonts[thisfont].font_id >= 0) { if (verbose) fprintf (stderr, "(VF)"); loaded_fonts[thisfont].type = VIRTUAL; } else { loaded_fonts[thisfont].type = PHYSICAL; font_id = dev_locate_font (tex_name, ptsize); loaded_fonts[thisfont].font_id = font_id; if (loaded_fonts[thisfont].font_id < 0) { fprintf (stderr, "%s: Can't locate a Type 1, TTF, PK, or virtual font file\n", tex_name); ERROR ("Not sure how to proceed. For now this is fatal\n\ Maybe in the future, I'll substitute some other font."); } } loaded_fonts[thisfont].size = ptsize; if (verbose) fprintf (stderr, ">"); return (thisfont); } double dvi_dev_xpos(void) { return dvi_state.h*dvi2pts; } double dvi_dev_ypos (void) { return -(dvi_state.v*dvi2pts); } static void do_moveto (SIGNED_QUAD x, SIGNED_QUAD y) { dvi_state.h = x; dvi_state.v = y; } void dvi_right (SIGNED_QUAD x) { dvi_state.h += x; } void dvi_down (SIGNED_QUAD y) { dvi_state.v += y; } static void do_string (unsigned char *s, int len) { spt_t width = 0, height = 0, depth = 0; int i; struct loaded_font *p; if (debug) { int i; fprintf (stderr, "do_string: (font: %d)\n", current_font); for (i=0; itfm_id, s, len); width = sqxfw (p->size, width); switch (p->type) { case PHYSICAL: dev_set_string (dvi_state.h, -dvi_state.v, s, len, width, p->font_id); if (compute_boxes) { height = tfm_string_height (p->tfm_id, s, len); depth = tfm_string_depth (p->tfm_id, s, len); height = sqxfw (p->size, height); depth = sqxfw (p->size, depth); dev_expand_box (width, height, depth); } break; case VIRTUAL: dvi_push(); for (i=0; itfm_id, ch); width = sqxfw (p->size, width); switch (p->type) { case PHYSICAL: if (ch > 255) { ERROR ("Tried to set a multibyte character in a non-virtual font"); } lch = (unsigned char) ch; dev_set_string (dvi_state.h, -dvi_state.v, &lch, 1, width, p->font_id); if (compute_boxes) { height = tfm_get_fw_height (p->tfm_id, ch); depth = tfm_get_fw_depth (p->tfm_id, ch); height = sqxfw (p->size, height); depth = sqxfw (p->size, depth); dev_expand_box (width, height, depth); } break; case VIRTUAL: { vf_set_char (ch, p->font_id); break; } } dvi_state.h += width; } void dvi_put (SIGNED_QUAD ch) { spt_t width, height = 0, depth = 0; struct loaded_font *p; unsigned char lch; if (current_font < 0) { ERROR ("dvi_put: No font selected"); } p = loaded_fonts+current_font; switch (p->type) { case PHYSICAL: width = tfm_get_fw_width (p->tfm_id, ch); width = sqxfw (p->size, width); /* Treat a single character as a one byte string and use the string routine. The possibly multi-byte character must be converted to a single-byte string */ if (ch > 255) { ERROR ("Tried to set a multibyte character in a non-virtual font"); } lch = (unsigned char) ch; dev_set_string (dvi_state.h, -dvi_state.v, &lch, 1, width, p->font_id); if (compute_boxes) { height = tfm_get_fw_height (p->tfm_id, ch); depth = tfm_get_fw_depth (p->tfm_id, ch); height = sqxfw (p->size, height); depth = sqxfw (p->size, depth); dev_expand_box (width, height, depth); } break; case VIRTUAL: vf_set_char (ch, p->font_id); break; } return; } void dvi_rule (SIGNED_QUAD width, SIGNED_QUAD height) { do_moveto (dvi_state.h, dvi_state.v); dev_rule (dvi_state.h, -dvi_state.v, width, height); } static void do_set1(void) { dvi_set (get_unsigned_byte(dvi_file)); } static void do_set2(void) { dvi_set (get_unsigned_pair(dvi_file)); } static void do_setrule(void) { SIGNED_QUAD width, height; height = get_signed_quad (dvi_file); width = get_signed_quad (dvi_file); if (width > 0 && height > 0) { dvi_rule (width, height); } dvi_right (width); } static void do_putrule(void) { SIGNED_QUAD width, height; height = get_signed_quad (dvi_file); width = get_signed_quad (dvi_file); if (width > 0 && height > 0) { dvi_rule (width, height); } } static void do_put1(void) { dvi_put (get_unsigned_byte(dvi_file)); } static void do_put2(void) { dvi_put (get_unsigned_pair(dvi_file)); } void dvi_push (void) { if (debug) { fprintf (stderr, "Pushing onto stack of depth %d\n", dvi_stack_depth); } if (dvi_stack_depth < DVI_MAX_STACK_DEPTH) { dvi_stack[dvi_stack_depth++] = dvi_state; } else ERROR ("DVI stack exceeded"); } void dvi_pop (void) { if (debug) { fprintf (stderr, "Popping off stack of depth %d\n", dvi_stack_depth); } if (dvi_stack_depth > 0) { dvi_state = dvi_stack[--dvi_stack_depth]; } else ERROR ("dvi_pop: Tried to pop an empty stack"); do_moveto (dvi_state.h, dvi_state.v); } static void do_right1(void) { dvi_right (get_signed_byte(dvi_file)); } static void do_right2(void) { dvi_right (get_signed_pair(dvi_file)); } static void do_right3(void) { dvi_right (get_signed_triple(dvi_file)); } static void do_right4(void) { dvi_right (get_signed_quad(dvi_file)); } void dvi_w (SIGNED_QUAD ch) { dvi_state.w = ch; dvi_right (ch); } void dvi_w0(void) { dvi_right (dvi_state.w); } static void do_w1(void) { dvi_w (get_signed_byte(dvi_file)); } static void do_w2(void) { dvi_w (get_signed_pair(dvi_file)); } static void do_w3(void) { dvi_w (get_signed_triple(dvi_file)); } static void do_w4(void) { dvi_w (get_signed_quad(dvi_file)); } void dvi_x (SIGNED_QUAD ch) { dvi_state.x = ch; dvi_right (ch); } void dvi_x0(void) { dvi_right (dvi_state.x); } static void do_x1(void) { dvi_x (get_signed_byte(dvi_file)); } static void do_x2(void) { dvi_x (get_signed_pair(dvi_file)); } static void do_x3(void) { dvi_x (get_signed_triple(dvi_file)); } static void do_x4(void) { dvi_x (get_signed_quad(dvi_file)); } static void do_down1(void) { dvi_down (get_signed_byte(dvi_file)); } static void do_down2(void) { dvi_down (get_signed_pair(dvi_file)); } static void do_down3(void) { dvi_down (get_signed_triple(dvi_file)); } static void do_down4(void) { dvi_down (get_signed_quad(dvi_file)); } void dvi_y (SIGNED_QUAD ch) { dvi_state.y = ch; dvi_down (ch); } void dvi_y0(void) { dvi_down (dvi_state.y); } static void do_y1(void) { dvi_y (get_signed_byte(dvi_file)); } static void do_y2(void) { dvi_y (get_signed_pair(dvi_file)); } static void do_y3(void) { dvi_y (get_signed_triple(dvi_file)); } static void do_y4(void) { dvi_y (get_signed_quad(dvi_file)); } void dvi_z (SIGNED_QUAD ch) { dvi_state.z = ch; dvi_down (ch); } void dvi_z0(void) { dvi_down (dvi_state.z); } static void do_z1(void) { dvi_z (get_signed_byte(dvi_file)); } static void do_z2(void) { dvi_z (get_signed_pair(dvi_file)); } static void do_z3(void) { dvi_z (get_signed_triple(dvi_file)); } static void do_z4(void) { dvi_z (get_signed_quad(dvi_file)); } static void do_fntdef(void) { int area_len, name_len, i; get_signed_quad(dvi_file); get_signed_quad(dvi_file); get_signed_quad(dvi_file); area_len = get_unsigned_byte(dvi_file); name_len = get_unsigned_byte(dvi_file); for (i=0; i 0) { do_string (s_buffer, s_len); } if (s_len == S_BUFFER_SIZE) continue; /* If we are here, we have an opcode that is something other than SET_CHAR */ if (opcode >= FNT_NUM_0 && opcode <= FNT_NUM_63) { do_fnt (opcode - FNT_NUM_0); continue; } switch (opcode) { case SET1: do_set1(); break; case SET2: do_set2(); break; case SET3: case SET4: ERROR ("Multibyte (>16 bits) character in DVI file. I can't handle this!"); break; case SET_RULE: do_setrule(); break; case PUT1: do_put1(); break; case PUT2: do_put2(); break; case PUT3: case PUT4: ERROR ("Multibyte character (>16 bits) in DVI file. I can't handle this!"); break; case PUT_RULE: do_putrule(); break; case NOP: break; case BOP: do_bop(); break; case EOP: do_eop(); return; case PUSH: dvi_push(); /* The following line needs to go here instead of in dvi_push() since logical structure of document is oblivous to virtual fonts. For example the last line on a page could be at stack level 3 and the page footer should be at stack level 3. However, if the page footer contains virtual fonts (or other nested constructions), it could fool the link breaker into thinking it was a continuation of the link */ dev_stack_depth (dvi_stack_depth); break; case POP: dvi_pop(); /* Above explanation holds for following line too */ dev_stack_depth (dvi_stack_depth); break; case RIGHT1: do_right1(); break; case RIGHT2: do_right2(); break; case RIGHT3: do_right3(); break; case RIGHT4: do_right4(); break; case W0: dvi_w0(); break; case W1: do_w1(); break; case W2: do_w2(); break; case W3: do_w3(); break; case W4: do_w4(); break; case X0: dvi_x0(); break; case X1: do_x1(); break; case X2: do_x2(); break; case X3: do_x3(); break; case X4: do_x4(); break; case DOWN1: do_down1(); break; case DOWN2: do_down2(); break; case DOWN3: do_down3(); break; case DOWN4: do_down4(); break; case Y0: dvi_y0(); break; case Y1: do_y1(); break; case Y2: do_y2(); break; case Y3: do_y3(); break; case Y4: do_y4(); break; case Z0: dvi_z0(); break; case Z1: do_z1(); break; case Z2: do_z2(); break; case Z3: do_z3(); break; case Z4: do_z4(); break; case FNT1: do_fnt1(); break; case FNT2: do_fnt2(); break; case FNT3: do_fnt3(); break; case FNT4: do_fnt4(); break; case XXX1: do_xxx1(); break; case XXX2: do_xxx2(); break; case XXX3: do_xxx3(); break; case XXX4: do_xxx4(); break; case FNT_DEF1: do_fntdef1(); break; case FNT_DEF2: do_fntdef2(); break; case FNT_DEF3: do_fntdef3(); break; case FNT_DEF4: do_fntdef4(); break; case PRE: case POST: case POST_POST: ERROR("Unexpected preamble or postamble in dvi file"); break; default: ERROR("Unexpected opcode or DVI file ended prematurely"); } } } error_t dvi_init (char *dvi_filename, char *pdf_filename, double mag, double x_offset, double y_offset) { if (!(dvi_file = MFOPEN (dvi_filename, FOPEN_RBIN_MODE))) { ERROR ("dvi_init: Specified DVI file doesn't exist"); return (FATAL_ERROR); } /* DVI files are most easily read backwards by searching for post_post and then post opcode */ find_post (); get_dvi_info(); do_scales(mag); dev_init(dvi2pts, x_offset, y_offset); get_page_info(); pdf_doc_init (pdf_filename); get_comment(); get_dvi_fonts(); clear_state(); return (NO_ERROR); } void dvi_close (void) { int i; /* We add comment in dvi_close instead of dvi_init so user has a change to overwrite it. The docinfo dictionary is treated as a write-once record */ /* Do some house cleaning */ MFCLOSE (dvi_file); for (i=0; i 0) current_font = saved_dvi_font[--num_saved_fonts]; else ERROR ("Tried to pop an empty font stack"); }