#include <X11/Xos.h>
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#include "DviP.h"
#define DeviceToX(dw, n) ((int)((n) * (dw)->dvi.scale_factor + .5))
#define XPos(dw) (DeviceToX((dw), (dw)->dvi.state->x - \
(dw)->dvi.text_device_width) + (dw)->dvi.text_x_width)
#define YPos(dw) (DeviceToX((dw), (dw)->dvi.state->y))
static int FakeCharacter(DviWidget, char *, int);
extern int MaxFontPosition(DviWidget);
void
HorizontalMove(DviWidget dw, int delta)
{
dw->dvi.state->x += delta;
}
void
HorizontalGoto(DviWidget dw, int NewPosition)
{
dw->dvi.state->x = NewPosition;
}
void
VerticalMove(DviWidget dw, int delta)
{
dw->dvi.state->y += delta;
}
void
VerticalGoto(DviWidget dw, int NewPosition)
{
dw->dvi.state->y = NewPosition;
}
void
AdjustCacheDeltas (DviWidget dw)
{
int extra;
int nadj;
int i;
nadj = 0;
extra = DeviceToX(dw, dw->dvi.text_device_width)
- dw->dvi.text_x_width;
if (extra == 0)
return;
for (i = 0; i <= dw->dvi.cache.index; i++)
if (dw->dvi.cache.adjustable[i])
++nadj;
dw->dvi.text_x_width += extra;
if (nadj <= 1)
return;
for (i = 0; i <= dw->dvi.cache.index; i++)
if (dw->dvi.cache.adjustable[i]) {
int x;
int *deltap;
x = extra/nadj;
deltap = &dw->dvi.cache.cache[i].delta;
#define MIN_DELTA 2
if (*deltap > 0 && x + *deltap < MIN_DELTA) {
x = MIN_DELTA - *deltap;
if (x <= 0)
*deltap = MIN_DELTA;
else
x = 0;
}
else
*deltap += x;
extra -= x;
--nadj;
dw->dvi.cache.adjustable[i] = 0;
}
}
void
FlushCharCache (DviWidget dw)
{
if (dw->dvi.cache.char_index != 0) {
AdjustCacheDeltas (dw);
XDrawText (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
dw->dvi.cache.start_x, dw->dvi.cache.start_y,
dw->dvi.cache.cache, dw->dvi.cache.index + 1);
}
dw->dvi.cache.index = 0;
dw->dvi.cache.max = DVI_TEXT_CACHE_SIZE;
#if 0
if (dw->dvi.noPolyText)
dw->dvi.cache.max = 1;
#endif
dw->dvi.cache.char_index = 0;
dw->dvi.cache.cache[0].nchars = 0;
dw->dvi.cache.start_x = dw->dvi.cache.x = XPos (dw);
dw->dvi.cache.start_y = dw->dvi.cache.y = YPos (dw);
}
void
Newline (DviWidget dw)
{
FlushCharCache (dw);
dw->dvi.text_x_width = dw->dvi.text_device_width = 0;
dw->dvi.word_flag = 0;
}
void
Word (DviWidget dw)
{
dw->dvi.word_flag = 1;
}
#define charWidth(fi,c) (\
(fi)->per_char ?\
(fi)->per_char[(c) - (fi)->min_char_or_byte2].width\
:\
(fi)->max_bounds.width\
)
static
int charExists (XFontStruct *fi, int c)
{
XCharStruct *p;
if (fi->per_char == NULL
|| (unsigned int)c < fi->min_char_or_byte2
|| (unsigned int)c > fi->max_char_or_byte2)
return 0;
p = fi->per_char + (c - fi->min_char_or_byte2);
return (p->lbearing != 0 || p->rbearing != 0 || p->width != 0
|| p->ascent != 0 || p->descent != 0 || p->attributes != 0);
}
static void
DoCharacter (DviWidget dw, int c, int wid)
{
register XFontStruct *font;
register XTextItem *text;
int x, y;
x = XPos(dw);
y = YPos(dw);
if (!(y + 24 >= dw->dvi.extents.y1
&& y - 24 <= dw->dvi.extents.y2
#if 0
&& x + 24 >= dw->dvi.extents.x1
&& x - 24 <= dw->dvi.extents.x2
#endif
))
return;
if (y != dw->dvi.cache.y
|| dw->dvi.cache.char_index >= DVI_CHAR_CACHE_SIZE) {
FlushCharCache (dw);
x = dw->dvi.cache.x;
dw->dvi.cache.adjustable[dw->dvi.cache.index] = 0;
}
if (dw->dvi.cache.font_size != dw->dvi.state->font_size ||
dw->dvi.cache.font_number != dw->dvi.state->font_number)
{
FlushCharCache (dw);
x = dw->dvi.cache.x;
dw->dvi.cache.font_size = dw->dvi.state->font_size;
dw->dvi.cache.font_number = dw->dvi.state->font_number;
dw->dvi.cache.font = QueryFont (dw,
dw->dvi.cache.font_number,
dw->dvi.cache.font_size);
if (dw->dvi.cache.cache[dw->dvi.cache.index].nchars != 0) {
++dw->dvi.cache.index;
if (dw->dvi.cache.index >= dw->dvi.cache.max)
FlushCharCache (dw);
dw->dvi.cache.cache[dw->dvi.cache.index].nchars = 0;
dw->dvi.cache.adjustable[dw->dvi.cache.index] = 0;
}
}
if (x != dw->dvi.cache.x || dw->dvi.word_flag) {
if (dw->dvi.cache.cache[dw->dvi.cache.index].nchars != 0) {
++dw->dvi.cache.index;
if (dw->dvi.cache.index >= dw->dvi.cache.max)
FlushCharCache (dw);
dw->dvi.cache.cache[dw->dvi.cache.index].nchars = 0;
dw->dvi.cache.adjustable[dw->dvi.cache.index] = 0;
}
dw->dvi.cache.adjustable[dw->dvi.cache.index]
= dw->dvi.word_flag;
dw->dvi.word_flag = 0;
}
font = dw->dvi.cache.font;
text = &dw->dvi.cache.cache[dw->dvi.cache.index];
if (text->nchars == 0) {
text->chars = &dw->dvi.cache.char_cache[dw->dvi.cache.char_index];
text->delta = x - dw->dvi.cache.x;
if (font != dw->dvi.font) {
text->font = font->fid;
dw->dvi.font = font;
} else
text->font = None;
dw->dvi.cache.x += text->delta;
}
if (charExists(font, c)) {
int w;
dw->dvi.cache.char_cache[dw->dvi.cache.char_index++] = (char) c;
++text->nchars;
w = charWidth(font, c);
dw->dvi.cache.x += w;
if (wid != 0) {
dw->dvi.text_x_width += w;
dw->dvi.text_device_width += wid;
}
}
}
static
int FindCharWidth (DviWidget dw, char *buf, int *widp)
{
int maxpos;
int i;
if (dw->dvi.device_font == 0
|| dw->dvi.state->font_number != dw->dvi.device_font_number) {
dw->dvi.device_font_number = dw->dvi.state->font_number;
dw->dvi.device_font
= QueryDeviceFont (dw, dw->dvi.device_font_number);
}
if (dw->dvi.device_font
&& device_char_width (dw->dvi.device_font,
dw->dvi.state->font_size, buf, widp))
return 1;
maxpos = MaxFontPosition (dw);
for (i = 1; i <= maxpos; i++) {
DeviceFont *f = QueryDeviceFont (dw, i);
if (f && device_font_special (f)
&& device_char_width (f, dw->dvi.state->font_size,
buf, widp)) {
dw->dvi.state->font_number = i;
return 1;
}
}
return 0;
}
int PutCharacter (DviWidget dw, char *buf)
{
int prevFont;
int c = -1;
int wid = 0;
DviCharNameMap *map;
if (!dw->dvi.display_enable)
return 0;
prevFont = dw->dvi.state->font_number;
if (!FindCharWidth (dw, buf, &wid))
return 0;
map = QueryFontMap (dw, dw->dvi.state->font_number);
if (map)
c = DviCharIndex (map, buf);
if (c >= 0)
DoCharacter (dw, c, wid);
else
(void) FakeCharacter (dw, buf, wid);
dw->dvi.state->font_number = prevFont;
return wid;
}
static
int FakeCharacter (DviWidget dw, char *buf, int wid)
{
int oldx, oldw;
char ch[2];
const char *chars = 0;
if (buf[0] == '\0' || buf[1] == '\0' || buf[2] != '\0')
return 0;
#define pack2(c1, c2) (((c1) << 8) | (c2))
switch (pack2(buf[0], buf[1])) {
case pack2('f', 'i'):
chars = "fi";
break;
case pack2('f', 'l'):
chars = "fl";
break;
case pack2('f', 'f'):
chars = "ff";
break;
case pack2('F', 'i'):
chars = "ffi";
break;
case pack2('F', 'l'):
chars = "ffl";
break;
}
if (!chars)
return 0;
oldx = dw->dvi.state->x;
oldw = dw->dvi.text_device_width;
ch[1] = '\0';
for (; *chars; chars++) {
ch[0] = *chars;
dw->dvi.state->x += PutCharacter (dw, ch);
}
dw->dvi.state->x = oldx;
dw->dvi.text_device_width = oldw + wid;
return 1;
}
void
PutNumberedCharacter (DviWidget dw, int c)
{
char *name;
int wid;
DviCharNameMap *map;
if (!dw->dvi.display_enable)
return;
if (dw->dvi.device_font == 0
|| dw->dvi.state->font_number != dw->dvi.device_font_number) {
dw->dvi.device_font_number = dw->dvi.state->font_number;
dw->dvi.device_font
= QueryDeviceFont (dw, dw->dvi.device_font_number);
}
if (dw->dvi.device_font == 0
|| !device_code_width (dw->dvi.device_font,
dw->dvi.state->font_size, c, &wid))
return;
if (dw->dvi.native) {
DoCharacter (dw, c, wid);
return;
}
map = QueryFontMap (dw, dw->dvi.state->font_number);
if (!map)
return;
for (name = device_name_for_code (dw->dvi.device_font, c);
name;
name = device_name_for_code ((DeviceFont *)0, c)) {
int code = DviCharIndex (map, name);
if (code >= 0) {
DoCharacter (dw, code, wid);
break;
}
if (FakeCharacter (dw, name, wid))
break;
}
}
void
ClearPage (DviWidget dw)
{
XClearWindow (XtDisplay (dw), XtWindow (dw));
}
static void
setGC (DviWidget dw)
{
int desired_line_width;
if (dw->dvi.line_thickness < 0)
desired_line_width = (int)(((double)dw->dvi.device_resolution
* dw->dvi.state->font_size)
/ (10.0*72.0*dw->dvi.sizescale));
else
desired_line_width = dw->dvi.line_thickness;
if (desired_line_width != dw->dvi.line_width) {
XGCValues values;
values.line_width = DeviceToX(dw, desired_line_width);
if (values.line_width == 0)
values.line_width = 1;
XChangeGC(XtDisplay (dw), dw->dvi.normal_GC,
GCLineWidth, &values);
dw->dvi.line_width = desired_line_width;
}
}
static void
setFillGC (DviWidget dw)
{
int fill_type;
unsigned long mask = GCFillStyle | GCForeground;
fill_type = (dw->dvi.fill * 10) / (DVI_FILL_MAX + 1);
if (dw->dvi.fill_type != fill_type) {
XGCValues values;
if (fill_type <= 0) {
values.foreground = dw->dvi.background;
values.fill_style = FillSolid;
} else if (fill_type >= 9) {
values.foreground = dw->dvi.foreground;
values.fill_style = FillSolid;
} else {
values.foreground = dw->dvi.foreground;
values.fill_style = FillOpaqueStippled;
values.stipple = dw->dvi.gray[fill_type - 1];
mask |= GCStipple;
}
XChangeGC(XtDisplay (dw), dw->dvi.fill_GC, mask, &values);
dw->dvi.fill_type = fill_type;
}
}
void
DrawLine (DviWidget dw, int x, int y)
{
int xp, yp;
AdjustCacheDeltas (dw);
setGC (dw);
xp = XPos (dw);
yp = YPos (dw);
XDrawLine (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
xp, yp,
xp + DeviceToX (dw, x), yp + DeviceToX (dw, y));
}
void
DrawCircle (DviWidget dw, int diam)
{
int d;
AdjustCacheDeltas (dw);
setGC (dw);
d = DeviceToX (dw, diam);
XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
XPos (dw), YPos (dw) - d/2,
d, d, 0, 64*360);
}
void
DrawFilledCircle (DviWidget dw, int diam)
{
int d;
AdjustCacheDeltas (dw);
setFillGC (dw);
d = DeviceToX (dw, diam);
XFillArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
XPos (dw), YPos (dw) - d/2,
d, d, 0, 64*360);
XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
XPos (dw), YPos (dw) - d/2,
d, d, 0, 64*360);
}
void
DrawEllipse (DviWidget dw, int a, int b)
{
AdjustCacheDeltas (dw);
setGC (dw);
XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
XPos (dw), YPos (dw) - DeviceToX (dw, b/2),
DeviceToX (dw, a), DeviceToX (dw, b), 0, 64*360);
}
void
DrawFilledEllipse (DviWidget dw, int a, int b)
{
AdjustCacheDeltas (dw);
setFillGC (dw);
XFillArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
XPos (dw), YPos (dw) - DeviceToX (dw, b/2),
DeviceToX (dw, a), DeviceToX (dw, b), 0, 64*360);
XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
XPos (dw), YPos (dw) - DeviceToX (dw, b/2),
DeviceToX (dw, a), DeviceToX (dw, b), 0, 64*360);
}
void
DrawArc (DviWidget dw, int x_0, int y_0, int x_1, int y_1)
{
int angle1, angle2;
int rad = (int)((sqrt ((double)x_0*x_0 + (double)y_0*y_0)
+ sqrt ((double)x_1*x_1 + (double)y_1*y_1)
+ 1.0)/2.0);
if ((x_0 == 0 && y_0 == 0) || (x_1 == 0 && y_1 == 0))
return;
angle1 = (int)(atan2 ((double)y_0, (double)-x_0)*180.0*64.0/M_PI);
angle2 = (int)(atan2 ((double)-y_1, (double)x_1)*180.0*64.0/M_PI);
angle2 -= angle1;
if (angle2 < 0)
angle2 += 64*360;
AdjustCacheDeltas (dw);
setGC (dw);
rad = DeviceToX (dw, rad);
XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
XPos (dw) + DeviceToX (dw, x_0) - rad,
YPos (dw) + DeviceToX (dw, y_0) - rad,
rad*2, rad*2, angle1, angle2);
}
void
DrawPolygon (DviWidget dw, int *v, int n)
{
XPoint *p;
int i;
int dx, dy;
n /= 2;
AdjustCacheDeltas (dw);
setGC (dw);
p = (XPoint *)XtMalloc((n + 2)*sizeof(XPoint));
p[0].x = XPos (dw);
p[0].y = YPos (dw);
dx = 0;
dy = 0;
for (i = 0; i < n; i++) {
dx += v[2*i];
p[i + 1].x = DeviceToX (dw, dx) + p[0].x;
dy += v[2*i + 1];
p[i + 1].y = DeviceToX (dw, dy) + p[0].y;
}
p[n+1].x = p[0].x;
p[n+1].y = p[0].y;
XDrawLines (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
p, n + 2, CoordModeOrigin);
XtFree((char *)p);
}
void
DrawFilledPolygon (DviWidget dw, int *v, int n)
{
XPoint *p;
int i;
int dx, dy;
n /= 2;
if (n < 2)
return;
AdjustCacheDeltas (dw);
setFillGC (dw);
p = (XPoint *)XtMalloc((n + 2)*sizeof(XPoint));
p[0].x = p[n+1].x = XPos (dw);
p[0].y = p[n+1].y = YPos (dw);
dx = 0;
dy = 0;
for (i = 0; i < n; i++) {
dx += v[2*i];
p[i + 1].x = DeviceToX (dw, dx) + p[0].x;
dy += v[2*i + 1];
p[i + 1].y = DeviceToX (dw, dy) + p[0].y;
}
XFillPolygon (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
p, n + 1, Complex, CoordModeOrigin);
XDrawLines (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
p, n + 2, CoordModeOrigin);
XtFree((char *)p);
}
#define POINTS_MAX 10000
static void
appendPoint(XPoint *points, int *pointi, int x, int y)
{
if (*pointi < POINTS_MAX) {
points[*pointi].x = x;
points[*pointi].y = y;
*pointi += 1;
}
}
#define FLATNESS 1
static void
flattenCurve(XPoint *points, int *pointi,
int x_2, int y_2, int x_3, int y_3, int x_4, int y_4)
{
int x_1, y_1, dx, dy, n1, n2, n;
x_1 = points[*pointi - 1].x;
y_1 = points[*pointi - 1].y;
dx = x_4 - x_1;
dy = y_4 - y_1;
n1 = dy*(x_2 - x_1) - dx*(y_2 - y_1);
n2 = dy*(x_3 - x_1) - dx*(y_3 - y_1);
if (n1 < 0)
n1 = -n1;
if (n2 < 0)
n2 = -n2;
n = n1 > n2 ? n1 : n2;
if (n*n / (dy*dy + dx*dx) <= FLATNESS*FLATNESS)
appendPoint (points, pointi, x_4, y_4);
else {
flattenCurve (points, pointi,
(x_1 + x_2)/2,
(y_1 + y_2)/2,
(x_1 + x_2*2 + x_3)/4,
(y_1 + y_2*2 + y_3)/4,
(x_1 + 3*x_2 + 3*x_3 + x_4)/8,
(y_1 + 3*y_2 + 3*y_3 + y_4)/8);
flattenCurve (points, pointi,
(x_2 + x_3*2 + x_4)/4,
(y_2 + y_3*2 + y_4)/4,
(x_3 + x_4)/2,
(y_3 + y_4)/2,
x_4,
y_4);
}
}
void
DrawSpline (DviWidget dw, int *v, int n)
{
int sx, sy, tx, ty;
int ox, oy, dx, dy;
int i;
int pointi;
XPoint points[POINTS_MAX];
if (n == 0 || (n & 1) != 0)
return;
AdjustCacheDeltas (dw);
setGC (dw);
ox = XPos (dw);
oy = YPos (dw);
dx = v[0];
dy = v[1];
sx = ox;
sy = oy;
tx = sx + DeviceToX (dw, dx);
ty = sy + DeviceToX (dw, dy);
pointi = 0;
appendPoint (points, &pointi, sx, sy);
appendPoint (points, &pointi, (sx + tx)/2, (sy + ty)/2);
for (i = 2; i < n; i += 2) {
int ux = ox + DeviceToX (dw, dx += v[i]);
int uy = oy + DeviceToX (dw, dy += v[i+1]);
flattenCurve (points, &pointi,
(sx + tx*5)/6, (sy + ty*5)/6,
(tx*5 + ux)/6, (ty*5 + uy)/6,
(tx + ux)/2, (ty + uy)/2);
sx = tx;
sy = ty;
tx = ux;
ty = uy;
}
appendPoint (points, &pointi, tx, ty);
XDrawLines (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
points, pointi, CoordModeOrigin);
}