#include "libtexpdf.h"
#define detM(M) ((M).a * (M).d - (M).b * (M).c)
#define detP(M) ((M)->a * (M)->d - (M)->b * (M)->c)
static int
inversematrix (pdf_tmatrix *W, const pdf_tmatrix *M)
{
double det;
det = detP(M);
if (fabs(det) < 1.e-8) {
WARN("Inverting matrix with zero determinant...");
return -1;
}
W->a = (M->d) / det; W->b = -(M->b) / det;
W->c = -(M->c) / det; W->d = (M->a) / det;
W->e = (M->c) * (M->f) - (M->d) * (M->e);
W->f = (M->b) * (M->e) - (M->a) * (M->f);
return 0;
}
#define vecprd(v,w) ((v).x * (w).x + (v).y * (w).y)
#define vecrot(v,w) ((v).x * (w).y - (v).y * (w).x)
#define dsign(v) (((v) >= 0.0) ? 1.0 : -1.0)
#define vecang(v,w) ( \
dsign(vecrot((v),(w))) * \
acos(vecprd((v),(w)) / sqrt(vecprd((v),(v)) * vecprd((w),(w)))) \
)
static int
pdf_coord__equal (const pdf_coord *p1, const pdf_coord *p2)
{
if (fabs(p1->x - p2->x) < 1.e-7 &&
fabs(p1->y - p2->y) < 1.e-7)
return 1;
return 0;
}
#define COORD_EQUAL(p,q) pdf_coord__equal((p),(q))
#if 0#endif
static int
pdf_coord__transform (pdf_coord *p, const pdf_tmatrix *M)
{
double x, y;
x = p->x; y = p->y;
p->x = x * M->a + y * M->c + M->e;
p->y = x * M->b + y * M->d + M->f;
return 0;
}
#if 0#endif
static int
pdf_coord__dtransform (pdf_coord *p, const pdf_tmatrix *M)
{
double x, y;
x = p->x; y = p->y;
p->x = x * M->a + y * M->c;
p->y = x * M->b + y * M->d;
return 0;
}
static int
pdf_coord__idtransform (pdf_coord *p, const pdf_tmatrix *M)
{
pdf_tmatrix W;
double x, y;
int error;
error = inversematrix(&W, M);
if (error)
return error;
x = p->x; y = p->y;
p->x = x * W.a + y * W.c;
p->y = x * W.b + y * W.d;
return 0;
}
void
texpdf_invertmatrix (pdf_tmatrix *M)
{
pdf_tmatrix W;
double det;
ASSERT(M);
det = detP(M);
if (fabs(det) < 1.e-8) {
WARN("Inverting matrix with zero determinant...");
W.a = 1.0; W.c = 0.0;
W.b = 0.0; W.d = 1.0;
W.e = 0.0; W.f = 0.0;
} else {
W.a = (M->d) / det; W.b = -(M->b) / det;
W.c = -(M->c) / det; W.d = (M->a) / det;
W.e = (M->c) * (M->f) - (M->d) * (M->e);
W.f = (M->b) * (M->e) - (M->a) * (M->f);
W.e /= det; W.f /= det;
}
pdf_copymatrix(M, &W);
return;
}
typedef struct pa_elem_
{
int type;
pdf_coord p[3];
} pa_elem;
struct pdf_path_
{
int num_paths;
int max_paths;
pa_elem *path;
};
static const struct {
char opchr;
int n_pts;
const char *strkey;
} petypes[] = {
#define PE_TYPE__INVALID -1
#define PE_TYPE__MOVETO 0
{'m', 1, "moveto" },
#define PE_TYPE__LINETO 1
{'l', 1, "lineto" },
#define PE_TYPE__CURVETO 2
{'c', 3, "curveto" },
#define PE_TYPE__CURVETO_V 3
{'v', 2, "vcurveto"},
#define PE_TYPE__CURVETO_Y 4
{'y', 2, "ycurveto"},
#define PE_TYPE__CLOSEPATH 5
{'h', 0, "closepath"},
#define PE_TYPE__TERMINATE 6
{' ', 0, NULL}
};
#define PE_VALID(p) ((p) && \
(p)->type > PE_TYPE__INVALID && (p)->type < PE_TYPE__TERMINATE)
#define PE_N_PTS(p) (PE_VALID((p)) ? petypes[(p)->type].n_pts : 0)
#define PE_OPCHR(p) (PE_VALID((p)) ? petypes[(p)->type].opchr : ' ')
#define PA_LENGTH(pa) ((pa)->num_paths)
#define GS_FLAG_CURRENTPOINT_SET (1 << 0)
#define FORMAT_BUFF_LEN 1024
static char fmt_buf[FORMAT_BUFF_LEN];
static void
init_a_path (pdf_path *p)
{
ASSERT(p);
p->num_paths = 0;
p->max_paths = 0;
p->path = NULL;
return;
}
static void
pdf_path__clearpath (pdf_path *p)
{
ASSERT(p);
p->num_paths = 0;
return;
}
static int
pdf_path__growpath (pdf_path *p, int max_pe)
{
if (max_pe < p->max_paths)
return 0;
p->max_paths = MAX(p->max_paths + 8, max_pe);
p->path = RENEW(p->path, p->max_paths, pa_elem);
return 0;
}
static void
clear_a_path (pdf_path *p)
{
ASSERT(p);
if (p->path)
RELEASE(p->path);
p->path = NULL;
p->num_paths = 0;
p->max_paths = 0;
return;
}
static int
pdf_path__copypath (pdf_path *p1, const pdf_path *p0)
{
pa_elem *pe0, *pe1;
int i;
pdf_path__growpath(p1, PA_LENGTH(p0));
for (i = 0; i < PA_LENGTH(p0); i++) {
pe1 = &(p1->path[i]);
pe0 = &(p0->path[i]);
pe1->type = pe0->type;
pe1->p[0].x = pe0->p[0].x;
pe1->p[0].y = pe0->p[0].y;
pe1->p[1].x = pe0->p[1].x;
pe1->p[1].y = pe0->p[1].y;
pe1->p[2].x = pe0->p[2].x;
pe1->p[2].y = pe0->p[2].y;
}
p1->num_paths = PA_LENGTH(p0);
return 0;
}
static int
pdf_path__moveto (pdf_path *pa,
pdf_coord *cp,
const pdf_coord *p0)
{
pa_elem *pe;
pdf_path__growpath(pa, PA_LENGTH(pa) + 1);
if (PA_LENGTH(pa) > 0) {
pe = &pa->path[pa->num_paths-1];
if (pe->type == PE_TYPE__MOVETO) {
pe->p[0].x = cp->x = p0->x;
pe->p[0].y = cp->y = p0->y;
return 0;
}
}
pe = &pa->path[pa->num_paths++];
pe->type = PE_TYPE__MOVETO;
pe->p[0].x = cp->x = p0->x;
pe->p[0].y = cp->y = p0->y;
return 0;
}
static pa_elem *
pdf_path__next_pe (pdf_path *pa, const pdf_coord *cp)
{
pa_elem *pe;
pdf_path__growpath(pa, PA_LENGTH(pa) + 2);
if (PA_LENGTH(pa) == 0) {
pe = &pa->path[pa->num_paths++];
pe->type = PE_TYPE__MOVETO;
pe->p[0].x = cp->x;
pe->p[0].y = cp->y;
return &pa->path[pa->num_paths++];
}
pe = &pa->path[pa->num_paths-1];
switch (pe->type) {
case PE_TYPE__MOVETO:
pe->p[0].x = cp->x;
pe->p[0].y = cp->y;
break;
case PE_TYPE__LINETO:
if (!COORD_EQUAL(&pe->p[0], cp)) {
pe = &pa->path[pa->num_paths++];
pe->type = PE_TYPE__MOVETO;
pe->p[0].x = cp->x;
pe->p[0].y = cp->y;
}
break;
case PE_TYPE__CURVETO:
if (!COORD_EQUAL(&pe->p[2], cp)) {
pe = &pa->path[pa->num_paths++];
pe->type = PE_TYPE__MOVETO;
pe->p[0].x = cp->x;
pe->p[0].y = cp->y;
}
break;
case PE_TYPE__CURVETO_Y:
case PE_TYPE__CURVETO_V:
if (!COORD_EQUAL(&pe->p[1], cp)) {
pe = &pa->path[pa->num_paths++];
pe->type = PE_TYPE__MOVETO;
pe->p[0].x = cp->x;
pe->p[0].y = cp->y;
}
break;
case PE_TYPE__CLOSEPATH:
pe = &pa->path[pa->num_paths++];
pe->type = PE_TYPE__MOVETO;
pe->p[0].x = cp->x;
pe->p[0].y = cp->y;
break;
}
return &pa->path[pa->num_paths++];
}
static int
pdf_path__transform (pdf_path *pa, const pdf_tmatrix *M)
{
pa_elem *pe;
int n = 0, i;
ASSERT(pa && M);
for (i = 0; i < PA_LENGTH(pa); i++) {
pe = &(pa->path[i]);
n = PE_N_PTS(pe);
while (n-- > 0)
pdf_coord__transform(&(pe->p[n]), M);
}
return 0;
}
static int
pdf_path__lineto (pdf_path *pa,
pdf_coord *cp,
const pdf_coord *p0)
{
pa_elem *pe;
pe = pdf_path__next_pe(pa, cp);
pe->type = PE_TYPE__LINETO;
pe->p[0].x = cp->x = p0->x;
pe->p[0].y = cp->y = p0->y;
return 0;
}
static int
pdf_path__curveto (pdf_path *pa,
pdf_coord *cp,
const pdf_coord *p0,
const pdf_coord *p1,
const pdf_coord *p2
)
{
pa_elem *pe;
pe = pdf_path__next_pe(pa, cp);
if (COORD_EQUAL(cp, p0)) {
pe->type = PE_TYPE__CURVETO_V;
pe->p[0].x = p1->x;
pe->p[0].y = p1->y;
pe->p[1].x = cp->x = p2->x;
pe->p[1].y = cp->y = p2->y;
} else if (COORD_EQUAL(p1, p2)) {
pe->type = PE_TYPE__CURVETO_Y;
pe->p[0].x = p0->x;
pe->p[0].y = p0->y;
pe->p[1].x = cp->x = p1->x;
pe->p[1].y = cp->y = p1->y;
} else {
pe->type = PE_TYPE__CURVETO;
pe->p[0].x = p0->x;
pe->p[0].y = p0->y;
pe->p[1].x = p1->x;
pe->p[1].y = p1->y;
pe->p[2].x = cp->x = p2->x;
pe->p[2].y = cp->y = p2->y;
}
return 0;
}
#if 0#endif
static int
pdf_path__elliptarc (pdf_path *pa,
pdf_coord *cp,
const pdf_coord *ca,
double r_x,
double r_y,
double xar,
double a_0,
double a_1,
int a_d
)
{
double b, b_x, b_y;
double d_a, q;
pdf_coord p0, p1, p2, p3;
pdf_coord e0, e1;
pdf_tmatrix T;
int n_c;
int i, error = 0;
if (fabs(r_x) < 1.e-8 ||
fabs(r_y) < 1.e-8)
return -1;
if (a_d < 0) {
for ( ; a_1 > a_0; a_1 -= 360.0);
} else {
for ( ; a_1 < a_0; a_0 -= 360.0);
}
d_a = a_1 - a_0;
for (n_c = 1; fabs(d_a) > 90.0 * n_c; n_c++);
d_a /= n_c;
if (fabs(d_a) < 1.e-8)
return -1;
a_0 *= M_PI / 180.0;
a_1 *= M_PI / 180.0;
d_a *= M_PI / 180.0;
xar *= M_PI / 180.0;
T.a = cos(xar); T.c = -sin(xar);
T.b = -T.c ; T.d = T.a;
T.e = 0.0 ; T.f = 0.0;
b = 4.0 * (1.0 - cos(.5 * d_a)) / (3.0 * sin(.5 * d_a));
b_x = r_x * b;
b_y = r_y * b;
p0.x = r_x * cos(a_0);
p0.y = r_y * sin(a_0);
pdf_coord__transform(&p0, &T);
p0.x += ca->x; p0.y += ca->y;
if (PA_LENGTH(pa) == 0) {
pdf_path__moveto(pa, cp, &p0);
} else if (!COORD_EQUAL(cp, &p0)) {
pdf_path__lineto(pa, cp, &p0);
}
for (i = 0; !error && i < n_c; i++) {
q = a_0 + i * d_a;
e0.x = cos(q); e0.y = sin(q);
e1.x = cos(q + d_a); e1.y = sin(q + d_a);
p0.x = r_x * e0.x;
p0.y = r_y * e0.y;
p3.x = r_x * e1.x;
p3.y = r_y * e1.y;
p1.x = -b_x * e0.y;
p1.y = b_y * e0.x;
p2.x = b_x * e1.y;
p2.y = -b_y * e1.x;
pdf_coord__transform(&p0, &T);
pdf_coord__transform(&p1, &T);
pdf_coord__transform(&p2, &T);
pdf_coord__transform(&p3, &T);
p0.x += ca->x; p0.y += ca->y;
p3.x += ca->x; p3.y += ca->y;
p1.x += p0.x ; p1.y += p0.y ;
p2.x += p3.x ; p2.y += p3.y ;
error = pdf_path__curveto(pa, &p0, &p1, &p2, &p3);
cp->x = p3.x; cp->y = p3.y;
}
return error;
}
static int
pdf_path__closepath (pdf_path *pa, pdf_coord *cp )
{
pa_elem *pe = NULL;
int i;
for (i = PA_LENGTH(pa) - 1; i >= 0; i--) {
pe = &pa->path[i];
if (pe->type == PE_TYPE__MOVETO)
break;
}
if (!pe || i < 0)
return -1;
cp->x = pe->p[0].x;
cp->y = pe->p[0].y;
pdf_path__growpath(pa, PA_LENGTH(pa) + 1);
pe = &pa->path[pa->num_paths++];
pe->type = PE_TYPE__CLOSEPATH;
return 0;
}
static int
pdf_path__isarect (pdf_path *pa,
int f_ir
)
{
pa_elem *pe0, *pe1, *pe2, *pe3, *pe4;
if (PA_LENGTH(pa) == 5) {
pe0 = &(pa->path[0]);
pe1 = &(pa->path[1]);
pe2 = &(pa->path[2]);
pe3 = &(pa->path[3]);
pe4 = &(pa->path[4]);
if (pe0->type == PE_TYPE__MOVETO &&
pe1->type == PE_TYPE__LINETO &&
pe2->type == PE_TYPE__LINETO &&
pe3->type == PE_TYPE__LINETO &&
pe4->type == PE_TYPE__CLOSEPATH) {
if (pe1->p[0].y - pe0->p[0].y == 0 &&
pe2->p[0].x - pe1->p[0].x == 0 &&
pe3->p[0].y - pe2->p[0].y == 0) {
if (pe1->p[0].x - pe0->p[0].x
== pe2->p[0].x - pe3->p[0].x) {
return 1;
}
} else if (f_ir &&
pe1->p[0].x - pe0->p[0].x == 0 &&
pe2->p[0].y - pe1->p[0].y == 0 &&
pe3->p[0].x - pe2->p[0].x == 0) {
if (pe1->p[0].y - pe0->p[0].y
== pe2->p[0].y - pe3->p[0].y) {
return 1;
}
}
}
}
return 0;
}
#define PT_OP_VALID(c) ( \
(c) == 'f' || (c) == 'F' || \
(c) == 's' || (c) == 'S' || \
(c) == 'b' || (c) == 'B' || \
(c) == 'W' || (c) == ' ' \
)
static int
INVERTIBLE_MATRIX (const pdf_tmatrix *M)
{
if (fabs(detP(M)) < 1.e-8) {
WARN("Transformation matrix not invertible.");
WARN("--- M = [%g %g %g %g %g %g]",
M->a, M->b, M->c, M->d, M->e, M->f);
return -1;
}
return 0;
}
static int
texpdf_dev__rectshape (pdf_doc *doc, const pdf_rect *r,
const pdf_tmatrix *M,
char opchr
)
{
char *buf = fmt_buf;
int len = 0;
int isclip = 0;
pdf_coord p;
double wd, ht;
ASSERT(r && PT_OP_VALID(opchr));
isclip = (opchr == 'W' || opchr == ' ') ? 1 : 0;
if (M && (isclip ||
!INVERTIBLE_MATRIX(M)))
return -1;
texpdf_graphics_mode(doc);
buf[len++] = ' ';
if (!isclip) {
buf[len++] = 'q';
if (M) {
buf[len++] = ' ';
len += texpdf_sprint_matrix(buf + len, M);
buf[len++] = ' ';
buf[len++] = 'c'; buf[len++] = 'm';
}
buf[len++] = ' ';
}
buf[len++] = 'n';
p.x = r->llx; p.y = r->lly;
wd = r->urx - r->llx;
ht = r->ury - r->lly;
buf[len++] = ' ';
len += pdf_sprint_coord (buf + len, &p);
buf[len++] = ' ';
len += pdf_sprint_length(buf + len, wd);
buf[len++] = ' ';
len += pdf_sprint_length(buf + len, ht);
buf[len++] = ' ';
buf[len++] = 'r'; buf[len++] = 'e';
if (opchr != ' ') {
buf[len++] = ' ';
buf[len++] = opchr;
buf[len++] = ' ';
buf[len++] = isclip ? 'n' : 'Q';
}
texpdf_doc_add_page_content(doc, buf, len);
return 0;
}
static int path_added = 0;
static int
texpdf_dev__flushpath (pdf_doc *p, pdf_path *pa,
char opchr,
int rule,
int ignore_rule)
{
pa_elem *pe, *pe1;
char *b = fmt_buf;
long b_len = FORMAT_BUFF_LEN;
pdf_rect r;
pdf_coord *pt;
int n_pts, n_seg;
int len = 0;
int isclip = 0;
int isrect, i, j;
ASSERT(pa && PT_OP_VALID(opchr));
isclip = (opchr == 'W') ? 1 : 0;
if (PA_LENGTH(pa) <= 0 && path_added == 0)
return 0;
path_added = 0;
texpdf_graphics_mode(p);
isrect = pdf_path__isarect(pa, ignore_rule);
if (isrect) {
pe = &(pa->path[0]);
pe1 = &(pa->path[2]);
r.llx = pe->p[0].x;
r.lly = pe->p[0].y;
r.urx = pe1->p[0].x - pe->p[0].x;
r.ury = pe1->p[0].y - pe->p[0].y;
b[len++] = ' ';
len += pdf_sprint_rect(b + len, &r);
b[len++] = ' ';
b[len++] = 'r';
b[len++] = 'e';
texpdf_doc_add_page_content(p, b, len);
len = 0;
} else {
n_seg = PA_LENGTH(pa);
for (i = 0, len = 0, pe = &pa->path[0];
i < n_seg; pe++, i++) {
n_pts = PE_N_PTS(pe);
for (j = 0, pt = &pe->p[0];
j < n_pts; j++, pt++) {
b[len++] = ' ';
len += pdf_sprint_coord(b + len, pt);
}
b[len++] = ' ';
b[len++] = PE_OPCHR(pe);
if (len + 128 > b_len) {
texpdf_doc_add_page_content(p, b, len);
len = 0;
}
}
if (len > 0) {
texpdf_doc_add_page_content(p, b, len);
len = 0;
}
}
b[len++] = ' ';
b[len++] = opchr;
if (rule == PDF_FILL_RULE_EVENODD)
b[len++] = '*';
if (isclip) {
b[len++] = ' '; b[len++] = 'n';
}
texpdf_doc_add_page_content(p, b, len);
return 0;
}
typedef struct pdf_gstate_
{
pdf_coord cp;
pdf_tmatrix matrix;
pdf_color strokecolor;
pdf_color fillcolor;
struct {
int num_dash;
double pattern[PDF_DASH_SIZE_MAX];
double offset;
} linedash;
double linewidth;
int linecap;
int linejoin;
double miterlimit;
int flatness;
pdf_path path;
long flags;
pdf_coord pt_fixee;
} pdf_gstate;
typedef struct m_stack_elem
{
void *data;
struct m_stack_elem *prev;
} m_stack_elem;
typedef struct m_stack
{
int size;
m_stack_elem *top;
m_stack_elem *bottom;
} m_stack;
static void
m_stack_init (m_stack *stack)
{
ASSERT(stack);
stack->size = 0;
stack->top = NULL;
stack->bottom = NULL;
return;
}
static void
m_stack_push (m_stack *stack, void *data)
{
m_stack_elem *elem;
ASSERT(stack);
elem = NEW(1, m_stack_elem);
elem->prev = stack->top;
elem->data = data;
stack->top = elem;
if (stack->size == 0)
stack->bottom = elem;
stack->size++;
return;
}
static void *
m_stack_pop (m_stack *stack)
{
m_stack_elem *elem;
void *data;
ASSERT(stack);
if (stack->size == 0)
return NULL;
data = stack->top->data;
elem = stack->top;
stack->top = elem->prev;
if (stack->size == 1)
stack->bottom = NULL;
RELEASE(elem);
stack->size--;
return data;
}
static void *
m_stack_top (m_stack *stack)
{
void *data;
ASSERT(stack);
if (stack->size == 0)
return NULL;
data = stack->top->data;
return data;
}
#define m_stack_depth(s) ((s)->size)
static m_stack gs_stack;
static void
init_a_gstate (pdf_gstate *gs)
{
gs->cp.x = 0.0;
gs->cp.y = 0.0;
pdf_setmatrix(&gs->matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
texpdf_color_black(&gs->strokecolor);
texpdf_color_black(&gs->fillcolor);
gs->linedash.num_dash = 0;
gs->linedash.offset = 0;
gs->linecap = 0;
gs->linejoin = 0;
gs->linewidth = 1.0;
gs->miterlimit = 10.0;
gs->flatness = 1;
gs->flags = 0;
init_a_path(&gs->path);
gs->pt_fixee.x = 0;
gs->pt_fixee.y = 0;
return;
}
static void
clear_a_gstate (pdf_gstate *gs)
{
clear_a_path(&gs->path);
memset(gs, 0, sizeof(pdf_gstate));
return;
}
static void
copy_a_gstate (pdf_gstate *gs1, pdf_gstate *gs2)
{
int i;
ASSERT(gs1 && gs2);
gs1->cp.x = gs2->cp.x;
gs1->cp.y = gs2->cp.y;
pdf_copymatrix(&gs1->matrix, &gs2->matrix);
pdf_path__copypath(&gs1->path, &gs2->path);
gs1->linedash.num_dash = gs2->linedash.num_dash;
for (i = 0; i < gs2->linedash.num_dash; i++) {
gs1->linedash.pattern[i] = gs2->linedash.pattern[i];
}
gs1->linedash.offset = gs2->linedash.offset;
gs1->linecap = gs2->linecap;
gs1->linejoin = gs2->linejoin;
gs1->linewidth = gs2->linewidth;
gs1->miterlimit = gs2->miterlimit;
gs1->flatness = gs2->flatness;
texpdf_color_copycolor(&gs1->fillcolor , &gs2->fillcolor);
texpdf_color_copycolor(&gs1->strokecolor, &gs2->strokecolor);
gs1->pt_fixee.x = gs2->pt_fixee.x;
gs1->pt_fixee.y = gs2->pt_fixee.y;
return;
}
void
texpdf_dev_init_gstates (void)
{
pdf_gstate *gs;
m_stack_init(&gs_stack);
gs = NEW(1, pdf_gstate);
init_a_gstate(gs);
m_stack_push(&gs_stack, gs);
return;
}
void
texpdf_dev_clear_gstates (void)
{
pdf_gstate *gs;
if (m_stack_depth(&gs_stack) > 1)
WARN("GS stack depth is not zero at the end of the document.");
while ((gs = m_stack_pop(&gs_stack)) != NULL) {
clear_a_gstate(gs);
RELEASE(gs);
}
return;
}
int
texpdf_dev_gsave (pdf_doc *p)
{
pdf_gstate *gs0, *gs1;
gs0 = m_stack_top(&gs_stack);
gs1 = NEW(1, pdf_gstate);
init_a_gstate(gs1);
copy_a_gstate(gs1, gs0);
m_stack_push(&gs_stack, gs1);
texpdf_doc_add_page_content(p, " q", 2);
return 0;
}
int
texpdf_dev_grestore (pdf_doc *p)
{
pdf_gstate *gs;
if (m_stack_depth(&gs_stack) <= 1) {
WARN("Too many grestores.");
return -1;
}
gs = m_stack_pop(&gs_stack);
clear_a_gstate(gs);
RELEASE(gs);
texpdf_doc_add_page_content(p, " Q", 2);
texpdf_dev_reset_fonts(0);
return 0;
}
int
texpdf_dev_push_gstate (void)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs0;
gs0 = NEW(1, pdf_gstate);
init_a_gstate(gs0);
m_stack_push(gss, gs0);
return 0;
}
int
texpdf_dev_pop_gstate (void)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs;
if (m_stack_depth(gss) <= 1) {
WARN("Too many grestores.");
return -1;
}
gs = m_stack_pop(gss);
clear_a_gstate(gs);
RELEASE(gs);
return 0;
}
int
texpdf_dev_current_depth (void)
{
return (m_stack_depth(&gs_stack) - 1);
}
void
texpdf_dev_grestore_to (pdf_doc *p, int depth)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs;
ASSERT(depth >= 0);
if (m_stack_depth(gss) > depth + 1) {
WARN("Closing pending transformations at end of page/XObject.");
}
while (m_stack_depth(gss) > depth + 1) {
texpdf_doc_add_page_content(p, " Q", 2);
gs = m_stack_pop(gss);
clear_a_gstate(gs);
RELEASE(gs);
}
texpdf_dev_reset_fonts(0);
return;
}
int
texpdf_dev_currentpoint (pdf_coord *p)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_coord *cpt = &gs->cp;
ASSERT(p);
p->x = cpt->x; p->y = cpt->y;
return 0;
}
int
texpdf_dev_currentmatrix (pdf_tmatrix *M)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_tmatrix *CTM = &gs->matrix;
ASSERT(M);
pdf_copymatrix(M, CTM);
return 0;
}
#if 0#endif
void
texpdf_dev_set_color (pdf_doc *p, const pdf_color *color, char mask, int force)
{
int len;
pdf_gstate *gs = m_stack_top(&gs_stack);
pdf_color *current = mask ? &gs->fillcolor : &gs->strokecolor;
ASSERT(texpdf_color_is_valid(color));
if (!(texpdf_dev_get_param(PDF_DEV_PARAM_COLORMODE) &&
(force || texpdf_color_compare(color, current))))
return;
texpdf_graphics_mode(p);
len = texpdf_color_to_string(color, fmt_buf, mask);
fmt_buf[len++] = ' ';
switch (texpdf_color_type(color)) {
case PDF_COLORSPACE_TYPE_RGB:
fmt_buf[len++] = 'R' | mask;
fmt_buf[len++] = 'G' | mask;
break;
case PDF_COLORSPACE_TYPE_CMYK:
fmt_buf[len++] = 'K' | mask;
break;
case PDF_COLORSPACE_TYPE_GRAY:
fmt_buf[len++] = 'G' | mask;
break;
default:
break;
}
texpdf_doc_add_page_content(p, fmt_buf, len);
texpdf_color_copycolor(current, color);
}
int
texpdf_dev_concat (pdf_doc *p, const pdf_tmatrix *M)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_path *cpa = &gs->path;
pdf_coord *cpt = &gs->cp;
pdf_tmatrix *CTM = &gs->matrix;
pdf_tmatrix W = {0, 0, 0, 0, 0, 0};
char *buf = fmt_buf;
int len = 0;
ASSERT(M);
if (fabs(detP(M)) < 1.0e-8) {
WARN("Transformation matrix not invertible.");
WARN("--- M = [%g %g %g %g %g %g]",
M->a, M->b, M->c, M->d, M->e, M->f);
return -1;
}
if (fabs(M->a - 1.0) > 1.e-8 || fabs(M->b) > 1.e-8
|| fabs(M->c) > 1.e-8 || fabs(M->d - 1.0) > 1.e-8
|| fabs(M->e) > 1.e-8 || fabs(M->f) > 1.e-8) {
buf[len++] = ' ';
len += texpdf_sprint_matrix(buf + len, M);
buf[len++] = ' ';
buf[len++] = 'c';
buf[len++] = 'm';
texpdf_doc_add_page_content(p, buf, len);
pdf_concatmatrix(CTM, M);
}
inversematrix(&W, M);
pdf_path__transform (cpa, &W);
pdf_coord__transform(cpt, &W);
return 0;
}
int
texpdf_dev_setmiterlimit (pdf_doc *p, double mlimit)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
int len = 0;
char *buf = fmt_buf;
if (gs->miterlimit != mlimit) {
buf[len++] = ' ';
len += pdf_sprint_length(buf + len, mlimit);
buf[len++] = ' ';
buf[len++] = 'M';
texpdf_doc_add_page_content(p, buf, len);
gs->miterlimit = mlimit;
}
return 0;
}
int
texpdf_dev_setlinecap (pdf_doc *p, int capstyle)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
int len = 0;
char *buf = fmt_buf;
if (gs->linecap != capstyle) {
len = sprintf(buf, " %d J", capstyle);
texpdf_doc_add_page_content(p, buf, len);
gs->linecap = capstyle;
}
return 0;
}
int
texpdf_dev_setlinejoin (pdf_doc *p, int joinstyle)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
int len = 0;
char *buf = fmt_buf;
if (gs->linejoin != joinstyle) {
len = sprintf(buf, " %d j", joinstyle);
texpdf_doc_add_page_content(p, buf, len);
gs->linejoin = joinstyle;
}
return 0;
}
int
texpdf_dev_setlinewidth (pdf_doc *p, double width)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
int len = 0;
char *buf = fmt_buf;
if (gs->linewidth != width) {
buf[len++] = ' ';
len += pdf_sprint_length(buf + len, width);
buf[len++] = ' ';
buf[len++] = 'w';
texpdf_doc_add_page_content(p, buf, len);
gs->linewidth = width;
}
return 0;
}
int
texpdf_dev_setdash (pdf_doc *p, int count, double *pattern, double offset)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
int len = 0;
char *buf = fmt_buf;
int i;
gs->linedash.num_dash = count;
gs->linedash.offset = offset;
texpdf_doc_add_page_content(p, " [", 2);
for (i = 0; i < count; i++) {
buf[0] = ' ';
len = pdf_sprint_length (buf + 1, pattern[i]);
texpdf_doc_add_page_content(p, buf, len + 1);
gs->linedash.pattern[i] = pattern[i];
}
texpdf_doc_add_page_content(p, "] ", 2);
len = pdf_sprint_length (buf, offset);
texpdf_doc_add_page_content(p, buf, len);
texpdf_doc_add_page_content(p, " d", 2);
return 0;
}
#if 0#endif
int
texpdf_dev_clip (pdf_doc *p)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_path *cpa = &gs->path;
return texpdf_dev__flushpath(p, cpa, 'W', PDF_FILL_RULE_NONZERO, 0);
}
int
texpdf_dev_eoclip (pdf_doc *p)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_path *cpa = &gs->path;
return texpdf_dev__flushpath(p, cpa, 'W', PDF_FILL_RULE_EVENODD, 0);
}
int
texpdf_dev_flushpath (pdf_doc *p, char p_op, int fill_rule)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_path *cpa = &gs->path;
int error = 0;
error = texpdf_dev__flushpath(p, cpa, p_op, fill_rule, 1);
pdf_path__clearpath(cpa);
gs->flags &= ~GS_FLAG_CURRENTPOINT_SET;
return error;
}
int
texpdf_dev_newpath (pdf_doc *doc)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_path *p = &gs->path;
if (PA_LENGTH(p) > 0) {
pdf_path__clearpath (p);
}
texpdf_doc_add_page_content(doc, " n", 2);
return 0;
}
int
texpdf_dev_moveto (double x, double y)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_path *cpa = &gs->path;
pdf_coord *cpt = &gs->cp;
pdf_coord p;
p.x = x; p.y = y;
return pdf_path__moveto(cpa, cpt, &p);
}
int
texpdf_dev_rmoveto (double x, double y)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_path *cpa = &gs->path;
pdf_coord *cpt = &gs->cp;
pdf_coord p;
p.x = cpt->x + x;
p.y = cpt->y + y;
return pdf_path__moveto(cpa, cpt, &p);
}
int
texpdf_dev_lineto (double x, double y)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_path *cpa = &gs->path;
pdf_coord *cpt = &gs->cp;
pdf_coord p0;
p0.x = x; p0.y = y;
return pdf_path__lineto(cpa, cpt, &p0);
}
int
texpdf_dev_rlineto (double x, double y)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_path *cpa = &gs->path;
pdf_coord *cpt = &gs->cp;
pdf_coord p0;
p0.x = x + cpt->x; p0.y = y + cpt->y;
return pdf_path__lineto(cpa, cpt, &p0);
}
int
texpdf_dev_curveto (double x0, double y0,
double x1, double y1,
double x2, double y2)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_path *cpa = &gs->path;
pdf_coord *cpt = &gs->cp;
pdf_coord p0, p1, p2;
p0.x = x0; p0.y = y0;
p1.x = x1; p1.y = y1;
p2.x = x2; p2.y = y2;
return pdf_path__curveto(cpa, cpt, &p0, &p1, &p2);
}
int
texpdf_dev_vcurveto (double x0, double y0,
double x1, double y1)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_path *cpa = &gs->path;
pdf_coord *cpt = &gs->cp;
pdf_coord p0, p1;
p0.x = x0; p0.y = y0;
p1.x = x1; p1.y = y1;
return pdf_path__curveto(cpa, cpt, cpt, &p0, &p1);
}
int
texpdf_dev_ycurveto (double x0, double y0,
double x1, double y1)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_path *cpa = &gs->path;
pdf_coord *cpt = &gs->cp;
pdf_coord p0, p1;
p0.x = x0; p0.y = y0;
p1.x = x1; p1.y = y1;
return pdf_path__curveto(cpa, cpt, &p0, &p1, &p1);
}
int
texpdf_dev_rcurveto (double x0, double y0,
double x1, double y1,
double x2, double y2)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_path *cpa = &gs->path;
pdf_coord *cpt = &gs->cp;
pdf_coord p0, p1, p2;
p0.x = x0 + cpt->x; p0.y = y0 + cpt->y;
p1.x = x1 + cpt->x; p1.y = y1 + cpt->y;
p2.x = x2 + cpt->x; p2.y = y2 + cpt->y;
return pdf_path__curveto(cpa, cpt, &p0, &p1, &p2);
}
int
texpdf_dev_closepath (void)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_coord *cpt = &gs->cp;
pdf_path *cpa = &gs->path;
return pdf_path__closepath(cpa, cpt);
}
void
texpdf_dev_dtransform (pdf_coord *p, const pdf_tmatrix *M)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_tmatrix *CTM = &gs->matrix;
ASSERT(p);
pdf_coord__dtransform(p, (M ? M : CTM));
return;
}
void
texpdf_dev_idtransform (pdf_coord *p, const pdf_tmatrix *M)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_tmatrix *CTM = &gs->matrix;
ASSERT(p);
pdf_coord__idtransform(p, (M ? M : CTM));
return;
}
void
texpdf_dev_transform (pdf_coord *p, const pdf_tmatrix *M)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_tmatrix *CTM = &gs->matrix;
ASSERT(p);
pdf_coord__transform(p, (M ? M : CTM));
return;
}
#if 0#endif
int
texpdf_dev_arc (double c_x , double c_y, double r,
double a_0 , double a_1)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_path *cpa = &gs->path;
pdf_coord *cpt = &gs->cp;
pdf_coord c;
c.x = c_x; c.y = c_y;
return pdf_path__elliptarc(cpa, cpt, &c, r, r, 0.0, a_0, a_1, +1);
}
int
texpdf_dev_arcn (double c_x , double c_y, double r,
double a_0 , double a_1)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_path *cpa = &gs->path;
pdf_coord *cpt = &gs->cp;
pdf_coord c;
c.x = c_x; c.y = c_y;
return pdf_path__elliptarc(cpa, cpt, &c, r, r, 0.0, a_0, a_1, -1);
}
int
texpdf_dev_arcx (double c_x , double c_y,
double r_x , double r_y,
double a_0 , double a_1,
int a_d ,
double xar)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_path *cpa = &gs->path;
pdf_coord *cpt = &gs->cp;
pdf_coord c;
c.x = c_x; c.y = c_y;
return pdf_path__elliptarc(cpa, cpt, &c, r_x, r_y, xar, a_0, a_1, a_d);
}
int
texpdf_dev_bspline (double x0, double y0,
double x1, double y1, double x2, double y2)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
pdf_path *cpa = &gs->path;
pdf_coord *cpt = &gs->cp;
pdf_coord p1, p2, p3;
p1.x = x0 + 2.0 * (x1 - x0) / 3.0;
p1.y = y0 + 2.0 * (y1 - y0) / 3.0;
p2.x = x1 + (x2 - x1) / 3.0;
p2.y = y1 + (y2 - y1) / 3.0;
p3.x = x2;
p3.y = y2;
return pdf_path__curveto(cpa, cpt, &p1, &p2, &p3);
}
#if 0#endif
int
texpdf_dev_rectfill (pdf_doc *p, double x, double y,
double w, double h)
{
pdf_rect r;
r.llx = x;
r.lly = y;
r.urx = x + w;
r.ury = y + h;
return texpdf_dev__rectshape(p, &r, NULL, 'f');
}
int
texpdf_dev_rectclip (pdf_doc *p, double x, double y,
double w, double h)
{
pdf_rect r;
r.llx = x;
r.lly = y;
r.urx = x + w;
r.ury = y + h;
return texpdf_dev__rectshape(p, &r, NULL, 'W');
}
int
texpdf_dev_rectadd (pdf_doc *p, double x, double y,
double w, double h)
{
pdf_rect r;
r.llx = x;
r.lly = y;
r.urx = x + w;
r.ury = y + h;
path_added = 1;
return texpdf_dev__rectshape(p, &r, NULL, ' ');
}
void
texpdf_dev_set_fixed_point (double x, double y)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
gs->pt_fixee.x = x;
gs->pt_fixee.y = y;
}
void
texpdf_dev_get_fixed_point (pdf_coord *p)
{
m_stack *gss = &gs_stack;
pdf_gstate *gs = m_stack_top(gss);
p->x = gs->pt_fixee.x;
p->y = gs->pt_fixee.y;
}