#include "libtexpdf.h"
int compat_mode = 0;
static int verbose = 0;
void
texpdf_dev_set_verbose (void)
{
verbose++;
}
const char*
texpdf_library_version(void)
{
return PACKAGE_VERSION;
}
double
texpdf_dev_scale (void)
{
return 1.0;
}
#define TEX_ONE_HUNDRED_BP 6578176
static struct {
double dvi2pts;
long min_bp_val;
int precision;
} dev_unit = {
0.0,
658,
2
};
double
dev_unit_dviunit (void)
{
return (1.0/dev_unit.dvi2pts);
}
#define DEV_PRECISION_MAX 8
static unsigned long ten_pow[10] = {
1ul, 10ul, 100ul, 1000ul, 10000ul, 100000ul, 1000000ul, 10000000ul, 100000000ul, 1000000000ul
};
static double ten_pow_inv[10] = {
1.0, 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001, 0.00000001, 0.000000001
};
#define bpt2spt(b) ( (spt_t) round( (b) / dev_unit.dvi2pts ) )
#define spt2bpt(s) ( (s) * dev_unit.dvi2pts )
#define dround_at(v,p) (ROUND( (v), ten_pow_inv[(p)] ))
static int
p_itoa (long value, char *buf)
{
int sign, ndigits;
char *p = buf;
if (value < 0) {
*p++ = '-';
value = -value;
sign = 1;
} else {
sign = 0;
}
ndigits = 0;
do {
p[ndigits++] = (value % 10) + '0';
value /= 10;
} while (value != 0);
{
int i;
for (i = 0; i < ndigits / 2 ; i++) {
char tmp = p[i];
p[i] = p[ndigits-i-1];
p[ndigits-i-1] = tmp;
}
}
p[ndigits] = '\0';
return (sign ? ndigits + 1 : ndigits);
}
static int
p_dtoa (double value, int prec, char *buf)
{
const long p[10] = { 1, 10, 100, 1000, 10000,
100000, 1000000, 10000000,
100000000, 1000000000 };
double i, f;
long g;
char *c = buf;
int n;
if (value < 0) {
value = -value;
*c++ = '-';
n = 1;
} else {
n = 0;
}
f = modf(value, &i);
g = (long) (f * p[prec] + 0.5);
if (g == p[prec]) {
g = 0;
i += 1;
}
if (i) {
int m = sprintf(c, "%.0f", i);
c += m;
n += m;
} else if (g == 0) {
*(c = buf) = '0';
n = 1;
}
if (g) {
int j = prec;
*c++ = '.';
while (j--) {
c[j] = (g % 10) + '0';
g /= 10;
}
c += prec - 1;
n += 1 + prec;
while (*c == '0') {
c--;
n--;
}
}
*(++c) = 0;
return n;
}
static int
dev_sprint_bp (char *buf, spt_t value, spt_t *error)
{
double value_in_bp;
double error_in_bp;
int prec = dev_unit.precision;
value_in_bp = spt2bpt(value);
if (error) {
error_in_bp = value_in_bp - dround_at(value_in_bp, prec);
*error = bpt2spt(error_in_bp);
}
return p_dtoa(value_in_bp, prec, buf);
}
int
texpdf_sprint_matrix (char *buf, const pdf_tmatrix *M)
{
int len;
int prec2 = MIN(dev_unit.precision + 2, DEV_PRECISION_MAX);
int prec0 = MAX(dev_unit.precision, 2);
len = p_dtoa(M->a, prec2, buf);
buf[len++] = ' ';
len += p_dtoa(M->b, prec2, buf+len);
buf[len++] = ' ';
len += p_dtoa(M->c, prec2, buf+len);
buf[len++] = ' ';
len += p_dtoa(M->d, prec2, buf+len);
buf[len++] = ' ';
len += p_dtoa(M->e, prec0, buf+len);
buf[len++] = ' ';
len += p_dtoa(M->f, prec0, buf+len);
buf[len] = '\0';
return len;
}
int
pdf_sprint_rect (char *buf, const pdf_rect *rect)
{
int len;
len = p_dtoa(rect->llx, dev_unit.precision, buf);
buf[len++] = ' ';
len += p_dtoa(rect->lly, dev_unit.precision, buf+len);
buf[len++] = ' ';
len += p_dtoa(rect->urx, dev_unit.precision, buf+len);
buf[len++] = ' ';
len += p_dtoa(rect->ury, dev_unit.precision, buf+len);
buf[len] = '\0';
return len;
}
int
pdf_sprint_coord (char *buf, const pdf_coord *p)
{
int len;
len = p_dtoa(p->x, dev_unit.precision, buf);
buf[len++] = ' ';
len += p_dtoa(p->y, dev_unit.precision, buf+len);
buf[len] = '\0';
return len;
}
int
pdf_sprint_length (char *buf, double value)
{
int len;
len = p_dtoa(value, dev_unit.precision, buf);
buf[len] = '\0';
return len;
}
int
pdf_sprint_number (char *buf, double value)
{
int len;
len = p_dtoa(value, DEV_PRECISION_MAX, buf);
buf[len] = '\0';
return len;
}
static struct
{
int autorotate;
int colormode;
} dev_param = {
1,
1,
};
#define GRAPHICS_MODE 1
#define TEXT_MODE 2
#define STRING_MODE 3
static int motion_state = GRAPHICS_MODE;
#define FORMAT_BUF_SIZE 4096
static char format_buffer[FORMAT_BUF_SIZE];
#define TEXT_WMODE_HH 0
#define TEXT_WMODE_HV 1
#define TEXT_WMODE_VH 4
#define TEXT_WMODE_VV 5
#define TEXT_WMODE_HD 3
#define TEXT_WMODE_VD 7
#define ANGLE_CHANGES(m1,m2) ((abs((m1)-(m2)) % 5) == 0 ? 0 : 1)
#define ROTATE_TEXT(m) ((m) != TEXT_WMODE_HH && (m) != TEXT_WMODE_VV)
static struct {
int font_id;
spt_t offset;
spt_t ref_x;
spt_t ref_y;
spt_t raise;
spt_t leading;
struct {
double slant;
double extend;
int rotate;
} matrix;
double bold_param;
int dir_mode;
int force_reset;
int is_mb;
} text_state = {
-1,
0,
0, 0,
0, 0,
{0.0, 1.0, 0},
0.0,
0,
0,
0
};
#define PDF_FONTTYPE_SIMPLE 1
#define PDF_FONTTYPE_BITMAP 2
#define PDF_FONTTYPE_COMPOSITE 3
struct dev_font {
char short_name[7];
int used_on_this_page;
char *tex_name;
spt_t sptsize;
int font_id;
int enc_id;
int real_font_index;
pdf_obj *resource;
char *used_chars;
int format;
int wmode;
double extend;
double slant;
double bold;
int mapc;
int ucs_group;
int ucs_plane;
int is_unicode;
cff_charsets *cff_charsets;
};
static struct dev_font *dev_fonts = NULL;
static int num_dev_fonts = 0;
static int max_dev_fonts = 0;
static int num_phys_fonts = 0;
#define CURRENTFONT() ((text_state.font_id < 0) ? NULL : &(dev_fonts[text_state.font_id]))
#define GET_FONT(n) (&(dev_fonts[(n)]))
static void
dev_set_text_matrix (pdf_doc *p, spt_t xpos, spt_t ypos, double slant, double extend, int rotate)
{
pdf_tmatrix tm;
int len = 0;
switch (rotate) {
case TEXT_WMODE_VH:
tm.a = slant ; tm.b = 1.0;
tm.c = -extend; tm.d = 0.0 ;
break;
case TEXT_WMODE_HV:
tm.a = 0.0; tm.b = -extend;
tm.c = 1.0; tm.d = -slant ;
break;
case TEXT_WMODE_HH:
tm.a = extend; tm.b = 0.0;
tm.c = slant ; tm.d = 1.0;
break;
case TEXT_WMODE_VV:
tm.a = 1.0; tm.b = -slant;
tm.c = 0.0; tm.d = extend;
break;
case TEXT_WMODE_HD:
tm.a = 0.0; tm.b = extend;
tm.c = -1.0; tm.d = slant ;
break;
case TEXT_WMODE_VD:
tm.a = -1.0; tm.b = slant;
tm.c = 0.0; tm.d = -extend;
break;
}
tm.e = xpos * dev_unit.dvi2pts;
tm.f = ypos * dev_unit.dvi2pts;
format_buffer[len++] = ' ';
len += texpdf_sprint_matrix(format_buffer+len, &tm);
format_buffer[len++] = ' ';
format_buffer[len++] = 'T';
format_buffer[len++] = 'm';
texpdf_doc_add_page_content(p, format_buffer, len);
text_state.ref_x = xpos;
text_state.ref_y = ypos;
text_state.matrix.slant = slant;
text_state.matrix.extend = extend;
text_state.matrix.rotate = rotate;
}
static void
reset_text_state (pdf_doc *p)
{
texpdf_doc_add_page_content(p, " BT", 3);
if (text_state.force_reset ||
text_state.matrix.slant != 0.0 ||
text_state.matrix.extend != 1.0 ||
ROTATE_TEXT(text_state.matrix.rotate)) {
dev_set_text_matrix(p, 0, 0,
text_state.matrix.slant,
text_state.matrix.extend,
text_state.matrix.rotate);
}
text_state.ref_x = 0;
text_state.ref_y = 0;
text_state.offset = 0;
text_state.force_reset = 0;
}
static void
text_mode (pdf_doc *p)
{
switch (motion_state) {
case TEXT_MODE:
break;
case STRING_MODE:
texpdf_doc_add_page_content(p, text_state.is_mb ? ">]TJ" : ")]TJ", 4);
break;
case GRAPHICS_MODE:
reset_text_state(p);
break;
}
motion_state = TEXT_MODE;
text_state.offset = 0;
}
void
texpdf_graphics_mode (pdf_doc *p)
{
switch (motion_state) {
case GRAPHICS_MODE:
break;
case STRING_MODE:
texpdf_doc_add_page_content(p, text_state.is_mb ? ">]TJ" : ")]TJ", 4);
case TEXT_MODE:
texpdf_doc_add_page_content(p, " ET", 3);
text_state.force_reset = 0;
text_state.font_id = -1;
break;
}
motion_state = GRAPHICS_MODE;
}
static void
start_string (pdf_doc *p, spt_t xpos, spt_t ypos, double slant, double extend, int rotate)
{
spt_t delx, dely, error_delx, error_dely;
spt_t desired_delx, desired_dely;
int len = 0;
delx = xpos - text_state.ref_x;
dely = ypos - text_state.ref_y;
switch (rotate) {
case TEXT_WMODE_VH:
desired_delx = dely;
desired_dely = (spt_t) (-(delx - dely*slant)/extend);
format_buffer[len++] = ' ';
len += dev_sprint_bp(format_buffer+len, desired_delx, &error_dely);
format_buffer[len++] = ' ';
len += dev_sprint_bp(format_buffer+len, desired_dely, &error_delx);
error_delx = -error_delx;
break;
case TEXT_WMODE_HV:
desired_delx = (spt_t)(-(dely + delx*slant)/extend);
desired_dely = delx;
format_buffer[len++] = ' ';
len += dev_sprint_bp(format_buffer+len, desired_delx, &error_dely);
format_buffer[len++] = ' ';
len += dev_sprint_bp(format_buffer+len, desired_dely, &error_delx);
error_dely = -error_dely;
break;
case TEXT_WMODE_HH:
desired_delx = (spt_t)((delx - dely*slant)/extend);
desired_dely = dely;
format_buffer[len++] = ' ';
len += dev_sprint_bp(format_buffer+len, desired_delx, &error_delx);
format_buffer[len++] = ' ';
len += dev_sprint_bp(format_buffer+len, desired_dely, &error_dely);
break;
case TEXT_WMODE_VV:
desired_delx = delx;
desired_dely = (spt_t)((dely + delx*slant)/extend);
format_buffer[len++] = ' ';
len += dev_sprint_bp(format_buffer+len, desired_delx, &error_delx);
format_buffer[len++] = ' ';
len += dev_sprint_bp(format_buffer+len, desired_dely, &error_dely);
break;
case TEXT_WMODE_HD:
desired_delx = -(spt_t)(-(dely + delx*slant)/extend);
desired_dely = -delx;
format_buffer[len++] = ' ';
len += dev_sprint_bp(format_buffer+len, desired_delx, &error_dely);
format_buffer[len++] = ' ';
len += dev_sprint_bp(format_buffer+len, desired_dely, &error_delx);
error_delx = -error_delx;
error_dely = -error_dely;
break;
case TEXT_WMODE_VD:
desired_delx = -delx;
desired_dely = -(spt_t)((dely + delx*slant)/extend);
format_buffer[len++] = ' ';
len += dev_sprint_bp(format_buffer+len, desired_delx, &error_delx);
format_buffer[len++] = ' ';
len += dev_sprint_bp(format_buffer+len, desired_dely, &error_dely);
error_delx = -error_delx;
error_dely = -error_dely;
break;
}
texpdf_doc_add_page_content(p, format_buffer, len);
texpdf_doc_add_page_content(p, text_state.is_mb ? " Td[<" : " Td[(", 5);
text_state.ref_x = xpos - error_delx;
text_state.ref_y = ypos - error_dely;
text_state.offset = 0;
}
static void
string_mode (pdf_doc *p, spt_t xpos, spt_t ypos, double slant, double extend, int rotate)
{
switch (motion_state) {
case STRING_MODE:
break;
case GRAPHICS_MODE:
reset_text_state(p);
case TEXT_MODE:
if (text_state.force_reset) {
dev_set_text_matrix(p, xpos, ypos, slant, extend, rotate);
texpdf_doc_add_page_content(p, text_state.is_mb ? "[<" : "[(", 2);
text_state.force_reset = 0;
} else {
start_string(p, xpos, ypos, slant, extend, rotate);
}
break;
}
motion_state = STRING_MODE;
}
static int
dev_set_font (pdf_doc *doc, int font_id)
{
struct dev_font *font;
struct dev_font *real_font;
int text_rotate;
double font_scale;
int len;
int vert_dir, vert_font;
text_mode(doc);
font = GET_FONT(font_id);
ASSERT(font);
if (font->real_font_index >= 0)
real_font = GET_FONT(font->real_font_index);
else
real_font = font;
text_state.is_mb = (font->format == PDF_FONTTYPE_COMPOSITE) ? 1 : 0;
vert_font = font->wmode ? 1 : 0;
if (dev_param.autorotate) {
vert_dir = text_state.dir_mode;
} else {
vert_dir = vert_font;
}
text_rotate = (vert_font << 2)|vert_dir;
if (font->slant != text_state.matrix.slant ||
font->extend != text_state.matrix.extend ||
ANGLE_CHANGES(text_rotate, text_state.matrix.rotate)) {
text_state.force_reset = 1;
}
text_state.matrix.slant = font->slant;
text_state.matrix.extend = font->extend;
text_state.matrix.rotate = text_rotate;
if (!real_font->resource) {
real_font->resource = texpdf_get_font_reference(real_font->font_id);
real_font->used_chars = texpdf_get_font_usedchars(real_font->font_id);
}
if (!real_font->used_on_this_page) {
texpdf_doc_add_page_resource(doc, "Font",
real_font->short_name,
texpdf_link_obj(real_font->resource));
real_font->used_on_this_page = 1;
}
font_scale = (double) font->sptsize * dev_unit.dvi2pts;
len = sprintf(format_buffer, " /%s", real_font->short_name);
format_buffer[len++] = ' ';
len += p_dtoa(font_scale, MIN(dev_unit.precision+1, DEV_PRECISION_MAX), format_buffer+len);
format_buffer[len++] = ' ';
format_buffer[len++] = 'T';
format_buffer[len++] = 'f';
texpdf_doc_add_page_content(doc, format_buffer, len);
if (font->bold > 0.0 || font->bold != text_state.bold_param) {
if (font->bold <= 0.0)
len = sprintf(format_buffer, " 0 Tr");
else
len = sprintf(format_buffer, " 2 Tr %.6f w", font->bold);
texpdf_doc_add_page_content(doc, format_buffer, len);
}
text_state.bold_param = font->bold;
text_state.font_id = font_id;
return 0;
}
#if 0#endif
int
texpdf_dev_get_font_wmode (int font_id)
{
struct dev_font *font;
font = GET_FONT(font_id);
if (font) {
return font->wmode;
}
return 0;
}
static unsigned char sbuf0[FORMAT_BUF_SIZE];
static unsigned char sbuf1[FORMAT_BUF_SIZE];
static int
handle_multibyte_string (struct dev_font *font,
const unsigned char **str_ptr, int *str_len, int ctype)
{
const unsigned char *p;
int i, length;
p = *str_ptr;
length = *str_len;
if (ctype == -1 && font->cff_charsets) {
const unsigned char *inbuf = p;
unsigned char *outbuf = sbuf0;
for (i = 0; i < length; i += 2) {
unsigned int gid;
gid = *inbuf++ << 8;
gid += *inbuf++;
gid = cff_charsets_lookup_cid(font->cff_charsets, gid);
*outbuf++ = gid >> 8;
*outbuf++ = gid & 0xff;
}
p = sbuf0;
length = outbuf - sbuf0;
}
else if (font->is_unicode) {
if (ctype == 1) {
if (length * 4 >= FORMAT_BUF_SIZE) {
WARN("Too long string...");
return -1;
}
for (i = 0; i < length; i++) {
sbuf1[i*4 ] = font->ucs_group;
sbuf1[i*4+1] = font->ucs_plane;
sbuf1[i*4+2] = '\0';
sbuf1[i*4+3] = p[i];
}
length *= 4;
} else if (ctype == 2) {
int len = 0;
if (length * 2 >= FORMAT_BUF_SIZE) {
WARN("Too long string...");
return -1;
}
for (i = 0; i < length; i += 2, len += 4) {
sbuf1[len ] = font->ucs_group;
if ((p[i] & 0xf8) == 0xd8) {
int c;
if ((p[i] & 0xfc) != 0xd8 || i + 2 >= length || (p[i+2] & 0xfc) != 0xdc) {
WARN("Invalid surrogate p[%d]=%02X...", i, p[i]);
return -1;
}
c = (((p[i] & 0x03) << 10) | (p[i+1] << 2) | (p[i+2] & 0x03)) + 0x100;
sbuf1[len+1] = (c >> 8) & 0xff;
sbuf1[len+2] = c & 0xff;
i += 2;
} else {
sbuf1[len+1] = font->ucs_plane;
sbuf1[len+2] = p[i];
}
sbuf1[len+3] = p[i+1];
}
length = len;
}
p = sbuf1;
} else if (ctype == 1 && font->mapc >= 0) {
if (length * 2 >= FORMAT_BUF_SIZE) {
WARN("Too long string...");
return -1;
}
for (i = 0; i < length; i++) {
sbuf1[i*2 ] = (font->mapc & 0xff);
sbuf1[i*2+1] = p[i];
}
length *= 2;
p = sbuf1;
}
#ifdef XETEX
if (ctype != -1 && font->enc_id >= 0) {
#else
if (font->enc_id >= 0) {
#endif
const unsigned char *inbuf;
unsigned char *outbuf;
long inbytesleft, outbytesleft;
CMap *cmap;
cmap = texpdf_CMap_cache_get(font->enc_id);
inbuf = p;
outbuf = sbuf0;
inbytesleft = length;
outbytesleft = FORMAT_BUF_SIZE;
texpdf_CMap_decode(cmap,
&inbuf, &inbytesleft, &outbuf, &outbytesleft);
if (inbytesleft != 0) {
WARN("CMap conversion failed. (%d bytes remains)", inbytesleft);
return -1;
}
length = FORMAT_BUF_SIZE - outbytesleft;
p = sbuf0;
}
*str_ptr = p;
*str_len = length;
return 0;
}
static pdf_coord *dev_coords = NULL;
static int num_dev_coords = 0;
static int max_dev_coords = 0;
void texpdf_dev_get_coord(double *xpos, double *ypos)
{
if (num_dev_coords > 0) {
*xpos = dev_coords[num_dev_coords-1].x;
*ypos = dev_coords[num_dev_coords-1].y;
} else {
*xpos = *ypos = 0.0;
}
}
void texpdf_dev_push_coord(double xpos, double ypos)
{
if (num_dev_coords >= max_dev_coords) {
max_dev_coords += 4;
dev_coords = RENEW(dev_coords, max_dev_coords, pdf_coord);
}
dev_coords[num_dev_coords].x = xpos;
dev_coords[num_dev_coords].y = ypos;
num_dev_coords++;
}
void texpdf_dev_pop_coord(void)
{
if (num_dev_coords > 0) num_dev_coords--;
}
void
texpdf_dev_set_string (pdf_doc *p, spt_t xpos, spt_t ypos,
const void *instr_ptr, int instr_len,
spt_t width,
int font_id, int ctype)
{
struct dev_font *font;
struct dev_font *real_font;
const unsigned char *str_ptr;
int length, i, len = 0;
spt_t kern, delh, delv;
spt_t text_xorigin;
spt_t text_yorigin;
if (font_id < 0 || font_id >= num_dev_fonts) {
ERROR("Invalid font: %d (%d)", font_id, num_dev_fonts);
return;
}
if (font_id != text_state.font_id) {
dev_set_font(p, font_id);
}
font = CURRENTFONT();
if (!font) {
ERROR("Currentfont not set.");
return;
}
if (font->real_font_index >= 0)
real_font = GET_FONT(font->real_font_index);
else
real_font = font;
text_xorigin = text_state.ref_x;
text_yorigin = text_state.ref_y;
str_ptr = instr_ptr;
length = instr_len;
if (font->format == PDF_FONTTYPE_COMPOSITE) {
if (handle_multibyte_string(font, &str_ptr, &length, ctype) < 0) {
ERROR("Error in converting input string...");
return;
}
if (real_font->used_chars != NULL) {
for (i = 0; i < length; i += 2) {
unsigned short cid = (str_ptr[i] << 8) | str_ptr[i + 1];
add_to_used_chars2(real_font->used_chars, cid);
}
}
} else {
if (real_font->used_chars != NULL) {
for (i = 0; i < length; i++)
real_font->used_chars[str_ptr[i]] = 1;
}
}
if (num_dev_coords > 0) {
xpos -= bpt2spt(dev_coords[num_dev_coords-1].x);
ypos -= bpt2spt(dev_coords[num_dev_coords-1].y);
}
if (text_state.dir_mode==0) {
delh = text_xorigin + text_state.offset - xpos;
delv = ypos - text_yorigin;
} else if (text_state.dir_mode==1) {
delh = ypos - text_yorigin + text_state.offset;
delv = xpos - text_xorigin;
} else {
delh = ypos + text_yorigin + text_state.offset;
delv = xpos + text_xorigin;
}
#define WORD_SPACE_MAX(f) (spt_t) (3.0 * (f)->extend * (f)->sptsize)
if (text_state.force_reset ||
labs(delv) > dev_unit.min_bp_val ||
labs(delh) > WORD_SPACE_MAX(font)) {
text_mode(p);
kern = 0;
} else {
kern = (spt_t) (1000.0 / font->extend * delh / font->sptsize);
}
if (motion_state != STRING_MODE)
string_mode(p, xpos, ypos,
font->slant, font->extend, text_state.matrix.rotate);
else if (kern != 0) {
text_state.offset -=
(spt_t) (kern * font->extend * (font->sptsize / 1000.0));
format_buffer[len++] = text_state.is_mb ? '>' : ')';
if (font->wmode)
len += p_itoa(-kern, format_buffer + len);
else {
len += p_itoa( kern, format_buffer + len);
}
format_buffer[len++] = text_state.is_mb ? '<' : '(';
texpdf_doc_add_page_content(p, format_buffer, len);
len = 0;
}
if (text_state.is_mb) {
if (FORMAT_BUF_SIZE - len < 2 * length)
ERROR("Buffer overflow...");
for (i = 0; i < length; i++) {
int first, second;
first = (str_ptr[i] >> 4) & 0x0f;
second = str_ptr[i] & 0x0f;
format_buffer[len++] = ((first >= 10) ? first + 'W' : first + '0');
format_buffer[len++] = ((second >= 10) ? second + 'W' : second + '0');
}
} else {
len += pdfobj_escape_str(format_buffer + len,
FORMAT_BUF_SIZE - len, str_ptr, length);
}
texpdf_doc_add_page_content(p, format_buffer, len);
text_state.offset += width;
}
void
texpdf_init_device (pdf_doc *p, double dvi2pts, int precision, int black_and_white)
{
if (precision < 0 ||
precision > DEV_PRECISION_MAX)
WARN("Number of decimal digits out of range [0-%d].",
DEV_PRECISION_MAX);
if (precision < 0) {
dev_unit.precision = 0;
} else if (precision > DEV_PRECISION_MAX) {
dev_unit.precision = DEV_PRECISION_MAX;
} else {
dev_unit.precision = precision;
}
dev_unit.dvi2pts = dvi2pts;
dev_unit.min_bp_val = (long) ROUND(1.0/(ten_pow[dev_unit.precision]*dvi2pts), 1);
if (dev_unit.min_bp_val < 0)
dev_unit.min_bp_val = -dev_unit.min_bp_val;
dev_param.colormode = (black_and_white ? 0 : 1);
texpdf_graphics_mode(p);
texpdf_color_clear_stack();
texpdf_dev_init_gstates();
num_dev_fonts = max_dev_fonts = 0;
dev_fonts = NULL;
num_dev_coords = max_dev_coords = 0;
dev_coords = NULL;
}
void
texpdf_close_device (void)
{
if (dev_fonts) {
int i;
for (i = 0; i < num_dev_fonts; i++) {
if (dev_fonts[i].tex_name)
RELEASE(dev_fonts[i].tex_name);
if (dev_fonts[i].resource)
texpdf_release_obj(dev_fonts[i].resource);
dev_fonts[i].tex_name = NULL;
dev_fonts[i].resource = NULL;
dev_fonts[i].cff_charsets = NULL;
}
RELEASE(dev_fonts);
}
if (dev_coords) RELEASE(dev_coords);
texpdf_dev_clear_gstates();
}
void
texpdf_dev_reset_fonts (int newpage)
{
int i;
for (i = 0; i < num_dev_fonts; i++) {
dev_fonts[i].used_on_this_page = 0;
}
text_state.font_id = -1;
text_state.matrix.slant = 0.0;
text_state.matrix.extend = 1.0;
text_state.matrix.rotate = TEXT_WMODE_HH;
if (newpage)
text_state.bold_param = 0.0;
text_state.is_mb = 0;
}
void
texpdf_dev_reset_color (pdf_doc *p, int force)
{
pdf_color *sc, *fc;
texpdf_color_get_current(&sc, &fc);
texpdf_dev_set_color(p, sc, 0, force);
texpdf_dev_set_color(p, fc, 0x20, force);
}
#if 0#endif
void
texpdf_dev_bop (pdf_doc *p, const pdf_tmatrix *M)
{
texpdf_graphics_mode(p);
text_state.force_reset = 0;
texpdf_dev_gsave(p);
texpdf_dev_concat(p, M);
texpdf_dev_reset_fonts(1);
texpdf_dev_reset_color(p, 0);
}
void
texpdf_dev_eop (pdf_doc *p)
{
int depth;
texpdf_graphics_mode(p);
depth = texpdf_dev_current_depth();
if (depth != 1) {
WARN("Unbalenced q/Q nesting...: %d", depth);
texpdf_dev_grestore_to(p, 0);
} else {
texpdf_dev_grestore(p);
}
}
static void
print_fontmap (const char *font_name, fontmap_rec *mrec)
{
if (!mrec)
return;
MESG("\n");
MESG("fontmap: %s -> %s", font_name, mrec->font_name);
if (mrec->enc_name)
MESG("(%s)", mrec->enc_name);
if (mrec->opt.extend != 1.0)
MESG("[extend:%g]", mrec->opt.extend);
if (mrec->opt.slant != 0.0)
MESG("[slant:%g]", mrec->opt.slant);
if (mrec->opt.bold != 0.0)
MESG("[bold:%g]", mrec->opt.bold);
if (mrec->opt.flags & FONTMAP_OPT_NOEMBED)
MESG("[noemb]");
if (mrec->opt.mapc >= 0)
MESG("[map:<%02x>]", mrec->opt.mapc);
if (mrec->opt.charcoll)
MESG("[csi:%s]", mrec->opt.charcoll);
if (mrec->opt.index)
MESG("[index:%d]", mrec->opt.index);
switch (mrec->opt.style) {
case FONTMAP_STYLE_BOLD:
MESG("[style:bold]");
break;
case FONTMAP_STYLE_ITALIC:
MESG("[style:italic]");
break;
case FONTMAP_STYLE_BOLDITALIC:
MESG("[style:bolditalic]");
break;
}
MESG("\n");
}
int texpdf_dev_load_native_font(const char *filename, uint32_t index,
spt_t ptsize, int layout_dir, int extend, int slant, int embolden) {
fontmap_rec *mrec;
char *fontmap_key = malloc(strlen(filename) + 40);
sprintf(fontmap_key, "%s/%u/%c/%d/%d/%d", filename, index, layout_dir == 0 ? 'H' : 'V', extend, slant, embolden);
mrec = texpdf_lookup_fontmap_record(native_fontmap, fontmap_key);
if (mrec == NULL) {
if (texpdf_insert_native_fontmap_record(filename, index, layout_dir, extend, slant, embolden) == -1) {
ERROR("Cannot proceed without the \"native\" font: %s", filename);
}
mrec = texpdf_lookup_fontmap_record(native_fontmap, fontmap_key);
}
return texpdf_dev_locate_font(native_fontmap, fontmap_key, ptsize);
}
int
texpdf_dev_locate_font (fontmap_t* map, const char *font_name, spt_t ptsize)
{
int i;
fontmap_rec *mrec;
struct dev_font *font;
if (!font_name)
return -1;
if (ptsize == 0) {
ERROR("texpdf_dev_locate_font() called with the zero ptsize.");
return -1;
}
for (i = 0; i < num_dev_fonts; i++) {
if (strcmp(font_name, dev_fonts[i].tex_name) == 0) {
if (ptsize == dev_fonts[i].sptsize)
return i;
if (dev_fonts[i].format != PDF_FONTTYPE_BITMAP)
break;
}
}
if (num_dev_fonts >= max_dev_fonts) {
max_dev_fonts += 16;
dev_fonts = RENEW(dev_fonts, max_dev_fonts, struct dev_font);
}
font = &dev_fonts[num_dev_fonts];
mrec = texpdf_lookup_fontmap_record(map, font_name);
if (verbose > 1)
print_fontmap(font_name, mrec);
font->font_id = pdf_font_findresource(map, font_name, ptsize * dev_unit.dvi2pts, mrec);
if (font->font_id < 0)
return -1;
if (mrec)
font->cff_charsets = mrec->opt.cff_charsets;
if (i < num_dev_fonts) {
font->real_font_index = i;
strcpy(font->short_name, dev_fonts[i].short_name);
}
else {
font->real_font_index = -1;
font->short_name[0] = 'F';
p_itoa(num_phys_fonts + 1, &font->short_name[1]);
num_phys_fonts++;
}
font->used_on_this_page = 0;
font->tex_name = NEW(strlen(font_name) + 1, char);
strcpy(font->tex_name, font_name);
font->sptsize = ptsize;
switch (texpdf_get_font_subtype(font->font_id)) {
case PDF_FONT_FONTTYPE_TYPE3:
font->format = PDF_FONTTYPE_BITMAP;
break;
case PDF_FONT_FONTTYPE_TYPE0:
font->format = PDF_FONTTYPE_COMPOSITE;
break;
default:
font->format = PDF_FONTTYPE_SIMPLE;
break;
}
font->wmode = texpdf_get_font_wmode (font->font_id);
font->enc_id = texpdf_get_font_encoding(font->font_id);
font->resource = NULL;
font->used_chars = NULL;
font->extend = 1.0;
font->slant = 0.0;
font->bold = 0.0;
font->mapc = -1;
font->is_unicode = 0;
font->ucs_group = 0;
font->ucs_plane = 0;
if (mrec) {
font->extend = mrec->opt.extend;
font->slant = mrec->opt.slant;
font->bold = mrec->opt.bold;
if (mrec->opt.mapc >= 0)
font->mapc = (mrec->opt.mapc >> 8) & 0xff;
else {
font->mapc = -1;
}
if (mrec->enc_name &&
!strcmp(mrec->enc_name, "unicode")) {
font->is_unicode = 1;
if (mrec->opt.mapc >= 0) {
font->ucs_group = (mrec->opt.mapc >> 24) & 0xff;
font->ucs_plane = (mrec->opt.mapc >> 16) & 0xff;
} else {
font->ucs_group = 0;
font->ucs_plane = 0;
}
} else {
font->is_unicode = 0;
}
}
return num_dev_fonts++;
}
static int
dev_sprint_line (char *buf, spt_t width,
spt_t p0_x, spt_t p0_y, spt_t p1_x, spt_t p1_y)
{
int len = 0;
double w;
w = width * dev_unit.dvi2pts;
len += p_dtoa(w, MIN(dev_unit.precision+1, DEV_PRECISION_MAX), buf+len);
buf[len++] = ' ';
buf[len++] = 'w';
buf[len++] = ' ';
len += dev_sprint_bp(buf+len, p0_x, NULL);
buf[len++] = ' ';
len += dev_sprint_bp(buf+len, p0_y, NULL);
buf[len++] = ' ';
buf[len++] = 'm';
buf[len++] = ' ';
len += dev_sprint_bp(buf+len, p1_x, NULL);
buf[len++] = ' ';
len += dev_sprint_bp(buf+len, p1_y, NULL);
buf[len++] = ' ';
buf[len++] = 'l';
buf[len++] = ' ';
buf[len++] = 'S';
return len;
}
#define PDF_LINE_THICKNESS_MAX 5.0
void
texpdf_dev_set_rule (pdf_doc *p, spt_t xpos, spt_t ypos, spt_t width, spt_t height)
{
int len = 0;
double width_in_bp;
if (num_dev_coords > 0) {
xpos -= bpt2spt(dev_coords[num_dev_coords-1].x);
ypos -= bpt2spt(dev_coords[num_dev_coords-1].y);
}
texpdf_graphics_mode(p);
format_buffer[len++] = ' ';
format_buffer[len++] = 'q';
format_buffer[len++] = ' ';
width_in_bp = ((width < height) ? width : height) * dev_unit.dvi2pts;
if (width_in_bp < 0.0 ||
width_in_bp > PDF_LINE_THICKNESS_MAX) {
pdf_rect rect;
rect.llx = dev_unit.dvi2pts * xpos;
rect.lly = dev_unit.dvi2pts * ypos;
rect.urx = dev_unit.dvi2pts * width;
rect.ury = dev_unit.dvi2pts * height;
len += pdf_sprint_rect(format_buffer+len, &rect);
format_buffer[len++] = ' ';
format_buffer[len++] = 'r';
format_buffer[len++] = 'e';
format_buffer[len++] = ' ';
format_buffer[len++] = 'f';
} else {
if (width > height) {
if (height < dev_unit.min_bp_val) {
WARN("Too thin line: height=%ld (%g bp)", height, width_in_bp);
WARN("Please consider using \"-d\" option.");
}
len += dev_sprint_line(format_buffer+len,
height,
xpos,
ypos + height/2,
xpos + width,
ypos + height/2);
} else {
if (width < dev_unit.min_bp_val) {
WARN("Too thin line: width=%ld (%g bp)", width, width_in_bp);
WARN("Please consider using \"-d\" option.");
}
len += dev_sprint_line(format_buffer+len,
width,
xpos + width/2,
ypos,
xpos + width/2,
ypos + height);
}
}
format_buffer[len++] = ' ';
format_buffer[len++] = 'Q';
texpdf_doc_add_page_content(p, format_buffer, len);
}
void
texpdf_dev_set_rect (pdf_rect *rect,
spt_t x_user, spt_t y_user,
spt_t width, spt_t height, spt_t depth)
{
double dev_x, dev_y;
pdf_coord p0, p1, p2, p3;
double min_x, min_y, max_x, max_y;
dev_x = x_user * dev_unit.dvi2pts;
dev_y = y_user * dev_unit.dvi2pts;
if (text_state.dir_mode) {
p0.x = dev_x - dev_unit.dvi2pts * depth;
p0.y = dev_y - dev_unit.dvi2pts * width;
p1.x = dev_x + dev_unit.dvi2pts * height;
p1.y = p0.y;
p2.x = p1.x;
p2.y = dev_y;
p3.x = p0.x;
p3.y = p2.y;
} else {
p0.x = dev_x;
p0.y = dev_y - dev_unit.dvi2pts * depth;
p1.x = dev_x + dev_unit.dvi2pts * width;
p1.y = p0.y;
p2.x = p1.x;
p2.y = dev_y + dev_unit.dvi2pts * height;
p3.x = p0.x;
p3.y = p2.y;
}
texpdf_dev_transform(&p0, NULL);
texpdf_dev_transform(&p1, NULL);
texpdf_dev_transform(&p2, NULL);
texpdf_dev_transform(&p3, NULL);
min_x = MIN(p0.x , p1.x);
min_x = MIN(min_x, p2.x);
min_x = MIN(min_x, p3.x);
max_x = MAX(p0.x , p1.x);
max_x = MAX(max_x, p2.x);
max_x = MAX(max_x, p3.x);
min_y = MIN(p0.y , p1.y);
min_y = MIN(min_y, p2.y);
min_y = MIN(min_y, p3.y);
max_y = MAX(p0.y , p1.y);
max_y = MAX(max_y, p2.y);
max_y = MAX(max_y, p3.y);
rect->llx = min_x;
rect->lly = min_y;
rect->urx = max_x;
rect->ury = max_y;
return;
}
int
texpdf_dev_get_dirmode (void)
{
return text_state.dir_mode;
}
void
texpdf_dev_set_dirmode (int text_dir)
{
struct dev_font *font;
int text_rotate;
int vert_dir, vert_font;
font = CURRENTFONT();
vert_font = (font && font->wmode) ? 1 : 0;
if (dev_param.autorotate) {
vert_dir = text_dir;
} else {
vert_dir = vert_font;
}
text_rotate = (vert_font << 2)|vert_dir;
if (font &&
ANGLE_CHANGES(text_rotate, text_state.matrix.rotate)) {
text_state.force_reset = 1;
}
text_state.matrix.rotate = text_rotate;
text_state.dir_mode = text_dir;
}
static void
dev_set_param_autorotate (int auto_rotate)
{
struct dev_font *font;
int text_rotate, vert_font, vert_dir;
font = CURRENTFONT();
vert_font = (font && font->wmode) ? 1 : 0;
if (auto_rotate) {
vert_dir = text_state.dir_mode;
} else {
vert_dir = vert_font;
}
text_rotate = (vert_font << 2)|vert_dir;
if (ANGLE_CHANGES(text_rotate, text_state.matrix.rotate)) {
text_state.force_reset = 1;
}
text_state.matrix.rotate = text_rotate;
dev_param.autorotate = auto_rotate;
}
int
texpdf_dev_get_param (int param_type)
{
int value = 0;
switch (param_type) {
case PDF_DEV_PARAM_AUTOROTATE:
value = dev_param.autorotate;
break;
case PDF_DEV_PARAM_COLORMODE:
value = dev_param.colormode;
break;
default:
ERROR("Unknown device parameter: %d", param_type);
}
return value;
}
void
texpdf_dev_set_param (int param_type, int value)
{
switch (param_type) {
case PDF_DEV_PARAM_AUTOROTATE:
dev_set_param_autorotate(value);
break;
case PDF_DEV_PARAM_COLORMODE:
dev_param.colormode = value;
break;
default:
ERROR("Unknown device parameter: %d", param_type);
}
return;
}
int
texpdf_dev_put_image (pdf_doc *doc,
int id,
transform_info *p,
double ref_x,
double ref_y,
int track_boxes)
{
char *res_name;
pdf_tmatrix M, M1;
pdf_rect r;
int len = 0;
if (num_dev_coords > 0) {
ref_x -= dev_coords[num_dev_coords-1].x;
ref_y -= dev_coords[num_dev_coords-1].y;
}
pdf_copymatrix(&M, &(p->matrix));
M.e += ref_x; M.f += ref_y;
if (dev_param.autorotate &&
text_state.dir_mode) {
double tmp;
tmp = -M.a; M.a = M.b; M.b = tmp;
tmp = -M.c; M.c = M.d; M.d = tmp;
}
texpdf_graphics_mode(doc);
texpdf_dev_gsave(doc);
texpdf_ximage_scale_image(id, &M1, &r, p);
pdf_concatmatrix(&M, &M1);
texpdf_dev_concat(doc, &M);
if (p->flags & INFO_DO_CLIP) {
#if 0#else
texpdf_dev_rectclip(doc, r.llx, r.lly, r.urx - r.llx, r.ury - r.lly);
#endif
}
res_name = texpdf_ximage_get_resname(id);
len = sprintf(work_buffer, " /%s Do", res_name);
texpdf_doc_add_page_content(doc, work_buffer, len);
texpdf_dev_grestore(doc);
texpdf_doc_add_page_resource(doc, "XObject",
res_name,
texpdf_ximage_get_reference(id));
#ifdef XETEX
if (track_boxes) {
pdf_tmatrix P;
int i;
pdf_rect rect;
pdf_coord corner[4];
texpdf_dev_set_rect(&rect, 65536 * ref_x, 65536 * ref_y,
65536 * (r.urx - r.llx), 65536 * (r.ury - r.lly), 0);
corner[0].x = rect.llx; corner[0].y = rect.lly;
corner[1].x = rect.llx; corner[1].y = rect.ury;
corner[2].x = rect.urx; corner[2].y = rect.ury;
corner[3].x = rect.urx; corner[3].y = rect.lly;
pdf_copymatrix(&P, &(p->matrix));
for (i = 0; i < 4; ++i) {
corner[i].x -= rect.llx;
corner[i].y -= rect.lly;
texpdf_dev_transform(&(corner[i]), &P);
corner[i].x += rect.llx;
corner[i].y += rect.lly;
}
rect.llx = corner[0].x;
rect.lly = corner[0].y;
rect.urx = corner[0].x;
rect.ury = corner[0].y;
for (i = 0; i < 4; ++i) {
if (corner[i].x < rect.llx)
rect.llx = corner[i].x;
if (corner[i].x > rect.urx)
rect.urx = corner[i].x;
if (corner[i].y < rect.lly)
rect.lly = corner[i].y;
if (corner[i].y > rect.ury)
rect.ury = corner[i].y;
}
texpdf_doc_expand_box(doc, &rect);
}
#endif
return 0;
}
void
texpdf_transform_info_clear (transform_info *info)
{
info->width = 0.0;
info->height = 0.0;
info->depth = 0.0;
info->bbox.llx = 0.0;
info->bbox.lly = 0.0;
info->bbox.urx = 0.0;
info->bbox.ury = 0.0;
pdf_setmatrix(&(info->matrix), 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
info->flags = 0;
}