special.c (28781B)
1 /*========================================================================*\ 2 3 Copyright (c) 1990-2002 Paul Vojta 4 5 Permission is hereby granted, free of charge, to any person obtaining a copy 6 of this software and associated documentation files (the "Software"), to 7 deal in the Software without restriction, including without limitation the 8 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 sell copies of the Software, and to permit persons to whom the Software is 10 furnished to do so, subject to the following conditions: 11 12 The above copyright notice and this permission notice shall be included in 13 all copies or substantial portions of the Software. 14 15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 PAUL VOJTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22 NOTE: 23 This module is based on prior work as noted below. 24 25 \*========================================================================*/ 26 27 /* 28 * Support drawing routines for TeXsun and TeX 29 * 30 * Copyright, (C) 1987, 1988 Tim Morgan, UC Irvine 31 * Adapted for xdvi by Jeffrey Lee, U. of Toronto 32 * 33 * At the time these routines are called, the values of hh and vv should 34 * have been updated to the upper left corner of the graph (the position 35 * the \special appears at in the dvi file). Then the coordinates in the 36 * graphics commands are in terms of a virtual page with axes oriented the 37 * same as the Imagen and the SUN normally have: 38 * 39 * 0,0 40 * +-----------> +x 41 * | 42 * | 43 * | 44 * \ / 45 * +y 46 * 47 * Angles are measured in the conventional way, from +x towards +y. 48 * Unfortunately, that reverses the meaning of "counterclockwise" 49 * from what it's normally thought of. 50 * 51 * A lot of floating point arithmetic has been converted to integer 52 * arithmetic for speed. In some places, this is kind-of kludgy, but 53 * it's worth it. 54 */ 55 56 #include <ctype.h> 57 #include <err.h> 58 #include <limits.h> /* abs() */ 59 #include <math.h> /* sqrt(), sin(), cos() */ 60 #include <stdlib.h> /* abs() */ 61 #include <string.h> /* memcmp() */ 62 63 #include "dvi-init.h" /* page_info */ 64 #include "dvi-draw.h" /* PXL_V, PXL_H */ 65 #include "events.h" /* do_color_change() */ 66 #include "special.h" 67 #include "xdvi.h" 68 #include "util.h" /* xmalloc() */ 69 70 #define rint(x) floor((x) + 0.5) 71 72 73 #define MAXPOINTS 300 /* Max points in a path */ 74 #define MAX_PEN_SIZE 7 /* Max pixels of pen width */ 75 #define TWOPI (3.14159265359 * 2.0) 76 77 78 static int xx[MAXPOINTS], yy[MAXPOINTS]; /* Path in milli-inches */ 79 static int path_len = 0; /* # points in current path */ 80 static int pen_size = 1; /* Pixel width of lines drawn */ 81 82 static Boolean whiten = False; 83 static Boolean shade = False; 84 static Boolean blacken = False; 85 86 /* Unfortunately, these values also appear in dvisun.c */ 87 #define xRESOLUTION (pixels_per_inch/shrink_factor) 88 #define yRESOLUTION (pixels_per_inch/shrink_factor) 89 90 91 /* 92 * X drawing routines 93 */ 94 95 #define toint(x) ((int) ((x) + 0.5)) 96 #define xconv(x) (toint(tpic_conv*(x))/shrink_factor + PXL_H) 97 #define yconv(y) (toint(tpic_conv*(y))/shrink_factor + PXL_V) 98 99 /* 100 * Draw a line from (fx,fy) to (tx,ty). 101 * Right now, we ignore pen_size. 102 */ 103 static void 104 line_btw(int fx, int fy, int tx, int ty) 105 { 106 int fcx = xconv(fx); 107 int tcx = xconv(tx); 108 int fcy = yconv(fy); 109 int tcy = yconv(ty); 110 111 if ((fcx >= max_x && tcx >= max_x) || (fcx < min_x && tcx < min_x) || 112 (fcy >= max_y && tcy >= max_y) || (fcy < min_y && tcy < min_y)) 113 return; 114 if (fg_active != fg_current) 115 do_color_change(); 116 117 XDrawLine(DISP, currwin.win, ruleGC, 118 fcx - currwin.base_x, fcy - currwin.base_y, 119 tcx - currwin.base_x, tcy - currwin.base_y); 120 } 121 122 /* 123 * Draw a dot at (x,y) 124 */ 125 static void 126 dot_at(int x, int y) 127 { 128 int cx = xconv(x); 129 int cy = yconv(y); 130 131 if (cx >= max_x || cx < min_x || cy >= max_y || cy < min_y) 132 return; 133 if (fg_active != fg_current) 134 do_color_change(); 135 136 XDrawPoint(DISP, currwin.win, ruleGC, 137 cx - currwin.base_x, cy - currwin.base_y); 138 } 139 140 /* 141 * Apply the requested attributes to the last path (box) drawn. 142 * Attributes are reset. 143 * (Not currently implemented.) 144 */ 145 146 static void 147 do_attribute_path(int last_min_x, int last_max_x, int last_min_y, int last_max_y) 148 { 149 } 150 151 /* 152 * Set the size of the virtual pen used to draw in milli-inches 153 */ 154 155 static void 156 set_pen_size(char *cp) 157 { 158 int ps; 159 160 if (sscanf(cp, " %d ", &ps) != 1) { 161 warnx("invalid .ps command format: %s", cp); 162 return; 163 } 164 165 pen_size = (ps * (xRESOLUTION + yRESOLUTION) + 1000) / 2000; 166 if (pen_size < 1) 167 pen_size = 1; 168 else if (pen_size > MAX_PEN_SIZE) 169 pen_size = MAX_PEN_SIZE; 170 } 171 172 173 /* 174 * Print the line defined by previous path commands 175 */ 176 177 static void 178 flush_path(void) 179 { 180 int i; 181 int last_min_x, last_max_x, last_min_y, last_max_y; 182 183 last_min_x = 30000; 184 last_min_y = 30000; 185 last_max_x = -30000; 186 last_max_y = -30000; 187 for (i = 1; i < path_len; i++) { 188 if (xx[i] > last_max_x) last_max_x = xx[i]; 189 if (xx[i] < last_min_x) last_min_x = xx[i]; 190 if (yy[i] > last_max_y) last_max_y = yy[i]; 191 if (yy[i] < last_min_y) last_min_y = yy[i]; 192 line_btw(xx[i], yy[i], xx[i+1], yy[i+1]); 193 } 194 if (xx[path_len] > last_max_x) last_max_x = xx[path_len]; 195 if (xx[path_len] < last_min_x) last_min_x = xx[path_len]; 196 if (yy[path_len] > last_max_y) last_max_y = yy[path_len]; 197 if (yy[path_len] < last_min_y) last_min_y = yy[path_len]; 198 path_len = 0; 199 do_attribute_path(last_min_x, last_max_x, last_min_y, last_max_y); 200 } 201 202 203 /* 204 * Print a dashed line along the previously defined path, with 205 * the dashes/inch defined. 206 */ 207 208 static void 209 flush_dashed(char *cp, Boolean dotted) 210 { 211 int i; 212 int numdots; 213 int lx0, ly0, lx1, ly1; 214 int cx0, cy0, cx1, cy1; 215 float inchesperdash; 216 double d, spacesize, a, b, dx, dy, milliperdash; 217 218 if (sscanf(cp, " %f ", &inchesperdash) != 1) { 219 warnx("invalid format for dotted/dashed line: %s", cp); 220 return; 221 } 222 if (path_len <= 1 || inchesperdash <= 0.0) { 223 warnx("invalid conditions for dotted/dashed line"); 224 return; 225 } 226 milliperdash = inchesperdash * 1000.0; 227 lx0 = xx[1]; ly0 = yy[1]; 228 lx1 = xx[2]; ly1 = yy[2]; 229 dx = lx1 - lx0; 230 dy = ly1 - ly0; 231 if (dotted) { 232 numdots = sqrt(dx*dx + dy*dy) / milliperdash + 0.5; 233 if (numdots == 0) numdots = 1; 234 for (i = 0; i <= numdots; i++) { 235 a = (float) i / (float) numdots; 236 cx0 = lx0 + a * dx + 0.5; 237 cy0 = ly0 + a * dy + 0.5; 238 dot_at(cx0, cy0); 239 } 240 } 241 else { 242 d = sqrt(dx*dx + dy*dy); 243 numdots = d / (2.0 * milliperdash) + 1.0; 244 if (numdots <= 1) 245 line_btw(lx0, ly0, lx1, ly1); 246 else { 247 spacesize = (d - numdots * milliperdash) / (numdots - 1); 248 for (i = 0; i < numdots - 1; i++) { 249 a = i * (milliperdash + spacesize) / d; 250 b = a + milliperdash / d; 251 cx0 = lx0 + a * dx + 0.5; 252 cy0 = ly0 + a * dy + 0.5; 253 cx1 = lx0 + b * dx + 0.5; 254 cy1 = ly0 + b * dy + 0.5; 255 line_btw(cx0, cy0, cx1, cy1); 256 b += spacesize / d; 257 } 258 cx0 = lx0 + b * dx + 0.5; 259 cy0 = ly0 + b * dy + 0.5; 260 line_btw(cx0, cy0, lx1, ly1); 261 } 262 } 263 path_len = 0; 264 } 265 266 267 /* 268 * Add a point to the current path 269 */ 270 271 static void 272 add_path(char *cp) 273 { 274 int pathx, pathy; 275 276 if (++path_len >= MAXPOINTS) 277 errx(1, "Too many points"); 278 if (sscanf(cp, " %d %d ", &pathx, &pathy) != 2) 279 errx(1, "Malformed path command"); 280 xx[path_len] = pathx; 281 yy[path_len] = pathy; 282 } 283 284 285 /* 286 * Draw to a floating point position 287 */ 288 289 static void 290 im_fdraw(double x, double y) 291 { 292 if (++path_len >= MAXPOINTS) 293 errx(1, "Too many arc points"); 294 xx[path_len] = x + 0.5; 295 yy[path_len] = y + 0.5; 296 } 297 298 299 /* 300 * Draw an ellipse with the indicated center and radices. 301 */ 302 303 static void 304 draw_ellipse(int xc, int yc, int xr, int yr) 305 { 306 double angle, theta; 307 int n; 308 int px0, py0, px1, py1; 309 310 angle = (xr + yr) / 2.0; 311 theta = sqrt(1.0 / angle); 312 n = TWOPI / theta + 0.5; 313 if (n < 12) n = 12; 314 else if (n > 80) n = 80; 315 n /= 2; 316 theta = TWOPI / n; 317 318 angle = 0.0; 319 px0 = xc + xr; /* cos(0) = 1 */ 320 py0 = yc; /* sin(0) = 0 */ 321 while ((angle += theta) <= TWOPI) { 322 px1 = xc + xr*cos(angle) + 0.5; 323 py1 = yc + yr*sin(angle) + 0.5; 324 line_btw(px0, py0, px1, py1); 325 px0 = px1; 326 py0 = py1; 327 } 328 line_btw(px0, py0, xc + xr, yc); 329 } 330 331 /* 332 * Draw an arc 333 */ 334 335 static void 336 arc(char *cp, Boolean invis) 337 { 338 int xc, yc, xrad, yrad, n; 339 float start_angle, end_angle, angle, theta, r; 340 double xradius, yradius, xcenter, ycenter; 341 342 if (sscanf(cp, " %d %d %d %d %f %f ", &xc, &yc, &xrad, &yrad, 343 &start_angle, &end_angle) != 6) { 344 warnx("invalid arc specification: %s", cp); 345 return; 346 } 347 348 if (invis) return; 349 350 /* We have a specialized fast way to draw closed circles/ellipses */ 351 if (start_angle <= 0.0 && end_angle >= 6.282) { 352 draw_ellipse(xc, yc, xrad, yrad); 353 return; 354 } 355 xcenter = xc; 356 ycenter = yc; 357 xradius = xrad; 358 yradius = yrad; 359 r = (xradius + yradius) / 2.0; 360 theta = sqrt(1.0 / r); 361 n = 0.3 * TWOPI / theta + 0.5; 362 if (n < 12) n = 12; 363 else if (n > 80) n = 80; 364 n /= 2; 365 theta = TWOPI / n; 366 flush_path(); 367 im_fdraw(xcenter + xradius * cos(start_angle), 368 ycenter + yradius * sin(start_angle)); 369 angle = start_angle + theta; 370 if (end_angle < start_angle) end_angle += TWOPI; 371 while (angle < end_angle) { 372 im_fdraw(xcenter + xradius * cos(angle), 373 ycenter + yradius * sin(angle)); 374 angle += theta; 375 } 376 im_fdraw(xcenter + xradius * cos(end_angle), 377 ycenter + yradius * sin(end_angle)); 378 flush_path(); 379 } 380 381 382 /* 383 * APPROXIMATE integer distance between two points 384 */ 385 386 #define dist(x0, y0, x1, y1) (abs(x0 - x1) + abs(y0 - y1)) 387 388 389 /* 390 * Draw a spline along the previously defined path 391 */ 392 393 static void 394 flush_spline(void) 395 { 396 int xp, yp; 397 int N; 398 int lastx, lasty; 399 Boolean lastvalid = False; 400 int t1, t2, t3; 401 int steps; 402 int j; 403 int i, w; 404 405 N = path_len + 1; 406 xx[0] = xx[1]; 407 yy[0] = yy[1]; 408 xx[N] = xx[N-1]; 409 yy[N] = yy[N-1]; 410 for (i = 0; i < N - 1; i++) { /* interval */ 411 steps = (dist(xx[i], yy[i], xx[i+1], yy[i+1]) + 412 dist(xx[i+1], yy[i+1], xx[i+2], yy[i+2])) / 80; 413 for (j = 0; j < steps; j++) { /* points within */ 414 w = (j * 1000 + 500) / steps; 415 t1 = w * w / 20; 416 w -= 500; 417 t2 = (750000 - w * w) / 10; 418 w -= 500; 419 t3 = w * w / 20; 420 xp = (t1*xx[i+2] + t2*xx[i+1] + t3*xx[i] + 50000) / 100000; 421 yp = (t1*yy[i+2] + t2*yy[i+1] + t3*yy[i] + 50000) / 100000; 422 if (lastvalid) line_btw(lastx, lasty, xp, yp); 423 lastx = xp; 424 lasty = yp; 425 lastvalid = True; 426 } 427 } 428 path_len = 0; 429 } 430 431 432 /* 433 * Shade the last box, circle, or ellipse 434 */ 435 436 static void 437 shade_last(void) 438 { 439 blacken = whiten = False; 440 shade = True; 441 } 442 443 444 /* 445 * Make the last box, circle, or ellipse, white inside (shade with white) 446 */ 447 448 static void 449 whiten_last(void) 450 { 451 whiten = True; 452 blacken = shade = False; 453 } 454 455 456 /* 457 * Make last box, etc, black inside 458 */ 459 460 static void 461 blacken_last(void) 462 { 463 blacken = True; 464 whiten = shade = False; 465 } 466 467 468 void 469 init_prescan(void) 470 { 471 scanned_page = scanned_page_color = scanned_page_reset = -1; 472 473 if (!resource._use_color) 474 scanned_page_color = total_pages + 1; 475 } 476 477 478 /* 479 * Table of dvips color names. Produced by passing the table in 480 * dvipsk/color.lpro through the following perl script, and then 481 * sorting by most common colors. 482 * 483 * #! /usr/bin/perl -w 484 * 485 * sub cvpart { 486 * $val = $_[0] < 1.0 ? 1.0 - $_[0] : 0.0; 487 * return int($val * 65535 + 0.5) 488 * } 489 * 490 * $pat = "^\\/(\\w+)\\s*\\{\\s*([0-9.]+)\\s*([0-9.]+)\\s*([0-9.]+)" 491 * . "\\s*([0-9.]+)\\s*setcmykcolor\\s*\\}\\s*DC\\s*\$"; 492 * while (<STDIN>) { 493 * chop; 494 * if (/^%%/) {next;} 495 * if (/$pat/o) { 496 * printf "\t{ \"%s\",\t { %g, %g, %g } },\n", 497 * $1, cvpart($2 + $5), cvpart($3 + $5), cvpart($4 + $5); 498 * } 499 * else { 500 * print "Bad line: ", $_, "\n"; 501 * } 502 * } 503 */ 504 505 struct dvipscolor { 506 const char *name; 507 struct rgb color; 508 } colornames[] = { 509 { "Black", { 0, 0, 0 } }, 510 { "White", { 65535, 65535, 65535 } }, 511 { "Gray", { 32768, 32768, 32768 } }, 512 { "Red", { 65535, 0, 0 } }, 513 { "Green", { 0, 65535, 0 } }, 514 { "Blue", { 0, 0, 65535 } }, 515 { "Yellow", { 65535, 65535, 0 } }, 516 { "Cyan", { 0, 65535, 65535 } }, 517 { "Magenta", { 65535, 0, 65535 } }, 518 { "GreenYellow", { 55705, 65535, 20316 } }, 519 { "Goldenrod", { 65535, 58982, 10486 } }, 520 { "Dandelion", { 65535, 46530, 10486 } }, 521 { "Apricot", { 65535, 44564, 31457 } }, 522 { "Peach", { 65535, 32768, 19661 } }, 523 { "Melon", { 65535, 35389, 32768 } }, 524 { "YellowOrange", { 65535, 38010, 0 } }, 525 { "Orange", { 65535, 25559, 8520 } }, 526 { "BurntOrange", { 65535, 32112, 0 } }, 527 { "Bittersweet", { 49807, 655, 0 } }, 528 { "RedOrange", { 65535, 15073, 8520 } }, 529 { "Mahogany", { 42598, 0, 0 } }, 530 { "Maroon", { 44564, 0, 0 } }, 531 { "BrickRed", { 47185, 0, 0 } }, 532 { "OrangeRed", { 65535, 0, 32768 } }, 533 { "RubineRed", { 65535, 0, 57015 } }, 534 { "WildStrawberry", { 65535, 2621, 39976 } }, 535 { "Salmon", { 65535, 30801, 40632 } }, 536 { "CarnationPink", { 65535, 24248, 65535 } }, 537 { "VioletRed", { 65535, 12452, 65535 } }, 538 { "Rhodamine", { 65535, 11796, 65535 } }, 539 { "Mulberry", { 41942, 5243, 64224 } }, 540 { "RedViolet", { 38666, 0, 43253 } }, 541 { "Fuchsia", { 29491, 655, 60292 } }, 542 { "Lavender", { 65535, 34078, 65535 } }, 543 { "Thistle", { 57671, 26869, 65535 } }, 544 { "Orchid", { 44564, 23593, 65535 } }, 545 { "DarkOrchid", { 39321, 13107, 52428 } }, 546 { "Purple", { 36044, 9175, 65535 } }, 547 { "Plum", { 32768, 0, 65535 } }, 548 { "Violet", { 13762, 7864, 65535 } }, 549 { "RoyalPurple", { 16384, 6553, 65535 } }, 550 { "BlueViolet", { 6553, 3277, 62914 } }, 551 { "Periwinkle", { 28180, 29491, 65535 } }, 552 { "CadetBlue", { 24903, 28180, 50462 } }, 553 { "CornflowerBlue", { 22937, 57015, 65535 } }, 554 { "MidnightBlue", { 0, 28835, 37355 } }, 555 { "NavyBlue", { 3932, 30146, 65535 } }, 556 { "RoyalBlue", { 0, 32768, 65535 } }, 557 { "Cerulean", { 3932, 58326, 65535 } }, 558 { "ProcessBlue", { 2621, 65535, 65535 } }, 559 { "SkyBlue", { 24903, 65535, 57671 } }, 560 { "Turquoise", { 9830, 65535, 52428 } }, 561 { "TealBlue", { 7864, 64224, 41942 } }, 562 { "Aquamarine", { 11796, 65535, 45875 } }, 563 { "BlueGreen", { 9830, 65535, 43908 } }, 564 { "Emerald", { 0, 65535, 32768 } }, 565 { "JungleGreen", { 655, 65535, 31457 } }, 566 { "SeaGreen", { 20316, 65535, 32768 } }, 567 { "ForestGreen", { 0, 57671, 0 } }, 568 { "PineGreen", { 0, 49151, 10486 } }, 569 { "LimeGreen", { 32768, 65535, 0 } }, 570 { "YellowGreen", { 36700, 65535, 17039 } }, 571 { "SpringGreen", { 48496, 65535, 15728 } }, 572 { "OliveGreen", { 0, 39321, 0 } }, 573 { "RawSienna", { 36044, 0, 0 } }, 574 { "Sepia", { 19661, 0, 0 } }, 575 { "Brown", { 26214, 0, 0 } }, 576 { "Tan", { 56360, 38010, 28835 } }, 577 }; 578 579 static void 580 reverse_color(struct rgb *rgbp) 581 { 582 if (resource.reverse == False) 583 return; 584 585 rgbp->r = 65535 - rgbp->r; 586 rgbp->g = 65535 - rgbp->g; 587 rgbp->b = 65535 - rgbp->b; 588 } 589 590 static Boolean 591 parse_color(const char *cp0, const char *cp, struct rgb *rgbp) 592 { 593 double r, g, b; 594 595 while (*cp == ' ') 596 ++cp; 597 598 if (strncasecmp(cp, "rgb ", 4) == 0) { 599 if (sscanf(cp + 3, "%lf %lf %lf", &r, &g, &b) == 3 600 && r >= 0 && r <= 1 && g >= 0 && g <= 1 && b >= 0 && b <= 1) { 601 rgbp->r = r * 65535 + 0.5; 602 rgbp->g = g * 65535 + 0.5; 603 rgbp->b = b * 65535 + 0.5; 604 reverse_color(rgbp); 605 return True; 606 } 607 } 608 else if (strncasecmp(cp, "gray ", 5) == 0) { 609 if (sscanf(cp + 4, "%lf", &r) == 1 && r >= 0 && r <= 1) { 610 rgbp->r = rgbp->g = rgbp->b = r * 65535 + 0.5; 611 reverse_color(rgbp); 612 return True; 613 } 614 } 615 else if (strncasecmp(cp, "cmyk ", 5) == 0) { 616 double k; 617 618 if (sscanf(cp + 4, "%lf %lf %lf %lf", &r, &g, &b, &k) == 4 619 && r >= 0 && r <= 1 && g >= 0 && g <= 1 && b >= 0 && b <= 1 620 && k >= 0 && k <= 1) { 621 r = 1.0 - r - k; /* cyan --> red */ 622 rgbp->r = (r < 0 ? 0 : r * 65535 + 0.5); 623 g = 1.0 - g - k; /* magenta --> green */ 624 rgbp->g = (g < 0 ? 0 : g * 65535 + 0.5); 625 b = 1.0 - b - k; /* yellow --> blue */ 626 rgbp->b = (b < 0 ? 0 : b * 65535 + 0.5); 627 reverse_color(rgbp); 628 return True; 629 } 630 } 631 else if (strncasecmp(cp, "hsb ", 4) == 0) { 632 double hue, sat, bri; 633 634 if (sscanf(cp + 3, "%lf %lf %lf", &hue, &sat, &bri) == 3 && hue >= 0 635 && hue <= 1 && sat >= 0 && sat <= 1 && bri >= 0 && bri <= 1) { 636 int h = (int) (6 * hue); 637 double p = bri * (1 - sat); 638 double q = bri * (1 - sat * (6 * hue - h)); 639 double t = p - q + bri; 640 641 switch (h) { 642 case 0: r = bri; g = t; b = p; break; /* Red - Yel */ 643 case 1: r = q; g = bri; b = p; break; /* Yel - Grn */ 644 case 2: r = p; g = bri; b = t; break; /* Grn - Cyn */ 645 case 3: r = p; g = q; b = bri; break; /* Cyn - Blu */ 646 case 4: r = t; g = p; b = bri; break; /* Blu - Mag */ 647 case 5: r = bri; g = p; b = q; break; /* Mag - Red */ 648 case 6: r = bri; g = b = p; break; /* Red */ 649 } 650 rgbp->r = r * 65535 + 0.5; 651 rgbp->g = g * 65535 + 0.5; 652 rgbp->b = b * 65535 + 0.5; 653 reverse_color(rgbp); 654 return True; 655 } 656 } 657 else { 658 /* check table of dvips color names */ 659 for (int i = 0; i < XtNumber(colornames); i++) { 660 if (strcmp(cp, colornames[i].name) != 0) 661 continue; 662 663 *rgbp = colornames[i].color; 664 reverse_color(rgbp); 665 return True; 666 } 667 } 668 669 if (cp0 != NULL) 670 warnx("Invalid color name in special `%s'", cp0); 671 672 return False; 673 } 674 675 676 /* 677 * Color stack used when scanning. These records stay around, to ease 678 * the burden on xmalloc(). The first entry is a dummy entry, giving 679 * the default foreground color (as modified). 680 */ 681 682 struct colorframe { 683 struct colorframe *next, *prev; 684 struct rgb color; 685 }; 686 687 static struct colorframe scanstack_head; 688 static int scanstack_len; 689 static struct colorframe *scanstack_current; 690 691 static void 692 init_page_colors(void) 693 { 694 int i; 695 696 page_colors = xmalloc(total_pages * sizeof *page_colors); 697 698 for (i = 0; i <= scanned_page + 1; ++i) { 699 page_colors[i].bg = bg_initial; /* from command line */ 700 page_colors[i].colorstack = &fg_initial; 701 page_colors[i].stacksize = 1; 702 } 703 while (i < total_pages) 704 page_colors[i++].colorstack = NULL; 705 706 scanstack_head.color = fg_initial; 707 scanstack_len = 1; /* nothing yet */ 708 scanstack_current = &scanstack_head; /* stack position */ 709 } 710 711 712 static void 713 scan_bg_color(const char *cp) 714 { 715 if (!resource._use_color) 716 return; 717 718 if (page_colors == NULL) 719 init_page_colors(); 720 721 (void) parse_color(cp, cp + 11, &page_colors[scanned_page + 1].bg); 722 } 723 724 static void 725 scan_color(const char *cp) 726 { 727 const char *cp1 = cp + 6; 728 729 if (!resource._use_color) 730 return; 731 732 while (*cp1 == ' ') ++cp1; 733 734 if (page_colors == NULL) 735 init_page_colors(); 736 737 if (strncasecmp(cp1, "push ", 5) == 0) { 738 if (scanstack_current->next == NULL) { /* if at end */ 739 scanstack_current->next = xmalloc(sizeof *scanstack_current); 740 scanstack_current->next->prev = scanstack_current; 741 scanstack_current->next->next = NULL; 742 } 743 scanstack_current = scanstack_current->next; 744 ++scanstack_len; 745 if (!parse_color(cp, cp1 + 5, &scanstack_current->color)) 746 scanstack_current->color = scanstack_current->prev->color; 747 } 748 else if (strncasecmp(cp1, "pop", 3) == 0) { 749 if (scanstack_len <= 1) { 750 warnx("Color pop occurred with empty color stack."); 751 } 752 else { 753 scanstack_current = scanstack_current->prev; 754 --scanstack_len; 755 } 756 } 757 else { 758 (void) parse_color(cp, cp1, &scanstack_head.color); 759 if (scanstack_len > 1) { 760 struct colorframe *cfp; 761 762 warnx("Global color change occurred with non-empty color stack" 763 ";\ncoping by setting all stack entries to that color."); 764 for (cfp = scanstack_head.next;; cfp = cfp->next) { 765 cfp->color = scanstack_head.color; 766 if (cfp == scanstack_current) break; 767 } 768 } 769 } 770 } 771 772 void 773 scan_color_eop(void) 774 { 775 int i; 776 const struct rgb *prev; 777 struct colorframe *cf; 778 const struct rgb *p1; 779 struct rgb *rgbp; 780 781 if (page_colors == NULL) 782 return; 783 784 /* set background color for next page */ 785 if (scanned_page + 1 < total_pages) 786 page_colors[scanned_page + 1].bg = page_colors[scanned_page].bg; 787 788 /* save the stack contents */ 789 page_colors[scanned_page].stacksize = scanstack_len; 790 791 prev = &fg_initial; 792 i = 1; 793 if (scanned_page > 0) { 794 prev = page_colors[scanned_page - 1].colorstack; 795 i = page_colors[scanned_page - 1].stacksize; 796 } 797 if (scanstack_len <= i) { 798 /* try to reuse the previous array */ 799 p1 = prev; 800 cf = &scanstack_head; 801 for (i = 0;;) { 802 if (p1->r != cf->color.r || p1->g != cf->color.g 803 || p1->b != cf->color.b) 804 break; 805 if (++i >= scanstack_len) { /* end loop; reuse memory */ 806 page_colors[scanned_page].colorstack = prev; 807 return; /* done */ 808 } 809 ++p1; 810 cf = cf->next; 811 } 812 } 813 page_colors[scanned_page].colorstack = rgbp 814 = xmalloc(scanstack_len * sizeof *rgbp); 815 cf = &scanstack_head; 816 for (i = 0; i < scanstack_len; ++i) { 817 *rgbp++ = cf->color; 818 cf = cf->next; 819 } 820 } 821 822 823 /* 824 * Page stack when displaying. Again, the records stay around once 825 * allocated. Bottom entries in the stack (inherited from previous 826 * pages) are stored in an array instead (see comments in xdvi.h). 827 */ 828 829 static struct colorframe *rcs_head; 830 831 832 /* 833 * We don't actually do any X calls yet to change colors; that can wait 834 * until a character or rule needs to be drawn. 835 */ 836 837 void 838 set_fg_color(const struct rgb *color) 839 { 840 struct fgrec **fgpp; 841 842 if (fg_current != NULL && 843 color->r == fg_current->color.r && 844 color->g == fg_current->color.g && 845 color->b == fg_current->color.b) 846 return; 847 848 for (fgpp = &bg_current->fg_head;;) { 849 fg_current = *fgpp; 850 if (fg_current == NULL) { /* if color is not in list */ 851 fg_current = *fgpp = xmalloc(sizeof *fg_current); 852 fg_current->next = NULL; 853 fg_current->color = *color; 854 fg_current->pixel_good = False; 855 break; 856 } 857 if (fg_current->color.r == color->r && 858 fg_current->color.g == color->g && 859 fg_current->color.b == color->b) 860 break; 861 fgpp = &fg_current->next; 862 } 863 if (debug & DBG_DVI) 864 printf("Changing fg color to %5d %5d %5d\n", 865 color->r, color->g, color->b); 866 } 867 868 static void 869 color_special(const char *cp) 870 { 871 if (!resource._use_color) 872 return; 873 874 while (*cp == ' ') ++cp; 875 876 if (strncasecmp(cp, "push ", 5) == 0) { 877 if (rcs_top == NULL) { 878 if (rcs_head == NULL) { 879 rcs_head = xmalloc(sizeof *rcs_head); 880 rcs_head->prev = rcs_head->next = NULL; 881 } 882 rcs_top = rcs_head; 883 } 884 else { 885 struct colorframe *rcs_next; 886 887 rcs_next = rcs_top->next; 888 if (rcs_next == NULL) { 889 rcs_next = rcs_top->next = xmalloc(sizeof *rcs_next); 890 rcs_next->prev = rcs_top; 891 rcs_next->next = NULL; 892 } 893 rcs_top = rcs_next; 894 } 895 if (!parse_color(NULL, cp + 5, &rcs_top->color)) { 896 if (rcs_top->prev != NULL) 897 rcs_top->color = rcs_top->prev->color; 898 else 899 rcs_top->color = color_bottom[color_bot_size - 1]; 900 } 901 set_fg_color(&rcs_top->color); 902 } 903 else if (strncasecmp(cp, "pop", 3) == 0) { 904 /* Pop stack */ 905 if (rcs_top != NULL) { 906 if (color_bot_size > 0) 907 rcs_top = rcs_top->prev; 908 else return; 909 } 910 else if (color_bot_size > 1) 911 --color_bot_size; 912 else { 913 if (scanned_page_reset >= 0) { 914 /* turn on scanning and redraw the page */ 915 scanned_page = 916 scanned_page_color = scanned_page_reset = -1; 917 ev_flags |= EV_NEWPAGE; /* force a redraw */ 918 } 919 return; 920 } 921 922 set_fg_color(rcs_top != NULL ? &rcs_top->color 923 : &color_bottom[color_bot_size - 1]); 924 } 925 else { 926 struct rgb color; 927 928 if (!parse_color(NULL, cp, &color)) return; 929 930 /* clear stack */ 931 if (rcs_head == NULL) { 932 rcs_head = xmalloc(sizeof *rcs_head); 933 rcs_head->prev = rcs_head->next = NULL; 934 } 935 rcs_top = rcs_head; 936 color_bot_size = 0; 937 938 /* Change top of stack */ 939 rcs_top->color = color; 940 941 set_fg_color(&color); 942 } 943 } 944 945 946 static int 947 atopix(const float val, const char unit[2]) 948 { 949 double factor; 950 951 #if A4 952 factor = 1.0 / 2.54; /* cm */ 953 #else 954 factor = 1.0; /* inches */ 955 #endif 956 switch (unit[0] << 8 | unit[1]) { 957 #if A4 958 case 'i' << 8 | 'n': factor = 1.0; break; 959 #else 960 case 'c' << 8 | 'm': factor = 1.0 / 2.54; break; 961 #endif 962 case 'm' << 8 | 'm': factor = 1.0 / 25.4; break; 963 case 'p' << 8 | 't': factor = 1.0 / 72.27; break; 964 case 'p' << 8 | 'c': factor = 12.0 / 72.27; break; 965 case 'b' << 8 | 'p': factor = 1.0 / 72.0; break; 966 case 'd' << 8 | 'd': factor = 1238.0 / 1157.0 / 72.27; break; 967 case 'c' << 8 | 'c': factor = 12 * 1238.0 / 1157.0 / 72.27; 968 break; 969 case 's' << 8 | 'p': factor = 1.0 / 72.27 / 65536; break; 970 } 971 972 return (int) (factor * val * pixels_per_inch + 0.5); 973 } 974 975 static void 976 scan_papersize(const char *cp) 977 { 978 float wf, hf; 979 char wu[3], hu[3]; 980 unsigned int w, h; 981 982 if (sscanf(cp, "%f%2s,%f%2s", &wf, wu, &hf, hu) != 4) { 983 warnx("error parsing papersize special '%s'", cp); 984 return; 985 } 986 987 w = atopix(wf, wu); 988 h = atopix(hf, hu); 989 990 if (w == 0 || h == 0) { 991 warnx("invalid papersize special '%s'", cp); 992 return; 993 } 994 995 page_info[scanned_page + 1].pw = page_info[scanned_page + 1].ww = w; 996 page_info[scanned_page + 1].ph = page_info[scanned_page + 1].wh = h; 997 } 998 999 1000 /* 1001 * The following copyright message applies to the rest of this file. --PV 1002 */ 1003 1004 /* 1005 * This program is Copyright (C) 1987 by the Board of Trustees of the 1006 * University of Illinois, and by the author Dirk Grunwald. 1007 * 1008 * This program may be freely copied, as long as this copyright 1009 * message remaines affixed. It may not be sold, although it may 1010 * be distributed with other software which is sold. If the 1011 * software is distributed, the source code must be made available. 1012 * 1013 * No warranty, expressed or implied, is given with this software. 1014 * It is presented in the hope that it will prove useful. 1015 * 1016 * Hacked in ignorance and desperation by jonah@db.toronto.edu 1017 */ 1018 1019 /* 1020 * The code to handle the \specials generated by tpic was modified 1021 * by Dirk Grunwald using the code Tim Morgan at Univ. of Calif, Irvine 1022 * wrote for TeXsun. 1023 */ 1024 1025 static char * 1026 endofcommand(char *cp) 1027 { 1028 while (isspace(*cp)) 1029 ++cp; 1030 if (*cp != '=') 1031 return NULL; 1032 do 1033 ++cp; 1034 while (isspace(*cp)); 1035 1036 return cp; 1037 } 1038 1039 void 1040 applicationDoSpecial(char *cp) 1041 { 1042 if (debug & DBG_DVI) 1043 printf(" `%s'\n", cp); 1044 1045 /* Skip white space */ 1046 while (isspace(*cp)) ++cp; 1047 1048 /* Ignore initial "xdvi:" */ 1049 if (memcmp(cp, "xdvi:", 5) == 0) { 1050 cp += 5; 1051 while (isspace(*cp)) 1052 ++cp; 1053 } 1054 1055 if (strncasecmp(cp, "color ", 6) == 0) { 1056 color_special(cp + 6); 1057 return; 1058 } 1059 1060 /* these should have been scanned */ 1061 1062 if (*cp == '!' || 1063 (strncasecmp(cp, "header", 6) == 0 && endofcommand(cp + 6) != NULL)) { 1064 return; 1065 } 1066 1067 if (strncasecmp(cp, "background ", 11) == 0) { 1068 if (resource._use_color && scanned_page_reset >= 0) { 1069 /* turn on scanning and redraw the page */ 1070 scanned_page = scanned_page_color = 1071 scanned_page_reset = -1; 1072 ev_flags |= EV_NEWPAGE; /* force a redraw */ 1073 } 1074 return; 1075 } 1076 1077 if (memcmp(cp, "papersize", 9) == 0 && endofcommand(cp + 9) != NULL) { 1078 if (scanned_page_reset >= 0) { 1079 /* turn on scanning and redraw the page */ 1080 scanned_page = scanned_page_color = 1081 scanned_page_reset = -1; 1082 ev_flags |= EV_NEWPAGE; /* force a redraw */ 1083 } 1084 return; 1085 } 1086 1087 /* tpic specials */ 1088 1089 if (*cp >= 'a' && *cp <= 'z' && cp[1] >= 'a' && cp[1] <= 'z' && 1090 (isspace(cp[2]) || cp[2] == '\0')) { 1091 1092 #define CMD(x, y) ((x) << 8 | (y)) 1093 switch (CMD(*cp, cp[1])) { 1094 case CMD('p','n'): set_pen_size(cp + 2); return; 1095 case CMD('f','p'): flush_path(); return; 1096 case CMD('d','a'): flush_dashed(cp + 2, False); return; 1097 case CMD('d','t'): flush_dashed(cp + 2, True); return; 1098 case CMD('p','a'): add_path(cp + 2); return; 1099 case CMD('a','r'): arc(cp + 2, False); return; 1100 case CMD('i','a'): arc(cp + 2, True); return; 1101 case CMD('s','p'): flush_spline(); return; 1102 case CMD('s','h'): shade_last(); return; 1103 case CMD('w','h'): whiten_last(); return; 1104 case CMD('b','k'): blacken_last(); return; 1105 case CMD('i','p'): /* throw away the path -- jansteen */ 1106 path_len = 0; return; 1107 } 1108 #undef CMD 1109 } 1110 1111 /* We support hypertex */ 1112 if (strncasecmp(cp, "html: ", 5) == 0) 1113 return; 1114 1115 warnx("special '%s' not implemented", cp); 1116 } 1117 1118 1119 void 1120 scan_special(char *cp) 1121 { 1122 char *p; 1123 1124 if (debug & DBG_PS) 1125 printf("Scanning special `%s'.\n", cp); 1126 1127 /* Skip white space */ 1128 while (isspace(*cp)) ++cp; 1129 1130 /* Ignore initial "xdvi:" */ 1131 if (memcmp(cp, "xdvi:", 5) == 0) { 1132 cp += 5; 1133 while (isspace(*cp)) ++cp; 1134 } 1135 1136 if (strncasecmp(cp, "background ", 11) == 0) { 1137 scan_bg_color(cp); 1138 return; 1139 } 1140 else if (strncasecmp(cp, "color ", 6) == 0) { 1141 scan_color(cp); 1142 return; 1143 } 1144 1145 if (memcmp(cp, "papersize", 9) == 0 && 1146 (p = endofcommand(cp + 9)) != NULL) { 1147 scan_papersize(p); 1148 return; 1149 } 1150 }