diff options
author | Karl Berry <karl@freefriends.org> | 2021-02-25 19:22:25 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2021-02-25 19:22:25 +0000 |
commit | ad547a6b5986815fda458221149728d9d9ab1d87 (patch) | |
tree | 16296910eb3eca724371474ea9aea3994dc69614 /Build/source/texk/dvipdfm-x/dvi.c | |
parent | 947b43de3dd21d58ccc2ffadefc4441ea1c2a813 (diff) |
restore Build,TODO from r57911
git-svn-id: svn://tug.org/texlive/trunk@57915 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/dvipdfm-x/dvi.c')
-rw-r--r-- | Build/source/texk/dvipdfm-x/dvi.c | 2884 |
1 files changed, 2884 insertions, 0 deletions
diff --git a/Build/source/texk/dvipdfm-x/dvi.c b/Build/source/texk/dvipdfm-x/dvi.c new file mode 100644 index 00000000000..473c52e8c3c --- /dev/null +++ b/Build/source/texk/dvipdfm-x/dvi.c @@ -0,0 +1,2884 @@ +/* This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks. + + Copyright (C) 2002-2021 by Jin-Hwan Cho and Shunsaku Hirata, + the dvipdfmx project team. + + Copyright (C) 2012-2015 by Khaled Hosny <khaledhosny@eglug.org> + + Copyright (C) 1998, 1999 by Mark A. Wicks <mwicks@kettering.edu> + + 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. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> + +#ifdef WIN32 +#include <fcntl.h> +#include <io.h> +#endif + +#include "system.h" +#include "mem.h" +#include "error.h" +#include "mfileio.h" +#include "numbers.h" +#include "dpxconf.h" +#include "dpxutil.h" + +#include "pdfdev.h" +#include "pdfdoc.h" +#include "pdfparse.h" +#include "pdfresource.h" +#include "pdfdraw.h" + +#include "fontmap.h" + +#include "dvicodes.h" +#include "tfm.h" +#include "vf.h" +#include "subfont.h" + +#include "spc_util.h" +#include "specials.h" + +#include "dvi.h" +#include "dvipdfmx.h" + +#include "dpxfile.h" +#include "pdfximage.h" +#include "tt_aux.h" +#include "tt_table.h" +#include "t1_load.h" +#include "t1_char.h" +#include "cff_dict.h" + +#define DVI_STACK_DEPTH_MAX 256u +#define TEX_FONTS_ALLOC_SIZE 16u +#define VF_NESTING_MAX 16u + +/* UTF-32 over U+FFFF -> UTF-16 surrogate pair */ +#define UTF32toUTF16HS(x) (0xd800 + (((x-0x10000) >> 10) & 0x3ff)) +#define UTF32toUTF16LS(x) (0xdc00 + ( x & 0x3ff)) + +/* Interal Variables */ +static FILE *dvi_file = NULL; +static char linear = 0; /* set to 1 for strict linear processing of the input */ + +static int32_t *page_loc = NULL; +static unsigned int num_pages = 0; + +static uint32_t dvi_file_size = 0; + +static struct dvi_header +{ + uint32_t unit_num; + uint32_t unit_den; + uint32_t mag; + uint32_t media_width, media_height; + unsigned int stackdepth; + char comment[257]; +} dvi_info = { + 25400000 , /* num */ + 473628672, /* den */ + 1000, /* mag */ + 0, 0, /* media width and height */ + 0, /* stackdepth */ + {'\0'} /* comment */ +}; + +static double dev_origin_x = 72.0, dev_origin_y = 770.0; + +double get_origin (int x) +{ + return x ? dev_origin_x : dev_origin_y; +} + +#define LTYPESETTING 0 /* typesetting from left to right */ +#define RTYPESETTING 1 /* typesetting from right to left */ +#define SKIMMING 2 /* skimming through reflected segment measuring its width */ +#define REVERSE(MODE) (LTYPESETTING + RTYPESETTING - MODE) + +struct dvi_lr +{ + int state, font; + unsigned int buf_index; +}; + +static struct dvi_lr lr_state; /* state at start of current skimming */ +static int lr_mode; /* current direction or skimming depth */ +static uint32_t lr_width; /* total width of reflected segment */ +static uint32_t lr_width_stack[DVI_STACK_DEPTH_MAX]; +static unsigned lr_width_stack_depth = 0; + +#define PHYSICAL 1 +#define VIRTUAL 2 +#define SUBFONT 3 +#define NATIVE 4 +#define DVI 1 +#define VF 2 + +struct gm { + spt_t advance, ascent, descent; +}; + +static 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 subfont_id; /* id returned by subfont_locate_font() */ + int tfm_id; + spt_t size; + int source; /* Source is either DVI or VF */ + uint32_t rgba_color; + uint8_t rgba_used; + /* Indicates that rgba_color is used or not. + * It enables full range of opacity: 0-255. + */ + int xgs_id; /* Transparency ExtGState */ + + /* XeTeX requires glyph metrics */ + struct gm *gm; + int shift_gid; + uint16_t num_glyphs; + + int layout_dir; + + float extend; + float slant; + float embolden; + + int is_unicode; + int minbytes; + char padbytes[4]; +} *loaded_fonts = NULL; +static int num_loaded_fonts = 0, max_loaded_fonts = 0; + +static void +need_more_fonts (unsigned n) +{ + if (num_loaded_fonts + n > max_loaded_fonts) { + max_loaded_fonts += TEX_FONTS_ALLOC_SIZE; + loaded_fonts = RENEW (loaded_fonts, max_loaded_fonts, struct loaded_font); + } +} + +static struct font_def +{ + int32_t tex_id; + spt_t point_size; + spt_t design_size; + char *font_name; + int font_id; /* index of _loaded_ font in loaded_fonts array */ + int used; + int native; /* boolean */ + uint32_t rgba_color; /* only used for native fonts in XeTeX */ + uint8_t rgba_used; /* Indicates that rgba_color is used or not. + * It enables full range of opacity: 0-255. + */ + uint32_t face_index; + int layout_dir; /* 1 = vertical, 0 = horizontal */ + int extend; + int slant; + int embolden; +} *def_fonts = NULL; + +#define XDV_FLAG_VERTICAL 0x0100 +#define XDV_FLAG_COLORED 0x0200 +#define XDV_FLAG_FEATURES 0x0400 +#define XDV_FLAG_EXTEND 0x1000 +#define XDV_FLAG_SLANT 0x2000 +#define XDV_FLAG_EMBOLDEN 0x4000 + +static int num_def_fonts = 0, max_def_fonts = 0; +static int compute_boxes = 0, link_annot = 1; +/* If "catch_phantom" is non-zero, dvipdfmx try to catch phantom texts. + * Dvipdfmx treats DVI horizontal movement instructions (x, w, right) differently + * when "catch_phantom" is non-zero. Amount of horizontal move will be added to + * the current annotation rectangle with "height" and "depth" estimated as, + * + * catch_phantom=1: with current font size + * catch_phantom=2: with specified height and depth + */ +static int catch_phantom = 0; +static double phantom_height = 0.0; +static double phantom_depth = 0.0; + +#define DVI_PAGE_BUF_CHUNK 0x10000U /* 64K should be plenty for most pages */ + +static unsigned char* dvi_page_buffer; +static unsigned int dvi_page_buf_size; +static unsigned int dvi_page_buf_index; + +/* functions to read numbers from the dvi file and store them in dvi_page_buffer */ +static int get_and_buffer_unsigned_byte (FILE *file) +{ + int ch; + if ((ch = fgetc (file)) < 0) + ERROR ("File ended prematurely\n"); + if (dvi_page_buf_index >= dvi_page_buf_size) { + dvi_page_buf_size += DVI_PAGE_BUF_CHUNK; + dvi_page_buffer = RENEW(dvi_page_buffer, dvi_page_buf_size, unsigned char); + } + dvi_page_buffer[dvi_page_buf_index++] = ch; + return ch; +} + +static unsigned int get_and_buffer_unsigned_pair (FILE *file) +{ + unsigned int pair = get_and_buffer_unsigned_byte(file); + pair = (pair << 8) | get_and_buffer_unsigned_byte(file); + return pair; +} + +static void get_and_buffer_bytes(FILE *file, unsigned int count) +{ + if (dvi_page_buf_index + count >= dvi_page_buf_size) { + dvi_page_buf_size = dvi_page_buf_index + count + DVI_PAGE_BUF_CHUNK; + dvi_page_buffer = RENEW(dvi_page_buffer, dvi_page_buf_size, unsigned char); + } + if (fread(dvi_page_buffer + dvi_page_buf_index, 1, count, file) != count) + ERROR ("File ended prematurely\n"); + dvi_page_buf_index += count; +} + +/* functions to fetch values from dvi_page_buffer */ + +static int get_buffered_unsigned_byte (void) +{ + return dvi_page_buffer[dvi_page_buf_index++]; +} + +static unsigned int get_buffered_unsigned_pair (void) +{ + unsigned int pair = dvi_page_buffer[dvi_page_buf_index++]; + pair = (pair << 8) | dvi_page_buffer[dvi_page_buf_index++]; + return pair; +} + +static int32_t get_buffered_signed_quad(void) +{ + int i; + int32_t quad = dvi_page_buffer[dvi_page_buf_index++]; + /* Check sign on first byte before reading others */ + if (quad >= 0x80) + quad -= 0x100; + for (i=0; i<3; i++) { + quad = (quad << 8) | dvi_page_buffer[dvi_page_buf_index++]; + } + return quad; +} + +static int32_t get_buffered_signed_num(unsigned char num) +{ + int32_t quad = dvi_page_buffer[dvi_page_buf_index++]; + if (quad > 0x7f) + quad -= 0x100; + switch (num) { + case 3: quad = (quad << 8) | dvi_page_buffer[dvi_page_buf_index++]; + case 2: quad = (quad << 8) | dvi_page_buffer[dvi_page_buf_index++]; + case 1: quad = (quad << 8) | dvi_page_buffer[dvi_page_buf_index++]; + default: break; + } + return quad; +} + +static int32_t get_buffered_unsigned_num(unsigned char num) +{ + int32_t quad = dvi_page_buffer[dvi_page_buf_index++]; + switch (num) { + case 3: if (quad > 0x7f) + quad -= 0x100; + quad = (quad << 8) | dvi_page_buffer[dvi_page_buf_index++]; + case 2: quad = (quad << 8) | dvi_page_buffer[dvi_page_buf_index++]; + case 1: quad = (quad << 8) | dvi_page_buffer[dvi_page_buf_index++]; + default: break; + } + return quad; +} + +#define skip_bufferd_bytes(n) dvi_page_buf_index += n + +unsigned int +dvi_npages (void) +{ + return num_pages; +} + +static const char invalid_signature[] = +"Something is wrong. Are you sure this is a DVI file?"; + +#define range_check_loc(loc) \ + if ((loc) > dvi_file_size) {\ + ERROR(invalid_signature); \ + } + +static int pre_id_byte, post_id_byte, is_ptex = 0, has_ptex = 0; + +static void +check_id_bytes (void) { + if (pre_id_byte != post_id_byte && (pre_id_byte != DVI_ID || post_id_byte != DVIV_ID)) + ERROR ("Inconsistent DVI id_bytes %d (pre) and %d (post)", pre_id_byte, post_id_byte); +} + +static void +need_XeTeX (int c) { + if (dpx_conf.compat_mode != dpx_mode_xdv_mode) + ERROR ("DVI opcode %i only valid for XeTeX", c); +} + +static void +need_pTeX (int c) { + if (!is_ptex) + ERROR ("DVI opcode %i only valid for Ascii pTeX", c); + has_ptex = 1; +} + +static int32_t +find_post (void) +{ + off_t dvi_size; + int32_t current; + int ch; + + /* First find end of file */ + dvi_size = xfile_size (dvi_file, "DVI"); + if (dvi_size > 0x7fffffff) + ERROR("DVI file size exceeds 31-bit"); + dvi_file_size = dvi_size; + current = dvi_size; + + /* Scan backwards through PADDING */ + do { + xseek_absolute (dvi_file, --current, "DVI"); + } while ((ch = 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 || + !(ch == DVI_ID || ch == DVIV_ID || ch == XDV_ID || ch == XDV_ID_OLD)) { + MESG("DVI ID = %d\n", ch); + ERROR(invalid_signature); + } + + post_id_byte = ch; + if (ch == XDV_ID || ch == XDV_ID_OLD) + dpx_conf.compat_mode = dpx_mode_xdv_mode; + is_ptex = ch == DVIV_ID; + + /* Make sure post_post is really there */ + current = current - 5; + xseek_absolute (dvi_file, current, "DVI"); + if ((ch = fgetc(dvi_file)) != POST_POST) { + MESG("Found %d where post_post opcode should be\n", ch); + ERROR(invalid_signature); + } + current = get_signed_quad(dvi_file); + xseek_absolute (dvi_file, current, "DVI"); + if ((ch = fgetc(dvi_file)) != POST) { + MESG("Found %d where post_post opcode should be\n", ch); + ERROR(invalid_signature); + } + + /* Finally check the ID byte in the preamble */ + /* An Ascii pTeX DVI file has id_byte DVI_ID in the preamble but DVIV_ID in the postamble. */ + xseek_absolute (dvi_file, 0, "DVI"); + if ((ch = get_unsigned_byte(dvi_file)) != PRE) { + MESG("Found %d where PRE was expected\n", ch); + ERROR(invalid_signature); + } + ch = get_unsigned_byte(dvi_file); + if (!(ch == DVI_ID || ch == XDV_ID || ch == XDV_ID_OLD)) { + MESG("DVI ID = %d\n", ch); + ERROR(invalid_signature); + } + pre_id_byte = ch; + check_id_bytes(); + + return current; +} + +static void +get_page_info (int32_t post_location) +{ + int i; + + xseek_absolute (dvi_file, post_location + 27, "DVI"); + num_pages = get_unsigned_pair(dvi_file); + if (num_pages == 0) { + ERROR("Page count is 0!"); + } + if (dpx_conf.verbose_level > 2) { + MESG("Page count:\t %4d\n", num_pages); + } + + page_loc = NEW(num_pages, int32_t); + + xseek_absolute (dvi_file, post_location + 1, "DVI"); + 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--) { + xseek_absolute (dvi_file, page_loc[i+1] + 41, "DVI"); + 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 = 1.52018, total_mag = 1.0; + +double +dvi_tell_mag (void) +{ + return total_mag; +} + +static void +do_scales (double mag) +{ + total_mag = (double) dvi_info.mag / 1000.0 * mag; + dvi2pts = (double) dvi_info.unit_num / (double) dvi_info.unit_den; + dvi2pts *= (72.0 / 254000.0); +} + +static void +get_dvi_info (int32_t post_location) +{ + xseek_absolute (dvi_file, post_location + 5, "DVI"); + + dvi_info.unit_num = get_unsigned_quad(dvi_file); + dvi_info.unit_den = get_unsigned_quad(dvi_file); + dvi_info.mag = get_unsigned_quad(dvi_file); + + dvi_info.media_height = get_unsigned_quad(dvi_file); + dvi_info.media_width = get_unsigned_quad(dvi_file); + + dvi_info.stackdepth = get_unsigned_pair(dvi_file); + + if (dvi_info.stackdepth > DVI_STACK_DEPTH_MAX) { + WARN("DVI need stack depth of %d,", dvi_info.stackdepth); + WARN("but DVI_STACK_DEPTH_MAX is %d.", DVI_STACK_DEPTH_MAX); + ERROR("Capacity exceeded."); + } + + if (dpx_conf.verbose_level > 2) { + MESG("DVI File Info\n"); + MESG("Unit: %ld / %ld\n", dvi_info.unit_num, dvi_info.unit_den); + MESG("Magnification: %ld\n", dvi_info.mag); + MESG("Media Height: %ld\n", dvi_info.media_height); + MESG("Media Width: %ld\n", dvi_info.media_width); + MESG("Stack Depth: %d\n", dvi_info.stackdepth); + } +} + +static void +get_preamble_dvi_info (void) +{ + int ch; + + ch = get_unsigned_byte(dvi_file); + if (ch != PRE) { + MESG("Found %d where PRE was expected\n", ch); + ERROR(invalid_signature); + } + + /* An Ascii pTeX DVI file has id_byte DVI_ID in the preamble but DVIV_ID in the postamble. */ + ch = get_unsigned_byte(dvi_file); + if (!(ch == DVI_ID || ch == XDV_ID || ch == XDV_ID_OLD)) { + MESG("DVI ID = %d\n", ch); + ERROR(invalid_signature); + } + + pre_id_byte = ch; + if (ch == XDV_ID || ch == XDV_ID_OLD) + dpx_conf.compat_mode = dpx_mode_xdv_mode; + is_ptex = ch == DVI_ID; /* maybe */ + + dvi_info.unit_num = get_positive_quad(dvi_file, "DVI", "unit_num"); + dvi_info.unit_den = get_positive_quad(dvi_file, "DVI", "unit_den"); + dvi_info.mag = get_positive_quad(dvi_file, "DVI", "mag"); + + ch = get_unsigned_byte(dvi_file); + if (fread(dvi_info.comment, + 1, ch, dvi_file) != ch) { + ERROR(invalid_signature); + } + dvi_info.comment[ch] = '\0'; + + if (dpx_conf.verbose_level > 2) { + MESG("DVI File Info\n"); + MESG("Unit: %ld / %ld\n", dvi_info.unit_num, dvi_info.unit_den); + MESG("Magnification: %ld\n", dvi_info.mag); + } + + if (dpx_conf.verbose_level > 0) { + MESG("DVI Comment: %s\n", dvi_info.comment); + } + + num_pages = 0x7FFFFFFU; /* for linear processing: we just keep going! */ +} + +const char * +dvi_comment (void) +{ + return dvi_info.comment; +} + +static void +proc_dvilua_font_record (int32_t tex_id, const char *font_name, uint32_t point_size, uint32_t design_size) +{ + char *file_name, *p, *q, *endptr; + uint32_t index = 0; + uint32_t embolden = 0; + int32_t slant = 0, extend = 0x00010000; + + ASSERT(font_name[0] == '['); + + /* redundant check but leave this here anyway */ + if (num_def_fonts >= max_def_fonts) { + max_def_fonts += TEX_FONTS_ALLOC_SIZE; + def_fonts = RENEW (def_fonts, max_def_fonts, struct font_def); + } + + file_name = NEW(strlen(font_name) + 1, char); + strcpy(file_name, font_name + 1); + endptr = file_name + strlen(file_name); + q = strchr(file_name, ']'); + if (q == NULL) { + ERROR("Syntax error in dvilua fnt_def: no ']' found in font name."); + } + *q = '\0'; + + p = q + 1; + if (p < endptr && *p == ':') { + for (p++; *p != '\0' && p < endptr; ) { + char *kvsep, *delim; + + delim = strchr(p, ';'); + kvsep = strchr(p, '='); + if (delim == NULL) + delim = endptr; + if (kvsep == NULL || kvsep >= delim) { + ERROR("Syntax error in dvilua fnt_def: not in key=value format: %s", font_name); + } + *kvsep = '\0'; + if (!strcmp(p, "index")) { + uint32_t value = strtoul(kvsep + 1, &q, 10); + if (q != delim) { + WARN("Syntax error in dvilua fnt_def: invalid value specified for \"%s\": %s", p, font_name); + } else { + index = value; + } + } else if (!strcmp(p, "embolden")) { + int32_t value = strtol(kvsep + 1, &q, 10); + if (q != delim) { + WARN("Syntax error in dvilua fnt_def: invalid value specified for \"%s\": %s", p, font_name); + } else { + embolden = value; + } + } else if (!strcmp(p, "slant")) { + int32_t value = strtol(kvsep + 1, &q, 10); + if (q != delim) { + WARN("Syntax error in dvilua fnt_def: invalid value specified for \"%s\": %s", p, font_name); + } else { + slant = value; + } + } else if (!strcmp(p, "extend")) { + int32_t value = strtol(kvsep + 1, &q, 10); + if (q != delim) { + WARN("Syntax error in dvilua fnt_def: invalid value specified for \"%s\": %s", p, font_name); + } else { + extend = value; + } + } else { + WARN("Ignoring unrecognized/unsupported key \"%s\" in dvilua fnt_def: %s", p, font_name); + } + p = delim + 1; + } + } + + def_fonts[num_def_fonts].tex_id = tex_id; + def_fonts[num_def_fonts].font_name = file_name; + def_fonts[num_def_fonts].face_index = index; + def_fonts[num_def_fonts].point_size = point_size; + def_fonts[num_def_fonts].design_size = design_size; + def_fonts[num_def_fonts].used = 0; + def_fonts[num_def_fonts].native = 1; + + def_fonts[num_def_fonts].layout_dir = 0; + def_fonts[num_def_fonts].rgba_color = 0xffffffff; + def_fonts[num_def_fonts].rgba_used = 0; + def_fonts[num_def_fonts].extend = extend; + def_fonts[num_def_fonts].slant = slant; + def_fonts[num_def_fonts].embolden = embolden; + + num_def_fonts++; + + return; +} + +#define SIG_DVILUA_FNT_DEF ('L' << 24|'u' << 16|'a' << 8|'F') + +static void +read_font_record (int32_t tex_id) +{ + int dir_length, name_length; + uint32_t checksum; + uint32_t point_size, design_size; + char *directory, *font_name; + + if (num_def_fonts >= max_def_fonts) { + max_def_fonts += TEX_FONTS_ALLOC_SIZE; + def_fonts = RENEW (def_fonts, max_def_fonts, struct font_def); + } + checksum = get_unsigned_quad(dvi_file); + point_size = get_positive_quad(dvi_file, "DVI", "point_size"); + design_size = get_positive_quad(dvi_file, "DVI", "design_size"); + 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) { + ERROR(invalid_signature); + } + directory[dir_length] = '\0'; + RELEASE(directory); /* unused */ + + font_name = NEW(name_length + 1, char); + if (fread(font_name, 1, name_length, dvi_file) != name_length) { + ERROR(invalid_signature); + } + font_name[name_length] = '\0'; + + if (checksum == SIG_DVILUA_FNT_DEF && name_length > 0 && font_name[0] == '[') { + proc_dvilua_font_record(tex_id, font_name, point_size, design_size); + RELEASE(font_name); + return; + } + def_fonts[num_def_fonts].tex_id = tex_id; + def_fonts[num_def_fonts].font_name = font_name; + def_fonts[num_def_fonts].point_size = point_size; + def_fonts[num_def_fonts].design_size = design_size; + def_fonts[num_def_fonts].used = 0; + def_fonts[num_def_fonts].native = 0; + def_fonts[num_def_fonts].rgba_color = 0xffffffff; + def_fonts[num_def_fonts].rgba_used = 0; + def_fonts[num_def_fonts].face_index = 0; + def_fonts[num_def_fonts].layout_dir = 0; + def_fonts[num_def_fonts].extend = 0x00010000; /* 1.0 */ + def_fonts[num_def_fonts].slant = 0; + def_fonts[num_def_fonts].embolden = 0; + num_def_fonts++; + + return; +} + +static void +read_native_font_record (int32_t tex_id) +{ + unsigned int flags; + uint32_t point_size; + char *font_name; + int len; + uint32_t index; + + if (num_def_fonts >= max_def_fonts) { + max_def_fonts += TEX_FONTS_ALLOC_SIZE; + def_fonts = RENEW (def_fonts, max_def_fonts, struct font_def); + } + point_size = get_positive_quad(dvi_file, "DVI", "point_size"); + flags = get_unsigned_pair(dvi_file); + + len = (int) get_unsigned_byte(dvi_file); /* font name length */ + font_name = NEW(len + 1, char); + if (fread(font_name, 1, len, dvi_file) != len) { + ERROR(invalid_signature); + } + font_name[len] = '\0'; + + index = get_positive_quad(dvi_file, "DVI", "index"); + + def_fonts[num_def_fonts].tex_id = tex_id; + def_fonts[num_def_fonts].font_name = font_name; + def_fonts[num_def_fonts].face_index = index; + def_fonts[num_def_fonts].point_size = point_size; + def_fonts[num_def_fonts].design_size = 655360; /* hard-code as 10pt for now, not used anyway */ + def_fonts[num_def_fonts].used = 0; + def_fonts[num_def_fonts].native = 1; + + def_fonts[num_def_fonts].layout_dir = 0; + def_fonts[num_def_fonts].rgba_color = 0xffffffff; + def_fonts[num_def_fonts].rgba_used = 0; + def_fonts[num_def_fonts].extend = 0x00010000; + def_fonts[num_def_fonts].slant = 0; + def_fonts[num_def_fonts].embolden = 0; + + if (flags & XDV_FLAG_VERTICAL) + def_fonts[num_def_fonts].layout_dir = 1; + + if (flags & XDV_FLAG_COLORED) { + def_fonts[num_def_fonts].rgba_color = get_unsigned_quad(dvi_file); + def_fonts[num_def_fonts].rgba_used = 1; + } + + if (flags & XDV_FLAG_EXTEND) + def_fonts[num_def_fonts].extend = get_signed_quad(dvi_file); + + if (flags & XDV_FLAG_SLANT) + def_fonts[num_def_fonts].slant = get_signed_quad(dvi_file); + + if (flags & XDV_FLAG_EMBOLDEN) + def_fonts[num_def_fonts].embolden = get_signed_quad(dvi_file); + + num_def_fonts++; + + return; +} + +static void +get_dvi_fonts (int32_t post_location) +{ + int code; + + xseek_absolute (dvi_file, post_location + 29, "DVI"); + while ((code = get_unsigned_byte(dvi_file)) != POST_POST) { + switch (code) { + case FNT_DEF1: case FNT_DEF2: case FNT_DEF3: case FNT_DEF4: + read_font_record(get_unsigned_num(dvi_file, code-FNT_DEF1)); + break; + case XDV_NATIVE_FONT_DEF: + need_XeTeX(code); + read_native_font_record(get_signed_quad(dvi_file)); + break; + default: + MESG("Unexpected op code: %3d\n", code); + ERROR(invalid_signature); + } + } + if (dpx_conf.verbose_level > 2) { + unsigned i; + + MESG("\n"); + MESG("DVI file font info\n"); + for (i = 0; i < num_def_fonts; i++) { + MESG("TeX Font: %10s loaded at ID=%5d, ", + def_fonts[i].font_name, def_fonts[i].tex_id); + MESG("size=%5.2fpt (scaled %4.1f%%)", + def_fonts[i].point_size * dvi2pts, + 100.0 * ((double) def_fonts[i].point_size / def_fonts[i].design_size)); + MESG("\n"); + } + } +} + +static void get_comment (void) +{ + int length; + + xseek_absolute (dvi_file, 14, "DVI"); + length = get_unsigned_byte(dvi_file); + if (fread(dvi_info.comment, + 1, length, dvi_file) != length) { + ERROR(invalid_signature); + } + dvi_info.comment[length] = '\0'; + if (dpx_conf.verbose_level > 0) { + MESG("DVI Comment: %s\n", dvi_info.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 +{ + int32_t h, v, w, x, y, z; + unsigned int d; +}; + +static struct dvi_registers dvi_state; +static struct dvi_registers dvi_stack[DVI_STACK_DEPTH_MAX]; +static unsigned dvi_stack_depth = 0 ; +static int current_font = -1; +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_state.d = 0; /* direction */ + pdf_dev_set_dirmode(0); + dvi_stack_depth = 0; + current_font = -1; +} + +/* Migrated from pdfdev.c: + * The following codes are originally put into pdfdev.c. + * But they are moved to here to make PDF output independent + * from DVI input. + * pdfdoc, pdfspecial and htex are also modified. pdfspecial + * and htex does tag/untag depth. pdfdev and pdfdoc now does + * not care about line-breaking at all. + */ +static unsigned marked_depth = 0; +static int tagged_depth = -1; + +static void +dvi_mark_depth (void) +{ + /* If decreasing below tagged_depth */ + if (link_annot && + marked_depth == tagged_depth && + dvi_stack_depth == tagged_depth - 1) { + /* + * See if this appears to be the end of a "logical unit" + * that's been broken. If so, flush the logical unit. + */ + pdf_doc_break_annot(); + } + marked_depth = dvi_stack_depth; +} + +/* + * The following routines setup and tear down a callback at a + * certain stack depth. This is used to handle broken (linewise) + * links. + */ +void +dvi_tag_depth (void) +{ + tagged_depth = marked_depth; + dvi_compute_boxes(1); +} + +void +dvi_untag_depth (void) +{ + tagged_depth = -1; + dvi_compute_boxes(0); +} + +void +dvi_compute_boxes (int flag) +{ + compute_boxes = flag; +} + +void +dvi_link_annot (int flag) +{ + link_annot = flag; +} + +int +dvi_is_tracking_boxes(void) +{ + return (compute_boxes && link_annot && marked_depth >= tagged_depth); +} + +void +dvi_set_linkmode (int mode) +{ + catch_phantom = mode != 0 ? 1 : 0; +} + +void +dvi_set_phantom_height (double height, double depth) +{ + phantom_height = height; + phantom_depth = depth; + catch_phantom = 2; +} + +/* All those things related to "compensation" is intruduced due to + * bcontent/econtent specials. They are originally implemented with + * modification to very important part of pdfdev functions such as + * pdf_dev_set_string(), and it made things very unclear. + * + * It is not good idea to modify the core part of PDF output routines + * just for implementing DVI "special" command. They do not make sense + * and just look odd when PDF output routines are separated from dvipdfmx. + */ +static struct spt_coord { + spt_t x, y; +} compensation = { 0, 0 }; + +void dvi_set_compensation (double x, double y) +{ + compensation.x = (spt_t) round(x / dvi2pts); + compensation.y = (spt_t) round(y / dvi2pts); +} + +static void +set_string (spt_t xpos, + spt_t ypos, + const void *instr_ptr, + int instr_len, + spt_t width, + int font_id) +{ + xpos -= compensation.x; + ypos -= compensation.y; + pdf_dev_set_string(xpos, ypos, instr_ptr, instr_len, width, font_id); +} + +static void +set_rule (spt_t xpos, spt_t ypos, spt_t width, spt_t height) +{ + xpos -= compensation.x; + ypos -= compensation.y; + pdf_dev_set_rule(xpos, ypos, width, height); +} + +static void +calc_rect (pdf_rect *r, spt_t xpos, spt_t ypos, spt_t width, spt_t height, spt_t depth) +{ + xpos -= compensation.x; + ypos -= compensation.y; + pdf_dev_set_rect(r, xpos, ypos, width, height, depth); +} + +void +dvi_do_special (const void *buffer, int32_t size) +{ + double x_user, y_user, mag; + const char *p; + int is_drawable = 0; + pdf_rect rect = {0.0, 0.0, 0.0, 0.0}; + + graphics_mode(); + + p = (const char *) buffer; + + x_user = dvi_state.h * dvi2pts; + y_user = -dvi_state.v * dvi2pts; + mag = dvi_tell_mag(); + + if (spc_exec_special(p, size, x_user, y_user, mag, &is_drawable, &rect) < 0) { + if (dpx_conf.verbose_level > 0) { + dump(p, p + size); + } + return; + } + + if (dvi_is_tracking_boxes() && is_drawable) { + pdf_doc_expand_box(&rect); + } + + return; +} + +double +dvi_unit_size (void) +{ + return dvi2pts; +} + + +int +dvi_locate_font (const char *tfm_name, spt_t ptsize) +{ + int cur_id = -1; + const char *name = tfm_name; + int subfont_id = -1, font_id; /* VF or device font ID */ + fontmap_rec *mrec; + + if (dpx_conf.verbose_level > 0) + MESG("<%s@%.2fpt", tfm_name, ptsize * dvi2pts); + + need_more_fonts(1); + + /* This routine needs to be recursive/reentrant. Load current high water + * mark into an automatic variable. + */ + cur_id = num_loaded_fonts++; + + mrec = pdf_lookup_fontmap_record(tfm_name); + /* Load subfont mapping table */ + if (mrec && mrec->charmap.sfd_name && mrec->charmap.subfont_id) { + subfont_id = sfd_load_record(mrec->charmap.sfd_name, mrec->charmap.subfont_id); + } + + memset(&loaded_fonts[cur_id], 0, sizeof (struct loaded_font)); + + /* TFM must exist here. */ + loaded_fonts[cur_id].tfm_id = tfm_open(tfm_name, 1); + loaded_fonts[cur_id].subfont_id = subfont_id; + loaded_fonts[cur_id].size = ptsize; + /* This will be reset later if it was really generated by the dvi file. */ + loaded_fonts[cur_id].source = VF; + + /* The order of searching fonts is as follows: + * + * 1. If mrec is null, that is, there is no map entry matching + * with tfm_name, then search a virtual font matching with + * tfm_name at first. If no virtual font is found, search a + * PK font matching with tfm_name. + * + * 2. If mrec is non-null, search a physical scalable font. + * + * 3. Notice that every subfont gets non-null mrec. In this case, + * enc_name corresponding to mrec will be used instead of mrec. + * That is enc_name is NULL, search a virtual font for Omega (.ovf) + * matching with the base name of the subfont. If no virtual font + * for Omega is found, it is a fatal error because there is no PK font + * for Omega. + */ + if (!mrec) { + font_id = vf_locate_font(tfm_name, ptsize); + if (font_id >= 0) { + loaded_fonts[cur_id].type = VIRTUAL; + loaded_fonts[cur_id].font_id = font_id; + if (dpx_conf.verbose_level > 0) + MESG("(VF)>"); + return cur_id; + } + } +#if 1 + /* Sorry, I don't understand this well... Please fix. + * The purpose of this seems to be: + * + * Map 8-bit char codes in subfont to 16-bit code with SFD mapping + * and map subfonts to single OVF font. + * + * But it apparently only does TFM -> OVF mapping but no character + * code mapping. Please see dvi_set(), you can't have both font->type + * VIRTUAL and font->subfont_id >= 0. Am I missing something? + */ + else if (subfont_id >= 0 && mrec->map_name) + { + fontmap_rec *mrec1 = pdf_lookup_fontmap_record(mrec->map_name); + /* enc_name=NULL should be used only for 'built-in' encoding. + * Please fix this! + */ + if (mrec1 && !mrec1->enc_name) { + font_id = vf_locate_font(mrec1->font_name, ptsize); + if (font_id < 0) + WARN("Could not locate Omega Virtual Font \"%s\" for \"%s\".", + mrec1->font_name, tfm_name); + else { + loaded_fonts[cur_id].type = VIRTUAL; + loaded_fonts[cur_id].font_id = font_id; + if (dpx_conf.verbose_level > 0) + MESG("(OVF)>"); + return cur_id; + } + } + } +#endif /* 1 */ + + /* Failed to load a virtual font so we try to load a physical font. */ + + /* If mrec->map_name is not NULL, font name identified in PDF output + * is different than tfm_name, this can happen for subfonts grouped + * into a single "intermediate" font foo@SFD@. + * This is necessary for optimal output; to avoid unnecessary creation + * of multiple instances of a same font, to avoid frequent font selection + * and break of string_mode. + */ + if (mrec && mrec->map_name) { + name = mrec->map_name; + } else { + name = tfm_name; + } + + /* We need ptsize for PK font creation. */ + font_id = pdf_dev_locate_font(name, ptsize); + if (font_id < 0) { + WARN("Could not locate a virtual/physical font for TFM \"%s\".", tfm_name); + if (mrec && mrec->map_name) { /* has map_name */ + fontmap_rec *mrec1 = pdf_lookup_fontmap_record(mrec->map_name); + WARN(">> This font is mapped to an intermediate 16-bit font \"%s\" with SFD charmap=<%s,%s>,", + mrec->map_name, mrec->charmap.sfd_name, mrec->charmap.subfont_id); + if (!mrec1) + WARN(">> but I couldn't find font mapping for \"%s\".", mrec->map_name); + else { + WARN(">> and then mapped to a physical font \"%s\" by fontmap.", mrec1->font_name); + WARN(">> Please check if kpathsea library can find this font: %s", mrec1->font_name); + } + } else if (mrec && !mrec->map_name) { + char *finaldot = strrchr(mrec->font_name, '.'); + if (finaldot && strcasecmp(finaldot, ".pfa") == 0) { + /* type1 fonts with pfa format are not supported */ + WARN("This font is mapped to a physical font \"%s\".", mrec->font_name); + ERROR("Sorry, pfa format not supported; please convert the font to pfb, e.g., with t1binary."); + } else { + WARN(">> This font is mapped to a physical font \"%s\".", mrec->font_name); + WARN(">> Please check if kpathsea library can find this font: %s", mrec->font_name); + } + } else { + WARN(">> There are no valid font mapping entry for this font."); + WARN(">> Font file name \"%s\" was assumed but failed to locate that font.", tfm_name); + } + ERROR("Cannot proceed without .vf or \"physical\" font for PDF output..."); + } + memset(loaded_fonts[cur_id].padbytes, 0, 4); + if (mrec) { + if (mrec->opt.mapc >= 0) { + loaded_fonts[cur_id].padbytes[2] = (mrec->opt.mapc >> 8) & 0xff; + } + if (mrec->enc_name && !strcmp(mrec->enc_name, "unicode")) { + loaded_fonts[cur_id].is_unicode = 1; + if (mrec->opt.mapc >= 0) { + loaded_fonts[cur_id].padbytes[0] = (mrec->opt.mapc >> 24) & 0xff; + loaded_fonts[cur_id].padbytes[1] = (mrec->opt.mapc >> 16) & 0xff; + } + } + } + loaded_fonts[cur_id].minbytes = pdf_dev_font_minbytes(font_id); + loaded_fonts[cur_id].type = PHYSICAL; + loaded_fonts[cur_id].font_id = font_id; + if (loaded_fonts[cur_id].minbytes > 4) { + WARN("Input encoding requries more than 4 bytes per char... (unsupported)"); + loaded_fonts[cur_id].minbytes = 4; + } + + if (dpx_conf.verbose_level > 0) + MESG(">"); + + return cur_id; +} + +#if defined(LIBDPX) +int +#else +static int +#endif /* LIBDPX */ +dvi_locate_native_font (const char *filename, uint32_t index, + spt_t ptsize, int layout_dir, int extend, int slant, int embolden) +{ + int cur_id = -1; + fontmap_rec *mrec; + char *fontmap_key; + FILE *fp; + char *path; + sfnt *sfont; + ULONG offset = 0; + int is_dfont = 0, is_type1 = 0; + + if (dpx_conf.verbose_level > 0) + MESG("<%s@%.2fpt", filename, ptsize * dvi2pts); + + if ((path = dpx_find_dfont_file(filename)) != NULL && + (fp = MFOPEN(path, "rb")) != NULL) + is_dfont = 1; + else if ((path = dpx_find_type1_file(filename)) != NULL) + is_type1 = 1; + else if (((path = dpx_find_opentype_file(filename)) == NULL + && (path = dpx_find_truetype_file(filename)) == NULL) + || (fp = MFOPEN(path, "rb")) == NULL) { + ERROR("Cannot proceed without the font: %s", filename); + } + need_more_fonts(1); + + cur_id = num_loaded_fonts++; + + fontmap_key = malloc(strlen(path) + 40); // CHECK this is enough + sprintf(fontmap_key, "%s/%u/%c/%d/%d/%d", path, index, layout_dir == 0 ? 'H' : 'V', extend, slant, embolden); + mrec = pdf_lookup_fontmap_record(fontmap_key); + if (mrec == NULL) { + if ((mrec = pdf_insert_native_fontmap_record(path, index, layout_dir, extend, slant, embolden)) == NULL) { + ERROR("Failed to insert font record for font: %s", filename); + } + } + + memset(&loaded_fonts[cur_id], 0, sizeof (struct loaded_font)); + + loaded_fonts[cur_id].font_id = pdf_dev_locate_font(fontmap_key, ptsize); + loaded_fonts[cur_id].size = ptsize; + loaded_fonts[cur_id].type = NATIVE; + loaded_fonts[cur_id].minbytes = pdf_dev_font_minbytes(loaded_fonts[cur_id].font_id); + if (loaded_fonts[cur_id].minbytes > 4) { + WARN("Input encoding requries more than 4 bytes per char... (unsupprted)"); + loaded_fonts[cur_id].minbytes = 4; + } + RELEASE(fontmap_key); + + if (is_type1) { + cff_font *cffont; + char *enc_vec[256]; + uint16_t num_glyphs; + int i; + + fp = DPXFOPEN(filename, DPX_RES_TYPE_T1FONT); + if (!fp) + return -1; + + if (!is_pfb(fp)) + ERROR("Failed to read Type 1 font \"%s\".", filename); + + memset(enc_vec, 0, 256 * sizeof(char *)); + cffont = t1_load_font(enc_vec, 0, fp); + if (!cffont) + ERROR("Failed to read Type 1 font \"%s\".", filename); + for (i = 0; i < 256; i++) { + if (enc_vec[i]) + RELEASE(enc_vec[i]); + } + loaded_fonts[cur_id].shift_gid = cffont->is_notdef_notzero; + loaded_fonts[cur_id].num_glyphs = num_glyphs = cffont->num_glyphs; + loaded_fonts[cur_id].gm = NEW(num_glyphs + 1, struct gm); + for (i = 0; i < num_glyphs; i++) { + double advance, ascent, descent; + cff_index *cstrings = cffont->cstrings; + t1_ginfo gm; + uint16_t gid; + + /* If .notdef is not the 1st glyph in CharStrings, glyph_id given by + * FreeType should be increased by 1 + */ + gid = (cffont->is_notdef_notzero) ? i + 1 : i; + if (gid == num_glyphs) + break; + t1char_get_metrics(cstrings->data + cstrings->offset[gid] - 1, + cstrings->offset[gid + 1] - cstrings->offset[gid], + cffont->subrs[0], &gm); + + advance = layout_dir == 0 ? gm.wx : gm.wy; + ascent = gm.bbox.ury; + descent = gm.bbox.lly; + loaded_fonts[cur_id].gm[gid].advance = (spt_t) (ptsize * ((advance / 1000.0) * mrec->opt.extend)); + loaded_fonts[cur_id].gm[gid].ascent = (spt_t) (ptsize * ((ascent / 1000.0))); + loaded_fonts[cur_id].gm[gid].descent = (spt_t) (ptsize * ((descent / 1000.0))); + } + cff_close(cffont); + DPXFCLOSE(fp); + } else { + struct tt_head_table *head; + struct tt_maxp_table *maxp; + struct tt_hhea_table *hhea; + struct tt_longMetrics *gm; + spt_t ascent, descent; + uint16_t num_glyphs; + int i; + + if (is_dfont) + sfont = dfont_open(fp, index); + else + sfont = sfnt_open(fp); + if (sfont->type == SFNT_TYPE_TTC) + offset = ttc_read_offset(sfont, index); + else if (sfont->type == SFNT_TYPE_DFONT) + offset = sfont->offset; + + sfnt_read_table_directory(sfont, offset); + head = tt_read_head_table(sfont); + maxp = tt_read_maxp_table(sfont); + hhea = tt_read_hhea_table(sfont); + ascent = (spt_t) (ptsize * ((double) hhea->ascent / (double) head->unitsPerEm)); + descent = (spt_t) (ptsize * ((double) hhea->descent / (double) head->unitsPerEm)); + loaded_fonts[cur_id].num_glyphs = num_glyphs = maxp->numGlyphs; + if (layout_dir == 1 && sfnt_find_table_pos(sfont, "vmtx") > 0) { + struct tt_vhea_table *vhea = tt_read_vhea_table(sfont); + sfnt_locate_table(sfont, "vmtx"); + gm = tt_read_longMetrics(sfont, maxp->numGlyphs, vhea->numOfLongVerMetrics, vhea->numOfExSideBearings); + RELEASE(vhea); + } else { + sfnt_locate_table(sfont, "hmtx"); + gm = tt_read_longMetrics(sfont, maxp->numGlyphs, hhea->numOfLongHorMetrics, hhea->numOfExSideBearings); + } + if (!gm) + ERROR("Failed to read TrueType/OpenType glyph metrics table."); + loaded_fonts[cur_id].gm = NEW(num_glyphs, struct gm); + for (i = 0; i < num_glyphs; i++) { + loaded_fonts[cur_id].gm[i].advance = (spt_t) (ptsize * ((double) gm[i].advance / (double) head->unitsPerEm) * mrec->opt.extend); + loaded_fonts[cur_id].gm[i].ascent = ascent; + loaded_fonts[cur_id].gm[i].descent = descent; + + } + RELEASE(gm); + RELEASE(hhea); + RELEASE(maxp); + RELEASE(head); + sfnt_close(sfont); + DPXFCLOSE(fp); + } + + RELEASE(path); + + loaded_fonts[cur_id].layout_dir = layout_dir; + loaded_fonts[cur_id].extend = mrec->opt.extend; + loaded_fonts[cur_id].slant = mrec->opt.slant; + loaded_fonts[cur_id].embolden = mrec->opt.bold; + + if (dpx_conf.verbose_level > 0) + MESG(">"); + + return cur_id; +} + +double +dvi_dev_xpos (void) +{ + return dvi_state.h * dvi2pts; +} + +double +dvi_dev_ypos (void) +{ + return -(dvi_state.v * dvi2pts); +} + +static void do_moveto (int32_t x, int32_t y) +{ + dvi_state.h = x; + dvi_state.v = y; +} + +void dvi_right (int32_t x) +{ + spt_t save_h, save_v; + + if (lr_mode >= SKIMMING) { + lr_width += x; + return; + } + + if (lr_mode == RTYPESETTING) + x = -x; + + save_h = dvi_state.h; + save_v = dvi_state.v; + + switch (dvi_state.d) { + case 0: + dvi_state.h += x; break; + case 1: + dvi_state.v += x; break; + case 3: + dvi_state.v -= x; break; + } + + if (dvi_is_tracking_boxes() && catch_phantom > 0) { + pdf_rect rect; + spt_t width, height, depth; + if (catch_phantom == 1) { + if (current_font >= 0 && current_font < num_loaded_fonts) { + height = loaded_fonts[current_font].size; + } else { + if (dpx_conf.verbose_level > 0) { + WARN("Don't know how to calculate the box height since current font is not set..."); + } + height = 0.0; + } + depth = 0.0; + } else { + height = phantom_height / dvi2pts; + depth = phantom_depth / dvi2pts; + } + switch (dvi_state.d) { + case 0: + width = dvi_state.h - save_h; + break; + case 1: + case 2: + width = dvi_state.v - save_v; + break; + default: + width = dvi_state.h - save_h; + } + calc_rect(&rect, save_h, -save_v, width, height, depth); + pdf_doc_expand_box(&rect); + } +} + +void dvi_down (int32_t y) +{ + if (lr_mode < SKIMMING) { + switch (dvi_state.d) { + case 0: + dvi_state.v += y; break; + case 1: + dvi_state.h -= y; break; + case 3: + dvi_state.h += y; break; + } + } +} + +/* _FIXME_ + * CMap decoder wants multibyte strings as input but + * how DVI char codes are converted to multibyte sting + * is not clear. + */ +void +dvi_set (int32_t ch) +{ + struct loaded_font *font; + spt_t width, height, depth; + unsigned char wbuf[4]; + int n, cbytes; + + if (current_font < 0) { + ERROR("No font selected!"); + } + /* The division by dvi2pts seems strange since we actually know the + * "dvi" size of the fonts contained in the DVI file. In other + * words, we converted from DVI units to pts and back again! + * The problem comes from fonts defined in VF files where we don't know + * the DVI size. It's keeping me sane to keep *point sizes* of *all* + * fonts in the dev.c file and convert them back if necessary. + */ + font = &loaded_fonts[current_font]; + + if (font->type == NATIVE) { + if (ch >= 0 && ch < font->num_glyphs) { + width = font->gm[ch].advance; + } else { + WARN("Invalid char for dvilua font (ignored): %04x", ch); + return; + } + } else { + width = tfm_get_fw_width(font->tfm_id, ch); + width = sqxfw(font->size, width); + } + + if (lr_mode >= SKIMMING) { + lr_width += width; + return; + } + + if (lr_mode == RTYPESETTING) + dvi_right(width); /* Will actually move left */ + + memcpy(wbuf, font->padbytes, 4); + switch (font->type) { + case PHYSICAL: + if (ch > 65535) { + /* FIXME: uptex specific undocumented */ + if (!font->is_unicode && tfm_is_jfm(font->tfm_id)) { + wbuf[0] = (UTF32toUTF16HS(ch) >> 8) & 0xff; + wbuf[1] = UTF32toUTF16HS(ch) & 0xff; + wbuf[2] = (UTF32toUTF16LS(ch) >> 8) & 0xff; + wbuf[3] = UTF32toUTF16LS(ch) & 0xff; + } else { + wbuf[0] = (ch >> 24) & 0xff; + wbuf[1] = (ch >> 16) & 0xff; + wbuf[2] = (ch >> 8) & 0xff; + wbuf[3] = ch & 0xff; + } + n = 4; + } else if (ch > 255) { + wbuf[2] = (ch >> 8) & 0xff; + wbuf[3] = ch & 0xff; + n = 2; + } else if (font->subfont_id >= 0) { + uint16_t uch = lookup_sfd_record(font->subfont_id, (unsigned char) ch); + wbuf[2] = (uch >> 8) & 0xff; + wbuf[3] = uch & 0xff; + n = 2; + } else { + wbuf[3] = (unsigned char) ch; + n = 1; + } + cbytes = font->minbytes > n ? font->minbytes : n; + set_string(dvi_state.h, -dvi_state.v, wbuf + 4 - cbytes, cbytes, width, font->font_id); + if (dvi_is_tracking_boxes()) { + pdf_rect rect; + + height = tfm_get_fw_height(font->tfm_id, ch); + depth = tfm_get_fw_depth (font->tfm_id, ch); + height = sqxfw(font->size, height); + depth = sqxfw(font->size, depth); + + calc_rect(&rect, dvi_state.h, -dvi_state.v, width, height, depth); + pdf_doc_expand_box(&rect); + } + break; + case NATIVE: + wbuf[0] = (ch >> 8) & 0xff; + wbuf[1] = ch & 0xff; + set_string(dvi_state.h, -dvi_state.v, wbuf, 2, width, font->font_id); + if (dvi_is_tracking_boxes()) { + pdf_rect rect; + + height = font->gm[ch].ascent; + depth = font->gm[ch].descent; + calc_rect(&rect, dvi_state.h, -dvi_state.v, width, height, depth); + pdf_doc_expand_box(&rect); + } + break; + case VIRTUAL: +#if 0 + /* See comment in locate_font() */ + if (font->subfont_id >= 0) + ch = lookup_sfd_record(font->subfont_id, (unsigned char) ch); +#endif /* 0 */ + vf_set_char(ch, font->font_id); /* push/pop invoked */ + break; + } + + if (lr_mode == LTYPESETTING) + dvi_right(width); + +} + +void +dvi_put (int32_t ch) +{ + struct loaded_font *font; + spt_t width, height, depth; + unsigned char wbuf[4]; + int n, cbytes; + + if (current_font < 0) { + ERROR("No font selected!"); + } + + font = &loaded_fonts[current_font]; + + memcpy(wbuf, font->padbytes, 4); + switch (font->type) { + case PHYSICAL: + width = tfm_get_fw_width(font->tfm_id, ch); + width = sqxfw(font->size, width); + /* Treat a single character as a one byte string and use the + * string routine. + */ + if (ch > 65535) { + /* FIXME: uptex specific undocumented */ + if (!font->is_unicode && tfm_is_jfm(font->tfm_id)) { + wbuf[0] = (UTF32toUTF16HS(ch) >> 8) & 0xff; + wbuf[1] = UTF32toUTF16HS(ch) & 0xff; + wbuf[2] = (UTF32toUTF16LS(ch) >> 8) & 0xff; + wbuf[3] = UTF32toUTF16LS(ch) & 0xff; + } else { + wbuf[0] = (ch >> 24) & 0xff; + wbuf[1] = (ch >> 16) & 0xff; + wbuf[2] = (ch >> 8) & 0xff; + wbuf[3] = ch & 0xff; + } + n = 4; + } else if (ch > 255) { + wbuf[2] = (ch >> 8) & 0xff; + wbuf[3] = ch & 0xff; + n = 2; + } else if (font->subfont_id >= 0) { + uint16_t uch = lookup_sfd_record(font->subfont_id, (unsigned char) ch); + wbuf[2] = (uch >> 8) & 0xff; + wbuf[3] = uch & 0xff; + n = 2; + } else { + wbuf[3] = (unsigned char) ch; + n = 1; + } + cbytes = font->minbytes > n ? font->minbytes : n; + set_string(dvi_state.h, -dvi_state.v, wbuf + 4 - cbytes, cbytes, width, font->font_id); + if (dvi_is_tracking_boxes()) { + pdf_rect rect; + + height = tfm_get_fw_height(font->tfm_id, ch); + depth = tfm_get_fw_depth (font->tfm_id, ch); + height = sqxfw(font->size, height); + depth = sqxfw(font->size, depth); + + calc_rect(&rect, dvi_state.h, -dvi_state.v, width, height, depth); + pdf_doc_expand_box(&rect); + } + break; + case VIRTUAL: +#if 0 + /* See comment in locate_font() */ + if (font->subfont_id >= 0) + ch = lookup_sfd_record(font->subfont_id, (unsigned char) ch); +#endif /* 0 */ + vf_set_char(ch, font->font_id); + break; + } + + return; +} + + +void +dvi_rule (int32_t width, int32_t height) +{ + if (width > 0 && height > 0) { + do_moveto(dvi_state.h, dvi_state.v); + + switch (dvi_state.d) { + case 0: + set_rule(dvi_state.h, -dvi_state.v, width, height); + break; + case 1: + set_rule(dvi_state.h, -dvi_state.v - width, height, width); + break; + case 3: + set_rule(dvi_state.h - height, -dvi_state.v , height, width); + break; + } + } +} + +void +dvi_dirchg (unsigned char dir) +{ + if (dpx_conf.verbose_level > 0) + fprintf(stderr, " > dvi_dirchg %d\n", dir); + dvi_state.d = dir; + pdf_dev_set_dirmode(dvi_state.d); /* 0: horizontal, 1,3: vertical */ +} + +static void +do_setrule (void) +{ + int32_t width, height; + + height = get_buffered_signed_quad(); + width = get_buffered_signed_quad(); + switch (lr_mode) { + case LTYPESETTING: + dvi_rule(width, height); + dvi_right(width); + break; + case RTYPESETTING: + dvi_right(width); + dvi_rule(width, height); + break; + default: + lr_width += width; + break; + } +} + +static void +do_putrule (void) +{ + int32_t width, height; + + height = get_buffered_signed_quad (); + width = get_buffered_signed_quad (); + switch (lr_mode) { + case LTYPESETTING: + dvi_rule(width, height); + break; + case RTYPESETTING: + dvi_right(width); + dvi_rule(width, height); + dvi_right(-width); + break; + default: + break; + } +} + +void +dvi_push (void) +{ + if (dvi_stack_depth >= DVI_STACK_DEPTH_MAX) + ERROR("DVI stack exceeded limit."); + + dvi_stack[dvi_stack_depth++] = dvi_state; +} + +void +dvi_pop (void) +{ + if (dvi_stack_depth <= 0) + ERROR ("Tried to pop an empty stack."); + + dvi_state = dvi_stack[--dvi_stack_depth]; + do_moveto(dvi_state.h, dvi_state.v); + pdf_dev_set_dirmode(dvi_state.d); /* 0: horizontal, 1,3: vertical */ +} + + +void +dvi_w (int32_t ch) +{ + dvi_state.w = ch; + dvi_right(ch); +} + +void +dvi_w0 (void) +{ + dvi_right(dvi_state.w); +} + +void +dvi_x (int32_t ch) +{ + dvi_state.x = ch; + dvi_right(ch); +} + +void +dvi_x0 (void) +{ + dvi_right(dvi_state.x); +} + +void +dvi_y (int32_t ch) +{ + dvi_state.y = ch; + dvi_down(ch); +} + +void +dvi_y0 (void) +{ + dvi_down(dvi_state.y); +} + +void +dvi_z (int32_t ch) +{ + dvi_state.z = ch; + dvi_down(ch); +} + +void +dvi_z0 (void) +{ + dvi_down(dvi_state.z); +} + +static void +skip_fntdef (void) +{ + int area_len, name_len; + + skip_bytes(12, dvi_file); + area_len = get_unsigned_byte(dvi_file); + name_len = get_unsigned_byte(dvi_file); + skip_bytes(area_len + name_len, dvi_file); +} + +/* when pre-scanning the page, we process fntdef + and remove the fntdef opcode from the buffer */ +static void +do_fntdef (int32_t tex_id) +{ + if (linear) + read_font_record(tex_id); + else + skip_fntdef(); + --dvi_page_buf_index; +} + +void +dvi_set_font (int font_id) +{ + current_font = font_id; +} + +static void +do_fnt (int32_t tex_id) +{ + int i; + + for (i = 0; i < num_def_fonts; i++) { + if (def_fonts[i].tex_id == tex_id) + break; + } + + if (i == num_def_fonts) { + ERROR("Tried to select a font that hasn't been defined: id=%d", tex_id); + } + + if (!def_fonts[i].used) { + int font_id; + + if (def_fonts[i].native) { + font_id = dvi_locate_native_font(def_fonts[i].font_name, + def_fonts[i].face_index, + def_fonts[i].point_size, + def_fonts[i].layout_dir, + def_fonts[i].extend, + def_fonts[i].slant, + def_fonts[i].embolden); + } else { + font_id = dvi_locate_font(def_fonts[i].font_name, + def_fonts[i].point_size); + } + loaded_fonts[font_id].rgba_color = def_fonts[i].rgba_color; + loaded_fonts[font_id].rgba_used = def_fonts[i].rgba_used; + /* if rgba_used == 0, not a colored font */ + if (loaded_fonts[font_id].rgba_used == 0) { + loaded_fonts[font_id].xgs_id = -1; + } else { + pdf_obj *xgs_dict; + int a = loaded_fonts[font_id].rgba_color & 0xff; + + /* Inefficient but don't care as transparency is not expected to be frequently used. */ + xgs_dict = pdf_new_dict(); + pdf_add_dict(xgs_dict, pdf_new_name("Type"), pdf_new_name("ExtGState")); + pdf_add_dict(xgs_dict, pdf_new_name("ca"), pdf_new_number(a/255.0)); + pdf_add_dict(xgs_dict, pdf_new_name("CA"), pdf_new_number(a/255.0)); + loaded_fonts[font_id].xgs_id = pdf_defineresource("ExtGState", NULL, xgs_dict, 0); + } + loaded_fonts[font_id].source = DVI; + def_fonts[i].used = 1; + def_fonts[i].font_id = font_id; + } + current_font = def_fonts[i].font_id; +} + +static void +do_xxx (int32_t size) +{ + if (lr_mode < SKIMMING) + dvi_do_special(dvi_page_buffer + dvi_page_buf_index, size); + dvi_page_buf_index += size; +} + +static void +do_bop (void) +{ + int i; + + if (processing_page) + ERROR("Got a bop in the middle of a page!"); + + /* For now, ignore TeX's count registers */ + for (i = 0; i < 10; i++) { + skip_bufferd_bytes(4); + } + /* Ignore previous page pointer since we have already + * saved this information + */ + skip_bufferd_bytes(4); + clear_state(); + processing_page = 1; + + pdf_doc_begin_page(dvi_tell_mag(), dev_origin_x, dev_origin_y); + spc_exec_at_begin_page(); + + return; +} + +static void +do_eop (void) +{ + processing_page = 0; + + if (dvi_stack_depth != 0) { + ERROR("DVI stack depth is not zero at end of page"); + } + spc_exec_at_end_page(); + + pdf_doc_end_page(); + + return; +} + +static void +do_dir (void) +{ + dvi_state.d = get_buffered_unsigned_byte(); + pdf_dev_set_dirmode(dvi_state.d); /* 0: horizontal, 1,3: vertical */ +} + +static void +lr_width_push (void) +{ + if (lr_width_stack_depth >= DVI_STACK_DEPTH_MAX) + ERROR("Segment width stack exceeded limit."); + + lr_width_stack[lr_width_stack_depth++] = lr_width; +} + +static void +lr_width_pop (void) +{ + if (lr_width_stack_depth <= 0) + ERROR("Tried to pop an empty segment width stack."); + + lr_width = lr_width_stack[--lr_width_stack_depth]; +} + +static void +dvi_begin_reflect (void) +{ + if (lr_mode >= SKIMMING) { + ++lr_mode; + } else { + lr_state.buf_index = dvi_page_buf_index; + lr_state.font = current_font; + lr_state.state = lr_mode; + lr_mode = SKIMMING; + lr_width = 0; + } +} + +static void +dvi_end_reflect (void) +{ + switch (lr_mode) { + case SKIMMING: + current_font = lr_state.font; + dvi_page_buf_index = lr_state.buf_index; + lr_mode = REVERSE(lr_state.state); /* must precede dvi_right */ + dvi_right(-(int32_t)lr_width); + lr_width_push(); + break; + case LTYPESETTING: + case RTYPESETTING: + lr_width_pop(); + dvi_right(-(int32_t)lr_width); + lr_mode = REVERSE(lr_mode); + break; + default: /* lr_mode > SKIMMING */ + lr_mode--; + } +} + +static void +skip_native_font_def (void) +{ + unsigned int flags; + int name_length; + + skip_bytes(4, dvi_file); /* skip point size */ + flags = get_unsigned_pair(dvi_file); + name_length = get_unsigned_byte(dvi_file); + skip_bytes(name_length + 4, dvi_file); + + if (flags & XDV_FLAG_COLORED) + skip_bytes(4, dvi_file); + + if (flags & XDV_FLAG_EXTEND) + skip_bytes(4, dvi_file); + + if (flags & XDV_FLAG_SLANT) + skip_bytes(4, dvi_file); + + if (flags & XDV_FLAG_EMBOLDEN) + skip_bytes(4, dvi_file); +} + +static void +do_native_font_def (int32_t tex_id) +{ + if (linear) + read_native_font_record(tex_id); + else + skip_native_font_def(); + --dvi_page_buf_index; /* don't buffer the opcode */ +} + +static void +skip_glyphs (void) +{ + unsigned int i, slen = 0; + slen = (unsigned int) get_buffered_unsigned_pair(); + for (i = 0; i < slen; i++) { + skip_bufferd_bytes(4); + skip_bufferd_bytes(4); + skip_bufferd_bytes(2); + } +} + +static void +do_glyphs (int do_actual_text) +{ + struct loaded_font *font; + spt_t width, height, depth, *xloc, *yloc; + unsigned char wbuf[2]; + int32_t i; + uint16_t glyph_id, slen = 0; + + if (current_font < 0) + ERROR("No font selected!"); + + font = &loaded_fonts[current_font]; + + if (do_actual_text) { + slen = (unsigned int) get_buffered_unsigned_pair(); + if (lr_mode >= SKIMMING) { + for (i = 0; i < slen; i++) { + skip_bufferd_bytes(2); + } + } else { + uint16_t *unicodes = NEW(slen, uint16_t); + for (i = 0; i < slen; i++) { + unicodes[i] = (uint16_t) get_buffered_unsigned_pair(); + } + pdf_dev_begin_actualtext (unicodes, slen); + RELEASE(unicodes); + } + } + + width = get_buffered_signed_quad(); + + if (lr_mode >= SKIMMING) { + lr_width += width; + skip_glyphs(); + return; + } + + if (lr_mode == RTYPESETTING) + dvi_right(width); /* Will actually move left */ + + slen = (unsigned int) get_buffered_unsigned_pair(); + xloc = NEW(slen, spt_t); + yloc = NEW(slen, spt_t); + for (i = 0; i < slen; i++) { + xloc[i] = get_buffered_signed_quad(); + yloc[i] = get_buffered_signed_quad(); + } + + if (font->rgba_used == 1) { + pdf_color color; + pdf_color_rgbcolor(&color, + (double)((unsigned char)(font->rgba_color >> 24) & 0xff) / 255, + (double)((unsigned char)(font->rgba_color >> 16) & 0xff) / 255, + (double)((unsigned char)(font->rgba_color >> 8) & 0xff) / 255); + pdf_color_push(&color, &color); + /* Opacity: + * Enter graphics_mode and then enclose with save/resotre + * since pdf_color_pop() may not restore graphics state. + */ + if (font->xgs_id >= 0) { + pdf_obj *ref; + char resname[16]; + char content[22]; + + sprintf(resname, "Xtx_Gs_%08x", current_font); + ref = pdf_get_resource_reference(font->xgs_id); + pdf_doc_add_page_resource("ExtGState", resname, ref); + graphics_mode(); + pdf_dev_gsave(); + sprintf(content, " /%s gs ", resname); + pdf_doc_add_page_content(content, strlen(content)); + } + } + + for (i = 0; i < slen; i++) { + spt_t advance = 0; + + glyph_id = get_buffered_unsigned_pair(); /* freetype glyph index */ + if (glyph_id < font->num_glyphs) { + if (font->shift_gid) + glyph_id++; + advance = font->gm[glyph_id].advance; + if (dvi_is_tracking_boxes()) { + pdf_rect rect; + height = font->gm[glyph_id].ascent ; + depth = -font->gm[glyph_id].descent; + calc_rect(&rect, dvi_state.h + xloc[i], -dvi_state.v - yloc[i], advance, height, depth); + pdf_doc_expand_box(&rect); + } + } + wbuf[0] = glyph_id >> 8; + wbuf[1] = glyph_id & 0xff; + set_string(dvi_state.h + xloc[i], -dvi_state.v - yloc[i], wbuf, 2, advance, font->font_id); + } + + if (font->rgba_used == 1) { + if (font->xgs_id >= 0) { + graphics_mode(); + pdf_dev_grestore(); + } + pdf_color_pop(); + } + RELEASE(xloc); + RELEASE(yloc); + + if (do_actual_text) { + pdf_dev_end_actualtext(); + } + + if (lr_mode == LTYPESETTING) + dvi_right(width); + + return; +} + +static void +check_postamble (void) +{ + int code; + + skip_bytes(28, dvi_file); + while ((code = get_unsigned_byte(dvi_file)) != POST_POST) { + switch (code) { + case FNT_DEF1: case FNT_DEF2: case FNT_DEF3: case FNT_DEF4: + skip_bytes(code + 1 - FNT_DEF1, dvi_file); + skip_fntdef(); + break; + case XDV_NATIVE_FONT_DEF: + skip_bytes(4, dvi_file); + skip_native_font_def(); + break; + default: + ERROR("Unexpected op code (%u) in postamble", code); + } + } + skip_bytes(4, dvi_file); + post_id_byte = get_unsigned_byte(dvi_file); + if (!(post_id_byte == DVI_ID || post_id_byte == DVIV_ID || post_id_byte == XDV_ID || post_id_byte == XDV_ID_OLD)) { + MESG("DVI ID = %d\n", post_id_byte); + ERROR(invalid_signature); + } + check_id_bytes(); + if (has_ptex && post_id_byte != DVIV_ID) + ERROR ("Saw opcode %i in DVI file not for Ascii pTeX", PTEXDIR); + + num_pages = 0; /* force loop to terminate */ +} + +/* Most of the work of actually interpreting + * the dvi file is here. + */ +void +dvi_do_page (double page_paper_height, double hmargin, double vmargin) +{ + unsigned char opcode; + + /* before this is called, we have scanned the page for papersize specials + and the complete DVI data is now in dvi_page_buffer */ + dvi_page_buf_index = 0; + + /* DVI coordinate */ + dev_origin_x = hmargin; + dev_origin_y = page_paper_height - vmargin; + + dvi_stack_depth = 0; + for (;;) { + opcode = get_buffered_unsigned_byte(); + + if (opcode <= SET_CHAR_127) { + dvi_set(opcode); + 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: case SET2: case SET3: + dvi_set(get_buffered_unsigned_num(opcode-SET1)); break; + case SET4: + ERROR("Multibyte (>24 bits) character not supported!"); + break; + + case SET_RULE: + do_setrule(); + break; + + case PUT1: case PUT2: case PUT3: + dvi_put(get_buffered_unsigned_num(opcode-PUT1)); break; + case PUT4: + ERROR("Multibyte (>24 bits) character not supported!"); + break; + + case PUT_RULE: + do_putrule(); + break; + + case NOP: + break; + + case BOP: + do_bop(); + break; + case EOP: + do_eop(); + if (linear) { + if ((opcode = get_unsigned_byte(dvi_file)) == POST) + check_postamble(); + else + ungetc(opcode, dvi_file); + } + return; + + case PUSH: + dvi_push(); + if (lr_mode >= SKIMMING) + lr_width_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 */ + dvi_mark_depth(); + break; + case POP: + dvi_pop(); + if (lr_mode >= SKIMMING) + lr_width_pop(); + /* Above explanation holds for following line too */ + dvi_mark_depth(); + break; + + case RIGHT1: case RIGHT2: case RIGHT3: case RIGHT4: + dvi_right(get_buffered_signed_num(opcode-RIGHT1)); break; + + case W0: dvi_w0(); break; + case W1: case W2: case W3: case W4: + dvi_w(get_buffered_signed_num(opcode-W1)); break; + + case X0: dvi_x0(); break; + case X1: case X2: case X3: case X4: + dvi_x(get_buffered_signed_num(opcode-X1)); break; + + case DOWN1: case DOWN2: case DOWN3: case DOWN4: + dvi_down(get_buffered_signed_num(opcode-DOWN1)); break; + + case Y0: dvi_y0(); break; + case Y1: case Y2: case Y3: case Y4: + dvi_y(get_buffered_signed_num(opcode-Y1)); break; + + case Z0: dvi_z0(); break; + case Z1: case Z2: case Z3: case Z4: + dvi_z(get_buffered_signed_num(opcode-Z1)); break; + + case FNT1: case FNT2: case FNT3: case FNT4: + do_fnt(get_buffered_unsigned_num(opcode-FNT1)); break; + + /* Specials */ + case XXX1: case XXX2: case XXX3: case XXX4: + { + int32_t size = get_buffered_unsigned_num(opcode-XXX1); + if (size < 0) + WARN("DVI: Special with %d bytes???", size); + else + do_xxx(size); + break; + } + + /* These should not occur - processed during pre-scanning */ + case FNT_DEF1: case FNT_DEF2: case FNT_DEF3: case FNT_DEF4: + break; + + /* pTeX extension */ + case PTEXDIR: + need_pTeX(opcode); + do_dir(); + break; + + /* XeTeX extensions */ + case XDV_GLYPHS: + need_XeTeX(opcode); + do_glyphs(0); + break; + case XDV_TEXT_AND_GLYPHS: + need_XeTeX(opcode); + do_glyphs(1); + break; + /* should not occur - processed during pre-scanning */ + case XDV_NATIVE_FONT_DEF: + need_XeTeX(opcode); + break; + case BEGIN_REFLECT: + need_XeTeX(opcode); + dvi_begin_reflect(); + break; + case END_REFLECT: + need_XeTeX(opcode); + dvi_end_reflect(); + break; + + case POST: + if (linear && !processing_page) { + /* for linear processing, this means there are no more pages */ + num_pages = 0; /* force loop to terminate */ + return; + } + /* else fall through to error case */ + case PRE: case POST_POST: + ERROR("Unexpected preamble or postamble in dvi file"); + break; + default: + ERROR("Unexpected opcode or DVI file ended prematurely"); + } + } +} + +double +dvi_init (char *dvi_filename, double mag) +{ + int32_t post_location; + + if (!dvi_filename) { /* no filename: reading from stdin, probably a pipe */ + int ch; +#ifdef WIN32 + setmode(fileno(stdin), _O_BINARY); +#endif + dvi_file = stdin; + linear = 1; + + get_preamble_dvi_info(); + do_scales(mag); + if ((ch = get_unsigned_byte(dvi_file)) == POST) + check_postamble(); + else + ungetc(ch, dvi_file); + } else { + char *p, *saved_orig_name; + dvi_file = NULL; + saved_orig_name = xstrdup(dvi_filename); + p = strrchr(dvi_filename, '.'); + if (p == NULL || (!FILESTRCASEEQ(p, ".dvi") && + !FILESTRCASEEQ(p, ".xdv"))) { + strcat(dvi_filename, ".xdv"); + dvi_file = MFOPEN(dvi_filename, FOPEN_RBIN_MODE); + if (!dvi_file) { + dvi_filename[strlen(dvi_filename) - 4] = '\0'; + strcat(dvi_filename, ".dvi"); + dvi_file = MFOPEN(dvi_filename, FOPEN_RBIN_MODE); + } + } + if (!dvi_file) + dvi_file = MFOPEN(saved_orig_name, FOPEN_RBIN_MODE); + free(saved_orig_name); + + if (!dvi_file) { + ERROR("Could not open specified DVI (or XDV) file: %s", + dvi_filename); + return 0.0; + } + + /* DVI files are most easily read backwards by + * searching for post_post and then post opcode. + */ + post_location = find_post(); + get_dvi_info(post_location); + do_scales(mag); + get_page_info(post_location); + get_comment(); + get_dvi_fonts(post_location); + } + clear_state(); + + dvi_page_buf_size = DVI_PAGE_BUF_CHUNK; + dvi_page_buffer = NEW(dvi_page_buf_size, unsigned char); + + return dvi2pts; +} + +void +dvi_close (void) +{ + int i; + + if (linear) { + /* probably reading a pipe from xetex; consume any remaining data */ + while (fgetc(dvi_file) != EOF) + ; + } + + /* 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); + dvi_file = NULL; + + if (def_fonts) { + for (i = 0; i < num_def_fonts; i++) { + if (def_fonts[i].font_name) + RELEASE(def_fonts[i].font_name); + def_fonts[i].font_name = NULL; + } + RELEASE(def_fonts); + } + def_fonts = NULL; + + if (page_loc) + RELEASE(page_loc); + page_loc = NULL; + num_pages = 0; + + for (i = 0; i < num_loaded_fonts; i++) + { + if (loaded_fonts[i].gm != NULL) + RELEASE(loaded_fonts[i].gm); + + loaded_fonts[i].gm = NULL; + } + + if (loaded_fonts) + RELEASE(loaded_fonts); + loaded_fonts = NULL; + num_loaded_fonts = 0; + + vf_close_all_fonts(); + tfm_close_all (); + + if (dvi_page_buffer) { + RELEASE(dvi_page_buffer); + dvi_page_buffer = NULL; + dvi_page_buf_size = 0; + } +} + +/* The following are need to implement virtual fonts + According to documentation, the vf "subroutine" + must have state pushed and must have + w,v,y, and z set to zero. The current font + is determined by the virtual font header, which + may be undefined */ + +static int saved_dvi_font[VF_NESTING_MAX]; +static int num_saved_fonts = 0; + +void +dvi_vf_init (int dev_font_id) +{ + dvi_push(); + + dvi_state.w = 0; dvi_state.x = 0; + dvi_state.y = 0; dvi_state.z = 0; + + /* do not reset dvi_state.d. */ + if (num_saved_fonts < VF_NESTING_MAX) { + saved_dvi_font[num_saved_fonts++] = current_font; + } else + ERROR("Virtual fonts nested too deeply!"); + current_font = dev_font_id; +} + +/* After VF subroutine is finished, we simply pop the DVI stack */ +void +dvi_vf_finish (void) +{ + dvi_pop(); + if (num_saved_fonts > 0) + current_font = saved_dvi_font[--num_saved_fonts]; + else { + ERROR("Tried to pop an empty font stack"); + } +} + + +/* Scan various specials */ +#include "dpxutil.h" +/* For MAX_PWD_LEN */ +#include "pdfencrypt.h" + +static int +scan_special_encrypt (int *key_bits, int32_t *permission, char *opassword, char *upassword, + const char **curptr, const char *endptr) +{ + int error = 0; + const char *p = *curptr; + + skip_white(&p, endptr); + + opassword[0] = '\0'; + upassword[0] = '\0'; + while (!error && p < endptr) { + char *kp = parse_c_ident(&p, endptr); + if (!kp) + break; + else { + pdf_obj *obj; + skip_white(&p, endptr); + if (!strcmp(kp, "ownerpw")) { + if ((obj = parse_pdf_string(&p, endptr))) { + if (pdf_string_value(obj)) { + int str_length = (MAX_PWD_LEN - 1 > pdf_string_length(obj) + ? pdf_string_length(obj) : MAX_PWD_LEN - 1); + strncpy(opassword, pdf_string_value(obj), str_length); + opassword[str_length] = '\0'; + } + pdf_release_obj(obj); + } else + error = -1; + } else if (!strcmp(kp, "userpw")) { + if ((obj = parse_pdf_string(&p, endptr))) { + if (pdf_string_value(obj)) { + int str_length = (MAX_PWD_LEN - 1 > pdf_string_length(obj) + ? pdf_string_length(obj) : MAX_PWD_LEN - 1); + strncpy(upassword, pdf_string_value(obj), str_length); + upassword[str_length] = '\0'; + } + pdf_release_obj(obj); + } else + error = -1; + } else if (!strcmp(kp, "length")) { + if ((obj = parse_pdf_number(&p, endptr)) && PDF_OBJ_NUMBERTYPE(obj)) { + *key_bits = (unsigned) pdf_number_value(obj); + } else + error = -1; + if (obj) + pdf_release_obj(obj); + } else if (!strcmp(kp, "perm")) { + if ((obj = parse_pdf_number(&p, endptr)) && PDF_OBJ_NUMBERTYPE(obj)) { + *permission = (unsigned) pdf_number_value(obj); + } else + error = -1; + if (obj) + pdf_release_obj(obj); + } else + error = -1; + RELEASE(kp); + } + skip_white(&p, endptr); + } + *curptr = p; + + return error; +} + +static int +scan_special_trailerid (unsigned char *id1, unsigned char *id2, + const char **curptr, const char *endptr) +{ + int error = 0; + pdf_obj *id_array; + const char *p = *curptr; + + skip_white(&p, endptr); + id_array = parse_pdf_array(&p, endptr, NULL); + if (id_array) { + if (pdf_array_length(id_array) == 2) { + pdf_obj *tmp1, *tmp2; + tmp1 = pdf_get_array(id_array, 0); + tmp2 = pdf_get_array(id_array, 1); + if (PDF_OBJ_STRINGTYPE(tmp1) && pdf_string_length(tmp1) == 16 && + PDF_OBJ_STRINGTYPE(tmp2) && pdf_string_length(tmp2) == 16) { + memcpy(id1, pdf_string_value(tmp1), 16); + memcpy(id2, pdf_string_value(tmp2), 16); + } else { + error = -1; + } + } else { + error = -1; + } + pdf_release_obj(id_array); + } else { + error = -1; + } + skip_white(&p, endptr); + *curptr = p; + + return error; +} + +static int +scan_special (double *wd, double *ht, double *xo, double *yo, int *lm, + int *majorversion, int *minorversion, + int *enable_encryption, int *key_bits, int32_t *permission, + char *opassword, char *upassword, + int *has_id, unsigned char *id1, unsigned char *id2, + const char *buf, uint32_t size) +{ + char *q; + const char *p = buf, *endptr; + int ns_pdf = 0, ns_dvipdfmx = 0, error = 0; + double tmp; + + endptr = p + size; + + skip_white(&p, endptr); + + q = parse_c_ident(&p, endptr); + if (q && !strcmp(q, "pdf")) { + skip_white(&p, endptr); + if (p < endptr && *p == ':') { + p++; + skip_white(&p, endptr); + RELEASE(q); + q = parse_c_ident(&p, endptr); ns_pdf = 1; + } + } + else if (q && !strcmp(q, "x")) { + skip_white(&p, endptr); + if (p < endptr && *p == ':') { + p++; + skip_white(&p, endptr); + RELEASE(q); + q = parse_c_ident(&p, endptr); + } + } + else if (q && !strcmp(q, "dvipdfmx")) { + skip_white(&p, endptr); + if (p < endptr && *p == ':') { + p++; + skip_white(&p, endptr); + RELEASE(q); + q = parse_c_ident(&p, endptr); ns_dvipdfmx = 1; + } + } + skip_white(&p, endptr); + + if (q) { + if (!strcmp(q, "landscape")) { + *lm = 1; + } else if (ns_pdf && !strcmp(q, "pagesize")) { + while (!error && p < endptr) { + char *kp = parse_c_ident(&p, endptr); + if (!kp) + break; + else { + skip_white(&p, endptr); + if (!strcmp(kp, "width")) { + error = dpx_util_read_length(&tmp, dvi_tell_mag(), &p, endptr); + if (!error) + *wd = tmp * dvi_tell_mag(); + } else if (!strcmp(kp, "height")) { + error = dpx_util_read_length(&tmp, dvi_tell_mag(), &p, endptr); + if (!error) + *ht = tmp * dvi_tell_mag(); + } else if (!strcmp(kp, "xoffset")) { + error = dpx_util_read_length(&tmp, dvi_tell_mag(), &p, endptr); + if (!error) + *xo = tmp * dvi_tell_mag(); + } else if (!strcmp(kp, "yoffset")) { + error = dpx_util_read_length(&tmp, dvi_tell_mag(), &p, endptr); + if (!error) + *yo = tmp * dvi_tell_mag(); + } else if (!strcmp(kp, "default")) { + *wd = paper_width; + *ht = paper_height; + *lm = landscape_mode; + *xo = *yo = 72.0; + } + RELEASE(kp); + } + skip_white(&p, endptr); + } + } else if (!strcmp(q, "papersize")) { + char qchr = 0; + if (*p == '=') p++; + skip_white(&p, endptr); + if (p < endptr && (*p == '\'' || *p == '\"')) { + qchr = *p; p++; + skip_white(&p, endptr); + } + error = dpx_util_read_length(&tmp, 1.0, &p, endptr); + if (!error) { + double tmp1; + + skip_white(&p, endptr); + if (p < endptr && *p == ',') { + p++; skip_white(&p, endptr); + } + error = dpx_util_read_length(&tmp1, 1.0, &p, endptr); + if (!error) { + *wd = tmp; + *ht = tmp1; + skip_white(&p, endptr); + } + } + if (!error && qchr) { /* Check if properly quoted */ + if (p >= endptr || *p != qchr) + error = -1; + } + if (error == 0) { + paper_width = *wd; + paper_height = *ht; + } + } else if (minorversion && ns_pdf && !strcmp(q, "minorversion")) { + char *kv; + if (*p == '=') p++; + skip_white(&p, endptr); + kv = parse_float_decimal(&p, endptr); + if (kv) { + *minorversion = (int)strtol(kv, NULL, 10); + RELEASE(kv); + } + } else if (majorversion && ns_pdf && !strcmp(q, "majorversion")) { + char *kv; + if (*p == '=') p++; + skip_white(&p, endptr); + kv = parse_float_decimal(&p, endptr); + if (kv) { + *majorversion = (int)strtol(kv, NULL, 10); + RELEASE(kv); + } + } else if (enable_encryption && ns_pdf && !strcmp(q, "encrypt")) { + *enable_encryption = 1; + error = scan_special_encrypt(key_bits, permission, opassword, upassword, &p, endptr); + } else if (ns_dvipdfmx && !strcmp(q, "config")) { + read_config_special(&p, endptr); + } else if (has_id && id1 && id2 && ns_pdf && !strcmp(q, "trailerid")) { + error = scan_special_trailerid(id1, id2, &p, endptr); + if (error) { + WARN("Invalid argument for pdf:trailerid special."); + *has_id = 0; + } else { + *has_id = 1; + } + } + RELEASE(q); + } + + return error; +} + + +void +dvi_scan_specials (int page_no, + double *page_width, double *page_height, + double *x_offset, double *y_offset, int *landscape, + int *majorversion, int *minorversion, + int *do_enc, int *key_bits, int32_t *permission, + char *owner_pw, char *user_pw, + int *has_id, unsigned char *id1, unsigned char *id2) +{ + FILE *fp = dvi_file; + int32_t offset; + unsigned char opcode; + static int buffered_page = -1; + unsigned int len; + + if (page_no == buffered_page || num_pages == 0) + return; /* because dvipdfmx wants to scan first page twice! */ + buffered_page = page_no; + + dvi_page_buf_index = 0; + + if (!linear) { + if (page_no >= num_pages) + ERROR("Invalid page number: %u", page_no); + offset = page_loc[page_no]; + + xseek_absolute (fp, offset, "DVI"); + } + + while ((opcode = get_and_buffer_unsigned_byte(fp)) != EOP) { + if (opcode <= SET_CHAR_127 || + (opcode >= FNT_NUM_0 && opcode <= FNT_NUM_63)) + continue; + else if (opcode == XXX1 || opcode == XXX2 || + opcode == XXX3 || opcode == XXX4) { + uint32_t size = get_and_buffer_unsigned_byte(fp); + switch (opcode) { + case XXX4: size = size * 0x100u + get_and_buffer_unsigned_byte(fp); + if (size > 0x7fff) + WARN("Unsigned number starting with %x exceeds 0x7fffffff", size); + case XXX3: size = size * 0x100u + get_and_buffer_unsigned_byte(fp); + case XXX2: size = size * 0x100u + get_and_buffer_unsigned_byte(fp); + default: break; + } + if (dvi_page_buf_index + size >= dvi_page_buf_size) { + dvi_page_buf_size = (dvi_page_buf_index + size + DVI_PAGE_BUF_CHUNK); + dvi_page_buffer = RENEW(dvi_page_buffer, dvi_page_buf_size, unsigned char); + } +#define buf ((char*)(dvi_page_buffer + dvi_page_buf_index)) + if (fread(buf, sizeof(char), size, fp) != size) + ERROR("Reading DVI file failed!"); + if (scan_special(page_width, page_height, x_offset, y_offset, landscape, + majorversion, minorversion, + do_enc, key_bits, permission, owner_pw, user_pw, + has_id, id1, id2, + buf, size)) + WARN("Reading special command failed: \"%.*s\"", size, buf); +#undef buf + dvi_page_buf_index += size; + continue; + } + + /* Skipping... */ + switch (opcode) { + case BOP: + get_and_buffer_bytes(fp, 44); + break; + case NOP: case PUSH: case POP: + case W0: case X0: case Y0: case Z0: + break; + case SET1: case PUT1: case RIGHT1: case DOWN1: + case W1: case X1: case Y1: case Z1: case FNT1: + get_and_buffer_bytes(fp, 1); + break; + + case SET2: case PUT2: case RIGHT2: case DOWN2: + case W2: case X2: case Y2: case Z2: case FNT2: + get_and_buffer_bytes(fp, 2); + break; + + case SET3: case PUT3: case RIGHT3: case DOWN3: + case W3: case X3: case Y3: case Z3: case FNT3: + get_and_buffer_bytes(fp, 3); + break; + + case SET4: case PUT4: case RIGHT4: case DOWN4: + case W4: case X4: case Y4: case Z4: case FNT4: + get_and_buffer_bytes(fp, 4); + break; + + case SET_RULE: case PUT_RULE: + get_and_buffer_bytes(fp, 8); + break; + + case FNT_DEF1: case FNT_DEF2: case FNT_DEF3: case FNT_DEF4: + do_fntdef(get_unsigned_num(fp, opcode-FNT_DEF1)); + break; + case XDV_GLYPHS: + need_XeTeX(opcode); + get_and_buffer_bytes(fp, 4); /* width */ + len = get_and_buffer_unsigned_pair(fp); /* glyph count */ + get_and_buffer_bytes(fp, len * 10); /* 2 bytes ID + 8 bytes x,y-location per glyph */ + break; + case XDV_TEXT_AND_GLYPHS: + need_XeTeX(opcode); + len = get_and_buffer_unsigned_pair(fp); /* utf16 code unit count */ + get_and_buffer_bytes(fp, len * 2); /* 2 bytes per code unit */ + get_and_buffer_bytes(fp, 4); /* width */ + len = get_and_buffer_unsigned_pair(fp); /* glyph count */ + get_and_buffer_bytes(fp, len * 10); /* 2 bytes ID + 8 bytes x,y-location per glyph */ + break; + case XDV_NATIVE_FONT_DEF: + need_XeTeX(opcode); + do_native_font_def(get_signed_quad(dvi_file)); + break; + case BEGIN_REFLECT: + case END_REFLECT: + need_XeTeX(opcode); + break; + + case PTEXDIR: + need_pTeX(opcode); + get_and_buffer_bytes(fp, 1); + break; + + case POST: + if (linear && dvi_page_buf_index == 1) { + /* this is actually an indication that we've reached the end of the input */ + return; + } + /* else fall through to error case */ + default: /* case PRE: case POST_POST: and others */ + ERROR("Unexpected opcode %d at pos=0x%x", opcode, tell_position(fp)); + break; + } + } + + return; +} +#if defined(LIBDPX) +#include "dvi_ng.c" +#endif /* LIBDPX */ |