1% This file is part of MetaPost; 2% the MetaPost program is in the public domain. 3% See the <Show version...> code in mpost.w for more info. 4 5% Here is TeX material that gets inserted after \input webmac 6\def\hang{\hangindent 3em\noindent\ignorespaces} 7\def\textindent#1{\hangindent2.5em\noindent\hbox to2.5em{\hss#1 }\ignorespaces} 8\def\PASCAL{Pascal} 9\def\ps{PostScript} 10\def\ph{\hbox{Pascal-H}} 11\def\psqrt#1{\sqrt{\mathstrut#1}} 12\def\k{_{k+1}} 13\def\pct!{{\char`\%}} % percent sign in ordinary text 14\font\tenlogo=logo10 % font used for the METAFONT logo 15\font\logos=logosl10 16\def\MF{{\tenlogo META}\-{\tenlogo FONT}} 17\def\MP{{\tenlogo META}\-{\tenlogo POST}} 18\def\<#1>{$\langle#1\rangle$} 19\def\section{\mathhexbox278} 20\let\swap=\leftrightarrow 21\def\round{\mathop{\rm round}\nolimits} 22\mathchardef\vbv="026A % synonym for `\|' 23\def\vb{\relax\ifmmode\vbv\else$\vbv$\fi} 24\def\[#1]{} % from pascal web 25\def\(#1){} % this is used to make section names sort themselves better 26\def\9#1{} % this is used for sort keys in the index via @@:sort key}{entry@@> 27 28\let\?=\relax % we want to be able to \write a \? 29 30\def\title{MetaPost \ps\ output} 31\def\topofcontents{\hsize 5.5in 32 \vglue -30pt plus 1fil minus 1.5in 33 \def\?##1]{\hbox to 1in{\hfil##1.\ }} 34 } 35\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in} 36\pdfoutput=1 37\pageno=3 38 39@ 40@d zero_t ((math_data *)mp->math)->zero_t 41@d number_zero(A) (((math_data *)(mp->math))->equal)(A,zero_t) 42@d number_greater(A,B) (((math_data *)(mp->math))->greater)(A,B) 43@d number_positive(A) number_greater(A, zero_t) 44@d number_to_scaled(A) (((math_data *)(mp->math))->to_scaled)(A) 45@d round_unscaled(A) (((math_data *)(mp->math))->round_unscaled)(A) 46@d true 1 47@d false 0 48@d null_font 0 49@d null 0 50@d unity 1.0 /* $2^{16}$, represents 1.00000 */ 51@d incr(A) (A)=(A)+1 /* increase a variable by unity */ 52@d decr(A) (A)=(A)-1 /* decrease a variable by unity */ 53@d negate(A) (A)=-(A) /* change the sign of a variable */ 54@d odd(A) (abs(A)%2==1) 55@d max_quarterword 0x3FFF /* largest allowable value in a |quarterword| */ 56 57@c 58#include <w2c/config.h> 59#include <stdio.h> 60#include <stdlib.h> 61#include <string.h> 62#include <stdarg.h> 63#include <assert.h> 64#include <math.h> 65#include "avl.h" 66#include "mplib.h" 67#include "mplibps.h" /* external header */ 68#include "mpmp.h" /* internal header */ 69#include "mppsout.h" /* internal header */ 70#include "mpmath.h" /* internal header */ 71#include "mpstrings.h" /* internal header */ 72@h 73@<Declarations@> 74@<Static variables in the outer block@> 75 76@ There is a small bit of code from the backend that bleads through 77to the frontend because I do not know how to set up the includes 78properly. That is the |typedef struct psout_data_struct * psout_data|. 79 80@ @(mppsout.h@>= 81#ifndef MPPSOUT_H 82#define MPPSOUT_H 1 83#include "avl.h" 84#include "mplib.h" 85#include "mpmp.h" 86#include "mplibps.h" 87@<Types...@> 88typedef struct psout_data_struct { 89 @<Globals@> 90} psout_data_struct ; 91@<Exported function headers@> 92#endif 93 94@ @c 95static boolean mp_isdigit (int a) { 96 return (a>='0'&&a<='9'); 97} 98static int mp_tolower (int a) { 99 if (a>='A' && a <='Z') 100 return a - 'A' + 'a'; 101 return a; 102} 103static int mp_strcasecmp (const char *s1, const char *s2) { 104 int r; 105 char *ss1, *ss2, *c; 106 ss1 = mp_strdup(s1); 107 c = ss1; 108 while (*c != '\0') { 109 *c = (char)mp_tolower(*c); c++; 110 } 111 ss2 = mp_strdup(s2); 112 c = ss2; 113 while (*c != '\0') { 114 *c = (char)mp_tolower(*c); c++; 115 } 116 r = strcmp(ss1,ss2); 117 free (ss1); free(ss2); 118 return r; 119} 120 121@ @<Exported function headers@>= 122void mp_ps_backend_initialize (MP mp) ; 123void mp_ps_backend_free (MP mp) ; 124 125@ 126@c void mp_ps_backend_initialize (MP mp) { 127 mp->ps = mp_xmalloc(mp,1,sizeof(psout_data_struct)); 128 memset(mp->ps,0,sizeof(psout_data_struct)); 129 @<Set initial values@>; 130} 131void mp_ps_backend_free (MP mp) { 132 @<Dealloc variables@>; 133 enc_free(mp); 134 t1_free(mp); 135 fm_free(mp); 136 mp_xfree(mp->ps); 137 mp->ps = NULL; 138} 139 140@ Writing to ps files 141 142@<Globals@>= 143integer ps_offset; 144 /* the number of characters on the current \ps\ file line */ 145 146@ @<Set initial values@>= 147mp->ps->ps_offset = 0; 148 149@ 150 151@d wps(A) (mp->write_ascii_file)(mp,mp->output_file,(A)) 152@d wps_chr(A) do { 153 char ss[2]; 154 ss[0]=(char)(A); ss[1]=0; 155 (mp->write_ascii_file)(mp,mp->output_file,(char *)ss); 156} while (0) 157@d wps_cr (mp->write_ascii_file)(mp,mp->output_file,"\n") 158@d wps_ln(A) { wterm_cr; (mp->write_ascii_file)(mp,mp->output_file,(A)); } 159 160@c 161static void mp_ps_print_ln (MP mp) { /* prints an end-of-line */ 162 wps_cr; 163 mp->ps->ps_offset=0; 164} 165 166@ @c 167static void mp_ps_print_char (MP mp, int s) { /* prints a single character */ 168 if ( s==13 ) { 169 wps_cr; mp->ps->ps_offset=0; 170 } else { 171 wps_chr(s); incr(mp->ps->ps_offset); 172 } 173} 174 175@ @c 176static void mp_ps_do_print (MP mp, const char *ss, size_t len) { /* prints string |s| */ 177 size_t j = 0; 178 if (len>255) { 179 while ( j<len ){ 180 mp_ps_print_char(mp, ss[j]); incr(j); 181 } 182 } else { 183 static char outbuf[256]; 184 strncpy(outbuf, ss, len+1); 185 while ( j<len ){ 186 if ( *(outbuf+j) == 13 ) { 187 *(outbuf+j) = '\n'; 188 mp->ps->ps_offset=0; 189 } else { 190 mp->ps->ps_offset++; 191 } 192 j++; 193 } 194 (mp->write_ascii_file)(mp,mp->output_file,outbuf); 195 } 196} 197 198@ Deciding where to break the ps output line. 199 200@d ps_room(A) if (mp->ps->ps_offset>0 && (mp->ps->ps_offset+(int)(A))>mp->max_print_line ) { 201 mp_ps_print_ln(mp); /* optional line break */ 202} 203 204@c 205static void mp_ps_print (MP mp, const char *ss) { 206 ps_room(strlen(ss)); 207 mp_ps_do_print(mp, ss, strlen(ss)); 208} 209static void mp_ps_dsc_print (MP mp, const char *dsc, const char *ss) { 210 ps_room(strlen(ss)); 211 if (mp->ps->ps_offset==0) { 212 mp_ps_do_print(mp, "%%+ ", 4); 213 mp_ps_do_print(mp, dsc, strlen(dsc)); 214 mp_ps_print_char(mp, ' '); 215 } 216 mp_ps_do_print(mp, ss, strlen(ss)); 217} 218 219@ The procedure |print_nl| is like |print|, but it makes sure that the 220string appears at the beginning of a new line. 221 222@c 223static void mp_ps_print_nl (MP mp, const char *s) { /* prints string |s| at beginning of line */ 224 if ( mp->ps->ps_offset>0 ) mp_ps_print_ln(mp); 225 mp_ps_print(mp, s); 226} 227 228@ The following procedure, which prints out the decimal representation of a 229given integer |n|, has been written carefully so that it works properly 230if |n=0| or if |(-n)| would cause overflow. It does not apply |mod| or |div| 231to negative arguments, since such operations are not implemented consistently 232by all \PASCAL\ compilers. 233 234@c 235static void mp_ps_print_int (MP mp,integer n) { /* prints an integer in decimal form */ 236 integer m; /* used to negate |n| in possibly dangerous cases */ 237 char outbuf [24]; /* dig[23], plus terminating \0 */ 238 unsigned char dig[23]; /* digits in a number, for rounding */ 239 int k = 0; /* index to current digit; we assume that $|n|<10^{23}$ */ 240 int l = 0; 241 if ( n<0 ) { 242 mp_ps_print_char(mp, '-'); 243 if ( n>-100000000 ) { 244 negate(n); 245 } else { 246 m=-1-n; n=m / 10; m=(m % 10)+1; k=1; 247 if ( m<10 ) { 248 dig[0]=(unsigned char)m; 249 } else { 250 dig[0]=0; incr(n); 251 } 252 } 253 } 254 do { 255 dig[k]=(unsigned char)(n % 10); n=n / 10; incr(k); 256 } while (n!=0); 257 /* print the digits */ 258 while ( k-->0 ){ 259 outbuf[l++] = (char)('0'+dig[k]); 260 } 261 outbuf[l] = '\0'; 262 (mp->write_ascii_file)(mp,mp->output_file,outbuf); 263} 264 265@ \MP\ also makes use of a trivial procedure to print two digits. The 266following subroutine is usually called with a parameter in the range |0<=n<=99|. 267 268@c 269static void mp_ps_print_dd (MP mp,integer n) { /* prints two least significant digits */ 270 n=abs(n) % 100; 271 mp_ps_print_char(mp, '0'+(n / 10)); 272 mp_ps_print_char(mp, '0'+(n % 10)); 273} 274 275@ Conversely, here is a procedure analogous to |print_int|. 276 277There are two versions of this function: |ps_print_double_scaled| is used 278if metapost runs in scaled (backward compatibility) mode, because that 279version produces results that are much closer to the old version that exported 280figures with scaled fields instead of double fields. It is not always the 281same because a little bit of precision has gone in the scaled to double 282conversion, but still quite a bit closer than |%.6f| in the 'double' case. 283 284@d unityold 65536 285@c 286static void mp_ps_print_double_new (MP mp, double s) { 287 char *value, *c; 288 int i; 289 value = mp_xmalloc(mp,1,32); 290 memset(value,0,32); 291 mp_snprintf(value,32,"%.6f", s); 292 for (i=31;i>=0;i--) { 293 if (value[i]) { 294 if (value[i] == '0') 295 value[i] = '\0'; 296 else 297 break; 298 } 299 } 300 if (value[i] == '.') 301 value[i] = '\0'; 302 c = value; 303 while (*c) { 304 mp_ps_print_char(mp, *c); 305 c++; 306 } 307 free(value); 308} 309 310static void mp_ps_print_double_scaled (MP mp, double ss) { 311 int delta; /* amount of allowable inaccuracy */ 312 int s = ss * unityold; 313 if ( s<0 ) { 314 mp_ps_print_char(mp, '-'); 315 negate(s); /* print the sign, if negative */ 316 } 317 mp_ps_print_int(mp, s / unityold); /* print the integer part */ 318 s=10*(s % unityold)+5; 319 if ( s!=5 ) { 320 delta=10; 321 mp_ps_print_char(mp, '.'); 322 do { 323 if ( delta>unityold ) 324 s=s+0100000-(delta / 2); /* round the final digit */ 325 mp_ps_print_char(mp, '0'+(s / unityold)); 326 s=10*(s % unityold); 327 delta=delta*10; 328 } while (s>delta); 329 } 330} 331static void mp_ps_print_double (MP mp, double s) { 332 if (mp->math_mode == mp_math_scaled_mode) { 333 mp_ps_print_double_scaled (mp, s); 334 } else { 335 mp_ps_print_double_new (mp, s); 336 } 337} 338 339 340@* \[44a] Dealing with font encodings. 341 342First, here are a few helpers for parsing files 343 344@d check_buf(size, buf_size) 345 if ((unsigned)(size) > (unsigned)(buf_size)) { 346 char S[128]; 347 mp_snprintf(S,128,"buffer overflow: (%u,%u) at file %s, line %d", 348 (unsigned)(size),(unsigned)(buf_size), __FILE__, __LINE__ ); 349 mp_fatal_error(mp,S); 350 } 351 352@d append_char_to_buf(c, p, buf, buf_size) do { 353 if (c == 9) 354 c = 32; 355 if (c == 13 || c == EOF) 356 c = 10; 357 if (c != ' ' || (p > buf && p[-1] != 32)) { 358 check_buf(p - buf + 1, (buf_size)); 359 *p++ = (char)c; 360 } 361} while (0) 362 363@d append_eol(p, buf, buf_size) do { 364 check_buf(p - buf + 2, (buf_size)); 365 if (p - buf > 1 && p[-1] != 10) 366 *p++ = 10; 367 if (p - buf > 2 && p[-2] == 32) { 368 p[-2] = 10; 369 p--; 370 } 371 *p = 0; 372} while (0) 373 374@d remove_eol(p, buf) do { 375 p = strend(buf) - 1; 376 if (*p == 10) 377 *p = 0; 378} while (0) 379 380@d skip(p, c) if (*p == c) p++ 381@d strend(s) strchr(s, 0) 382@d str_prefix(s1, s2) (strncmp((s1), (s2), strlen(s2)) == 0) 383 384 385@ @<Types...@>= 386typedef struct { 387 boolean loaded; /* the encoding has been loaded? */ 388 char *file_name; /* encoding file name */ 389 char *enc_name; /* encoding true name */ 390 integer objnum; /* object number */ 391 char **glyph_names; 392 integer tounicode; /* object number of associated ToUnicode entry */ 393} enc_entry; 394 395 396@ 397 398@d ENC_STANDARD 0 399@d ENC_BUILTIN 1 400 401@<Glob...@>= 402#define ENC_BUF_SIZE 0x1000 403char enc_line[ENC_BUF_SIZE]; 404void * enc_file; 405 406@ 407@d enc_eof() (mp->eof_file)(mp,mp->ps->enc_file) 408@d enc_close() (mp->close_file)(mp,mp->ps->enc_file) 409 410@c 411static int enc_getchar(MP mp) { 412 size_t len = 1; 413 unsigned char abyte=0; 414 void *byte_ptr = &abyte; 415 (mp->read_binary_file)(mp,mp->ps->enc_file,&byte_ptr,&len); 416 return abyte; 417} 418 419@ @c 420static boolean mp_enc_open (MP mp, char *n) { 421 mp->ps->enc_file=(mp->open_file)(mp,n, "r", mp_filetype_encoding); 422 if (mp->ps->enc_file!=NULL) 423 return true; 424 else 425 return false; 426} 427static void mp_enc_getline (MP mp) { 428 char *p; 429 int c; 430RESTART: 431 if (enc_eof ()) { 432 mp_error(mp, "unexpected end of file", NULL, true); 433 } 434 p = mp->ps->enc_line; 435 do { 436 c = enc_getchar (mp); 437 append_char_to_buf (c, p, mp->ps->enc_line, ENC_BUF_SIZE); 438 } while (c && c != 10); 439 append_eol (p, mp->ps->enc_line, ENC_BUF_SIZE); 440 if (p - mp->ps->enc_line < 2 || *mp->ps->enc_line == '%') 441 goto RESTART; 442} 443static void mp_load_enc (MP mp, char *enc_name, 444 char **enc_encname, char **glyph_names){ 445 char buf[ENC_BUF_SIZE], *p, *r; 446 int names_count; 447 char *myname; 448 unsigned save_selector = mp->selector; 449 if (!mp_enc_open (mp,enc_name)) { 450 char err [256]; 451 mp_snprintf(err,255, "cannot open encoding file %s for reading", enc_name); 452 mp_print (mp,err); 453 return; 454 } 455 mp_normalize_selector(mp); 456 mp_print (mp,"{"); 457 mp_print (mp, enc_name); 458 mp_enc_getline (mp); 459 if (*mp->ps->enc_line != '/' || (r = strchr (mp->ps->enc_line, '[')) == NULL) { 460 char msg[256]; 461 remove_eol (r, mp->ps->enc_line); 462 mp_snprintf (msg, 256, "invalid encoding vector (a name or `[' missing): `%s'", mp->ps->enc_line); 463 mp_error(mp, msg, NULL, true); 464 } 465 while (*(r-1)==' ') r--; /* strip trailing spaces from encoding name */ 466 myname = mp_xmalloc(mp,(size_t)(r-mp->ps->enc_line),1); 467 memcpy(myname,(mp->ps->enc_line+1),(size_t)((r-mp->ps->enc_line)-1)); 468 *(myname+(r-mp->ps->enc_line-1))=0; 469 *enc_encname = myname; 470 while (*r!='[') r++; 471 r++; /* skip '[' */ 472 names_count = 0; 473 skip (r, ' '); 474 for (;;) { 475 while (*r == '/') { 476 for (p = buf, r++; 477 *r != ' ' && *r != 10 && *r != ']' && *r != '/'; *p++ = *r++); 478 *p = 0; 479 skip (r, ' '); 480 if (names_count > 256) { 481 mp_error(mp, "encoding vector contains more than 256 names", NULL, true); 482 } 483 if (mp_xstrcmp (buf, notdef) != 0) 484 glyph_names[names_count] = mp_xstrdup (mp,buf); 485 names_count++; 486 } 487 if (*r != 10 && *r != '%') { 488 if (str_prefix (r, "] def")) 489 goto DONE; 490 else { 491 char msg[256]; 492 remove_eol (r, mp->ps->enc_line); 493 mp_snprintf(msg, 256,"invalid encoding vector: a name or `] def' expected: `%s'", mp->ps->enc_line); 494 mp_error(mp, msg, NULL, true); 495 } 496 } 497 mp_enc_getline (mp); 498 r = mp->ps->enc_line; 499 } 500DONE: 501 enc_close (); 502 mp_print (mp,"}"); 503 mp->selector = save_selector; 504} 505static void mp_read_enc (MP mp, enc_entry * e) { 506 if (e->loaded) 507 return; 508 mp_xfree(e->enc_name); 509 e->enc_name = NULL; 510 mp_load_enc (mp,e->file_name, &e->enc_name, e->glyph_names); 511 e->loaded = true; 512} 513 514@ |write_enc| is used to write either external encoding (given in map file) or 515 internal encoding (read from the font file); 516 the 2nd argument is a pointer to the encoding entry; 517 518@c 519static void mp_write_enc (MP mp, enc_entry * e) { 520 int i; 521 size_t s, foffset; 522 char **g; 523 if (e->objnum != 0) /* the encoding has been written already */ 524 return; 525 e->objnum = 1; 526 g = e->glyph_names; 527 528 mp_ps_print(mp,"\n%%%%BeginResource: encoding "); 529 mp_ps_print(mp, e->enc_name); 530 mp_ps_print_nl(mp, "/"); 531 mp_ps_print(mp, e->enc_name); 532 mp_ps_print(mp, " [ "); 533 mp_ps_print_ln (mp); 534 foffset = strlen(e->file_name)+3; 535 for (i = 0; i < 256; i++) { 536 s = strlen(g[i]); 537 if (s+1+foffset>=80) { 538 mp_ps_print_ln (mp); 539 foffset = 0; 540 } 541 foffset += s+2; 542 mp_ps_print_char(mp,'/'); 543 mp_ps_print(mp, g[i]); 544 mp_ps_print_char(mp,' '); 545 } 546 if (foffset>75) 547 mp_ps_print_ln (mp); 548 mp_ps_print_nl (mp,"] def\n"); 549 mp_ps_print(mp,"%%%%EndResource"); 550} 551 552 553@ All encoding entries go into AVL tree for fast search by name. 554 555@<Glob...@>= 556avl_tree enc_tree; 557 558@ 559 560@<Static variables in the outer block@>= 561static char notdef[] = ".notdef"; 562 563@ @<Set initial...@>= 564mp->ps->enc_tree = NULL; 565 566@ @c 567static int comp_enc_entry (void *p, const void *pa, const void *pb) { 568 (void)p; 569 return strcmp (((const enc_entry *) pa)->file_name, 570 ((const enc_entry *) pb)->file_name); 571} 572static void *destroy_enc_entry (void *pa) { 573 enc_entry *p; 574 int i; 575 p = (enc_entry *) pa; 576 mp_xfree (p->file_name); 577 if (p->glyph_names != NULL) 578 for (i = 0; i < 256; i++) 579 if (p->glyph_names[i] != notdef) 580 mp_xfree (p->glyph_names[i]); 581 mp_xfree (p->enc_name); 582 mp_xfree (p->glyph_names); 583 mp_xfree (p); 584 return NULL; 585} 586 587@ Not having an |mp| instance here means that lots of |malloc| and 588|strdup| checks are needed. Spotted by Peter Breitenlohner. 589 590@c 591static void *copy_enc_entry (const void *pa) { 592 const enc_entry *p; 593 enc_entry *q; 594 int i; 595 p = (const enc_entry *) pa; 596 q = malloc (sizeof (enc_entry)); 597 if (q!=NULL) { 598 memset(q,0,sizeof(enc_entry)); 599 if (p->enc_name!=NULL) { 600 q->enc_name = strdup (p->enc_name); 601 if (q->enc_name == NULL) 602 return NULL; 603 } 604 q->loaded = p->loaded; 605 if (p->file_name != NULL) { 606 q->file_name = strdup (p->file_name); 607 if (q->file_name == NULL) 608 return NULL; 609 } 610 q->objnum = p->objnum; 611 q->tounicode = p->tounicode; 612 q->glyph_names = malloc (256 * sizeof (char *)); 613 if (p->glyph_names == NULL) 614 return NULL; 615 for (i = 0; i < 256; i++) { 616 if (p->glyph_names[i] != NULL) { 617 q->glyph_names[i] = strdup(p->glyph_names[i]); 618 if (q->glyph_names[i] == NULL) 619 return NULL; 620 } 621 } 622 } 623 return (void *)q; 624} 625 626static enc_entry * mp_add_enc (MP mp, char *s) { 627 int i; 628 enc_entry tmp, *p; 629 630 if (mp->ps->enc_tree == NULL) { 631 mp->ps->enc_tree = avl_create (comp_enc_entry, 632 copy_enc_entry, 633 destroy_enc_entry, 634 malloc, free, NULL); 635 } 636 tmp.file_name = s; 637 p = (enc_entry *) avl_find (&tmp, mp->ps->enc_tree); 638 if (p != NULL) /* encoding already registered */ 639 return p; 640 p = mp_xmalloc (mp,1,sizeof (enc_entry)); 641 memset(p,0,sizeof(enc_entry)); 642 p->loaded = false; 643 p->file_name = mp_xstrdup (mp,s); 644 p->objnum = 0; 645 p->tounicode = 0; 646 p->glyph_names = mp_xmalloc (mp,256,sizeof (char *)); 647 for (i = 0; i < 256; i++) { 648 p->glyph_names[i] = mp_xstrdup(mp, notdef); 649 } 650 assert (avl_ins (p, mp->ps->enc_tree, avl_false)>0); 651 destroy_enc_entry(p); 652 return avl_find (&tmp, mp->ps->enc_tree); 653} 654 655@ cleaning up... 656 657 658@ @<Declarations@>= 659static void enc_free (MP mp); 660 661@ @c static void enc_free (MP mp) { 662 if (mp->ps->enc_tree != NULL) 663 avl_destroy (mp->ps->enc_tree); 664} 665 666@ @<Declarations@>= 667static void mp_reload_encodings (MP mp) ; 668static void mp_font_encodings (MP mp, font_number lastfnum, boolean encodings_only) ; 669 670@ @c void mp_reload_encodings (MP mp) { 671 font_number f; 672 enc_entry *e; 673 fm_entry *fm_cur; 674 font_number lastfnum = mp->last_fnum; 675 for (f=null_font+1;f<=lastfnum;f++) { 676 if (mp->font_enc_name[f]!=NULL ) { 677 mp_xfree(mp->font_enc_name[f]); 678 mp->font_enc_name[f]=NULL; 679 } 680 if (mp_has_fm_entry (mp,f,&fm_cur)) { 681 if (fm_cur != NULL && fm_cur->ps_name != NULL &&is_reencoded (fm_cur)) { 682 e = fm_cur->encoding; 683 mp_read_enc (mp,e); 684 } 685 } 686 } 687} 688static void mp_font_encodings (MP mp, font_number lastfnum, boolean encodings_only) { 689 font_number f; 690 enc_entry *e; 691 fm_entry *fm; 692 for (f=null_font+1;f<=lastfnum;f++) { 693 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp,f, &fm)) { 694 if (fm != NULL && (fm->ps_name != NULL)) { 695 if (is_reencoded (fm)) { 696 if (encodings_only || (!is_subsetted (fm))) { 697 e = fm->encoding; 698 mp_write_enc (mp, e); 699 /* clear for next run */ 700 e->objnum = 0; 701 } 702 } 703 } 704 } 705 } 706} 707 708@* \[44b] Parsing font map files. 709 710@d FM_BUF_SIZE 1024 711 712@<Glob...@>= 713void * fm_file; 714size_t fm_byte_waiting; 715size_t fm_byte_length; 716unsigned char *fm_bytes; 717 718@ This is comparable to t1 font loading (see below) but because the first 719thing done is not calling |fm_getchar()| but |fm_eof()|, the initial value 720of length has to be one more than waiting. 721 722@<Set initial ...@>= 723mp->ps->fm_byte_waiting=0; 724mp->ps->fm_byte_length=1; 725mp->ps->fm_bytes=NULL; 726 727@ 728@d fm_eof() (mp->ps->fm_byte_waiting>=mp->ps->fm_byte_length) 729@d fm_close() do { 730 (mp->close_file)(mp,mp->ps->fm_file); 731 mp_xfree(mp->ps->fm_bytes); 732 mp->ps->fm_bytes = NULL; 733 mp->ps->fm_byte_waiting=0; 734 mp->ps->fm_byte_length=1; 735} while (0) 736@d valid_code(c) (c >= 0 && c < 256) 737 738@c 739static int fm_getchar (MP mp) { 740 if (mp->ps->fm_bytes == NULL) { 741 void *byte_ptr ; 742 (void)fseek(mp->ps->fm_file,0,SEEK_END); 743 mp->ps->fm_byte_length = (size_t)ftell(mp->ps->fm_file); 744 (void)fseek(mp->ps->fm_file,0,SEEK_SET); 745 if (mp->ps->fm_byte_length==0) 746 return EOF; 747 mp->ps->fm_bytes = mp_xmalloc(mp, mp->ps->fm_byte_length, 1); 748 byte_ptr = (void *)mp->ps->fm_bytes; 749 (mp->read_binary_file)(mp,mp->ps->fm_file,&byte_ptr,&mp->ps->fm_byte_length); 750 } 751 if(mp->ps->fm_byte_waiting >= mp->ps->fm_byte_length) 752 return 10; 753 return *(mp->ps->fm_bytes+mp->ps->fm_byte_waiting++); 754} 755 756@ @<Types...@>= 757enum _mode { FM_DUPIGNORE, FM_REPLACE, FM_DELETE }; 758enum _ltype { MAPFILE, MAPLINE }; 759enum _tfmavail { TFM_UNCHECKED, TFM_FOUND, TFM_NOTFOUND }; 760typedef struct mitem { 761 int mode; /* |FM_DUPIGNORE| or |FM_REPLACE| or |FM_DELETE| */ 762 int type; /* map file or map line */ 763 char *map_line; /* pointer to map file name or map line */ 764 int lineno; /* line number in map file */ 765} mapitem; 766 767@ @<Glob...@>= 768mapitem *mitem; 769fm_entry *fm_cur; 770fm_entry *loaded_tfm_found; 771fm_entry *avail_tfm_found; 772fm_entry *non_tfm_found; 773fm_entry *not_avail_tfm_found; 774 775@ @<Set initial...@>= 776mp->ps->mitem = NULL; 777 778@ @<Declarations@>= 779static const char nontfm[] = "<nontfm>"; 780 781@ 782@d read_field(r, q, buf) do { 783 q = buf; 784 while (*r != ' ' && *r != '\0') 785 *q++ = *r++; 786 *q = '\0'; 787 skip (r, ' '); 788} while (0) 789 790@d set_field(F) do { 791 if (q > buf) 792 fm->F = mp_xstrdup(mp,buf); 793 if (*r == '\0') 794 goto DONE; 795} while (0) 796 797@d cmp_return(a, b) 798 if (a > b) 799 return 1; 800 if (a < b) 801 return -1 802 803@d do_strdup(a) (a==NULL ? NULL : strdup(a)) 804 805@c 806static fm_entry *new_fm_entry (MP mp) { 807 fm_entry *fm; 808 fm = mp_xmalloc (mp,1,sizeof(fm_entry)); 809 fm->tfm_name = NULL; 810 fm->ps_name = NULL; 811 fm->flags = 4; 812 fm->ff_name = NULL; 813 fm->subset_tag = NULL; 814 fm->encoding = NULL; 815 fm->tfm_num = null_font; 816 fm->tfm_avail = TFM_UNCHECKED; 817 fm->type = 0; 818 fm->slant = 0; 819 fm->extend = 0; 820 fm->ff_objnum = 0; 821 fm->fn_objnum = 0; 822 fm->fd_objnum = 0; 823 fm->charset = NULL; 824 fm->all_glyphs = false; 825 fm->links = 0; 826 fm->pid = -1; 827 fm->eid = -1; 828 return fm; 829} 830 831static void *copy_fm_entry (const void *p) { 832 fm_entry *fm; 833 const fm_entry *fp; 834 fp = (const fm_entry *)p; 835 fm = malloc (sizeof(fm_entry)); 836 if (fm==NULL) 837 return NULL; 838 memcpy(fm, fp, sizeof(fm_entry)); 839 fm->tfm_name = do_strdup(fp->tfm_name); 840 fm->ps_name = do_strdup(fp->ps_name); 841 fm->ff_name = do_strdup(fp->ff_name); 842 fm->subset_tag = do_strdup(fp->subset_tag); 843 fm->charset = do_strdup(fp->charset); 844 return (void *)fm; 845} 846 847 848static void * delete_fm_entry (void *p) { 849 fm_entry *fm = (fm_entry *)p; 850 mp_xfree (fm->tfm_name); 851 mp_xfree (fm->ps_name); 852 mp_xfree (fm->ff_name); 853 mp_xfree (fm->subset_tag); 854 mp_xfree (fm->charset); 855 mp_xfree (fm); 856 return NULL; 857} 858 859static ff_entry *new_ff_entry (MP mp) { 860 ff_entry *ff; 861 ff = mp_xmalloc (mp,1,sizeof(ff_entry)); 862 ff->ff_name = NULL; 863 ff->ff_path = NULL; 864 return ff; 865} 866 867static void *copy_ff_entry (const void *p) { 868 ff_entry *ff; 869 const ff_entry *fp; 870 fp = (const ff_entry *)p; 871 ff = (ff_entry *)malloc (sizeof(ff_entry)); 872 if (ff == NULL) 873 return NULL; 874 ff->ff_name = do_strdup(fp->ff_name); 875 ff->ff_path = do_strdup(fp->ff_path); 876 return ff; 877} 878 879static void * delete_ff_entry (void *p) { 880 ff_entry *ff = (ff_entry *)p; 881 mp_xfree (ff->ff_name); 882 mp_xfree (ff->ff_path); 883 mp_xfree (ff); 884 return NULL; 885} 886 887static char *mk_base_tfm (MP mp, char *tfmname, int *i) { 888 static char buf[SMALL_BUF_SIZE]; 889 char *p = tfmname, *r = strend (p) - 1, *q = r; 890 while (q > p && mp_isdigit (*q)) 891 --q; 892 if (!(q > p) || q == r || (*q != '+' && *q != '-')) 893 return NULL; 894 check_buf (q - p + 1, SMALL_BUF_SIZE); 895 strncpy (buf, p, (size_t) (q - p)); 896 buf[q - p] = '\0'; 897 *i = atoi (q); 898 return buf; 899} 900 901@ @<Declarations@>= 902static boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm); 903 904@ @c 905boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm) { 906 fm_entry *res = NULL; 907 res = mp_fm_lookup (mp, f); 908 if (fm != NULL) { 909 *fm =res; 910 } 911 return (res != NULL); 912} 913 914@ @<Glob...@>= 915avl_tree tfm_tree; 916avl_tree ps_tree; 917avl_tree ff_tree; 918 919@ @<Set initial...@>= 920mp->ps->tfm_tree = NULL; 921mp->ps->ps_tree = NULL; 922mp->ps->ff_tree = NULL; 923 924@ AVL sort |fm_entry| into |tfm_tree| by |tfm_name | 925 926@c 927static int comp_fm_entry_tfm (void *p, const void *pa, const void *pb) { 928 (void)p; 929 return strcmp (((const fm_entry *) pa)->tfm_name, 930 ((const fm_entry *) pb)->tfm_name); 931} 932 933@ AVL sort |fm_entry| into |ps_tree| by |ps_name|, |slant|, and |extend| 934 935@c static int comp_fm_entry_ps (void *p, const void *pa, const void *pb) { 936 int i; 937 const fm_entry *p1 = (const fm_entry *) pa; 938 const fm_entry *p2 = (const fm_entry *) pb; 939 (void)p; 940 assert (p1->ps_name != NULL && p2->ps_name != NULL); 941 if ((i = strcmp (p1->ps_name, p2->ps_name))) 942 return i; 943 cmp_return (p1->slant, p2->slant); 944 cmp_return (p1->extend, p2->extend); 945 if (p1->tfm_name != NULL && p2->tfm_name != NULL && 946 (i = strcmp (p1->tfm_name, p2->tfm_name))) 947 return i; 948 return 0; 949} 950 951@ AVL sort |ff_entry| into |ff_tree| by |ff_name| 952 953@c static int comp_ff_entry (void *p, const void *pa, const void *pb) { 954 (void)p; 955 return strcmp (((const ff_entry *) pa)->ff_name, 956 ((const ff_entry *) pb)->ff_name); 957} 958 959@ @c static void create_avl_trees (MP mp) { 960 if (mp->ps->tfm_tree == NULL) { 961 mp->ps->tfm_tree = avl_create (comp_fm_entry_tfm, 962 copy_fm_entry, 963 delete_fm_entry, 964 malloc, free, NULL); 965 assert (mp->ps->tfm_tree != NULL); 966 } 967 if (mp->ps->ps_tree == NULL) { 968 mp->ps->ps_tree = avl_create (comp_fm_entry_ps, 969 copy_fm_entry, 970 delete_fm_entry, 971 malloc, free, NULL); 972 assert (mp->ps->ps_tree != NULL); 973 } 974 if (mp->ps->ff_tree == NULL) { 975 mp->ps->ff_tree = avl_create (comp_ff_entry, 976 copy_ff_entry, 977 delete_ff_entry, 978 malloc, free, NULL); 979 assert (mp->ps->ff_tree != NULL); 980 } 981} 982 983@ The function |avl_do_entry| is not completely symmetrical with regards 984to |tfm_name| and |ps_name handling|, e. g. a duplicate |tfm_name| gives a 985|goto exit|, and no |ps_name| link is tried. This is to keep it compatible 986with the original version. 987 988@d LINK_TFM 0x01 989@d LINK_PS 0x02 990@d set_tfmlink(fm) ((fm)->links |= LINK_TFM) 991@d set_pslink(fm) ((fm)->links |= LINK_PS) 992@d has_tfmlink(fm) ((fm)->links & LINK_TFM) 993@d has_pslink(fm) ((fm)->links & LINK_PS) 994 995@c 996static int avl_do_entry (MP mp, fm_entry * fp, int mode) { 997 fm_entry *p; 998 char s[128]; 999 /* handle |tfm_name| link */ 1000 1001 if (strcmp (fp->tfm_name, nontfm)) { 1002 p = (fm_entry *) avl_find (fp, mp->ps->tfm_tree); 1003 if (p != NULL) { 1004 if (mode == FM_DUPIGNORE) { 1005 mp_snprintf(s,128,"fontmap entry for `%s' already exists, duplicates ignored", 1006 fp->tfm_name); 1007 mp_warn(mp,s); 1008 goto exit; 1009 } else { /* mode == |FM_REPLACE| / |FM_DELETE| */ 1010 if (mp_has_font_size(mp,p->tfm_num)) { 1011 mp_snprintf(s,128, 1012 "fontmap entry for `%s' has been used, replace/delete not allowed", 1013 fp->tfm_name); 1014 mp_warn(mp,s); 1015 goto exit; 1016 } 1017 (void) avl_del (p,mp->ps->tfm_tree,NULL); 1018 p = NULL; 1019 } 1020 } 1021 if (mode != FM_DELETE) { 1022 if (p==NULL) { 1023 assert (avl_ins(fp, mp->ps->tfm_tree, avl_false)>0); 1024 } 1025 set_tfmlink (fp); 1026 } 1027 } 1028 1029 /* handle |ps_name| link */ 1030 1031 if (fp->ps_name != NULL) { 1032 assert (fp->tfm_name != NULL); 1033 p = (fm_entry *) avl_find (fp, mp->ps->ps_tree); 1034 if (p != NULL) { 1035 if (mode == FM_DUPIGNORE) { 1036 mp_snprintf(s,128, 1037 "ps_name entry for `%s' already exists, duplicates ignored", 1038 fp->ps_name); 1039 mp_warn(mp,s); 1040 goto exit; 1041 } else { /* mode == |FM_REPLACE| / |FM_DELETE| */ 1042 if (mp_has_font_size(mp,p->tfm_num)) { 1043 /* REPLACE/DELETE not allowed */ 1044 mp_snprintf(s,128, 1045 "fontmap entry for `%s' has been used, replace/delete not allowed", 1046 p->tfm_name); 1047 mp_warn(mp,s); 1048 goto exit; 1049 } 1050 (void)avl_del (p,mp->ps->ps_tree,NULL); 1051 p= NULL; 1052 } 1053 } 1054 if (mode != FM_DELETE) { 1055 if (p==NULL) { 1056 assert (avl_ins(fp, mp->ps->ps_tree, avl_false)>0); 1057 } 1058 set_pslink (fp); 1059 } 1060 } 1061 exit: 1062 if (!has_tfmlink (fp) && !has_pslink (fp)) /* e. g. after |FM_DELETE| */ 1063 return 1; /* deallocation of |fm_entry| structure required */ 1064 else 1065 return 0; 1066} 1067 1068@ consistency check for map entry, with warn flag 1069 1070@c 1071static int check_fm_entry (MP mp, fm_entry * fm, boolean warn) { 1072 int a = 0; 1073 char s[128]; 1074 assert (fm != NULL); 1075 if (fm->ps_name != NULL) { 1076 if (is_basefont (fm)) { 1077 if (is_fontfile (fm) && !is_included (fm)) { 1078 if (warn) { 1079 mp_snprintf(s,128, "invalid entry for `%s': " 1080 "font file must be included or omitted for base fonts", 1081 fm->tfm_name); 1082 mp_warn(mp,s); 1083 } 1084 a += 1; 1085 } 1086 } else { /* not a base font */ 1087 /* if no font file given, drop this entry */ 1088 /* |if (!is_fontfile (fm)) { 1089 if (warn) { 1090 mp_snprintf(s,128, 1091 "invalid entry for `%s': font file missing", 1092 fm->tfm_name); 1093 mp_warn(mp,s); 1094 } 1095 a += 2; 1096 }| 1097 */ 1098 } 1099 } 1100 if (is_truetype (fm) && is_reencoded (fm) && !is_subsetted (fm)) { 1101 if (warn) { 1102 mp_snprintf(s,128, 1103 "invalid entry for `%s': only subsetted TrueType font can be reencoded", 1104 fm->tfm_name); 1105 mp_warn(mp,s); 1106 } 1107 a += 4; 1108 } 1109 if ((fm->slant != 0 || fm->extend != 0) && 1110 (is_truetype (fm))) { 1111 if (warn) { 1112 mp_snprintf(s,128, 1113 "invalid entry for `%s': " 1114 "SlantFont/ExtendFont can be used only with embedded T1 fonts", 1115 fm->tfm_name); 1116 mp_warn(mp,s); 1117 } 1118 a += 8; 1119 } 1120 if (abs (fm->slant) > 1000) { 1121 if (warn) { 1122 mp_snprintf(s,128, 1123 "invalid entry for `%s': too big value of SlantFont (%d/1000.0)", 1124 fm->tfm_name, (int)fm->slant); 1125 mp_warn(mp,s); 1126 } 1127 a += 16; 1128 } 1129 if (abs (fm->extend) > 2000) { 1130 if (warn) { 1131 mp_snprintf(s,128, 1132 "invalid entry for `%s': too big value of ExtendFont (%d/1000.0)", 1133 fm->tfm_name, (int)fm->extend); 1134 mp_warn(mp,s); 1135 } 1136 a += 32; 1137 } 1138 if (fm->pid != -1 && 1139 !(is_truetype (fm) && is_included (fm) && 1140 is_subsetted (fm) && !is_reencoded (fm))) { 1141 if (warn) { 1142 mp_snprintf(s,128, 1143 "invalid entry for `%s': " 1144 "PidEid can be used only with subsetted non-reencoded TrueType fonts", 1145 fm->tfm_name); 1146 mp_warn(mp,s); 1147 } 1148 a += 64; 1149 } 1150 return a; 1151} 1152 1153@ returns true if s is one of the 14 std. font names; speed-trimmed. 1154 1155@c static boolean check_basefont (char *s) { 1156 static const char *basefont_names[] = { 1157 "Courier", /* 0:7 */ 1158 "Courier-Bold", /* 1:12 */ 1159 "Courier-Oblique", /* 2:15 */ 1160 "Courier-BoldOblique", /* 3:19 */ 1161 "Helvetica", /* 4:9 */ 1162 "Helvetica-Bold", /* 5:14 */ 1163 "Helvetica-Oblique", /* 6:17 */ 1164 "Helvetica-BoldOblique", /* 7:21 */ 1165 "Symbol", /* 8:6 */ 1166 "Times-Roman", /* 9:11 */ 1167 "Times-Bold", /* 10:10 */ 1168 "Times-Italic", /* 11:12 */ 1169 "Times-BoldItalic", /* 12:16 */ 1170 "ZapfDingbats" /* 13:12 */ 1171 }; 1172 static const int Index[] = 1173 { -1, -1, -1, -1, -1, -1, 8, 0, -1, 4, 10, 9, -1, -1, 5, 2, 12, 6, 1174 -1, 3, -1, 7 1175 }; 1176 const size_t n = strlen (s); 1177 int k = -1; 1178 if (n > 21) 1179 return false; 1180 if (n == 12) { /* three names have length 12 */ 1181 switch (*s) { 1182 case 'C': 1183 k = 1; /* Courier-Bold */ 1184 break; 1185 case 'T': 1186 k = 11; /* Times-Italic */ 1187 break; 1188 case 'Z': 1189 k = 13; /* ZapfDingbats */ 1190 break; 1191 default: 1192 return false; 1193 } 1194 } else 1195 k = Index[n]; 1196 if (k > -1 && !strcmp (basefont_names[k], s)) 1197 return true; 1198 return false; 1199} 1200 1201@ 1202@d is_cfg_comment(c) (c == 10 || c == '*' || c == '#' || c == ';' || c == '%') 1203 1204@c static void fm_scan_line (MP mp) { 1205 int a, b, c, j, u = 0, v = 0; 1206 float d; 1207 fm_entry *fm; 1208 char fm_line[FM_BUF_SIZE], buf[FM_BUF_SIZE]; 1209 char *p, *q, *s; 1210 char warn_s[128]; 1211 char *r = NULL; 1212 switch (mp->ps->mitem->type) { 1213 case MAPFILE: 1214 p = fm_line; 1215 do { 1216 c = fm_getchar (mp); 1217 append_char_to_buf (c, p, fm_line, FM_BUF_SIZE); 1218 } while (c != 10); 1219 *(--p) = '\0'; 1220 r = fm_line; 1221 break; 1222 case MAPLINE: 1223 r = mp->ps->mitem->map_line; 1224 break; 1225 default: 1226 assert (0); 1227 } 1228 if (*r == '\0' || is_cfg_comment (*r)) 1229 return; 1230 fm = new_fm_entry (mp); 1231 read_field (r, q, buf); 1232 set_field (tfm_name); 1233 p = r; 1234 read_field (r, q, buf); 1235 if (*buf != '<' && *buf != '"') 1236 set_field (ps_name); 1237 else 1238 r = p; /* unget the field */ 1239 if (mp_isdigit (*r)) { /* font flags given */ 1240 fm->flags = atoi (r); 1241 while (mp_isdigit (*r)) 1242 r++; 1243 } 1244 if(fm->ps_name == NULL) 1245 fm->ps_name = xstrdup(fm->tfm_name); 1246 while (1) { /* loop through "specials", encoding, font file */ 1247 skip (r, ' '); 1248 switch (*r) { 1249 case '\0': 1250 goto DONE; 1251 case '"': /* opening quote */ 1252 r++; 1253 u = v = 0; 1254 do { 1255 skip (r, ' '); 1256 if (sscanf (r, "%f %n", &d, &j) > 0) { 1257 s = r + j; /* jump behind number, eat also blanks, if any */ 1258 if (*(s - 1) == 'E' || *(s - 1) == 'e') 1259 s--; /* e. g. 0.5ExtendFont: \%f = 0.5E */ 1260 if (str_prefix (s, "SlantFont")) { 1261 d *= (float)1000.0; /* correct rounding also for neg. numbers */ 1262 fm->slant = (short int) (d > 0 ? d + 0.5 : d - 0.5); 1263 r = s + strlen ("SlantFont"); 1264 } else if (str_prefix (s, "ExtendFont")) { 1265 d *= (float)1000.0; 1266 fm->extend = (short int) (d > 0 ? d + 0.5 : d - 0.5); 1267 if (fm->extend == 1000) 1268 fm->extend = 0; 1269 r = s + strlen ("ExtendFont"); 1270 } else { /* unknown name */ 1271 for (r = s; 1272 *r != ' ' && *r != '"' && *r != '\0'; 1273 r++); /* jump over name */ 1274 c = *r; /* remember char for temporary end of string */ 1275 *r = '\0'; 1276 mp_snprintf(warn_s,128, 1277 "invalid entry for `%s': unknown name `%s' ignored", 1278 fm->tfm_name, s); 1279 mp_warn(mp,warn_s); 1280 *r = (char)c; 1281 } 1282 } else 1283 for (; *r != ' ' && *r != '"' && *r != '\0'; r++); 1284 } 1285 while (*r == ' '); 1286 if (*r == '"') /* closing quote */ 1287 r++; 1288 else { 1289 mp_snprintf(warn_s,128, 1290 "invalid entry for `%s': closing quote missing", 1291 fm->tfm_name); 1292 mp_warn(mp,warn_s); 1293 goto bad_line; 1294 } 1295 break; 1296 case 'P': /* handle cases for subfonts like 'PidEid=3,1' */ 1297 if (sscanf (r, "PidEid=%i, %i %n", &a, &b, &c) >= 2) { 1298 fm->pid = (short int)a; 1299 fm->eid = (short int)b; 1300 r += c; 1301 break; 1302 } /* fallthrough */ 1303 default: /* encoding or font file specification */ 1304 a = b = 0; 1305 if (*r == '<') { 1306 a = *r++; 1307 if (*r == '<' || *r == '[') 1308 b = *r++; 1309 } 1310 read_field (r, q, buf); 1311 /* encoding, formats: '8r.enc' or '<8r.enc' or '<[8r.enc' */ 1312 if (strlen (buf) > 4 && mp_strcasecmp (strend (buf) - 4, ".enc") == 0) { 1313 fm->encoding = mp_add_enc (mp, buf); 1314 u = v = 0; /* u, v used if intervening blank: "<< foo" */ 1315 } else if (strlen (buf) > 0) { /* file name given */ 1316 /* font file, formats: 1317 * subsetting: '<cmr10.pfa' 1318 * no subsetting: '<<cmr10.pfa' 1319 * no embedding: 'cmr10.pfa' 1320 */ 1321 if (a == '<' || u == '<') { 1322 set_included (fm); 1323 if ((a == '<' && b == 0) || (a == 0 && v == 0)) 1324 set_subsetted (fm); 1325 /* otherwise b == '<' (or '[') => no subsetting */ 1326 } 1327 set_field (ff_name); 1328 u = v = 0; 1329 } else { 1330 u = a; 1331 v = b; 1332 } 1333 } 1334 } 1335 DONE: 1336 if (fm->ps_name != NULL && check_basefont (fm->ps_name)) 1337 set_basefont (fm); 1338 if (is_fontfile (fm) 1339 && mp_strcasecmp (strend (fm_fontfile (fm)) - 4, ".ttf") == 0) 1340 set_truetype (fm); 1341 if (check_fm_entry (mp,fm, true) != 0) 1342 goto bad_line; 1343 /* 1344 Until here the map line has been completely scanned without errors; 1345 fm points to a valid, freshly filled-out |fm_entry| structure. 1346 Now follows the actual work of registering/deleting. 1347 */ 1348 if (avl_do_entry (mp, fm, mp->ps->mitem->mode) == 0) { /* if success */ 1349 delete_fm_entry (fm); 1350 return; 1351 } 1352 bad_line: 1353 delete_fm_entry (fm); 1354} 1355 1356@ 1357@c static void fm_read_info (MP mp) { 1358 char *n; 1359 char s[256]; 1360 if (mp->ps->tfm_tree == NULL) 1361 create_avl_trees (mp); 1362 if (mp->ps->mitem->map_line == NULL) /* nothing to do */ 1363 return; 1364 mp->ps->mitem->lineno = 1; 1365 switch (mp->ps->mitem->type) { 1366 case MAPFILE: 1367 n = mp->ps->mitem->map_line; 1368 mp->ps->fm_file = (mp->open_file)(mp, n, "r", mp_filetype_fontmap); 1369 if (!mp->ps->fm_file) { 1370 mp_snprintf(s,256,"cannot open font map file %s",n); 1371 mp_warn(mp,s); 1372 } else { 1373 unsigned save_selector = mp->selector; 1374 mp_normalize_selector(mp); 1375 mp_print (mp, "{"); 1376 mp_print (mp, n); 1377 while (!fm_eof ()) { 1378 fm_scan_line (mp); 1379 mp->ps->mitem->lineno++; 1380 } 1381 fm_close (); 1382 mp_print (mp,"}"); 1383 mp->selector = save_selector; 1384 mp->ps->fm_file = NULL; 1385 } 1386 /* mp_xfree(n); */ 1387 break; 1388 case MAPLINE: 1389 fm_scan_line (mp); 1390 break; 1391 default: 1392 assert (0); 1393 } 1394 mp->ps->mitem->map_line = NULL; /* done with this line */ 1395 return; 1396} 1397 1398@ @c static void init_fm (fm_entry * fm, font_number f) { 1399 if (fm->tfm_num == null_font ) { 1400 fm->tfm_num = f; 1401 fm->tfm_avail = TFM_FOUND; 1402 } 1403} 1404 1405@ @<Exported function ...@>= 1406fm_entry * mp_fm_lookup (MP mp, font_number f); 1407 1408@ @c 1409fm_entry * mp_fm_lookup (MP mp, font_number f) { 1410 char *tfm; 1411 fm_entry *fm; 1412 fm_entry tmp; 1413 int e; 1414 if (mp->ps->tfm_tree == NULL) 1415 mp_read_psname_table (mp); /* only to read default map file */ 1416 tfm = mp->font_name[f]; 1417 assert (strcmp (tfm, nontfm)); 1418 /* Look up for full <tfmname>[+-]<expand> */ 1419 tmp.tfm_name = tfm; 1420 fm = (fm_entry *) avl_find (&tmp, mp->ps->tfm_tree); 1421 if (fm != NULL) { 1422 init_fm (fm, f); 1423 return (fm_entry *) fm; 1424 } 1425 tfm = mk_base_tfm (mp, mp->font_name[f], &e); 1426 if (tfm == NULL) /* not an expanded font, nothing to do */ 1427 return NULL; 1428 1429 tmp.tfm_name = tfm; 1430 fm = (fm_entry *) avl_find (&tmp, mp->ps->tfm_tree); 1431 if (fm != NULL) { /* found an entry with the base tfm name, e.g. cmr10 */ 1432 return (fm_entry *) fm; /* font expansion uses the base font */ 1433 } 1434 return NULL; 1435} 1436 1437@ Early check whether a font file exists. Used e. g. for replacing fonts 1438 of embedded PDF files: Without font file, the font within the embedded 1439 PDF-file is used. Search tree |ff_tree| is used in 1st instance, as it 1440 may be faster than the |kpse_find_file()|, and |kpse_find_file()| is called 1441 only once per font file name + expansion parameter. This might help 1442 keeping speed, if many PDF pages with same fonts are to be embedded. 1443 1444 The |ff_tree| contains only font files, which are actually needed, 1445 so this tree typically is much smaller than the |tfm_tree| or |ps_tree|. 1446 1447@c 1448static ff_entry *check_ff_exist (MP mp, fm_entry * fm) { 1449 ff_entry *ff; 1450 ff_entry tmp; 1451 1452 assert (fm->ff_name != NULL); 1453 tmp.ff_name = fm->ff_name; 1454 ff = (ff_entry *) avl_find (&tmp, mp->ps->ff_tree); 1455 if (ff == NULL) { /* not yet in database */ 1456 ff = new_ff_entry (mp); 1457 ff->ff_name = mp_xstrdup (mp,fm->ff_name); 1458 ff->ff_path = mp_xstrdup (mp,fm->ff_name); 1459 assert(avl_ins (ff, mp->ps->ff_tree, avl_false)>0); 1460 delete_ff_entry(ff); 1461 ff = (ff_entry *) avl_find (&tmp, mp->ps->ff_tree); 1462 } 1463 return ff; 1464} 1465 1466@ Process map file given by its name or map line contents. Items not 1467beginning with [+-=] flush default map file, if it has not yet been 1468read. Leading blanks and blanks immediately following [+-=] are ignored. 1469 1470 1471@c static void mp_process_map_item (MP mp, char *s, int type) { 1472 char *p; 1473 int mode; 1474 if (*s == ' ') 1475 s++; /* ignore leading blank */ 1476 switch (*s) { 1477 case '+': /* +mapfile.map, +mapline */ 1478 mode = FM_DUPIGNORE; /* insert entry, if it is not duplicate */ 1479 s++; 1480 break; 1481 case '=': /* =mapfile.map, =mapline */ 1482 mode = FM_REPLACE; /* try to replace earlier entry */ 1483 s++; 1484 break; 1485 case '-': /* -mapfile.map, -mapline */ 1486 mode = FM_DELETE; /* try to delete entry */ 1487 s++; 1488 break; 1489 default: 1490 mode = FM_DUPIGNORE; /* like +, but also: */ 1491 mp_xfree(mp->ps->mitem->map_line); 1492 mp->ps->mitem->map_line = NULL; /* flush default map file name */ 1493 } 1494 if (*s == ' ') 1495 s++; /* ignore blank after [+-=] */ 1496 p = s; /* map item starts here */ 1497 switch (type) { 1498 case MAPFILE: /* remove blank at end */ 1499 while (*p != '\0' && *p != ' ') 1500 p++; 1501 *p = '\0'; 1502 break; 1503 case MAPLINE: /* blank at end allowed */ 1504 break; 1505 default: 1506 assert (0); 1507 } 1508 if (mp->ps->mitem->map_line != NULL) /* read default map file first */ 1509 fm_read_info (mp); 1510 if (*s != '\0') { /* only if real item to process */ 1511 mp->ps->mitem->mode = mode; 1512 mp->ps->mitem->type = type; 1513 mp->ps->mitem->map_line = s; 1514 fm_read_info (mp); 1515 } 1516} 1517 1518@ @<Exported function headers@>= 1519void mp_map_file (MP mp, mp_string t); 1520void mp_map_line (MP mp, mp_string t); 1521void mp_init_map_file (MP mp, int is_troff); 1522 1523@ @c 1524void mp_map_file (MP mp, mp_string t) { 1525 char *ss = mp_str (mp,t); 1526 char *s = mp_xstrdup(mp, ss); 1527 mp_process_map_item (mp, s, MAPFILE); 1528} 1529void mp_map_line (MP mp, mp_string t) { 1530 char *ss = mp_str (mp,t); 1531 char *s = mp_xstrdup(mp,ss); 1532 mp_process_map_item (mp, s, MAPLINE); 1533 mp_xfree(s); 1534} 1535 1536@ 1537@c void mp_init_map_file (MP mp, int is_troff) { 1538 char *r; 1539 mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem)); 1540 mp->ps->mitem->mode = FM_DUPIGNORE; 1541 mp->ps->mitem->type = MAPFILE; 1542 mp->ps->mitem->map_line = NULL; 1543 r = (mp->find_file)(mp,"mpost.map", "r", mp_filetype_fontmap); 1544 if (r != NULL) { 1545 mp_xfree(r); 1546 mp->ps->mitem->map_line = mp_xstrdup (mp,"mpost.map"); 1547 } else { 1548 if (is_troff) { 1549 mp->ps->mitem->map_line = mp_xstrdup (mp,"troff.map"); 1550 } else { 1551 mp->ps->mitem->map_line = mp_xstrdup (mp,"pdftex.map"); 1552 } 1553 } 1554} 1555 1556@ @<Dealloc variables@>= 1557if (mp->ps->mitem!=NULL) { 1558 mp_xfree(mp->ps->mitem->map_line); 1559 mp_xfree(mp->ps->mitem); 1560} 1561 1562@ @<Declarations@>= 1563static void fm_free (MP mp); 1564 1565@ @c 1566static void fm_free (MP mp) { 1567 if (mp->ps->tfm_tree != NULL) 1568 avl_destroy (mp->ps->tfm_tree); 1569 if (mp->ps->ps_tree != NULL) 1570 avl_destroy (mp->ps->ps_tree); 1571 if (mp->ps->ff_tree != NULL) 1572 avl_destroy (mp->ps->ff_tree); 1573} 1574 1575@ The file |ps_tab_file| gives a table of \TeX\ font names and corresponding 1576PostScript names for fonts that do not have to be downloaded, i.e., fonts that 1577can be used when |internal[prologues]>0|. Each line consists of a \TeX\ name, 1578one or more spaces, a PostScript name, and possibly a space and some other junk. 1579This routine reads the table, updates |font_ps_name| entries starting after 1580|last_ps_fnum|, and sets |last_ps_fnum:=last_fnum|. 1581 1582@d ps_tab_name "psfonts.map" /* locates font name translation table */ 1583 1584@<Exported function ...@>= 1585void mp_read_psname_table (MP mp) ; 1586 1587@ @c 1588void mp_read_psname_table (MP mp) { 1589 font_number k; 1590 char *s; 1591 static int isread = 0; 1592 if (mp->ps->mitem == NULL) { 1593 mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem)); 1594 mp->ps->mitem->mode = FM_DUPIGNORE; 1595 mp->ps->mitem->type = MAPFILE; 1596 mp->ps->mitem->map_line = NULL; 1597 } 1598 s = mp_xstrdup (mp,ps_tab_name); 1599 mp->ps->mitem->map_line = s; 1600 if (isread == 0) { 1601 isread++; 1602 fm_read_info (mp); 1603 } 1604 for (k=mp->last_ps_fnum+1;k<=mp->last_fnum;k++) { 1605 if (mp_has_fm_entry(mp, k, NULL)) { 1606 mp_xfree(mp->font_ps_name[k]); 1607 mp->font_ps_name[k] = mp_fm_font_name(mp,k); 1608 } 1609 } 1610 mp->last_ps_fnum=mp->last_fnum; 1611} 1612 1613 1614@ The traditional function is a lot shorter now. 1615 1616 1617 1618@* \[44c] Helper functions for Type1 fonts. 1619 1620Avoid to redefine |Byte| and |Bytef| from |<zlib.h>|. 1621 1622@<Types...@>= 1623typedef char char_entry; 1624#ifndef ZCONF_H 1625typedef unsigned char Byte; 1626typedef Byte Bytef; 1627#endif 1628 1629@ @<Glob...@>= 1630char_entry *char_ptr, *char_array; 1631size_t char_limit; 1632char *job_id_string; 1633 1634@ @<Set initial...@>= 1635mp->ps->char_array = NULL; 1636mp->ps->job_id_string = NULL; 1637 1638@ 1639@d SMALL_ARRAY_SIZE 256 1640@d Z_NULL 0 1641 1642@c 1643void mp_set_job_id (MP mp) { 1644 char *name_string, *s; 1645 size_t slen; 1646 if (mp->ps->job_id_string != NULL) 1647 return; 1648 if ( mp->job_name==NULL ) 1649 mp->job_name = mp_xstrdup(mp,"mpout"); 1650 name_string = mp_xstrdup (mp,mp->job_name); 1651 slen = SMALL_BUF_SIZE + 1652 strlen (name_string) ; 1653 s = mp_xmalloc (mp,slen, sizeof (char)); 1654 @= /*@@-bufferoverflowhigh@@*/ @> 1655 sprintf (s,"%.4u/%.2u/%.2u %.2u:%.2u %s", 1656 ((unsigned)number_to_scaled (internal_value(mp_year))>>16), 1657 ((unsigned)number_to_scaled (internal_value(mp_month))>>16), 1658 ((unsigned)number_to_scaled (internal_value(mp_day))>>16), 1659 ((unsigned)number_to_scaled (internal_value(mp_time))>>16) / 60, 1660 ((unsigned)number_to_scaled (internal_value(mp_time))>>16) % 60, 1661 name_string); 1662 @= /*@@=bufferoverflowhigh@@*/ @> 1663 mp->ps->job_id_string = mp_xstrdup (mp,s); 1664 mp_xfree (s); 1665 mp_xfree (name_string); 1666} 1667static void fnstr_append (MP mp, const char *ss) { 1668 size_t n = strlen (ss) + 1; 1669 alloc_array (char, n, SMALL_ARRAY_SIZE); 1670 strcat (mp->ps->char_ptr, ss); 1671 mp->ps->char_ptr = strend (mp->ps->char_ptr); 1672} 1673 1674@ @<Exported function headers@>= 1675void mp_set_job_id (MP mp) ; 1676 1677@ @<Dealloc variables@>= 1678mp_xfree(mp->ps->job_id_string); 1679 1680@ this is not really a true crc32, but it should be just enough to keep 1681 subsets prefixes somewhat disjunct 1682 1683@c 1684static unsigned long crc32 (unsigned long oldcrc, const Byte *buf, size_t len) { 1685 unsigned long ret = 0; 1686 size_t i; 1687 if (oldcrc==0) 1688 ret = (unsigned long)((23<<24)+(45<<16)+(67<<8)+89); 1689 else 1690 for (i=0;i<len;i++) 1691 ret = (ret<<2)+buf[i]; 1692 return ret; 1693} 1694static boolean mp_char_marked (MP mp,font_number f, eight_bits c) { 1695 integer b; /* |char_base[f]| */ 1696 b=mp->char_base[f]; 1697 if ( (c>=mp->font_bc[f])&&(c<=mp->font_ec[f])&&(mp->font_info[b+c].qqqq.b3!=0) ) 1698 return true; 1699 else 1700 return false; 1701} 1702 1703static void make_subset_tag (MP mp, fm_entry * fm_cur, char **glyph_names, font_number tex_font) 1704{ 1705 char tag[7]; 1706 unsigned long crc; 1707 int i; 1708 size_t l ; 1709 if (mp->ps->job_id_string ==NULL) 1710 mp_fatal_error(mp, "no job id!"); 1711 l = strlen (mp->ps->job_id_string) + 1; 1712 1713 alloc_array (char, l, SMALL_ARRAY_SIZE); 1714 strcpy (mp->ps->char_array, mp->ps->job_id_string); 1715 mp->ps->char_ptr = strend (mp->ps->char_array); 1716 if (fm_cur->tfm_name != NULL) { 1717 fnstr_append (mp," TFM name: "); 1718 fnstr_append (mp,fm_cur->tfm_name); 1719 } 1720 fnstr_append (mp," PS name: "); 1721 if (fm_cur->ps_name != NULL) 1722 fnstr_append (mp,fm_cur->ps_name); 1723 fnstr_append (mp," Encoding: "); 1724 if (fm_cur->encoding != NULL && (fm_cur->encoding)->file_name != NULL) 1725 fnstr_append (mp,(fm_cur->encoding)->file_name); 1726 else 1727 fnstr_append (mp,"built-in"); 1728 fnstr_append (mp," CharSet: "); 1729 for (i = 0; i < 256; i++) 1730 if (mp_char_marked (mp,tex_font, (eight_bits)i) && 1731 glyph_names[i] != notdef && 1732 strcmp(glyph_names[i],notdef) != 0) { 1733 if (glyph_names[i]!=NULL) { 1734 fnstr_append (mp,"/"); 1735 fnstr_append (mp,glyph_names[i]); 1736 } 1737 } 1738 if (fm_cur->charset != NULL) { 1739 fnstr_append (mp," Extra CharSet: "); 1740 fnstr_append (mp, fm_cur->charset); 1741 } 1742 crc = crc32 (0L, Z_NULL, 0); 1743 crc = crc32 (crc, (Bytef *) mp->ps->char_array, strlen (mp->ps->char_array)); 1744 /* we need to fit a 32-bit number into a string of 6 uppercase chars long; 1745 * there are 26 uppercase chars ==> each char represents a number in range 1746 * |0..25|. The maximal number that can be represented by the tag is 1747 * $26^6 - 1$, which is a number between $2^28$ and $2^29$. Thus the bits |29..31| 1748 * of the CRC must be dropped out. 1749 */ 1750 for (i = 0; i < 6; i++) { 1751 tag[i] = (char)('A' + crc % 26); 1752 crc /= 26; 1753 } 1754 tag[6] = 0; 1755 mp_xfree(fm_cur->subset_tag); 1756 fm_cur->subset_tag = mp_xstrdup (mp,tag); 1757} 1758 1759 1760 1761@ 1762@d external_enc() (fm_cur->encoding)->glyph_names 1763@d is_used_char(c) mp_char_marked (mp, tex_font, (eight_bits)c) 1764@d end_last_eexec_line() 1765 mp->ps->hexline_length = HEXLINE_WIDTH; 1766 end_hexline(mp); 1767 mp->ps->t1_eexec_encrypt = false 1768@d t1_log(s) mp_print(mp,s) 1769@d t1_putchar(c) wps_chr(c) 1770@d embed_all_glyphs(tex_font) false 1771@d t1_char(c) c 1772@d extra_charset() mp->ps->dvips_extra_charset 1773@d update_subset_tag() 1774@d fixedcontent true 1775 1776@<Glob...@>= 1777#define PRINTF_BUF_SIZE 1024 1778char *dvips_extra_charset; 1779char *cur_enc_name; 1780unsigned char *grid; 1781char *ext_glyph_names[256]; 1782char print_buf[PRINTF_BUF_SIZE]; 1783size_t t1_byte_waiting; 1784size_t t1_byte_length; 1785unsigned char *t1_bytes; 1786 1787@ @<Set initial ...@>= 1788mp->ps->dvips_extra_charset=NULL; 1789mp->ps->t1_byte_waiting=0; 1790mp->ps->t1_byte_length=0; 1791mp->ps->t1_bytes=NULL; 1792 1793@ 1794@d t1_ungetchar() mp->ps->t1_byte_waiting-- 1795@d t1_eof() (mp->ps->t1_byte_waiting>=mp->ps->t1_byte_length) 1796@d t1_close() do { 1797 (mp->close_file)(mp,mp->ps->t1_file); 1798 mp_xfree(mp->ps->t1_bytes); 1799 mp->ps->t1_bytes = NULL; 1800 mp->ps->t1_byte_waiting=0; 1801 mp->ps->t1_byte_length=0; 1802} while (0) 1803@d valid_code(c) (c >= 0 && c < 256) 1804 1805@c 1806static int t1_getchar (MP mp) { 1807 if (mp->ps->t1_bytes == NULL) { 1808 void *byte_ptr ; 1809 (void)fseek(mp->ps->t1_file,0,SEEK_END); 1810 mp->ps->t1_byte_length = (size_t)ftell(mp->ps->t1_file); 1811 (void)fseek(mp->ps->t1_file,0,SEEK_SET); 1812 mp->ps->t1_bytes = mp_xmalloc(mp, mp->ps->t1_byte_length, 1); 1813 byte_ptr = (void *)mp->ps->t1_bytes; 1814 (mp->read_binary_file)(mp,mp->ps->t1_file,&byte_ptr,&mp->ps->t1_byte_length); 1815 } 1816 return *(mp->ps->t1_bytes+mp->ps->t1_byte_waiting++); 1817} 1818 1819@ @<Static variables in the outer block@>= 1820static const char *standard_glyph_names[256] = 1821 { notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, 1822 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, 1823 notdef, notdef, notdef, notdef, notdef, notdef, 1824 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, 1825 "space", "exclam", "quotedbl", "numbersign", 1826 "dollar", "percent", "ampersand", "quoteright", "parenleft", 1827 "parenright", "asterisk", "plus", "comma", "hyphen", "period", 1828 "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", 1829 "eight", "nine", "colon", "semicolon", "less", 1830 "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", 1831 "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", 1832 "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", 1833 "backslash", "bracketright", "asciicircum", "underscore", 1834 "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", 1835 "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", 1836 "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", 1837 notdef, notdef, notdef, notdef, notdef, notdef, notdef, 1838 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, 1839 notdef, notdef, notdef, notdef, notdef, notdef, 1840 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, 1841 notdef, notdef, notdef, "exclamdown", "cent", 1842 "sterling", "fraction", "yen", "florin", "section", "currency", 1843 "quotesingle", "quotedblleft", "guillemotleft", 1844 "guilsinglleft", "guilsinglright", "fi", "fl", notdef, "endash", 1845 "dagger", "daggerdbl", "periodcentered", notdef, 1846 "paragraph", "bullet", "quotesinglbase", "quotedblbase", 1847 "quotedblright", "guillemotright", "ellipsis", "perthousand", 1848 notdef, "questiondown", notdef, "grave", "acute", "circumflex", 1849 "tilde", "macron", "breve", "dotaccent", "dieresis", notdef, 1850 "ring", "cedilla", notdef, "hungarumlaut", "ogonek", "caron", "emdash", 1851 notdef, notdef, notdef, notdef, notdef, notdef, 1852 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, 1853 notdef, "AE", notdef, "ordfeminine", notdef, notdef, 1854 notdef, notdef, "Lslash", "Oslash", "OE", "ordmasculine", notdef, 1855 notdef, notdef, notdef, notdef, "ae", notdef, notdef, 1856 notdef, "dotlessi", notdef, notdef, "lslash", "oslash", "oe", 1857 "germandbls", notdef, notdef, notdef, notdef }; 1858static const char charstringname[] = "/CharStrings"; 1859 1860@ @<Glob...@>= 1861char **t1_glyph_names; 1862char *t1_builtin_glyph_names[256]; 1863char charsetstr[0x4000]; 1864boolean read_encoding_only; 1865int t1_encoding; 1866 1867@ @c 1868#define T1_BUF_SIZE 0x100 1869 1870#define CS_HSTEM 1 1871#define CS_VSTEM 3 1872#define CS_VMOVETO 4 1873#define CS_RLINETO 5 1874#define CS_HLINETO 6 1875#define CS_VLINETO 7 1876#define CS_RRCURVETO 8 1877#define CS_CLOSEPATH 9 1878#define CS_CALLSUBR 10 1879#define CS_RETURN 11 1880#define CS_ESCAPE 12 1881#define CS_HSBW 13 1882#define CS_ENDCHAR 14 1883#define CS_RMOVETO 21 1884#define CS_HMOVETO 22 1885#define CS_VHCURVETO 30 1886#define CS_HVCURVETO 31 1887#define CS_1BYTE_MAX (CS_HVCURVETO + 1) 1888 1889#define CS_DOTSECTION CS_1BYTE_MAX + 0 1890#define CS_VSTEM3 CS_1BYTE_MAX + 1 1891#define CS_HSTEM3 CS_1BYTE_MAX + 2 1892#define CS_SEAC CS_1BYTE_MAX + 6 1893#define CS_SBW CS_1BYTE_MAX + 7 1894#define CS_DIV CS_1BYTE_MAX + 12 1895#define CS_CALLOTHERSUBR CS_1BYTE_MAX + 16 1896#define CS_POP CS_1BYTE_MAX + 17 1897#define CS_SETCURRENTPOINT CS_1BYTE_MAX + 33 1898#define CS_2BYTE_MAX (CS_SETCURRENTPOINT + 1) 1899#define CS_MAX CS_2BYTE_MAX 1900 1901@ @<Types...@>= 1902typedef unsigned char byte; 1903typedef struct { 1904 byte nargs; /* number of arguments */ 1905 boolean bottom; /* take arguments from bottom of stack? */ 1906 boolean clear; /* clear stack? */ 1907 boolean valid; 1908} cc_entry; /* CharString Command */ 1909typedef struct { 1910 char *glyph_name; /* glyph name (or notdef for Subrs entry) */ 1911 byte *data; 1912 unsigned short len; /* length of the whole string */ 1913 unsigned short cslen; /* length of the encoded part of the string */ 1914 boolean is_used; 1915 boolean valid; 1916} cs_entry; 1917 1918@ 1919@d t1_c1 52845 1920@d t1_c2 22719 1921 1922@<Glob...@>= 1923unsigned short t1_dr, t1_er; 1924unsigned short t1_cslen; 1925short t1_lenIV; 1926 1927@ @<Types...@>= 1928typedef char t1_line_entry; 1929typedef char t1_buf_entry; 1930 1931@ @<Glob...@>= 1932t1_line_entry *t1_line_ptr, *t1_line_array; 1933size_t t1_line_limit; 1934t1_buf_entry *t1_buf_ptr, *t1_buf_array; 1935size_t t1_buf_limit; 1936int cs_start; 1937cs_entry *cs_tab, *cs_ptr, *cs_notdef; 1938char *cs_dict_start, *cs_dict_end; 1939int cs_count, cs_size, cs_size_pos; 1940cs_entry *subr_tab; 1941char *subr_array_start, *subr_array_end; 1942int subr_max, subr_size, subr_size_pos; 1943 1944@ @<Set initial...@>= 1945mp->ps->t1_line_array = NULL; 1946mp->ps->t1_buf_array = NULL; 1947 1948@ 1949 This list contains the begin/end tokens commonly used in the 1950 /Subrs array of a Type 1 font. 1951 1952@<Static variables in the outer block@>= 1953static const char *cs_token_pairs_list[][2] = { 1954 {" RD", "NP"}, 1955 {" -|", "|"}, 1956 {" RD", "noaccess put"}, 1957 {" -|", "noaccess put"}, 1958 {NULL, NULL} 1959}; 1960 1961@ @<Glob...@>= 1962const char **cs_token_pair; 1963boolean t1_pfa, t1_cs, t1_scan, t1_eexec_encrypt, t1_synthetic; 1964int t1_in_eexec; /* 0 before eexec-encrypted, 1 during, 2 after */ 1965int t1_block_length; 1966int last_hexbyte; 1967void *t1_file; 1968int hexline_length; 1969 1970@ 1971@d HEXLINE_WIDTH 64 1972 1973@<Set initial ...@>= 1974mp->ps->hexline_length = 0; 1975 1976@ 1977@d t1_prefix(s) str_prefix(mp->ps->t1_line_array, s) 1978@d t1_buf_prefix(s) str_prefix(mp->ps->t1_buf_array, s) 1979@d t1_suffix(s) str_suffix(mp->ps->t1_line_array, mp->ps->t1_line_ptr, s) 1980@d t1_buf_suffix(s) str_suffix(mp->ps->t1_buf_array, mp->ps->t1_buf_ptr, s) 1981@d t1_charstrings() strstr(mp->ps->t1_line_array, charstringname) 1982@d t1_subrs() t1_prefix("/Subrs") 1983@d t1_end_eexec() t1_suffix("mark currentfile closefile") 1984@d t1_cleartomark() t1_prefix("cleartomark") 1985 1986@c 1987static void end_hexline (MP mp) { 1988 if (mp->ps->hexline_length >= HEXLINE_WIDTH) { 1989 wps_cr; 1990 mp->ps->hexline_length = 0; 1991 } 1992} 1993static void t1_check_pfa (MP mp) { 1994 const int c = t1_getchar (mp); 1995 mp->ps->t1_pfa = (c != 128) ? true : false; 1996 t1_ungetchar (); 1997} 1998static int t1_getbyte (MP mp) 1999{ 2000 int c = t1_getchar (mp); 2001 if (mp->ps->t1_pfa) 2002 return c; 2003 if (mp->ps->t1_block_length == 0) { 2004 if (c != 128) 2005 mp_fatal_error (mp, "invalid marker"); 2006 c = t1_getchar (mp); 2007 if (c == 3) { 2008 while (!t1_eof ()) 2009 (void)t1_getchar (mp); 2010 return EOF; 2011 } 2012 mp->ps->t1_block_length = t1_getchar (mp) & 0xff; 2013 mp->ps->t1_block_length |= (int)(((unsigned)t1_getchar (mp) & 0xff) << 8); 2014 mp->ps->t1_block_length |= (int)(((unsigned)t1_getchar (mp) & 0xff) << 16); 2015 mp->ps->t1_block_length |= (int)(((unsigned)t1_getchar (mp) & 0xff) << 24); 2016 c = t1_getchar (mp); 2017 } 2018 mp->ps->t1_block_length--; 2019 return c; 2020} 2021static int hexval (int c) { 2022 if (c >= 'A' && c <= 'F') 2023 return c - 'A' + 10; 2024 else if (c >= 'a' && c <= 'f') 2025 return c - 'a' + 10; 2026 else if (c >= '0' && c <= '9') 2027 return c - '0'; 2028 else 2029 return -1; 2030} 2031static byte edecrypt (MP mp, byte cipher) { 2032 byte plain; 2033 if (mp->ps->t1_pfa) { 2034 while (cipher == 10 || cipher == 13) 2035 cipher = (byte)t1_getbyte (mp); 2036 mp->ps->last_hexbyte = cipher = (byte)(((byte)hexval (cipher) << 4) + 2037 hexval (t1_getbyte (mp))); 2038 } 2039 plain = (byte)(cipher ^ (mp->ps->t1_dr >> 8)); 2040 mp->ps->t1_dr = (unsigned short)((cipher + mp->ps->t1_dr) * t1_c1 + t1_c2); 2041 return plain; 2042} 2043static byte cdecrypt (byte cipher, unsigned short *cr) 2044{ 2045 const byte plain = (byte)(cipher ^ (*cr >> 8)); 2046 *cr = (unsigned short)((cipher + *cr) * t1_c1 + t1_c2); 2047 return plain; 2048} 2049static byte eencrypt (MP mp, byte plain) 2050{ 2051 const byte cipher = (byte)(plain ^ (mp->ps->t1_er >> 8)); 2052 mp->ps->t1_er = (unsigned short)((cipher + mp->ps->t1_er) * t1_c1 + t1_c2); 2053 return cipher; 2054} 2055 2056static byte cencrypt (byte plain, unsigned short *cr) 2057{ 2058 const byte cipher = (byte)(plain ^ (*cr >> 8)); 2059 *cr = (unsigned short)((cipher + *cr) * t1_c1 + t1_c2); 2060 return cipher; 2061} 2062 2063static char *eol (char *s) { 2064 char *p = strend (s); 2065 if (p!=NULL && p - s > 1 && p[-1] != 10) { 2066 *p++ = 10; 2067 *p = 0; 2068 } 2069 return p; 2070} 2071static float t1_scan_num (MP mp, char *p, char **r) 2072{ 2073 float f; 2074 char s[128]; 2075 skip (p, ' '); 2076 if (sscanf (p, "%g", &f) != 1) { 2077 remove_eol (p, mp->ps->t1_line_array); 2078 mp_snprintf(s,128, "a number expected: `%s'", mp->ps->t1_line_array); 2079 mp_fatal_error(mp,s); 2080 } 2081 if (r != NULL) { 2082 for (; mp_isdigit (*p) || *p == '.' || 2083 *p == 'e' || *p == 'E' || *p == '+' || *p == '-'; p++); 2084 *r = p; 2085 } 2086 return f; 2087} 2088 2089static boolean str_suffix (const char *begin_buf, const char *end_buf, 2090 const char *s) 2091{ 2092 const char *s1 = end_buf - 1, *s2 = strend (s) - 1; 2093 if (*s1 == 10) 2094 s1--; 2095 while (s1 >= begin_buf && s2 >= s) { 2096 if (*s1-- != *s2--) 2097 return false; 2098 } 2099 return s2 < s; 2100} 2101 2102@ 2103 2104@d alloc_array(T, n, s) do { 2105 size_t nn = (size_t)n; 2106 if (mp->ps->T##_array == NULL) { 2107 mp->ps->T##_limit = s; 2108 if (nn > mp->ps->T##_limit) 2109 mp->ps->T##_limit = nn; 2110 mp->ps->T##_array = mp_xmalloc (mp,mp->ps->T##_limit,sizeof(T##_entry)); 2111 mp->ps->T##_ptr = mp->ps->T##_array; 2112 } 2113 else if ((size_t)(mp->ps->T##_ptr - mp->ps->T##_array) + nn > mp->ps->T##_limit) { 2114 size_t last_ptr_index; 2115 last_ptr_index = (size_t)(mp->ps->T##_ptr - mp->ps->T##_array); 2116 mp->ps->T##_limit *= 2; 2117 mp->ps->T##_limit += s; 2118 if ((size_t)(mp->ps->T##_ptr - mp->ps->T##_array) + nn > mp->ps->T##_limit) 2119 mp->ps->T##_limit = (size_t)(mp->ps->T##_ptr - mp->ps->T##_array) + nn; 2120 mp->ps->T##_array = mp_xrealloc(mp,mp->ps->T##_array,mp->ps->T##_limit, sizeof(T##_entry)); 2121 mp->ps->T##_ptr = mp->ps->T##_array + last_ptr_index; 2122 } 2123} while (0) 2124 2125@c 2126static void t1_getline (MP mp) { 2127 int c, l, eexec_scan; 2128 char *p; 2129 static const char eexec_str[] = "currentfile eexec"; 2130 static int eexec_len = 17; /* |strlen(eexec_str)| */ 2131 RESTART: 2132 if (t1_eof ()) 2133 mp_fatal_error (mp,"unexpected end of file"); 2134 mp->ps->t1_line_ptr = mp->ps->t1_line_array; 2135 alloc_array (t1_line, 1, T1_BUF_SIZE); 2136 mp->ps->t1_cslen = 0; 2137 eexec_scan = 0; 2138 c = t1_getbyte (mp); 2139 if (c == EOF) 2140 goto EXIT; 2141 while (!t1_eof ()) { 2142 if (mp->ps->t1_in_eexec == 1) 2143 c = edecrypt (mp,(byte)c); 2144 alloc_array (t1_line, 1, T1_BUF_SIZE); 2145 append_char_to_buf (c, mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit); 2146 if (mp->ps->t1_in_eexec == 0 && eexec_scan >= 0 && eexec_scan < eexec_len) { 2147 if (mp->ps->t1_line_array[eexec_scan] == eexec_str[eexec_scan]) 2148 eexec_scan++; 2149 else 2150 eexec_scan = -1; 2151 } 2152 if (c == 10 || (mp->ps->t1_pfa && eexec_scan == eexec_len && c == 32)) 2153 break; 2154 if (mp->ps->t1_cs && mp->ps->t1_cslen == 0 && 2155 (mp->ps->t1_line_ptr - mp->ps->t1_line_array > 4) && 2156 (t1_suffix (" RD ") || t1_suffix (" -| "))) { 2157 p = mp->ps->t1_line_ptr - 5; 2158 while (*p != ' ') 2159 p--; 2160 l = (int)t1_scan_num (mp, p + 1, 0); 2161 mp->ps->t1_cslen = (unsigned short)l; 2162 mp->ps->cs_start = (int)(mp->ps->t1_line_ptr - mp->ps->t1_line_array); 2163 /* |mp->ps->cs_start| is an index now */ 2164 alloc_array (t1_line, l, T1_BUF_SIZE); 2165 while (l-- > 0) { 2166 *mp->ps->t1_line_ptr = (t1_line_entry)edecrypt (mp,(byte)t1_getbyte (mp)); 2167 mp->ps->t1_line_ptr++; 2168 } 2169 } 2170 c = t1_getbyte (mp); 2171 } 2172 alloc_array (t1_line, 2, T1_BUF_SIZE); /* |append_eol| can append 2 chars */ 2173 append_eol (mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit); 2174 if (mp->ps->t1_line_ptr - mp->ps->t1_line_array < 2) 2175 goto RESTART; 2176 if (eexec_scan == eexec_len) 2177 mp->ps->t1_in_eexec = 1; 2178 EXIT: 2179 /* ensure that |mp->ps->t1_buf_array| has as much room as |t1_line_array| */ 2180 mp->ps->t1_buf_ptr = mp->ps->t1_buf_array; 2181 alloc_array (t1_buf, mp->ps->t1_line_limit, mp->ps->t1_line_limit); 2182} 2183 2184static void t1_putline (MP mp) 2185{ 2186 char ss[256]; 2187 int ss_cur = 0; 2188 static const char *hexdigits = "0123456789ABCDEF"; 2189 char *p = mp->ps->t1_line_array; 2190 if (mp->ps->t1_line_ptr - mp->ps->t1_line_array <= 1) 2191 return; 2192 if (mp->ps->t1_eexec_encrypt) { 2193 while (p < mp->ps->t1_line_ptr) { 2194 byte b = eencrypt (mp,(byte)*p++); 2195 if (ss_cur>=253) { 2196 ss[ss_cur] = '\0'; 2197 (mp->write_ascii_file)(mp,mp->output_file,(char *)ss); 2198 ss_cur = 0; 2199 } 2200 ss[ss_cur++] = hexdigits[b / 16]; 2201 ss[ss_cur++] = hexdigits[b % 16]; 2202 mp->ps->hexline_length += 2; 2203 if (mp->ps->hexline_length >= HEXLINE_WIDTH) { 2204 ss[ss_cur++] = '\n'; 2205 mp->ps->hexline_length = 0; 2206 } 2207 } 2208 } else { 2209 while (p < mp->ps->t1_line_ptr) { 2210 if (ss_cur>=255) { 2211 ss[ss_cur] = '\0'; 2212 (mp->write_ascii_file)(mp,mp->output_file,(char *)ss); 2213 ss_cur = 0; 2214 } 2215 ss[ss_cur++] = (char)(*p++); 2216 } 2217 } 2218 ss[ss_cur] = '\0'; 2219 (mp->write_ascii_file)(mp,mp->output_file,(char *)ss); 2220} 2221 2222static void t1_puts (MP mp, const char *s) 2223{ 2224 if (s != mp->ps->t1_line_array) 2225 strcpy (mp->ps->t1_line_array, s); 2226 mp->ps->t1_line_ptr = strend (mp->ps->t1_line_array); 2227 t1_putline (mp); 2228} 2229 2230static void t1_init_params (MP mp, const char *open_name_prefix, 2231 char *cur_file_name) { 2232 if ((open_name_prefix != NULL) && strlen(open_name_prefix)) { 2233 t1_log (open_name_prefix); 2234 t1_log (cur_file_name); 2235 } 2236 mp->ps->t1_lenIV = 4; 2237 mp->ps->t1_dr = 55665; 2238 mp->ps->t1_er = 55665; 2239 mp->ps->t1_in_eexec = 0; 2240 mp->ps->t1_cs = false; 2241 mp->ps->t1_scan = true; 2242 mp->ps->t1_synthetic = false; 2243 mp->ps->t1_eexec_encrypt = false; 2244 mp->ps->t1_block_length = 0; 2245 t1_check_pfa (mp); 2246} 2247static void t1_close_font_file (MP mp, const char *close_name_suffix) { 2248 if ((close_name_suffix != NULL) && strlen(close_name_suffix)) { 2249 t1_log (close_name_suffix); 2250 } 2251 t1_close (); 2252} 2253 2254static void t1_check_block_len (MP mp, boolean decrypt) { 2255 int l, c; 2256 char s[128]; 2257 if (mp->ps->t1_block_length == 0) 2258 return; 2259 c = t1_getbyte (mp); 2260 if (decrypt) 2261 c = edecrypt (mp,(byte)c); 2262 l = mp->ps->t1_block_length; 2263 if (!(l == 0 && (c == 10 || c == 13))) { 2264 mp_snprintf(s,128,"%i bytes more than expected were ignored", l+ 1); 2265 mp_warn(mp,s); 2266 while (l-- > 0) 2267 (void)t1_getbyte (mp); 2268 } 2269} 2270static void t1_start_eexec (MP mp, fm_entry *fm_cur) { 2271 int i; 2272 if (!mp->ps->t1_pfa) 2273 t1_check_block_len (mp, false); 2274 for (mp->ps->t1_line_ptr = mp->ps->t1_line_array, i = 0; i < 4; i++) { 2275 (void)edecrypt (mp, (byte)t1_getbyte (mp)); 2276 *mp->ps->t1_line_ptr++ = 0; 2277 } 2278 mp->ps->t1_eexec_encrypt = true; 2279 if (!mp->ps->read_encoding_only) 2280 if (is_included (fm_cur)) 2281 t1_putline (mp); /* to put the first four bytes */ 2282} 2283static void t1_stop_eexec (MP mp) { 2284 int c; 2285 end_last_eexec_line (); 2286 if (!mp->ps->t1_pfa) 2287 t1_check_block_len (mp,true); 2288 else { 2289 c = edecrypt (mp, (byte)t1_getbyte (mp)); 2290 if (!(c == 10 || c == 13)) { 2291 if (mp->ps->last_hexbyte == 0) 2292 t1_puts (mp,"00"); 2293 else 2294 mp_warn (mp,"unexpected data after eexec"); 2295 } 2296 } 2297 mp->ps->t1_cs = false; 2298 mp->ps->t1_in_eexec = 2; 2299} 2300static void t1_modify_fm (MP mp) { 2301 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array); 2302} 2303 2304static void t1_modify_italic (MP mp) { 2305 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array); 2306} 2307 2308@ @<Types...@>= 2309typedef struct { 2310 const char *pdfname; 2311 const char *t1name; 2312 float value; 2313 boolean valid; 2314} key_entry; 2315 2316@ 2317@d FONT_KEYS_NUM 11 2318 2319@<Declarations@>= 2320static key_entry font_keys[FONT_KEYS_NUM] = { 2321 {"Ascent", "Ascender", 0, false}, 2322 {"CapHeight", "CapHeight", 0, false}, 2323 {"Descent", "Descender", 0, false}, 2324 {"FontName", "FontName", 0, false}, 2325 {"ItalicAngle", "ItalicAngle", 0, false}, 2326 {"StemV", "StdVW", 0, false}, 2327 {"XHeight", "XHeight", 0, false}, 2328 {"FontBBox", "FontBBox", 0, false}, 2329 {"", "", 0, false}, 2330 {"", "", 0, false}, 2331 {"", "", 0, false} 2332}; 2333 2334 2335@ 2336@d ASCENT_CODE 0 2337@d CAPHEIGHT_CODE 1 2338@d DESCENT_CODE 2 2339@d FONTNAME_CODE 3 2340@d ITALIC_ANGLE_CODE 4 2341@d STEMV_CODE 5 2342@d XHEIGHT_CODE 6 2343@d FONTBBOX1_CODE 7 2344@d FONTBBOX2_CODE 8 2345@d FONTBBOX3_CODE 9 2346@d FONTBBOX4_CODE 10 2347@d MAX_KEY_CODE (FONTBBOX1_CODE + 1) 2348 2349@c 2350static void t1_scan_keys (MP mp, font_number tex_font,fm_entry *fm_cur) { 2351 int i, k; 2352 char *p, *r; 2353 key_entry *key; 2354 if (fm_extend (fm_cur) != 0 || fm_slant (fm_cur) != 0) { 2355 if (t1_prefix ("/FontMatrix")) { 2356 t1_modify_fm (mp); 2357 return; 2358 } 2359 if (t1_prefix ("/ItalicAngle")) { 2360 t1_modify_italic (mp); 2361 return; 2362 } 2363 } 2364 if (t1_prefix ("/FontType")) { 2365 p = mp->ps->t1_line_array + strlen ("FontType") + 1; 2366 if ((i = (int)t1_scan_num (mp,p, 0)) != 1) { 2367 char s[128]; 2368 mp_snprintf(s,125,"Type%d fonts unsupported by metapost", i); 2369 mp_fatal_error(mp,s); 2370 } 2371 return; 2372 } 2373 for (key = font_keys; key - font_keys < MAX_KEY_CODE; key++) 2374 if (str_prefix (mp->ps->t1_line_array + 1, key->t1name)) 2375 break; 2376 if (key - font_keys == MAX_KEY_CODE) 2377 return; 2378 key->valid = true; 2379 p = mp->ps->t1_line_array + strlen (key->t1name) + 1; 2380 skip (p, ' '); 2381 if ((k = (int)(key - font_keys)) == FONTNAME_CODE) { 2382 if (*p != '/') { 2383 char s[128]; 2384 remove_eol (p, mp->ps->t1_line_array); 2385 mp_snprintf(s,128,"a name expected: `%s'", mp->ps->t1_line_array); 2386 mp_fatal_error(mp,s); 2387 } 2388 r = ++p; /* skip the slash */ 2389 if (is_included (fm_cur)) { 2390 /* save the fontname */ 2391 strncpy (mp->ps->fontname_buf, p, FONTNAME_BUF_SIZE); 2392 for (i=0; mp->ps->fontname_buf[i] != 10; i++); 2393 mp->ps->fontname_buf[i]=0; 2394 2395 if(is_subsetted (fm_cur)) { 2396 if (fm_cur->encoding!=NULL && fm_cur->encoding->glyph_names!=NULL) 2397 make_subset_tag (mp,fm_cur, fm_cur->encoding->glyph_names, tex_font); 2398 else 2399 make_subset_tag (mp,fm_cur, mp->ps->t1_builtin_glyph_names, tex_font); 2400 2401 alloc_array (t1_line, (size_t)(r-mp->ps->t1_line_array)+6+1+strlen(mp->ps->fontname_buf)+1, 2402 T1_BUF_SIZE); 2403 strncpy (r, fm_cur->subset_tag , 6); 2404 *(r+6) = '-'; 2405 strncpy (r+7, mp->ps->fontname_buf, strlen(mp->ps->fontname_buf)+1); 2406 mp->ps->t1_line_ptr = eol (r); 2407 } else { 2408 /* |for (q = p; *q != ' ' && *q != 10; *q++);|*/ 2409 /*|*q = 0;|*/ 2410 mp->ps->t1_line_ptr = eol (r); 2411 } 2412 } 2413 return; 2414 } 2415 if ((k == STEMV_CODE || k == FONTBBOX1_CODE) 2416 && (*p == '[' || *p == '{')) 2417 p++; 2418 if (k == FONTBBOX1_CODE) { 2419 for (i = 0; i < 4; i++) { 2420 key[i].value = t1_scan_num (mp, p, &r); 2421 p = r; 2422 } 2423 return; 2424 } 2425 key->value = t1_scan_num (mp, p, 0); 2426} 2427static void t1_scan_param (MP mp, font_number tex_font,fm_entry *fm_cur) 2428{ 2429 static const char *lenIV = "/lenIV"; 2430 if (!mp->ps->t1_scan || *mp->ps->t1_line_array != '/') 2431 return; 2432 if (t1_prefix (lenIV)) { 2433 mp->ps->t1_lenIV = (short int)t1_scan_num (mp,mp->ps->t1_line_array + strlen (lenIV), 0); 2434 return; 2435 } 2436 t1_scan_keys (mp, tex_font,fm_cur); 2437} 2438static void copy_glyph_names (MP mp, char **glyph_names, int a, int b) { 2439 if (glyph_names[b] != notdef) 2440 mp_xfree (glyph_names[b]); 2441 glyph_names[b] = mp_xstrdup (mp,glyph_names[a]); 2442} 2443static void t1_builtin_enc (MP mp) { 2444 int i, a, b, c, counter = 0; 2445 char *r, *p; 2446 /* 2447 * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array| 2448 */ 2449 if (t1_suffix ("def")) { /* predefined encoding */ 2450 (void)sscanf (mp->ps->t1_line_array + strlen ("/Encoding"), "%256s", mp->ps->t1_buf_array); 2451 if (strcmp (mp->ps->t1_buf_array, "StandardEncoding") == 0) { 2452 for (i = 0; i < 256; i++) { 2453 if (mp->ps->t1_builtin_glyph_names[i] != notdef) 2454 mp_xfree(mp->ps->t1_builtin_glyph_names[i]); 2455 mp->ps->t1_builtin_glyph_names[i] = 2456 mp_xstrdup (mp,standard_glyph_names[i]); 2457 } 2458 mp->ps->t1_encoding = ENC_STANDARD; 2459 } else { 2460 char s[128]; 2461 mp_snprintf(s,128, "cannot subset font (unknown predefined encoding `%s')", 2462 mp->ps->t1_buf_array); 2463 mp_fatal_error(mp,s); 2464 } 2465 return; 2466 } else 2467 mp->ps->t1_encoding = ENC_BUILTIN; 2468 /* 2469 * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|, and the encoding is 2470 * not a predefined encoding 2471 * 2472 * We have two possible forms of Encoding vector. The first case is 2473 * 2474 * /Encoding [/a /b /c...] readonly def 2475 * 2476 * and the second case can look like 2477 * 2478 * /Encoding 256 array 0 1 255 {1 index exch /.notdef put} for 2479 * dup 0 /x put 2480 * dup 1 /y put 2481 * ... 2482 * readonly def 2483 */ 2484 for (i = 0; i < 256; i++) { 2485 if (mp->ps->t1_builtin_glyph_names[i] != notdef) { 2486 mp_xfree(mp->ps->t1_builtin_glyph_names[i]); 2487 mp->ps->t1_builtin_glyph_names[i] = mp_xstrdup(mp, notdef); 2488 } 2489 } 2490 if (t1_prefix ("/Encoding [") || t1_prefix ("/Encoding[")) { /* the first case */ 2491 r = strchr (mp->ps->t1_line_array, '[') + 1; 2492 skip (r, ' '); 2493 for (;;) { 2494 while (*r == '/') { 2495 for (p = mp->ps->t1_buf_array, r++; 2496 *r != 32 && *r != 10 && *r != ']' && *r != '/'; 2497 *p++ = *r++); 2498 *p = 0; 2499 skip (r, ' '); 2500 if (counter > 255) { 2501 mp_fatal_error 2502 (mp, "encoding vector contains more than 256 names"); 2503 } 2504 if (strcmp (mp->ps->t1_buf_array, notdef) != 0) { 2505 if (mp->ps->t1_builtin_glyph_names[counter] != notdef) 2506 mp_xfree(mp->ps->t1_builtin_glyph_names[counter]); 2507 mp->ps->t1_builtin_glyph_names[counter] = mp_xstrdup (mp,mp->ps->t1_buf_array); 2508 } 2509 counter++; 2510 } 2511 if (*r != 10 && *r != '%') { 2512 if (str_prefix (r, "] def") 2513 || str_prefix (r, "] readonly def")) 2514 break; 2515 else { 2516 char s[128]; 2517 remove_eol (r, mp->ps->t1_line_array); 2518 mp_snprintf(s,128,"a name or `] def' or `] readonly def' expected: `%s'", 2519 mp->ps->t1_line_array); 2520 mp_fatal_error(mp,s); 2521 } 2522 } 2523 t1_getline (mp); 2524 r = mp->ps->t1_line_array; 2525 } 2526 } else { /* the second case */ 2527 p = strchr (mp->ps->t1_line_array, 10); 2528 for (;p!=NULL;) { 2529 if (*p == 10) { 2530 t1_getline (mp); 2531 p = mp->ps->t1_line_array; 2532 } 2533 /* 2534 check for `dup <index> <glyph> put' 2535 */ 2536 if (sscanf (p, "dup %i%256s put", &i, mp->ps->t1_buf_array) == 2 && 2537 *mp->ps->t1_buf_array == '/' && valid_code (i)) { 2538 if (strcmp (mp->ps->t1_buf_array + 1, notdef) != 0) { 2539 if (mp->ps->t1_builtin_glyph_names[i] != notdef) 2540 mp_xfree(mp->ps->t1_builtin_glyph_names[i]); 2541 mp->ps->t1_builtin_glyph_names[i] = 2542 mp_xstrdup (mp,mp->ps->t1_buf_array + 1); 2543 } 2544 p = strstr (p, " put") + strlen (" put"); 2545 skip (p, ' '); 2546 } 2547 /* 2548 check for `dup dup <to> exch <from> get put' 2549 */ 2550 else if (sscanf (p, "dup dup %i exch %i get put", &b, &a) == 2 2551 && valid_code (a) && valid_code (b)) { 2552 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a, b); 2553 p = strstr (p, " get put") + strlen (" get put"); 2554 skip (p, ' '); 2555 } 2556 /* 2557 check for `dup dup <from> <size> getinterval <to> exch putinterval' 2558 */ 2559 else if (sscanf 2560 (p, "dup dup %i %i getinterval %i exch putinterval", 2561 &a, &c, &b) == 3 && valid_code (a) && valid_code (b) 2562 && valid_code (c)) { 2563 for (i = 0; i < c; i++) 2564 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a + i, b + i); 2565 p = strstr (p, " putinterval") + strlen (" putinterval"); 2566 skip (p, ' '); 2567 } 2568 /* 2569 check for `def' or `readonly def' 2570 */ 2571 else if ((p == mp->ps->t1_line_array || (p > mp->ps->t1_line_array && p[-1] == ' ')) 2572 && strcmp (p, "def\n") == 0) 2573 return; 2574 /* 2575 skip an unrecognizable word 2576 */ 2577 else { 2578 while (*p != ' ' && *p != 10) 2579 p++; 2580 skip (p, ' '); 2581 } 2582 } 2583 } 2584} 2585 2586static void t1_check_end (MP mp) { 2587 if (t1_eof ()) 2588 return; 2589 t1_getline (mp); 2590 if (t1_prefix ("{restore}")) 2591 t1_putline (mp); 2592} 2593 2594@ @<Set initial values...@>= 2595{ 2596 int i; 2597 for (i = 0; i < 256; i++) { 2598 mp->ps->t1_builtin_glyph_names[i] = strdup(notdef); 2599 assert(mp->ps->t1_builtin_glyph_names[i]); 2600 } 2601} 2602 2603@ @<Types...@>= 2604typedef struct { 2605 char *ff_name; /* base name of font file */ 2606 char *ff_path; /* full path to font file */ 2607} ff_entry; 2608 2609@ @c 2610static boolean t1_open_fontfile (MP mp, fm_entry *fm_cur,const char *open_name_prefix) { 2611 ff_entry *ff; 2612 ff = check_ff_exist (mp, fm_cur); 2613 mp->ps->t1_file = NULL; 2614 if (ff->ff_path != NULL) { 2615 mp->ps->t1_file = (mp->open_file)(mp,ff->ff_path, "r", mp_filetype_font); 2616 } 2617 if (mp->ps->t1_file == NULL) { 2618 char err [256]; 2619 mp_snprintf(err, 255, "cannot open Type 1 font file %s for reading", ff->ff_path); 2620 mp_warn (mp,err); 2621 return false; 2622 } 2623 t1_init_params (mp,open_name_prefix,fm_cur->ff_name); 2624 mp->ps->fontfile_found = true; 2625 return true; 2626} 2627 2628static void t1_scan_only (MP mp, font_number tex_font, fm_entry *fm_cur) { 2629 do { 2630 t1_getline (mp); 2631 t1_scan_param (mp,tex_font, fm_cur); 2632 } 2633 while (mp->ps->t1_in_eexec == 0); 2634 t1_start_eexec (mp,fm_cur); 2635 do { 2636 t1_getline (mp); 2637 t1_scan_param (mp,tex_font, fm_cur); 2638 } 2639 while (!(t1_charstrings () || t1_subrs ())); 2640} 2641 2642static void t1_include (MP mp, font_number tex_font, fm_entry *fm_cur) { 2643 do { 2644 t1_getline (mp); 2645 t1_scan_param (mp,tex_font, fm_cur); 2646 t1_putline (mp); 2647 } 2648 while (mp->ps->t1_in_eexec == 0); 2649 t1_start_eexec (mp,fm_cur); 2650 do { 2651 t1_getline (mp); 2652 t1_scan_param (mp,tex_font, fm_cur); 2653 t1_putline (mp); 2654 } 2655 while (!(t1_charstrings () || t1_subrs ())); 2656 mp->ps->t1_cs = true; 2657 do { 2658 t1_getline (mp); 2659 t1_putline (mp); 2660 } 2661 while (!t1_end_eexec ()); 2662 t1_stop_eexec (mp); 2663 if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */ 2664 do { 2665 t1_getline (mp); 2666 t1_putline (mp); 2667 } 2668 while (!t1_cleartomark ()); 2669 t1_check_end (mp); /* write "{restore}if" if found */ 2670 } 2671} 2672 2673@ 2674@d check_subr(SUBR) if (SUBR >= mp->ps->subr_size || SUBR < 0) { 2675 char s[128]; 2676 mp_snprintf(s,128,"Subrs array: entry index out of range (%i)",SUBR); 2677 mp_fatal_error(mp,s); 2678 } 2679 2680@c 2681static const char **check_cs_token_pair (MP mp) { 2682 const char **p = (const char **) cs_token_pairs_list; 2683 for (; p[0] != NULL; ++p) 2684 if (t1_buf_prefix (p[0]) && t1_buf_suffix (p[1])) 2685 return p; 2686 return NULL; 2687} 2688 2689static void cs_store (MP mp, boolean is_subr) { 2690 char *p; 2691 cs_entry *ptr; 2692 int subr; 2693 for (p = mp->ps->t1_line_array, mp->ps->t1_buf_ptr = mp->ps->t1_buf_array; *p != ' '; 2694 *mp->ps->t1_buf_ptr++ = *p++); 2695 *mp->ps->t1_buf_ptr = 0; 2696 if (is_subr) { 2697 subr = (int)t1_scan_num (mp, p + 1, 0); 2698 check_subr (subr); 2699 ptr = mp->ps->subr_tab + subr; 2700 } else { 2701 ptr = mp->ps->cs_ptr++; 2702 if (mp->ps->cs_ptr - mp->ps->cs_tab > mp->ps->cs_size) { 2703 char s[128]; 2704 mp_snprintf(s,128,"CharStrings dict: more entries than dict size (%i)",mp->ps->cs_size); 2705 mp_fatal_error(mp,s); 2706 } 2707 ptr->glyph_name = mp_xstrdup (mp,mp->ps->t1_buf_array + 1); 2708 } 2709 /* copy " RD " + cs data to |mp->ps->t1_buf_array| */ 2710 memcpy (mp->ps->t1_buf_array, mp->ps->t1_line_array + mp->ps->cs_start - 4, 2711 (size_t) (mp->ps->t1_cslen + 4)); 2712 /* copy the end of cs data to |mp->ps->t1_buf_array| */ 2713 for (p = mp->ps->t1_line_array + mp->ps->cs_start + mp->ps->t1_cslen, mp->ps->t1_buf_ptr = 2714 mp->ps->t1_buf_array + mp->ps->t1_cslen + 4; *p != 10; *mp->ps->t1_buf_ptr++ = *p++); 2715 *mp->ps->t1_buf_ptr++ = 10; 2716 if (is_subr && mp->ps->cs_token_pair == NULL) 2717 mp->ps->cs_token_pair = check_cs_token_pair (mp); 2718 ptr->len = (unsigned short)(mp->ps->t1_buf_ptr - mp->ps->t1_buf_array); 2719 ptr->cslen = mp->ps->t1_cslen; 2720 ptr->data = mp_xmalloc (mp, (size_t)ptr->len , sizeof (byte)); 2721 memcpy (ptr->data, mp->ps->t1_buf_array, (size_t)ptr->len); 2722 ptr->valid = true; 2723} 2724 2725#define store_subr(mp) cs_store(mp,true) 2726#define store_cs(mp) cs_store(mp,false) 2727 2728#define CC_STACK_SIZE 24 2729 2730static double cc_stack[CC_STACK_SIZE], *stack_ptr = cc_stack; 2731static cc_entry cc_tab[CS_MAX]; 2732static boolean is_cc_init = false; 2733 2734 2735#define cc_pop(N) \ 2736 if (stack_ptr - cc_stack < (N)) \ 2737 stack_error(N); \ 2738 stack_ptr -= N 2739 2740#define stack_error(N) { \ 2741 char s[256]; \ 2742 mp_snprintf(s,255,"CharString: invalid access (%i) to stack (%i entries)", \ 2743 (int) N, (int)(stack_ptr - cc_stack)); \ 2744 mp_warn(mp,s); \ 2745 goto cs_error; \ 2746} 2747 2748 2749#define cc_get(N) ((N) < 0 ? *(stack_ptr + (N)) : *(cc_stack + (N))) 2750 2751#define cc_push(V) *stack_ptr++ = (double)(V) 2752#define cc_clear() stack_ptr = cc_stack 2753 2754#define set_cc(N, B, A, C) \ 2755 cc_tab[N].nargs = A; \ 2756 cc_tab[N].bottom = B; \ 2757 cc_tab[N].clear = C; \ 2758 cc_tab[N].valid = true 2759 2760static void cc_init (void) { 2761 int i; 2762 if (is_cc_init) 2763 return; 2764 for (i = 0; i < CS_MAX; i++) 2765 cc_tab[i].valid = false; 2766 set_cc (CS_HSTEM, true, 2, true); 2767 set_cc (CS_VSTEM, true, 2, true); 2768 set_cc (CS_VMOVETO, true, 1, true); 2769 set_cc (CS_RLINETO, true, 2, true); 2770 set_cc (CS_HLINETO, true, 1, true); 2771 set_cc (CS_VLINETO, true, 1, true); 2772 set_cc (CS_RRCURVETO, true, 6, true); 2773 set_cc (CS_CLOSEPATH, false, 0, true); 2774 set_cc (CS_CALLSUBR, false, 1, false); 2775 set_cc (CS_RETURN, false, 0, false); 2776 /* 2777 |set_cc(CS_ESCAPE, false, 0, false);| 2778 */ 2779 set_cc (CS_HSBW, true, 2, true); 2780 set_cc (CS_ENDCHAR, false, 0, true); 2781 set_cc (CS_RMOVETO, true, 2, true); 2782 set_cc (CS_HMOVETO, true, 1, true); 2783 set_cc (CS_VHCURVETO, true, 4, true); 2784 set_cc (CS_HVCURVETO, true, 4, true); 2785 set_cc (CS_DOTSECTION, false, 0, true); 2786 set_cc (CS_VSTEM3, true, 6, true); 2787 set_cc (CS_HSTEM3, true, 6, true); 2788 set_cc (CS_SEAC, true, 5, true); 2789 set_cc (CS_SBW, true, 4, true); 2790 set_cc (CS_DIV, false, 2, false); 2791 set_cc (CS_CALLOTHERSUBR, false, 0, false); 2792 set_cc (CS_POP, false, 0, false); 2793 set_cc (CS_SETCURRENTPOINT, true, 2, true); 2794 is_cc_init = true; 2795} 2796 2797@ 2798 2799@d cs_getchar(mp) cdecrypt(*data++, &cr) 2800 2801@d mark_subr(mp,n) cs_mark(mp,0, n) 2802@d mark_cs(mp,s) cs_mark(mp,s, 0) 2803@d SMALL_BUF_SIZE 256 2804 2805@c 2806static void cs_warn (MP mp, const char *cs_name, int subr, const char *fmt, ...) { 2807 char buf[SMALL_BUF_SIZE]; 2808 char s[300]; 2809 va_list args; 2810 va_start (args, fmt); 2811 @= /*@@-bufferoverflowhigh@@*/ @> 2812 (void)vsprintf (buf, fmt, args); 2813 @= /*@@=bufferoverflowhigh@@*/ @> 2814 va_end (args); 2815 if (cs_name == NULL) { 2816 mp_snprintf(s,299,"Subr (%i): %s", (int) subr, buf); 2817 } else { 2818 mp_snprintf(s,299,"CharString (/%s): %s", cs_name, buf); 2819 } 2820 mp_warn(mp,s); 2821} 2822 2823static void cs_mark (MP mp, const char *cs_name, int subr) 2824{ 2825 byte *data; 2826 int i, b, cs_len; 2827 integer a, a1, a2; 2828 unsigned short cr; 2829 static integer lastargOtherSubr3 = 3; /* the argument of last call to 2830 OtherSubrs[3] */ 2831 cs_entry *ptr; 2832 cc_entry *cc; 2833 if (cs_name == NULL) { 2834 check_subr (subr); 2835 ptr = mp->ps->subr_tab + subr; 2836 if (!ptr->valid) 2837 return; 2838 } else { 2839 if (mp->ps->cs_notdef != NULL && 2840 (cs_name == notdef || strcmp (cs_name, notdef) == 0)) 2841 ptr = mp->ps->cs_notdef; 2842 else { 2843 for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++) 2844 if (strcmp (ptr->glyph_name, cs_name) == 0) 2845 break; 2846 if (ptr == mp->ps->cs_ptr) { 2847 char s[128]; 2848 mp_snprintf (s,128,"glyph `%s' undefined", cs_name); 2849 mp_warn(mp,s); 2850 return; 2851 } 2852 if (ptr->glyph_name == notdef) 2853 mp->ps->cs_notdef = ptr; 2854 } 2855 } 2856 /* only marked CharString entries and invalid entries can be skipped; 2857 valid marked subrs must be parsed to keep the stack in sync */ 2858 if (!ptr->valid || (ptr->is_used && cs_name != NULL)) 2859 return; 2860 ptr->is_used = true; 2861 cr = 4330; 2862 cs_len = (int)ptr->cslen; 2863 data = ptr->data + 4; 2864 for (i = 0; i < mp->ps->t1_lenIV; i++, cs_len--) 2865 (void)cs_getchar (mp); 2866 while (cs_len > 0) { 2867 --cs_len; 2868 b = cs_getchar (mp); 2869 if (b >= 32) { 2870 if (b <= 246) 2871 a = b - 139; 2872 else if (b <= 250) { 2873 --cs_len; 2874 a = (int)((unsigned)(b - 247) << 8) + 108 + cs_getchar (mp); 2875 } else if (b <= 254) { 2876 --cs_len; 2877 a = -(int)((unsigned)(b - 251) << 8) - 108 - cs_getchar (mp); 2878 } else { 2879 cs_len -= 4; 2880 a = (cs_getchar (mp) & 0xff) << 24; 2881 a |= (cs_getchar (mp) & 0xff) << 16; 2882 a |= (cs_getchar (mp) & 0xff) << 8; 2883 a |= (cs_getchar (mp) & 0xff) << 0; 2884 if (sizeof (integer) > 4 && (a & 0x80000000)) 2885 a |= ~0x7FFFFFFF; 2886 } 2887 cc_push (a); 2888 } else { 2889 if (b == CS_ESCAPE) { 2890 b = cs_getchar (mp) + CS_1BYTE_MAX; 2891 cs_len--; 2892 } 2893 if (b >= CS_MAX) { 2894 cs_warn (mp,cs_name, subr, "command value out of range: %i", 2895 (int) b); 2896 goto cs_error; 2897 } 2898 cc = cc_tab + b; 2899 if (!cc->valid) { 2900 cs_warn (mp,cs_name, subr, "command not valid: %i", (int) b); 2901 goto cs_error; 2902 } 2903 if (cc->bottom) { 2904 if (stack_ptr - cc_stack < cc->nargs) 2905 cs_warn (mp,cs_name, subr, 2906 "less arguments on stack (%i) than required (%i)", 2907 (int) (stack_ptr - cc_stack), (int) cc->nargs); 2908 else if (stack_ptr - cc_stack > cc->nargs) 2909 cs_warn (mp,cs_name, subr, 2910 "more arguments on stack (%i) than required (%i)", 2911 (int) (stack_ptr - cc_stack), (int) cc->nargs); 2912 } 2913 switch (cc - cc_tab) { 2914 case CS_CALLSUBR: 2915 a1 = (integer)cc_get (-1); 2916 cc_pop (1); 2917 mark_subr (mp,a1); 2918 if (!mp->ps->subr_tab[a1].valid) { 2919 cs_warn (mp,cs_name, subr, "cannot call subr (%i)", (int) a1); 2920 goto cs_error; 2921 } 2922 break; 2923 case CS_DIV: 2924 cc_pop (2); 2925 cc_push (0); 2926 break; 2927 case CS_CALLOTHERSUBR: 2928 a1 = (integer)cc_get (-1); 2929 if (a1 == 3) 2930 lastargOtherSubr3 = (integer)cc_get (-3); 2931 a1 = (integer)cc_get (-2) + 2; 2932 cc_pop (a1); 2933 break; 2934 case CS_POP: 2935 cc_push (lastargOtherSubr3); 2936 /* the only case when we care about the value being pushed onto 2937 stack is when POP follows CALLOTHERSUBR (changing hints by 2938 OtherSubrs[3]) 2939 */ 2940 break; 2941 case CS_SEAC: 2942 a1 = (integer)cc_get (3); 2943 a2 = (integer)cc_get (4); 2944 cc_clear (); 2945 mark_cs (mp,standard_glyph_names[a1]); 2946 mark_cs (mp,standard_glyph_names[a2]); 2947 break; 2948 default: 2949 if (cc->clear) 2950 cc_clear (); 2951 } 2952 } 2953 } 2954 return; 2955 cs_error: /* an error occured during parsing */ 2956 cc_clear (); 2957 ptr->valid = false; 2958 ptr->is_used = false; 2959} 2960 2961static void t1_subset_ascii_part (MP mp, font_number tex_font, fm_entry *fm_cur) 2962{ 2963 int i, j; 2964 t1_getline (mp); 2965 while (!t1_prefix ("/Encoding")) { 2966 t1_scan_param (mp,tex_font, fm_cur); 2967 /* Patch the initial font directory cacheing mechanism found in some 2968 * pfb fonts. 2969 * 2970 * Even though the T1 spec does not explicitly state that 'FontDirectory' 2971 * should appear at the start of a line, luckily this is standard practise. 2972 */ 2973 if (t1_prefix ("FontDirectory")) { 2974 char *endloc, *p; 2975 char new_line[T1_BUF_SIZE] = {0}; 2976 p = mp->ps->t1_line_array; 2977 while ((endloc = strstr(p,fm_cur->ps_name)) != NULL) { 2978 int n = (endloc-mp->ps->t1_line_array) + strlen(fm_cur->subset_tag) + 2 + strlen(fm_cur->ps_name); 2979 if (n >= T1_BUF_SIZE) { 2980 mp_fatal_error (mp, "t1_subset_ascii_part: buffer overrun detected."); 2981 } 2982 strncat(new_line,p,(endloc-p)); 2983 strcat(new_line,fm_cur->subset_tag); 2984 strcat(new_line,"-"); 2985 strcat(new_line,fm_cur->ps_name); 2986 p = endloc + strlen(fm_cur->ps_name); 2987 } 2988 if (strlen(new_line) + strlen(p) + 1 >= T1_BUF_SIZE ) { 2989 mp_fatal_error (mp, "t1_subset_ascii_part: buffer overrun detected."); 2990 } 2991 strcat(new_line, p); 2992 strcpy(mp->ps->t1_line_array,new_line); 2993 mp->ps->t1_line_ptr = mp->ps->t1_line_array + strlen(mp->ps->t1_line_array); 2994 t1_putline (mp); 2995 } else { 2996 t1_putline (mp); 2997 } 2998 t1_getline (mp); 2999 } 3000 t1_builtin_enc (mp); 3001 if (is_reencoded (fm_cur)) 3002 mp->ps->t1_glyph_names = external_enc (); 3003 else 3004 mp->ps->t1_glyph_names = mp->ps->t1_builtin_glyph_names; 3005 if ((!is_subsetted (fm_cur)) && mp->ps->t1_encoding == ENC_STANDARD) 3006 t1_puts (mp,"/Encoding StandardEncoding def\n"); 3007 else { 3008 t1_puts 3009 (mp,"/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n"); 3010 for (i = 0, j = 0; i < 256; i++) { 3011 if (is_used_char (i) && mp->ps->t1_glyph_names[i] != notdef && 3012 strcmp(mp->ps->t1_glyph_names[i],notdef) != 0) { 3013 j++; 3014 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit, 3015 "dup %i /%s put\n", (int) t1_char (i), 3016 mp->ps->t1_glyph_names[i]); 3017 t1_puts(mp,mp->ps->t1_line_array); 3018 } 3019 } 3020 /* We didn't mark anything for the Encoding array. */ 3021 /* We add "dup 0 /.notdef put" for compatibility */ 3022 /* with Acrobat 5.0. */ 3023 if (j == 0) 3024 t1_puts (mp,"dup 0 /.notdef put\n"); 3025 t1_puts (mp,"readonly def\n"); 3026 } 3027 do { 3028 t1_getline (mp); 3029 t1_scan_param (mp,tex_font, fm_cur); 3030 if (!t1_prefix ("/UniqueID")) /* ignore UniqueID for subsetted fonts */ 3031 t1_putline (mp); 3032 } 3033 while (mp->ps->t1_in_eexec == 0); 3034} 3035 3036#define t1_subr_flush(mp) t1_flush_cs(mp,true) 3037#define t1_cs_flush(mp) t1_flush_cs(mp,false) 3038 3039static void cs_init (MP mp) { 3040 mp->ps->cs_ptr = mp->ps->cs_tab = NULL; 3041 mp->ps->cs_dict_start = mp->ps->cs_dict_end = NULL; 3042 mp->ps->cs_count = mp->ps->cs_size = mp->ps->cs_size_pos = 0; 3043 mp->ps->cs_token_pair = NULL; 3044 mp->ps->subr_tab = NULL; 3045 mp->ps->subr_array_start = mp->ps->subr_array_end = NULL; 3046 mp->ps->subr_max = mp->ps->subr_size = mp->ps->subr_size_pos = 0; 3047} 3048 3049static void init_cs_entry ( cs_entry * cs) { 3050 cs->data = NULL; 3051 cs->glyph_name = NULL; 3052 cs->len = 0; 3053 cs->cslen = 0; 3054 cs->is_used = false; 3055 cs->valid = false; 3056} 3057 3058static void t1_mark_glyphs (MP mp, font_number tex_font); 3059 3060static void t1_read_subrs (MP mp, font_number tex_font, fm_entry *fm_cur, int read_only) 3061{ 3062 int i, s; 3063 cs_entry *ptr; 3064 t1_getline (mp); 3065 while (!(t1_charstrings () || t1_subrs ())) { 3066 t1_scan_param (mp,tex_font, fm_cur); 3067 if (!read_only) 3068 t1_putline (mp); 3069 t1_getline (mp); 3070 } 3071 FOUND: 3072 mp->ps->t1_cs = true; 3073 mp->ps->t1_scan = false; 3074 if (!t1_subrs ()) 3075 return; 3076 mp->ps->subr_size_pos = (int)(strlen ("/Subrs") + 1); 3077 /* |subr_size_pos| points to the number indicating dict size after "/Subrs" */ 3078 mp->ps->subr_size = (int)t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->subr_size_pos, 0); 3079 if (mp->ps->subr_size == 0) { 3080 while (!t1_charstrings ()) 3081 t1_getline (mp); 3082 return; 3083 } 3084 /* |subr_tab = xtalloc (subr_size, cs_entry);| */ 3085 mp->ps->subr_tab = (cs_entry *)mp_xmalloc (mp,(size_t)mp->ps->subr_size, sizeof (cs_entry)); 3086 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++) 3087 init_cs_entry (ptr); 3088 mp->ps->subr_array_start = mp_xstrdup (mp,mp->ps->t1_line_array); 3089 t1_getline (mp); 3090 while (mp->ps->t1_cslen) { 3091 store_subr (mp); 3092 t1_getline (mp); 3093 } 3094 /* mark the first four entries without parsing */ 3095 for (i = 0; i < mp->ps->subr_size && i < 4; i++) 3096 mp->ps->subr_tab[i].is_used = true; 3097 /* the end of the Subrs array might have more than one line so we need to 3098 concatnate them to |subr_array_end|. Unfortunately some fonts don't have 3099 the Subrs array followed by the CharStrings dict immediately (synthetic 3100 fonts). If we cannot find CharStrings in next |POST_SUBRS_SCAN| lines then 3101 we will treat the font as synthetic and ignore everything until next 3102 Subrs is found 3103 */ 3104#define POST_SUBRS_SCAN 5 3105 s = 0; 3106 *mp->ps->t1_buf_array = 0; 3107 for (i = 0; i < POST_SUBRS_SCAN; i++) { 3108 if (t1_charstrings ()) 3109 break; 3110 s += (int)(mp->ps->t1_line_ptr - mp->ps->t1_line_array); 3111 alloc_array (t1_buf, s, T1_BUF_SIZE); 3112 strcat (mp->ps->t1_buf_array, mp->ps->t1_line_array); 3113 t1_getline (mp); 3114 } 3115 mp->ps->subr_array_end = mp_xstrdup (mp,mp->ps->t1_buf_array); 3116 if (i == POST_SUBRS_SCAN) { /* CharStrings not found; 3117 suppose synthetic font */ 3118 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++) 3119 if (ptr->valid) 3120 mp_xfree (ptr->data); 3121 mp_xfree (mp->ps->subr_tab); 3122 mp_xfree (mp->ps->subr_array_start); 3123 mp_xfree (mp->ps->subr_array_end); 3124 cs_init (mp); 3125 mp->ps->t1_cs = false; 3126 mp->ps->t1_synthetic = true; 3127 while (!(t1_charstrings () || t1_subrs ())) 3128 t1_getline (mp); 3129 goto FOUND; 3130 } 3131} 3132 3133@ @c 3134static void t1_flush_cs (MP mp, boolean is_subr) 3135{ 3136 char *p; 3137 byte *r, *return_cs = NULL; 3138 cs_entry *tab, *end_tab, *ptr; 3139 char *start_line, *line_end; 3140 int count, size_pos; 3141 unsigned short cr, cs_len = 0; /* to avoid warning about uninitialized use of |cs_len| */ 3142 if (is_subr) { 3143 start_line = mp->ps->subr_array_start; 3144 line_end = mp->ps->subr_array_end; 3145 size_pos = mp->ps->subr_size_pos; 3146 tab = mp->ps->subr_tab; 3147 count = mp->ps->subr_max + 1; 3148 end_tab = mp->ps->subr_tab + count; 3149 } else { 3150 start_line = mp->ps->cs_dict_start; 3151 line_end = mp->ps->cs_dict_end; 3152 size_pos = mp->ps->cs_size_pos; 3153 tab = mp->ps->cs_tab; 3154 end_tab = mp->ps->cs_ptr; 3155 count = mp->ps->cs_count; 3156 } 3157 mp->ps->t1_line_ptr = mp->ps->t1_line_array; 3158 for (p = start_line; p - start_line < size_pos;) 3159 *mp->ps->t1_line_ptr++ = *p++; 3160 while (mp_isdigit (*p)) 3161 p++; 3162 mp_snprintf (mp->ps->t1_line_ptr, (int)mp->ps->t1_line_limit, "%u", (unsigned)count); 3163 strcat (mp->ps->t1_line_ptr, p); 3164 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array); 3165 t1_putline (mp); 3166 3167 /* create |return_cs| to replace unsused subr's */ 3168 if (is_subr) { 3169 cr = 4330; 3170 cs_len = 0; 3171 return_cs = mp_xmalloc (mp, (size_t)(mp->ps->t1_lenIV + 1) , sizeof(byte)); 3172 if ( mp->ps->t1_lenIV >= 0) { 3173 for (cs_len = 0, r = return_cs; 3174 cs_len<(unsigned short)mp->ps->t1_lenIV; cs_len++, r++) 3175 *r = cencrypt (0x00, &cr); 3176 *r = cencrypt (CS_RETURN, &cr); 3177 } else { 3178 *return_cs = CS_RETURN; 3179 } 3180 cs_len++; 3181 } 3182 3183 for (ptr = tab; ptr < end_tab; ptr++) { 3184 if (ptr->is_used) { 3185 if (is_subr) 3186 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit, 3187 "dup %i %u", (int) (ptr - tab), ptr->cslen); 3188 else 3189 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit, 3190 "/%s %u", ptr->glyph_name, ptr->cslen); 3191 p = strend (mp->ps->t1_line_array); 3192 memcpy (p, ptr->data, (size_t)ptr->len); 3193 mp->ps->t1_line_ptr = p + ptr->len; 3194 t1_putline (mp); 3195 } else { 3196 /* replace unsused subr's by |return_cs| */ 3197 if (is_subr) { 3198 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit, 3199 "dup %i %u%s ", (int) (ptr - tab), 3200 cs_len, mp->ps->cs_token_pair[0]); 3201 p = strend (mp->ps->t1_line_array); 3202 memcpy (p, return_cs, (size_t)cs_len); 3203 mp->ps->t1_line_ptr = p + cs_len; 3204 t1_putline (mp); 3205 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit, 3206 " %s", mp->ps->cs_token_pair[1]); 3207 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array); 3208 t1_putline (mp); 3209 } 3210 } 3211 mp_xfree (ptr->data); 3212 if (ptr->glyph_name != notdef) 3213 mp_xfree (ptr->glyph_name); 3214 } 3215 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit, "%s", line_end); 3216 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array); 3217 t1_putline (mp); 3218 if (is_subr) 3219 mp_xfree (return_cs); 3220 mp_xfree (tab); 3221 mp_xfree (start_line); 3222 mp_xfree (line_end); 3223 if (is_subr) { 3224 mp->ps->subr_array_start = NULL; 3225 mp->ps->subr_array_end = NULL; 3226 mp->ps->subr_tab = NULL; 3227 } else { 3228 mp->ps->cs_dict_start = NULL; 3229 mp->ps->cs_dict_end = NULL; 3230 mp->ps->cs_tab = NULL; 3231 } 3232} 3233 3234static void t1_mark_glyphs (MP mp, font_number tex_font) 3235{ 3236 int i; 3237 char *charset = extra_charset (); 3238 char *g, *s, *r; 3239 cs_entry *ptr; 3240 if (mp->ps->t1_synthetic || embed_all_glyphs (tex_font)) { /* mark everything */ 3241 if (mp->ps->cs_tab != NULL) 3242 for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++) 3243 if (ptr->valid) 3244 ptr->is_used = true; 3245 if (mp->ps->subr_tab != NULL) { 3246 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++) 3247 if (ptr->valid) 3248 ptr->is_used = true; 3249 mp->ps->subr_max = mp->ps->subr_size - 1; 3250 } 3251 return; 3252 } 3253 mark_cs (mp,notdef); 3254 for (i = 0; i < 256; i++) 3255 if (is_used_char (i)) { 3256 if (mp->ps->t1_glyph_names[i] == notdef || 3257 strcmp(mp->ps->t1_glyph_names[i],notdef)==0) { 3258 char S[128]; 3259 mp_snprintf(S,128, "character %i is mapped to %s", i, notdef); 3260 mp_warn(mp,S); 3261 } else 3262 mark_cs (mp,mp->ps->t1_glyph_names[i]); 3263 } 3264 if (charset == NULL) 3265 goto SET_SUBR_MAX; 3266 g = s = charset + 1; /* skip the first '/' */ 3267 r = strend (g); 3268 while (g < r) { 3269 while (*s != '/' && s < r) 3270 s++; 3271 *s = 0; /* terminate g by rewriting '/' to 0 */ 3272 mark_cs (mp,g); 3273 g = s + 1; 3274 } 3275 SET_SUBR_MAX: 3276 if (mp->ps->subr_tab != NULL) 3277 for (mp->ps->subr_max = -1, ptr = mp->ps->subr_tab; 3278 ptr - mp->ps->subr_tab < mp->ps->subr_size; 3279 ptr++) 3280 if (ptr->is_used && ptr - mp->ps->subr_tab > mp->ps->subr_max) 3281 mp->ps->subr_max = (int)(ptr - mp->ps->subr_tab); 3282} 3283 3284static void t1_do_subset_charstrings (MP mp, font_number tex_font) 3285{ 3286 cs_entry *ptr; 3287 mp->ps->cs_size_pos = (int)( 3288 strstr (mp->ps->t1_line_array, charstringname) + strlen (charstringname) 3289 - mp->ps->t1_line_array + 1); 3290 /* |cs_size_pos| points to the number indicating 3291 dict size after "/CharStrings" */ 3292 mp->ps->cs_size = (int)t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->cs_size_pos, 0); 3293 mp->ps->cs_ptr = mp->ps->cs_tab = mp_xmalloc (mp,(size_t)mp->ps->cs_size, sizeof(cs_entry)); 3294 for (ptr = mp->ps->cs_tab; ptr - mp->ps->cs_tab < mp->ps->cs_size; ptr++) 3295 init_cs_entry (ptr); 3296 mp->ps->cs_notdef = NULL; 3297 mp->ps->cs_dict_start = mp_xstrdup (mp,mp->ps->t1_line_array); 3298 t1_getline (mp); 3299 while (mp->ps->t1_cslen) { 3300 store_cs (mp); 3301 t1_getline (mp); 3302 } 3303 mp->ps->cs_dict_end = mp_xstrdup (mp,mp->ps->t1_line_array); 3304 t1_mark_glyphs (mp,tex_font); 3305} 3306 3307static void t1_subset_charstrings (MP mp, font_number tex_font) 3308{ 3309 cs_entry *ptr; 3310 t1_do_subset_charstrings (mp, tex_font); 3311 if (mp->ps->subr_tab != NULL) { 3312 if (mp->ps->cs_token_pair == NULL) 3313 mp_fatal_error 3314 (mp, "This Type 1 font uses mismatched subroutine begin/end token pairs."); 3315 t1_subr_flush (mp); 3316 } 3317 for (mp->ps->cs_count = 0, ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++) 3318 if (ptr->is_used) 3319 mp->ps->cs_count++; 3320 t1_cs_flush (mp); 3321} 3322 3323static void t1_subset_end (MP mp) 3324{ 3325 if (mp->ps->t1_synthetic) { /* copy to "dup /FontName get exch definefont pop" */ 3326 while (!strstr (mp->ps->t1_line_array, "definefont")) { 3327 t1_getline (mp); 3328 t1_putline (mp); 3329 } 3330 while (!t1_end_eexec ()) 3331 t1_getline (mp); /* ignore the rest */ 3332 t1_putline (mp); /* write "mark currentfile closefile" */ 3333 } else 3334 while (!t1_end_eexec ()) { /* copy to "mark currentfile closefile" */ 3335 t1_getline (mp); 3336 t1_putline (mp); 3337 } 3338 t1_stop_eexec (mp); 3339 if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */ 3340 while (!t1_cleartomark ()) { 3341 t1_getline (mp); 3342 t1_putline (mp); 3343 } 3344 if (!mp->ps->t1_synthetic) /* don't check "{restore}if" for synthetic fonts */ 3345 t1_check_end (mp); /* write "{restore}if" if found */ 3346 } 3347} 3348 3349static int t1_updatefm (MP mp, font_number f, fm_entry *fm) 3350{ 3351 char *s, *p; 3352 mp->ps->read_encoding_only = true; 3353 if (!t1_open_fontfile (mp,fm,NULL)) { 3354 return 0; 3355 } 3356 t1_scan_only (mp,f, fm); 3357 s = mp_xstrdup(mp,mp->ps->fontname_buf); 3358 p = s; 3359 while (*p != ' ' && *p != 0) 3360 p++; 3361 *p=0; 3362 mp_xfree(fm->ps_name); 3363 fm->ps_name = s; 3364 t1_close_font_file (mp,""); 3365 return 1; 3366} 3367 3368 3369static void writet1 (MP mp, font_number tex_font, fm_entry *fm_cur) { 3370 unsigned save_selector = mp->selector; 3371 mp_normalize_selector(mp); 3372 mp->ps->read_encoding_only = false; 3373 if (!is_included (fm_cur)) { /* scan parameters from font file */ 3374 if (!t1_open_fontfile (mp,fm_cur,"{")) 3375 return; 3376 t1_scan_only (mp,tex_font, fm_cur); 3377 t1_close_font_file (mp,"}"); 3378 return; 3379 } 3380 if (!is_subsetted (fm_cur)) { /* include entire font */ 3381 if (!t1_open_fontfile (mp,fm_cur,"<<")) 3382 return; 3383 t1_include (mp,tex_font,fm_cur); 3384 t1_close_font_file (mp,">>"); 3385 return; 3386 } 3387 /* partial downloading */ 3388 if (!t1_open_fontfile (mp,fm_cur,"<")) 3389 return; 3390 t1_subset_ascii_part (mp,tex_font,fm_cur); 3391 t1_start_eexec (mp,fm_cur); 3392 cc_init (); 3393 cs_init (mp); 3394 t1_read_subrs (mp,tex_font, fm_cur, false); 3395 t1_subset_charstrings (mp,tex_font); 3396 t1_subset_end (mp); 3397 t1_close_font_file (mp,">"); 3398 mp->selector = save_selector; 3399} 3400 3401@ @<Declarations@>= 3402static void t1_free (MP mp); 3403 3404@ @c 3405static void t1_free (MP mp) { 3406 int k; 3407 3408 mp_xfree (mp->ps->subr_array_start); 3409 mp_xfree (mp->ps->subr_array_end); 3410 mp_xfree (mp->ps->cs_dict_start); 3411 mp_xfree (mp->ps->cs_dict_end); 3412 cs_init(mp); 3413 3414 mp_xfree (mp->ps->t1_line_array); 3415 mp_xfree (mp->ps->char_array); 3416 mp->ps->char_array=NULL; 3417 3418 mp->ps->t1_line_array = mp->ps->t1_line_ptr = NULL; 3419 mp->ps->t1_line_limit = 0; 3420 mp_xfree (mp->ps->t1_buf_array); 3421 mp->ps->t1_buf_array = mp->ps->t1_buf_ptr = NULL; 3422 mp->ps->t1_buf_limit = 0; 3423 3424 for (k=0;k<=255;k++) { 3425 if (mp->ps->t1_builtin_glyph_names[k] != notdef) 3426 mp_xfree(mp->ps->t1_builtin_glyph_names[k]); 3427 mp->ps->t1_builtin_glyph_names[k] = notdef; 3428 } 3429} 3430 3431@* Embedding Charstrings. 3432 3433The SVG backend uses some routines that use an ascii representation of 3434a type1 font. First, here is the type associated with it: 3435 3436@<Types ...@>= 3437typedef struct mp_ps_font { 3438 int font_num; /* just to put something in */ 3439 char **t1_glyph_names; 3440 cs_entry *cs_tab; 3441 cs_entry *cs_ptr; 3442 cs_entry *subr_tab; 3443 int subr_size; 3444 int t1_lenIV; 3445 int slant; 3446 int extend; 3447 @<Variables for the charstring parser@> 3448} mp_ps_font; 3449 3450@ The parser creates a structure and fills it. 3451 3452@c 3453 3454mp_ps_font *mp_ps_font_parse (MP mp, int tex_font) { 3455 mp_ps_font *f; 3456 fm_entry *fm_cur; 3457 char msg[128]; 3458 (void)mp_has_fm_entry (mp, (font_number)tex_font, &fm_cur); 3459 if (fm_cur == NULL) { 3460 mp_snprintf(msg,128,"fontmap entry for `%s' not found", mp->font_name[tex_font]); 3461 mp_warn(mp,msg); 3462 return NULL; 3463 } 3464 if (is_truetype(fm_cur) || 3465 (fm_cur->ps_name == NULL && fm_cur->ff_name == NULL) || 3466 (!is_included(fm_cur))) { 3467 mp_snprintf(msg,128,"font `%s' cannot be embedded", mp->font_name[tex_font]); 3468 mp_warn(mp,msg); 3469 return NULL; 3470 } 3471 if (!t1_open_fontfile (mp,fm_cur,"<")) { /* message handled there */ 3472 return NULL; 3473 } 3474 f = mp_xmalloc(mp, 1, sizeof(struct mp_ps_font)); 3475 f->font_num = tex_font; 3476 f->t1_glyph_names = NULL; 3477 f->cs_tab = NULL; 3478 f->cs_ptr = NULL; 3479 f->subr_tab = NULL; 3480 f->orig_x = f->orig_y = 0.0; 3481 f->slant = (int)fm_cur->slant; 3482 f->extend = (int)fm_cur->extend; 3483 t1_getline (mp); 3484 while (!t1_prefix ("/Encoding")) { 3485 t1_scan_param (mp, (font_number)tex_font, fm_cur); 3486 t1_getline (mp); 3487 } 3488 t1_builtin_enc (mp); 3489 if (is_reencoded (fm_cur)) { 3490 mp_read_enc (mp, fm_cur->encoding);; 3491 f->t1_glyph_names = external_enc (); 3492 } else { 3493 f->t1_glyph_names = mp->ps->t1_builtin_glyph_names; 3494 } 3495 do { 3496 t1_getline (mp); 3497 t1_scan_param (mp, (font_number)tex_font, fm_cur); 3498 } while (mp->ps->t1_in_eexec == 0); 3499 3500 /* t1_start_eexec (mp,fm_cur); */ 3501 cc_init (); 3502 cs_init (mp); 3503 /* the boolean is needed to make sure that |t1_read_subrs| 3504 doesn't output stuff */ 3505 t1_read_subrs (mp, (font_number)tex_font, fm_cur, true); 3506 mp->ps->t1_synthetic = true ; 3507 t1_do_subset_charstrings (mp, (font_number)tex_font); 3508 f->cs_tab = mp->ps->cs_tab; 3509 mp->ps->cs_tab = NULL; 3510 f->cs_ptr = mp->ps->cs_ptr; 3511 mp->ps->cs_ptr = NULL; 3512 f->subr_tab = mp->ps->subr_tab; 3513 mp->ps->subr_tab = NULL; 3514 f->subr_size = mp->ps->subr_size; 3515 mp->ps->subr_size = mp->ps->subr_size_pos = 0; 3516 f->t1_lenIV = mp->ps->t1_lenIV; 3517 t1_close_font_file (mp,">"); 3518 return f; 3519} 3520 3521@ @<Exported function headers@>= 3522mp_ps_font *mp_ps_font_parse (MP mp, int tex_font); 3523 3524@ Freeing the structure 3525 3526@c 3527void mp_ps_font_free (MP mp, mp_ps_font *f) { 3528 cs_entry *ptr; 3529 for (ptr = f->cs_tab; ptr < f->cs_ptr; ptr++) { 3530 if (ptr->glyph_name != notdef) 3531 mp_xfree (ptr->glyph_name); 3532 mp_xfree(ptr->data); 3533 } 3534 mp_xfree(f->cs_tab); 3535 f->cs_tab = NULL; 3536 for (ptr = f->subr_tab; ptr - f->subr_tab < f->subr_size; ptr++) { 3537 if (ptr->glyph_name != notdef) 3538 mp_xfree (ptr->glyph_name); 3539 mp_xfree(ptr->data); 3540 } 3541 mp_xfree(f->subr_tab); 3542 f->subr_tab = NULL; 3543 t1_free(mp); 3544 mp_xfree(f); 3545} 3546 3547@ @<Exported function headers@>= 3548void mp_ps_font_free (MP mp, mp_ps_font *f); 3549 3550 3551@ Parsing Charstrings. 3552 3553@<Variables for the charstring parser@>= 3554double cur_x, cur_y; /* current point */ 3555double orig_x, orig_y; /* origin (for seac) */ 3556mp_edge_object *h; /* the whole picture */ 3557mp_graphic_object *p; /* the current subpath in the picture */ 3558mp_gr_knot pp; /* the last known knot in the subpath */ 3559 3560 3561@ @c 3562mp_edge_object *mp_ps_do_font_charstring (MP mp, mp_ps_font *f, char *nam) { 3563 mp_edge_object *h = NULL; 3564 f->h = NULL; f->p = NULL; f->pp = NULL; /* just in case */ 3565 f->cur_x = f->cur_y = 0.0; 3566 f->orig_x = f->orig_y = 0.0; 3567 if (nam==NULL) { 3568 mp_warn(mp,"nonexistant glyph requested"); 3569 return h; 3570 } 3571 if (cs_parse(mp,f,nam, 0)) { 3572 h = f->h; 3573 } else { 3574 char err[256]; 3575 mp_snprintf(err,255,"Glyph interpreter failed (missing glyph '%s'?)", nam); 3576 mp_warn(mp,err); 3577 if (f->h != NULL) { 3578 finish_subpath(mp, f); 3579 mp_gr_toss_objects(f->h); 3580 } 3581 } 3582 f->h = NULL; f->p = NULL; f->pp = NULL; 3583 return h; 3584} 3585 3586mp_edge_object *mp_ps_font_charstring (MP mp, mp_ps_font *f, int c) { 3587 char *s = NULL; 3588 if (f != NULL && f->t1_glyph_names != NULL && c>=0 && c<256) 3589 s = f->t1_glyph_names[c]; 3590 return mp_ps_do_font_charstring(mp,f,s); 3591} 3592 3593 3594 3595@ @<Exported function headers@>= 3596mp_edge_object *mp_ps_font_charstring (MP mp, mp_ps_font *f, int c); 3597mp_edge_object *mp_ps_do_font_charstring (MP mp, mp_ps_font *f, char *n); 3598 3599 3600@ 3601@<Declarations@>= 3602boolean cs_parse (MP mp, mp_ps_font *f, const char *cs_name, int subr); 3603 3604@ 3605@c 3606static void start_subpath(MP mp, mp_ps_font *f, double dx, double dy) 3607{ 3608 assert(f->pp == NULL); 3609 assert(f->p == NULL); 3610 f->pp = mp_xmalloc(mp, 1, sizeof (struct mp_gr_knot_data)); 3611 f->pp->data.types.left_type = mp_explicit; 3612 f->pp->data.types.right_type = mp_explicit; 3613 f->pp->x_coord = (f->cur_x + dx); 3614 f->pp->y_coord = (f->cur_y + dy); 3615 f->pp->left_x = f->pp->right_x = f->pp->x_coord; 3616 f->pp->left_y = f->pp->right_y = f->pp->y_coord; 3617 f->pp->next = NULL; 3618 f->cur_x += dx; 3619 f->cur_y += dy; 3620 f->p = mp_new_graphic_object(mp,mp_fill_code); 3621 gr_path_p((mp_fill_object *)f->p) = f->pp; 3622} 3623 3624static void add_line_segment(MP mp, mp_ps_font *f, double dx, double dy) 3625{ 3626 mp_gr_knot n; 3627 assert(f->pp != NULL); 3628 n = mp_xmalloc(mp,1, sizeof (struct mp_gr_knot_data)); 3629 n->data.types.left_type = mp_explicit; 3630 n->data.types.right_type = mp_explicit; 3631 n->next = gr_path_p((mp_fill_object *)f->p); /* loop */ 3632 n->x_coord = (f->cur_x + dx); 3633 n->y_coord = (f->cur_y + dy); 3634 n->right_x = n->x_coord; 3635 n->right_y = n->y_coord; 3636 n->left_x = n->x_coord; 3637 n->left_y = n->y_coord; 3638 f->pp->next = n; 3639 f->pp = n; 3640 f->cur_x += dx; 3641 f->cur_y += dy; 3642} 3643 3644static void add_curve_segment(MP mp, mp_ps_font *f, double dx1, double dy1, double dx2, 3645 double dy2, double dx3, double dy3) 3646{ 3647 mp_gr_knot n; 3648 n = mp_xmalloc(mp, 1, sizeof (struct mp_gr_knot_data)); 3649 n->data.types.left_type = mp_explicit; 3650 n->data.types.right_type = mp_explicit; 3651 n->next = gr_path_p((mp_fill_object *)f->p); /* loop */ 3652 n->x_coord = (f->cur_x + dx1 + dx2 + dx3); 3653 n->y_coord = (f->cur_y + dy1 + dy2 + dy3); 3654 n->right_x = n->x_coord; 3655 n->right_y = n->y_coord; 3656 n->left_x = (f->cur_x + dx1 + dx2); 3657 n->left_y = (f->cur_y + dy1 + dy2); 3658 f->pp->right_x = (f->cur_x + dx1); 3659 f->pp->right_y = (f->cur_y + dy1); 3660 f->pp->next = n; 3661 f->pp = n; 3662 f->cur_x += dx1 + dx2 + dx3; 3663 f->cur_y += dy1 + dy2 + dy3; 3664} 3665 3666static void finish_subpath(MP mp, mp_ps_font *f) 3667{ 3668 if (f->p != NULL) { 3669 if (f->h->body == NULL) { 3670 f->h->body = f->p; 3671 } else { 3672 mp_graphic_object *q = f->h->body; 3673 while (gr_link(q) != NULL) 3674 q = gr_link(q); 3675 q->next = f->p; 3676 } 3677 } 3678 if (f->p!=NULL) { 3679 mp_gr_knot r, rr; 3680 assert(f->pp != NULL); 3681 r = gr_path_p((mp_fill_object *)f->p); 3682 rr = r; 3683 if (r) { 3684 if (r == f->pp ) { 3685 r->next = r; 3686 } else if ( r->x_coord == f->pp->x_coord && r->y_coord == f->pp->y_coord ) { 3687 while (rr->next != f->pp) 3688 rr = rr->next; 3689 rr->next = r; 3690 r->left_x = f->pp->left_x; 3691 r->left_y = f->pp->left_y; 3692 mp_xfree(f->pp); 3693 } 3694 } 3695 } 3696 f->p = NULL; 3697 f->pp = NULL; 3698} 3699 3700@ 3701@d cs_no_debug(A) cs_do_debug(mp,f,A,#A) 3702@d cs_debug(A) 3703 3704@<Declarations@>= 3705void cs_do_debug (MP mp, mp_ps_font *f, int i, char *s); 3706static void finish_subpath(MP mp, mp_ps_font *f); 3707static void add_curve_segment(MP mp, mp_ps_font *f, double dx1, double dy1, double dx2, 3708 double dy2, double dx3, double dy3); 3709static void add_line_segment(MP mp, mp_ps_font *f, double dx, double dy); 3710static void start_subpath(MP mp, mp_ps_font *f, double dx, double dy); 3711 3712@ @c 3713void cs_do_debug (MP mp, mp_ps_font *f, int i, char *s) { 3714 int n = cc_tab[i].nargs; 3715 (void)mp; /* for -Wall */ 3716 (void)f; /* for -Wall */ 3717 while (n>0) { 3718 fprintf (stdout,"%d ", (int)cc_get((-n))); 3719 n--; 3720 } 3721 fprintf (stdout,"%s\n", s); 3722} 3723 3724boolean cs_parse (MP mp, mp_ps_font *f, const char *cs_name, int subr) 3725{ 3726 byte *data; 3727 int i, b, cs_len; 3728 integer a, a1, a2; 3729 unsigned short cr; 3730 static integer lastargOtherSubr3 = 3; 3731 3732 cs_entry *ptr; 3733 cc_entry *cc; 3734 3735 if (cs_name == NULL) { 3736 ptr = f->subr_tab + subr; 3737 } else { 3738 i = 0; 3739 for (ptr = f->cs_tab; ptr < f->cs_ptr; ptr++, i++) { 3740 if (strcmp (ptr->glyph_name, cs_name) == 0) 3741 break; 3742 } 3743 ptr = f->cs_tab+i; /* this is the right charstring */ 3744 } 3745 if (ptr==f->cs_ptr) 3746 return false; 3747 data = ptr->data + 4; 3748 cr = 4330; 3749 cs_len = (int)ptr->cslen; 3750 for (i = 0; i < f->t1_lenIV; i++, cs_len--) 3751 (void)cs_getchar (mp); 3752 3753 while (cs_len > 0) { 3754 --cs_len; 3755 b = cs_getchar(mp); 3756 if (b >= 32) { 3757 if (b <= 246) 3758 a = b - 139; 3759 else if (b <= 250) { 3760 --cs_len; 3761 a = (int)((unsigned)(b - 247) << 8) + 108 + cs_getchar (mp); 3762 } else if (b <= 254) { 3763 --cs_len; 3764 a = -(int)((unsigned)(b - 251) << 8) - 108 - cs_getchar (mp); 3765 } else { 3766 cs_len -= 4; 3767 a = (cs_getchar (mp) & 0xff) << 24; 3768 a |= (cs_getchar (mp) & 0xff) << 16; 3769 a |= (cs_getchar (mp) & 0xff) << 8; 3770 a |= (cs_getchar (mp) & 0xff) << 0; 3771 if (sizeof (integer) > 4 && (a & 0x80000000)) 3772 a |= ~0x7FFFFFFF; 3773 } 3774 cc_push (a); 3775 } else { 3776 if (b == CS_ESCAPE) { 3777 b = cs_getchar (mp) + CS_1BYTE_MAX; 3778 cs_len--; 3779 } 3780 if (b >= CS_MAX) { 3781 cs_warn (mp,cs_name, subr, "command value out of range: %i", 3782 (int) b); 3783 goto cs_error; 3784 } 3785 cc = cc_tab + b; 3786 if (!cc->valid) { 3787 cs_warn (mp,cs_name, subr, "command not valid: %i", (int) b); 3788 goto cs_error; 3789 } 3790 if (cc->bottom) { 3791 if (stack_ptr - cc_stack < cc->nargs) 3792 cs_warn (mp,cs_name, subr, 3793 "less arguments on stack (%i) than required (%i)", 3794 (int) (stack_ptr - cc_stack), (int) cc->nargs); 3795 else if (stack_ptr - cc_stack > cc->nargs) 3796 cs_warn (mp,cs_name, subr, 3797 "more arguments on stack (%i) than required (%i)", 3798 (int) (stack_ptr - cc_stack), (int) cc->nargs); 3799 } 3800 switch (cc - cc_tab) { 3801 case CS_CLOSEPATH: /* - CLOSEPATH |- */ 3802 cs_debug(CS_CLOSEPATH); 3803 finish_subpath(mp, f); 3804 cc_clear (); 3805 break; 3806 case CS_HLINETO: /* |- dx HLINETO |- */ 3807 cs_debug(CS_HLINETO); 3808 add_line_segment(mp,f,cc_get(-1),0); 3809 cc_clear (); 3810 break; 3811 case CS_HVCURVETO: /* |- dx1 dx2 dy2 dy3 HVCURVETO |- */ 3812 cs_debug(CS_HVCURVETO); 3813 add_curve_segment(mp,f,cc_get(-4),0,cc_get(-3),cc_get(-2),0,cc_get(-1)); 3814 cc_clear (); 3815 break; 3816 case CS_RLINETO: /* |- dx dy RLINETO |- */ 3817 cs_debug(CS_RLINETO); 3818 add_line_segment(mp,f,cc_get(-2),cc_get(-1)); 3819 cc_clear (); 3820 break; 3821 case CS_RRCURVETO: /* |- dx1 dy1 dx2 dy2 dx3 dy3 RRCURVETO |- */ 3822 cs_debug(CS_RRCURVETO); 3823 add_curve_segment(mp,f,cc_get(-6),cc_get(-5),cc_get(-4),cc_get(-3),cc_get(-2),cc_get(-1)); 3824 cc_clear (); 3825 break; 3826 case CS_VHCURVETO: /* |- dy1 dx2 dy2 dx3 VHCURVETO |- */ 3827 cs_debug(CS_VHCURVETO); 3828 add_curve_segment(mp,f,0, cc_get(-4),cc_get(-3),cc_get(-2),cc_get(-1),0); 3829 cc_clear (); 3830 break; 3831 case CS_VLINETO: /* |- dy VLINETO |- */ 3832 cs_debug(CS_VLINETO); 3833 add_line_segment(mp,f,0,cc_get(-1)); 3834 cc_clear (); 3835 break; 3836 case CS_HMOVETO: /* |- dx HMOVETO |- */ 3837 cs_debug(CS_HMOVETO); 3838 /* treating in-line moves as 'line segments' work better than attempting 3839 to split the path up in two separate sections, at least for now. */ 3840 if (f->pp == NULL) { /* this is the first */ 3841 start_subpath(mp,f,cc_get(-1),0); 3842 } else { 3843 add_line_segment(mp,f,cc_get(-1),0); 3844 } 3845 cc_clear (); 3846 break; 3847 case CS_RMOVETO: /* |- dx dy RMOVETO |- */ 3848 cs_debug(CS_RMOVETO); 3849 if (f->pp == NULL) { /* this is the first */ 3850 start_subpath(mp,f,cc_get(-2),cc_get(-1)); 3851 } else { 3852 add_line_segment(mp,f,cc_get(-2),cc_get(-1)); 3853 } 3854 cc_clear (); 3855 break; 3856 case CS_VMOVETO: /* |- dy VMOVETO |- */ 3857 cs_debug(CS_VMOVETO); 3858 if (f->pp == NULL) { /* this is the first */ 3859 start_subpath(mp,f,0,cc_get(-1)); 3860 } else { 3861 add_line_segment(mp,f,0,cc_get(-1)); 3862 } 3863 cc_clear (); 3864 break; 3865 /* hinting commands */ 3866 case CS_DOTSECTION: /* - DOTSECTION |- */ 3867 cs_debug(CS_DOTSECTION); 3868 cc_clear (); 3869 break; 3870 case CS_HSTEM: /* |- y dy HSTEM |- */ 3871 cs_debug(CS_HSTEM); 3872 cc_clear (); 3873 break; 3874 case CS_HSTEM3: /* |- y0 dy0 y1 dy1 y2 dy2 HSTEM3 |- */ 3875 cs_debug(CS_HSTEM3); 3876 cc_clear (); 3877 break; 3878 case CS_VSTEM: /* |- x dx VSTEM |- */ 3879 cs_debug(CS_VSTEM); 3880 cc_clear (); 3881 break; 3882 case CS_VSTEM3: /* |- x0 dx0 x1 dx1 x2 dx2 VSTEM3 |- */ 3883 cs_debug(CS_VSTEM3); 3884 cc_clear (); 3885 break; 3886 /* start and close commands */ 3887 case CS_SEAC: /* |- asb adx ady bchar achar SEAC |- */ 3888 cs_debug(CS_SEAC); 3889 { double adx, ady, asb; 3890 asb = cc_get (0); 3891 adx = cc_get (1); 3892 ady = cc_get (2); 3893 a1 = (integer)cc_get (3); 3894 a2 = (integer)cc_get (4); 3895 cc_clear (); 3896 (void)cs_parse(mp,f,standard_glyph_names[a1],0); /* base */ 3897 f->orig_x += (adx - asb); 3898 f->orig_y += ady; 3899 (void)cs_parse(mp,f,standard_glyph_names[a2],0); 3900 } 3901 break; 3902 case CS_ENDCHAR: /* - ENDCHAR |- */ 3903 cs_debug(CS_ENDCHAR); 3904 cc_clear (); 3905 return true; 3906 break; 3907 case CS_HSBW: /* |- sbx wx HSBW |- */ 3908 cs_debug(CS_HSBW); 3909 if (!f->h) { 3910 f->h = mp_xmalloc(mp, 1,sizeof(mp_edge_object)); 3911 f->h->body = NULL; f->h->next = NULL; 3912 f->h->parent = mp; 3913 f->h->filename = NULL; 3914 f->h->minx = f->h->miny = f->h->maxx = f->h->maxy = 0.0; 3915 } 3916 f->cur_x = cc_get(-2) + f->orig_x; 3917 f->cur_y = 0.0 + f->orig_y; 3918 f->orig_x = f->cur_x; 3919 f->orig_y = f->cur_y; 3920 cc_clear (); 3921 break; 3922 case CS_SBW: /* |- sbx sby wx wy SBW |- */ 3923 cs_debug(CS_SBW); 3924 if (!f->h) { 3925 f->h = mp_xmalloc(mp, 1,sizeof(mp_edge_object)); 3926 f->h->body = NULL; f->h->next = NULL; 3927 f->h->parent = mp; 3928 f->h->filename = NULL; 3929 f->h->minx = f->h->miny = f->h->maxx = f->h->maxy = 0.0; 3930 } 3931 f->cur_x = cc_get(-4) + f->orig_x; 3932 f->cur_y = cc_get(-3) + f->orig_y; 3933 f->orig_x = f->cur_x; 3934 f->orig_y = f->cur_y; 3935 cc_clear (); 3936 break; 3937 /* arithmetic */ 3938 case CS_DIV: /* num1 num2 DIV quotient */ 3939 cs_debug(CS_DIV); 3940 { double num,den,res; 3941 num = cc_get (-2); 3942 den = cc_get (-1); 3943 res = num/den; 3944 cc_pop (2); 3945 cc_push (res); 3946 break; 3947 } 3948 /* subrs */ 3949 case CS_CALLSUBR: /* subr CALLSUBR - */ 3950 cs_debug(CS_CALLSUBR); 3951 a1 = (integer)cc_get (-1); 3952 cc_pop (1); 3953 (void)cs_parse(mp,f,NULL,a1); 3954 break; 3955 case CS_RETURN: /* - RETURN - */ 3956 cs_debug(CS_RETURN); 3957 return true; 3958 break; 3959 case CS_CALLOTHERSUBR: /* arg1 ... argn n othersubr CALLOTHERSUBR - */ 3960 a1 = (integer)cc_get (-1); 3961 if (a1 == 3) 3962 lastargOtherSubr3 = (integer)cc_get (-3); 3963 a1 = (integer)cc_get(-2) + 2; 3964 cc_pop (a1); 3965 break; 3966 case CS_POP: /* - POP number */ 3967 cc_push (lastargOtherSubr3); 3968 break; 3969 case CS_SETCURRENTPOINT: /* |- x y SETCURRENTPOINT |- */ 3970 cs_debug(CS_SETCURRENTPOINT); 3971 /* totally ignoring setcurrentpoint actually works better for most fonts ? */ 3972 cc_clear (); 3973 break; 3974 default: 3975 if (cc->clear) 3976 cc_clear (); 3977 } 3978 } 3979 } 3980 return true; 3981cs_error: /* an error occured during parsing */ 3982 cc_clear (); 3983 ptr->valid = false; 3984 ptr->is_used = false; 3985 return false; 3986} 3987 3988@* \[44d] Embedding fonts. 3989 3990@ The |tfm_num| is officially of type |font_number|, but that 3991type does not exist yet at this point in the output order. 3992 3993@<Types...@>= 3994typedef struct { 3995 char *tfm_name; /* TFM file name */ 3996 char *ps_name; /* PostScript name */ 3997 integer flags; /* font flags */ 3998 char *ff_name; /* font file name */ 3999 char *subset_tag; /* pseudoUniqueTag for subsetted font */ 4000 enc_entry *encoding; /* pointer to corresponding encoding */ 4001 unsigned int tfm_num; /* number of the TFM refering this entry */ 4002 unsigned short type; /* font type (T1/TTF/...) */ 4003 short slant; /* SlantFont */ 4004 short extend; /* ExtendFont */ 4005 integer ff_objnum; /* FontFile object number */ 4006 integer fn_objnum; /* FontName/BaseName object number */ 4007 integer fd_objnum; /* FontDescriptor object number */ 4008 char *charset; /* string containing used glyphs */ 4009 boolean all_glyphs; /* embed all glyphs? */ 4010 unsigned short links; /* link flags from |tfm_tree| and |ps_tree| */ 4011 short tfm_avail; /* flags whether a tfm is available */ 4012 short pid; /* Pid for truetype fonts */ 4013 short eid; /* Eid for truetype fonts */ 4014} fm_entry; 4015 4016 4017@ 4018@<Glob...@>= 4019#define FONTNAME_BUF_SIZE 128 4020boolean fontfile_found; 4021boolean is_otf_font; 4022char fontname_buf[FONTNAME_BUF_SIZE]; 4023 4024@ 4025@d F_INCLUDED 0x01 4026@d F_SUBSETTED 0x02 4027@d F_TRUETYPE 0x04 4028@d F_BASEFONT 0x08 4029 4030@d set_included(fm) ((fm)->type |= F_INCLUDED) 4031@d set_subsetted(fm) ((fm)->type |= F_SUBSETTED) 4032@d set_truetype(fm) ((fm)->type |= F_TRUETYPE) 4033@d set_basefont(fm) ((fm)->type |= F_BASEFONT) 4034 4035@d is_included(fm) ((fm)->type & F_INCLUDED) 4036@d is_subsetted(fm) ((fm)->type & F_SUBSETTED) 4037@d is_truetype(fm) ((fm)->type & F_TRUETYPE) 4038@d is_basefont(fm) ((fm)->type & F_BASEFONT) 4039@d is_reencoded(fm) ((fm)->encoding != NULL) 4040@d is_fontfile(fm) (fm_fontfile(fm) != NULL) 4041@d is_t1fontfile(fm) (is_fontfile(fm) && !is_truetype(fm)) 4042 4043@d fm_slant(fm) (fm)->slant 4044@d fm_extend(fm) (fm)->extend 4045@d fm_fontfile(fm) (fm)->ff_name 4046 4047@<Declarations@>= 4048static boolean mp_font_is_reencoded (MP mp, font_number f); 4049static boolean mp_font_is_included (MP mp, font_number f); 4050static boolean mp_font_is_subsetted (MP mp, font_number f); 4051 4052@ @c 4053boolean mp_font_is_reencoded (MP mp, font_number f) { 4054 fm_entry *fm; 4055 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) { 4056 if (fm != NULL 4057 && (fm->ps_name != NULL) 4058 && is_reencoded (fm)) 4059 return true; 4060 } 4061 return false; 4062} 4063boolean mp_font_is_included (MP mp, font_number f) { 4064 fm_entry *fm; 4065 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) { 4066 if (fm != NULL 4067 && (fm->ps_name != NULL && fm->ff_name != NULL) 4068 && is_included (fm)) 4069 return true; 4070 } 4071 return false; 4072} 4073boolean mp_font_is_subsetted (MP mp, font_number f) { 4074 fm_entry *fm; 4075 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f,&fm)) { 4076 if (fm != NULL 4077 && (fm->ps_name != NULL && fm->ff_name != NULL) 4078 && is_included (fm) && is_subsetted (fm)) 4079 return true; 4080 } 4081 return false; 4082} 4083 4084@ @<Declarations@>= 4085static char * mp_fm_encoding_name (MP mp, font_number f); 4086static char * mp_fm_font_name (MP mp, font_number f); 4087static char * mp_fm_font_subset_name (MP mp, font_number f); 4088 4089@ 4090@c char * mp_fm_encoding_name (MP mp, font_number f) { 4091 enc_entry *e; 4092 fm_entry *fm; 4093 if (mp_has_fm_entry (mp, f, &fm)) { 4094 if (fm != NULL && (fm->ps_name != NULL)) { 4095 if (is_reencoded (fm)) { 4096 e = fm->encoding; 4097 if (e->enc_name!=NULL) 4098 return mp_xstrdup(mp,e->enc_name); 4099 } else { 4100 return NULL; 4101 } 4102 } 4103 } 4104 { 4105 char msg[256]; 4106 mp_snprintf (msg, 256, "fontmap encoding problems for font %s", mp->font_name[f]); 4107 mp_error(mp, msg, NULL, true); 4108 } 4109 return NULL; 4110} 4111char * mp_fm_font_name (MP mp, font_number f) { 4112 fm_entry *fm; 4113 if (mp_has_fm_entry (mp, f,&fm)) { 4114 if (fm != NULL && (fm->ps_name != NULL)) { 4115 if (mp_font_is_included(mp, f) && !mp->font_ps_name_fixed[f]) { 4116 /* find the real fontname, and update |ps_name| and |subset_tag| if needed */ 4117 if (t1_updatefm(mp,f,fm)) { 4118 mp->font_ps_name_fixed[f] = true; 4119 } else { 4120 char msg[256]; 4121 mp_snprintf (msg, 256, "font loading problems for font %s", mp->font_name[f]); 4122 mp_error(mp, msg, NULL, true); 4123 } 4124 } 4125 return mp_xstrdup(mp,fm->ps_name); 4126 } 4127 } 4128 { 4129 char msg[256]; 4130 mp_snprintf (msg, 256, "fontmap name problems for font %s", mp->font_name[f]); 4131 mp_error(mp, msg, NULL, true); 4132 } 4133 return NULL; 4134} 4135 4136static char * mp_fm_font_subset_name (MP mp, font_number f) { 4137 fm_entry *fm; 4138 if (mp_has_fm_entry (mp, f, &fm)) { 4139 if (fm != NULL && (fm->ps_name != NULL)) { 4140 if (is_subsetted(fm)) { 4141 char *s = mp_xmalloc(mp,strlen(fm->ps_name)+8,1); 4142 mp_snprintf(s,(int)strlen(fm->ps_name)+8,"%s-%s",fm->subset_tag,fm->ps_name); 4143 return s; 4144 } else { 4145 return mp_xstrdup(mp,fm->ps_name); 4146 } 4147 } 4148 } 4149 { 4150 char msg[256]; 4151 mp_snprintf (msg, 256, "fontmap name problems for font %s", mp->font_name[f]); 4152 mp_error(mp, msg, NULL, true); 4153 } 4154 return NULL; 4155} 4156 4157@ @<Declarations@>= 4158static integer mp_fm_font_slant (MP mp, font_number f); 4159static integer mp_fm_font_extend (MP mp, font_number f); 4160 4161@ 4162@c static integer mp_fm_font_slant (MP mp, font_number f) { 4163 fm_entry *fm; 4164 if (mp_has_fm_entry (mp, f, &fm)) { 4165 if (fm != NULL && (fm->ps_name != NULL)) { 4166 return fm->slant; 4167 } 4168 } 4169 return 0; 4170} 4171static integer mp_fm_font_extend (MP mp, font_number f) { 4172 fm_entry *fm; 4173 if (mp_has_fm_entry (mp, f, &fm)) { 4174 if (fm != NULL && (fm->ps_name != NULL)) { 4175 return fm->extend; 4176 } 4177 } 4178 return 0; 4179} 4180 4181@ @<Declarations@>= 4182static boolean mp_do_ps_font (MP mp, font_number f); 4183 4184@ @c static boolean mp_do_ps_font (MP mp, font_number f) { 4185 fm_entry *fm_cur; 4186 (void)mp_has_fm_entry (mp, f, &fm_cur); /* for side effects */ 4187 if (fm_cur == NULL) 4188 return true; 4189 if (is_truetype(fm_cur) || 4190 (fm_cur->ps_name == NULL && fm_cur->ff_name == NULL)) { 4191 return false; 4192 } 4193 if (is_included(fm_cur)) { 4194 mp_ps_print_nl(mp,"%%BeginResource: font "); 4195 if (is_subsetted(fm_cur)) { 4196 mp_ps_print(mp, fm_cur->subset_tag); 4197 mp_ps_print_char(mp,'-'); 4198 } 4199 mp_ps_print(mp, fm_cur->ps_name); 4200 mp_ps_print_ln(mp); 4201 writet1 (mp,f,fm_cur); 4202 mp_ps_print_nl(mp,"%%EndResource"); 4203 mp_ps_print_ln(mp); 4204 } 4205 return true; 4206} 4207 4208@ Included subset fonts do not need and encoding vector, make 4209sure we skip that case. 4210 4211@<Declarations@>= 4212static void mp_list_used_resources (MP mp, int prologues, int procset); 4213 4214@ @c static void mp_list_used_resources (MP mp, int prologues, int procset) { 4215 font_number f; /* fonts used in a text node or as loop counters */ 4216 int ff; /* a loop counter */ 4217 int ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */ 4218 boolean firstitem; 4219 if ( procset>0 ) 4220 mp_ps_print_nl(mp, "%%DocumentResources: procset mpost"); 4221 else 4222 mp_ps_print_nl(mp, "%%DocumentResources: procset mpost-minimal"); 4223 ldf=null_font; 4224 firstitem=true; 4225 for (f=null_font+1;f<=mp->last_fnum;f++) { 4226 if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) { 4227 for (ff=ldf;ff>=null_font;ff--) { 4228 if ( mp_has_font_size(mp,(font_number)ff) ) 4229 if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 ) 4230 goto FOUND; 4231 } 4232 if ( mp_font_is_subsetted(mp,f) ) 4233 goto FOUND; 4234 if ( (size_t)mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])> 4235 (size_t)mp->max_print_line ) 4236 mp_ps_print_nl(mp, "%%+ encoding"); 4237 if ( firstitem ) { 4238 firstitem=false; 4239 mp_ps_print_nl(mp, "%%+ encoding"); 4240 } 4241 mp_ps_print_char(mp, ' '); 4242 mp_ps_dsc_print(mp, "encoding", mp->font_enc_name[f]); 4243 ldf=(int)f; 4244 } 4245 FOUND: 4246 ; 4247 } 4248 ldf=null_font; 4249 firstitem=true; 4250 for (f=null_font+1;f<=mp->last_fnum;f++) { 4251 if ( mp_has_font_size(mp,f) ) { 4252 for (ff=ldf;ff>=null_font;ff--) { 4253 if ( mp_has_font_size(mp,(font_number)ff) ) 4254 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 ) 4255 goto FOUND2; 4256 } 4257 if ( (size_t)mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])> 4258 (size_t)mp->max_print_line ) 4259 mp_ps_print_nl(mp, "%%+ font"); 4260 if ( firstitem ) { 4261 firstitem=false; 4262 mp_ps_print_nl(mp, "%%+ font"); 4263 } 4264 mp_ps_print_char(mp, ' '); 4265 if ( (prologues==3)&& (mp_font_is_subsetted(mp,f)) ) { 4266 char *s = mp_fm_font_subset_name(mp,f); 4267 mp_ps_dsc_print(mp, "font", s); 4268 mp_xfree(s); 4269 } else { 4270 mp_ps_dsc_print(mp, "font", mp->font_ps_name[f]); 4271 } 4272 ldf=(int)f; 4273 } 4274 FOUND2: 4275 ; 4276 } 4277 mp_ps_print_ln(mp); 4278} 4279 4280@ @<Declarations@>= 4281static void mp_list_supplied_resources (MP mp, int prologues, int procset); 4282 4283@ @c static void mp_list_supplied_resources (MP mp, int prologues, int procset) { 4284 font_number f; /* fonts used in a text node or as loop counters */ 4285 int ff; /* a loop counter */ 4286 int ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */ 4287 boolean firstitem; 4288 if ( procset>0 ) 4289 mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost"); 4290 else 4291 mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost-minimal"); 4292 ldf=null_font; 4293 firstitem=true; 4294 for (f=null_font+1;f<=mp->last_fnum;f++) { 4295 if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) { 4296 for (ff=ldf;ff>= null_font;ff++) { 4297 if ( mp_has_font_size(mp,(font_number)ff) ) 4298 if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 ) 4299 goto FOUND; 4300 } 4301 if ( (prologues==3)&&(mp_font_is_subsetted(mp,f))) 4302 goto FOUND; 4303 if ( (size_t)mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>(size_t)mp->max_print_line ) 4304 mp_ps_print_nl(mp, "%%+ encoding"); 4305 if ( firstitem ) { 4306 firstitem=false; 4307 mp_ps_print_nl(mp, "%%+ encoding"); 4308 } 4309 mp_ps_print_char(mp, ' '); 4310 mp_ps_dsc_print(mp, "encoding", mp->font_enc_name[f]); 4311 ldf=(int)f; 4312 } 4313 FOUND: 4314 ; 4315 } 4316 ldf=null_font; 4317 firstitem=true; 4318 if (prologues==3) { 4319 for (f=null_font+1;f<=mp->last_fnum;f++) { 4320 if ( mp_has_font_size(mp,f) ) { 4321 for (ff=ldf;ff>= null_font;ff--) { 4322 if ( mp_has_font_size(mp,(font_number)ff) ) 4323 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 ) 4324 goto FOUND2; 4325 } 4326 if ( ! mp_font_is_included(mp,f) ) 4327 goto FOUND2; 4328 if ( (size_t)mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(size_t)mp->max_print_line ) 4329 mp_ps_print_nl(mp, "%%+ font"); 4330 if ( firstitem ) { 4331 firstitem=false; 4332 mp_ps_print_nl(mp, "%%+ font"); 4333 } 4334 mp_ps_print_char(mp, ' '); 4335 if ( mp_font_is_subsetted(mp,f) ) { 4336 char *s = mp_fm_font_subset_name(mp,f); 4337 mp_ps_dsc_print(mp, "font", s); 4338 mp_xfree(s); 4339 } else { 4340 mp_ps_dsc_print(mp, "font", mp->font_ps_name[f]); 4341 } 4342 ldf=(int)f; 4343 } 4344 FOUND2: 4345 ; 4346 } 4347 mp_ps_print_ln(mp); 4348 } 4349} 4350 4351@ @<Declarations...@>= 4352static void mp_list_needed_resources (MP mp, int prologues); 4353 4354@ @c static void mp_list_needed_resources (MP mp, int prologues) { 4355 font_number f; /* fonts used in a text node or as loop counters */ 4356 int ff; /* a loop counter */ 4357 int ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */ 4358 boolean firstitem; 4359 ldf=null_font; 4360 firstitem=true; 4361 for (f=null_font+1;f<=mp->last_fnum;f++ ) { 4362 if ( mp_has_font_size(mp,f)) { 4363 for (ff=ldf;ff>=null_font;ff--) { 4364 if ( mp_has_font_size(mp,(font_number)ff) ) 4365 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 ) 4366 goto FOUND; 4367 }; 4368 if ((prologues==3)&&(mp_font_is_included(mp,f)) ) 4369 goto FOUND; 4370 if ( (size_t)mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(size_t)mp->max_print_line ) 4371 mp_ps_print_nl(mp, "%%+ font"); 4372 if ( firstitem ) { 4373 firstitem=false; 4374 mp_ps_print_nl(mp, "%%DocumentNeededResources: font"); 4375 } 4376 mp_ps_print_char(mp, ' '); 4377 mp_ps_dsc_print(mp, "font", mp->font_ps_name[f]); 4378 ldf=(int)f; 4379 } 4380 FOUND: 4381 ; 4382 } 4383 if ( ! firstitem ) { 4384 mp_ps_print_ln(mp); 4385 ldf=null_font; 4386 /* clang: never read: firstitem=true; */ 4387 for (f=null_font+1;f<= mp->last_fnum;f++) { 4388 if ( mp_has_font_size(mp,f) ) { 4389 for (ff=ldf;ff>=null_font;ff-- ) { 4390 if ( mp_has_font_size(mp,(font_number)ff) ) 4391 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 ) 4392 goto FOUND2; 4393 } 4394 if ((prologues==3)&&(mp_font_is_included(mp,f)) ) 4395 goto FOUND2; 4396 mp_ps_print(mp, "%%IncludeResource: font "); 4397 mp_ps_print(mp, mp->font_ps_name[f]); 4398 mp_ps_print_ln(mp); 4399 ldf=(int)f; 4400 } 4401 FOUND2: 4402 ; 4403 } 4404 } 4405} 4406 4407@ @<Declarations@>= 4408static void mp_write_font_definition (MP mp, font_number f, int prologues); 4409 4410@ 4411 4412@d applied_reencoding(A) ((mp_font_is_reencoded(mp,(A)))&& 4413 ((! mp_font_is_subsetted(mp,(A)))||(prologues==2))) 4414 4415@c static void mp_write_font_definition(MP mp, font_number f, int prologues) { 4416 if ( (applied_reencoding(f))||(mp_fm_font_slant(mp,f)!=0)|| 4417 (mp_fm_font_extend(mp,f)!=0)|| 4418 (mp_xstrcmp(mp->font_name[f],"psyrgo")==0)|| 4419 (mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0) ) { 4420 if ( (mp_font_is_subsetted(mp,f))&& 4421 (mp_font_is_included(mp,f))&&(prologues==3)) { 4422 char *s = mp_fm_font_subset_name(mp,f); 4423 mp_ps_name_out(mp, s,true); 4424 mp_xfree(s); 4425 } else { 4426 mp_ps_name_out(mp, mp->font_ps_name[f],true); 4427 } 4428 mp_ps_print(mp, " fcp"); 4429 mp_ps_print_ln(mp); 4430 if ( applied_reencoding(f) ) { 4431 mp_ps_print(mp, "/Encoding "); 4432 mp_ps_print(mp, mp->font_enc_name[f]); 4433 mp_ps_print(mp, " def "); 4434 }; 4435 if ( mp_fm_font_slant(mp,f)!=0 ) { 4436 mp_ps_print_int(mp, mp_fm_font_slant(mp,f)); 4437 mp_ps_print(mp, " SlantFont "); 4438 }; 4439 if ( mp_fm_font_extend(mp,f)!=0 ) { 4440 mp_ps_print_int(mp, mp_fm_font_extend(mp,f)); 4441 mp_ps_print(mp, " ExtendFont "); 4442 }; 4443 if ( mp_xstrcmp(mp->font_name[f],"psyrgo")==0 ) { 4444 mp_ps_print(mp, " 890 ScaleFont "); 4445 mp_ps_print(mp, " 277 SlantFont "); 4446 }; 4447 if ( mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0 ) { 4448 mp_ps_print(mp, " FontMatrix [-1 0 0 1 0 0] matrix concatmatrix /FontMatrix exch def "); 4449 mp_ps_print(mp, "/Metrics 2 dict dup begin "); 4450 mp_ps_print(mp, "/space[0 -278]def "); 4451 mp_ps_print(mp, "/a12[-904 -939]def "); 4452 mp_ps_print(mp, "end def "); 4453 }; 4454 mp_ps_print(mp, "currentdict end"); 4455 mp_ps_print_ln(mp); 4456 mp_ps_print_defined_name(mp,f,prologues); 4457 mp_ps_print(mp, " exch definefont pop"); 4458 mp_ps_print_ln(mp); 4459 } 4460} 4461 4462@ @<Declarations@>= 4463static void mp_ps_print_defined_name (MP mp, font_number f, int prologues); 4464 4465@ 4466@c static void mp_ps_print_defined_name(MP mp, font_number f, int prologues) { 4467 mp_ps_print(mp, " /"); 4468 if ((mp_font_is_subsetted(mp,f))&& 4469 (mp_font_is_included(mp,f))&&(prologues==3)) { 4470 char *s = mp_fm_font_subset_name(mp,f); 4471 mp_ps_print(mp, s); 4472 mp_xfree(s); 4473 } else { 4474 mp_ps_print(mp, mp->font_ps_name[f]); 4475 } 4476 if ( mp_xstrcmp(mp->font_name[f],"psyrgo")==0 ) 4477 mp_ps_print(mp, "-Slanted"); 4478 if ( mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0 ) 4479 mp_ps_print(mp, "-Reverse"); 4480 if ( applied_reencoding(f) ) { 4481 mp_ps_print(mp, "-"); 4482 mp_ps_print(mp, mp->font_enc_name[f]); 4483 } 4484 if ( mp_fm_font_slant(mp,f)!=0 ) { 4485 mp_ps_print(mp, "-Slant_"); mp_ps_print_int(mp, mp_fm_font_slant(mp,f)) ; 4486 } 4487 if ( mp_fm_font_extend(mp,f)!=0 ) { 4488 mp_ps_print(mp, "-Extend_"); mp_ps_print_int(mp, mp_fm_font_extend(mp,f)); 4489 } 4490} 4491 4492@ @<Include encodings and fonts for edge structure~|h|@>= 4493mp_font_encodings(mp,mp->last_fnum,(prologues==2)); 4494@<Embed fonts that are available@> 4495 4496@ @<Embed fonts that are available@>= 4497{ 4498next_size=0; 4499@<Make |cur_fsize| a copy of the |font_sizes| array@>; 4500do { 4501 done_fonts=true; 4502 for (f=null_font+1;f<=mp->last_fnum;f++) { 4503 if ( cur_fsize[f]!=null ) { 4504 if (prologues==3 ) { 4505 if ( ! mp_do_ps_font(mp,f) ) { 4506 if ( mp_has_fm_entry(mp,f, NULL) ) { 4507 mp_error(mp, "Font embedding failed", NULL, true); 4508 } 4509 } 4510 } 4511 if (cur_fsize[f]==mp_void) 4512 cur_fsize[f]=null; 4513 else 4514 cur_fsize[f]=mp_link(cur_fsize[f]); 4515 if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; } 4516 } 4517 } 4518 if ( ! done_fonts ) 4519 @<Increment |next_size| and apply |mark_string_chars| to all text nodes with 4520 that size index@>; 4521} while (! done_fonts); 4522} 4523 4524@ @<Increment |next_size| and apply |mark_string_chars| to all text nodes...@>= 4525{ 4526 next_size++; 4527 mp_apply_mark_string_chars(mp, h, next_size); 4528} 4529 4530@ We also need to keep track of which characters are used in text nodes 4531in the edge structure that is being shipped out. This is done by procedures 4532that use the left-over |b3| field in the |char_info| words; i.e., 4533|char_info(f)(c).b3| gives the status of character |c| in font |f|. 4534 4535@<Types...@>= 4536enum mp_char_mark_state {mp_unused=0, mp_used}; 4537 4538@ @<Declarations@>= 4539static void mp_mark_string_chars (MP mp,font_number f, char *s, size_t l) ; 4540 4541@ @c 4542void mp_mark_string_chars (MP mp,font_number f, char *s, size_t l) { 4543 integer b; /* |char_base[f]| */ 4544 int bc,ec; /* only characters between these bounds are marked */ 4545 unsigned char *k; /* an index into string |s| */ 4546 b=mp->char_base[f]; 4547 bc=(int)mp->font_bc[f]; 4548 ec=(int)mp->font_ec[f]; 4549 k=(unsigned char *)s; 4550 while (l-->0){ 4551 if ( (*k>=bc)&&(*k<=ec) ) 4552 mp->font_info[b+*k].qqqq.b3=mp_used; 4553 k++; 4554 } 4555} 4556 4557 4558@ @<Declarations@>= 4559static void mp_unmark_font (MP mp,font_number f) ; 4560 4561@ @c 4562void mp_unmark_font (MP mp,font_number f) { 4563 int k; /* an index into |font_info| */ 4564 for (k= mp->char_base[f]+mp->font_bc[f]; 4565 k<=mp->char_base[f]+mp->font_ec[f]; 4566 k++) 4567 mp->font_info[k].qqqq.b3=mp_unused; 4568} 4569 4570 4571@ @<Declarations@>= 4572static void mp_print_improved_prologue (MP mp, mp_edge_object *h, int p1, int procset) ; 4573 4574@ @c 4575void mp_print_improved_prologue (MP mp, mp_edge_object *h, int prologues, int procset) { 4576 quarterword next_size; /* the size index for fonts being listed */ 4577 mp_node *cur_fsize; /* current positions in |font_sizes| */ 4578 boolean done_fonts; /* have we finished listing the fonts in the header? */ 4579 font_number f; /* a font number for loops */ 4580 cur_fsize = mp_xmalloc(mp,(size_t)(mp->font_max+1),sizeof(mp_node)); 4581 mp_list_used_resources(mp, prologues, procset); 4582 mp_list_supplied_resources(mp, prologues, procset); 4583 mp_list_needed_resources(mp, prologues); 4584 mp_ps_print_nl(mp, "%%EndComments"); 4585 mp_ps_print_nl(mp, "%%BeginProlog"); 4586 if ( procset>0 ) 4587 mp_ps_print_nl(mp, "%%BeginResource: procset mpost"); 4588 else 4589 mp_ps_print_nl(mp, "%%BeginResource: procset mpost-minimal"); 4590 mp_ps_print_nl(mp, "/bd{bind def}bind def" 4591 "/fshow {exch findfont exch scalefont setfont show}bd"); 4592 if ( procset>0 ) @<Print the procset@>; 4593 mp_ps_print_nl(mp, "/fcp{findfont dup length dict begin" 4594 "{1 index/FID ne{def}{pop pop}ifelse}forall}bd"); 4595 mp_ps_print_nl(mp, "/fmc{FontMatrix dup length array copy dup dup}bd" 4596 "/fmd{/FontMatrix exch def}bd"); 4597 mp_ps_print_nl(mp, "/Amul{4 -1 roll exch mul 1000 div}bd" 4598 "/ExtendFont{fmc 0 get Amul 0 exch put fmd}bd"); 4599 mp_ps_print_nl(mp, "/ScaleFont{dup fmc 0 get" 4600 " Amul 0 exch put dup dup 3 get Amul 3 exch put fmd}bd"); 4601 mp_ps_print_nl(mp, "/SlantFont{fmc 2 get dup 0 eq{pop 1}if" 4602 " Amul FontMatrix 0 get mul 2 exch put fmd}bd"); 4603 mp_ps_print_nl(mp, "%%EndResource"); 4604 @<Include encodings and fonts for edge structure~|h|@>; 4605 mp_ps_print_nl(mp, "%%EndProlog"); 4606 mp_ps_print_nl(mp, "%%BeginSetup"); 4607 mp_ps_print_ln(mp); 4608 for (f=null_font+1;f<=mp->last_fnum;f++) { 4609 if ( mp_has_font_size(mp,f) ) { 4610 if ( mp_has_fm_entry(mp,f,NULL) ) { 4611 mp_write_font_definition(mp,f, prologues); 4612 mp_ps_name_out(mp, mp->font_name[f],true); 4613 mp_ps_print_defined_name(mp,f, prologues); 4614 mp_ps_print(mp, " def"); 4615 } else { 4616 char s[256]; 4617 mp_snprintf(s,256,"font %s cannot be found in any fontmapfile!", mp->font_name[f]); 4618 mp_warn(mp,s); 4619 mp_ps_name_out(mp, mp->font_name[f],true); 4620 mp_ps_name_out(mp, mp->font_name[f],true); 4621 mp_ps_print(mp, " def"); 4622 } 4623 mp_ps_print_ln(mp); 4624 } 4625 } 4626 mp_ps_print_nl(mp, "%%EndSetup"); 4627 mp_ps_print_nl(mp, "%%Page: 1 1"); 4628 mp_ps_print_ln(mp); 4629 mp_xfree(cur_fsize); 4630} 4631 4632@ @<Declarations@>= 4633static font_number mp_print_font_comments (MP mp , mp_edge_object *h, int prologues); 4634 4635 4636@ 4637@c 4638static font_number mp_print_font_comments (MP mp , mp_edge_object *h, int prologues) { 4639 quarterword next_size; /* the size index for fonts being listed */ 4640 mp_node *cur_fsize; /* current positions in |font_sizes| */ 4641 int ff; /* a loop counter */ 4642 boolean done_fonts; /* have we finished listing the fonts in the header? */ 4643 font_number f; /* a font number for loops */ 4644 int ds; /* design size and scale factor for a text node, scaled */ 4645 int ldf=0; /* the last \.{DocumentFont} listed (otherwise |null_font|) */ 4646 cur_fsize = mp_xmalloc(mp,(size_t)(mp->font_max+1),sizeof(mp_node)); 4647 if ( prologues>0 ) { 4648 @<Give a \.{DocumentFonts} comment listing all fonts with non-null 4649 |font_sizes| and eliminate duplicates@>; 4650 } else { 4651 next_size=0; 4652 @<Make |cur_fsize| a copy of the |font_sizes| array@>; 4653 do { done_fonts=true; 4654 for (f=null_font+1;f<=mp->last_fnum;f++) { 4655 if ( cur_fsize[f]!=null ) { 4656 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>; 4657 } 4658 if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; }; 4659 } 4660 if ( ! done_fonts ) { 4661 @<Increment |next_size| and apply |mark_string_chars| to all text nodes with 4662 that size index@>; 4663 } 4664 } while (! done_fonts); 4665 } 4666 mp_xfree(cur_fsize); 4667 return (font_number)ldf; 4668} 4669 4670@ @<Make |cur_fsize| a copy of the |font_sizes| array@>= 4671for (f=null_font+1;f<= mp->last_fnum;f++) 4672 cur_fsize[f]=mp->font_sizes[f] 4673 4674@ It's not a good idea to make any assumptions about the |font_ps_name| entries, 4675so we carefully remove duplicates. There is no harm in using a slow, brute-force 4676search. 4677 4678@<Give a \.{DocumentFonts} comment listing all fonts with non-null...@>= 4679{ 4680 ldf=null_font; 4681 for (f=null_font+1;f<= mp->last_fnum;f++) { 4682 if ( mp->font_sizes[f]!=null ) { 4683 if ( ldf==null_font ) 4684 mp_ps_print_nl(mp, "%%DocumentFonts:"); 4685 for (ff=ldf;ff>=null_font;ff--) { 4686 if ( mp->font_sizes[ff]!=null ) 4687 if ( mp_xstrcmp(mp->font_ps_name[f],mp->font_ps_name[ff])==0 ) 4688 goto FOUND; 4689 } 4690 if ( (size_t)mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(size_t)mp->max_print_line ) 4691 mp_ps_print_nl(mp, "%%+"); 4692 mp_ps_print_char(mp, ' '); 4693 mp_ps_print(mp, mp->font_ps_name[f]); 4694 ldf=(int)f; 4695 FOUND: 4696 ; 4697 } 4698 } 4699} 4700 4701@ @c 4702static void mp_hex_digit_out (MP mp,quarterword d) { 4703 if ( d<10 ) mp_ps_print_char(mp, d+'0'); 4704 else mp_ps_print_char(mp, d+'a'-10); 4705} 4706 4707@ We output the marks as a hexadecimal bit string starting at 4708|font_bc[f]|. 4709 4710@<Declarations@>= 4711static halfword mp_ps_marks_out (MP mp,font_number f); 4712 4713@ 4714 4715@c 4716static halfword mp_ps_marks_out (MP mp,font_number f) { 4717 eight_bits bc,ec; /* only encode characters between these bounds */ 4718 int p; /* |font_info| index for the current character */ 4719 int d; /* used to construct a hexadecimal digit */ 4720 unsigned b; /* used to construct a hexadecimal digit */ 4721 bc=mp->font_bc[f]; 4722 ec=mp->font_ec[f]; 4723 @<Restrict the range |bc..ec| so that it contains no unused characters 4724 at either end@>; 4725 @<Print the initial label indicating that the bitmap starts at |bc|@>; 4726 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>; 4727 while ( (ec<mp->font_ec[f])&&(mp->font_info[p].qqqq.b3==mp_unused) ) { 4728 p++; ec++; 4729 } 4730 return (ec+1); 4731} 4732 4733@ We could save time by setting the return value before the loop that 4734decrements |ec|, but there is no point in being so tricky. 4735 4736@<Restrict the range |bc..ec| so that it contains no unused characters...@>= 4737p=mp->char_base[f]+bc; 4738while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) { 4739 p++; bc++; 4740} 4741p=mp->char_base[f]+ec; 4742while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) { 4743 p--; ec--; 4744} 4745 4746@ @<Print the initial label indicating that the bitmap starts at |bc|@>= 4747mp_ps_print_char(mp, ' '); 4748mp_hex_digit_out(mp, (quarterword)(bc / 16)); 4749mp_hex_digit_out(mp, (quarterword)(bc % 16)); 4750mp_ps_print_char(mp, ':') 4751 4752@ 4753 4754@<Print a hexadecimal encoding of the marks for characters |bc..ec|@>= 4755b=8; d=0; 4756for (p=mp->char_base[f]+bc;p<=mp->char_base[f]+ec;p++) { 4757 if ( b==0 ) { 4758 mp_hex_digit_out(mp, (quarterword)d); 4759 d=0; b=8; 4760 } 4761 if ( mp->font_info[p].qqqq.b3!=mp_unused ) 4762 d+=(int)b; 4763 b=b>>1; 4764} 4765mp_hex_digit_out(mp, (quarterword)d) 4766 4767 4768@ Here is a simple function that determines whether there are any marked 4769characters in font~|f|. 4770 4771@<Declarations@>= 4772static boolean mp_check_ps_marks (MP mp,font_number f) ; 4773 4774@ @c 4775static boolean mp_check_ps_marks (MP mp,font_number f) { 4776 int p; /* |font_info| index for the current character */ 4777 for (p=mp->char_base[f];p<=mp->char_base[f]+mp->font_ec[f];p++) { 4778 if ( mp->font_info[p].qqqq.b3==mp_used ) 4779 return true; 4780 } 4781 return false; 4782} 4783 4784 4785@ There used to be a check against |emergency_line_length| here, because 4786it was believed that processing programs might not know how to deal with 4787long lines. Nowadays (1.204), we trust backends to do the right thing. 4788 4789@d mp_link(A) (A)->link /* the |link| field of a memory word */ 4790@d sc_factor(A) ((mp_font_size_node)(A))->sc_factor_ /* the scale factor stored in a font size node */ 4791 4792@<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>= 4793{ 4794 if ( mp_check_ps_marks(mp, f ) ) { 4795 double dds; 4796 mp_ps_print_nl(mp, "%*Font: "); 4797 mp_ps_print(mp, mp->font_name[f]); 4798 mp_ps_print_char(mp, ' '); 4799 ds=(mp->font_dsize[f] + 8) / 16.0; 4800 dds = (double)ds / 65536.0; 4801 mp_ps_print_double(mp, mp_take_double(mp, dds, sc_factor(cur_fsize[f]))); 4802 mp_ps_print_char(mp, ' '); 4803 mp_ps_print_double(mp, dds); 4804 mp_ps_marks_out(mp, f ); 4805 } 4806 cur_fsize[f]=mp_link(cur_fsize[f]); 4807} 4808 4809@ @<Print the procset@>= 4810{ 4811 mp_ps_print_nl(mp, "/hlw{0 dtransform exch truncate exch idtransform pop setlinewidth}bd"); 4812 mp_ps_print_nl(mp, "/vlw{0 exch dtransform truncate idtransform setlinewidth pop}bd"); 4813 mp_ps_print_nl(mp, "/l{lineto}bd/r{rlineto}bd/c{curveto}bd/m{moveto}bd" 4814 "/p{closepath}bd/n{newpath}bd"); 4815 mp_ps_print_nl(mp, "/C{setcmykcolor}bd/G{setgray}bd/R{setrgbcolor}bd" 4816 "/lj{setlinejoin}bd/ml{setmiterlimit}bd"); 4817 mp_ps_print_nl(mp, "/lc{setlinecap}bd/S{stroke}bd/F{fill}bd/q{gsave}bd" 4818 "/Q{grestore}bd/s{scale}bd/t{concat}bd"); 4819 mp_ps_print_nl(mp, "/sd{setdash}bd/rd{[] 0 setdash}bd/P{showpage}bd/B{q F Q}bd/W{clip}bd"); 4820} 4821 4822 4823@ The prologue defines \.{fshow} and corrects for the fact that \.{fshow} 4824arguments use |font_name| instead of |font_ps_name|. Downloaded bitmap fonts 4825might not have reasonable |font_ps_name| entries, but we just charge ahead 4826anyway. The user should not make \&{prologues} positive if this will cause 4827trouble. 4828@:prologues_}{\&{prologues} primitive@> 4829 4830@<Declarations@>= 4831static void mp_print_prologue (MP mp, mp_edge_object *h, int prologues, int procset); 4832 4833@ @c 4834void mp_print_prologue (MP mp, mp_edge_object *h, int prologues, int procset) { 4835 font_number f; 4836 font_number ldf ; 4837 ldf = mp_print_font_comments (mp, h, prologues); 4838 mp_ps_print_ln(mp); 4839 if ( (prologues==1) && (mp->last_ps_fnum==0) ) 4840 mp_read_psname_table(mp); 4841 mp_ps_print(mp, "%%BeginProlog"); mp_ps_print_ln(mp); 4842 if ( (prologues>0)||(procset>0) ) { 4843 if ( ldf!=null_font ) { 4844 if ( prologues>0 ) { 4845 for (f=null_font+1;f<=mp->last_fnum;f++) { 4846 if ( mp_has_font_size(mp,f) ) { 4847 mp_ps_name_out(mp, mp->font_name[f],true); 4848 mp_ps_name_out(mp, mp->font_ps_name[f],true); 4849 mp_ps_print(mp, " def"); 4850 mp_ps_print_ln(mp); 4851 } 4852 } 4853 if ( procset==0 ) { 4854 mp_ps_print(mp, "/fshow {exch findfont exch scalefont setfont show}bind def"); 4855 mp_ps_print_ln(mp); 4856 } 4857 } 4858 } 4859 if (procset>0 ) { 4860 mp_ps_print_nl(mp, "%%BeginResource: procset mpost"); 4861 if ( (prologues>0)&&(ldf!=null_font) ) 4862 mp_ps_print_nl(mp, 4863 "/bd{bind def}bind def/fshow {exch findfont exch scalefont setfont show}bd"); 4864 else 4865 mp_ps_print_nl(mp, "/bd{bind def}bind def"); 4866 @<Print the procset@>; 4867 mp_ps_print_nl(mp, "%%EndResource"); 4868 mp_ps_print_ln(mp); 4869 } 4870 } 4871 mp_ps_print(mp, "%%EndProlog"); 4872 mp_ps_print_nl(mp, "%%Page: 1 1"); mp_ps_print_ln(mp); 4873} 4874 4875@ \MP\ used to have one single routine to print to both `write' files 4876and the PostScript output. Web2c redefines ``Character |k| cannot be 4877printed'', and that resulted in some bugs where 8-bit characters were 4878written to the PostScript file (reported by Wlodek Bzyl). 4879 4880Also, Hans Hagen requested spaces to be output as "\\040" instead of 4881a plain space, since that makes it easier to parse the result file 4882for postprocessing. 4883 4884@<Character |k| is not allowed in PostScript output@>= 4885 (k<=' ')||(k>'~') 4886 4887@ We often need to print a pair of coordinates. 4888 4889@c 4890void mp_ps_pair_out (MP mp, double x, double y) { 4891 ps_room(26); 4892 mp_ps_print_double(mp, x); mp_ps_print_char(mp, ' '); 4893 mp_ps_print_double(mp, y); mp_ps_print_char(mp, ' '); 4894} 4895 4896@ @<Declarations@>= 4897static void mp_ps_pair_out (MP mp, double x, double y) ; 4898 4899@ @c 4900void mp_ps_print_cmd (MP mp, const char *l, const char *s) { 4901 if ( number_positive (internal_value(mp_procset))) { ps_room(strlen(s)); mp_ps_print(mp,s); } 4902 else { ps_room(strlen(l)); mp_ps_print(mp, l); }; 4903} 4904 4905@ @<Declarations@>= 4906static void mp_ps_print_cmd (MP mp, const char *l, const char *s) ; 4907 4908@ @c 4909void mp_ps_string_out (MP mp, const char *s, size_t l) { 4910 ASCII_code k; /* bits to be converted to octal */ 4911 mp_ps_print(mp, "("); 4912 while (l-->0) { 4913 k=(ASCII_code)*s++; 4914 if ( mp->ps->ps_offset+5>mp->max_print_line ) { 4915 mp_ps_print_char(mp, '\\'); 4916 mp_ps_print_ln(mp); 4917 } 4918 if ( (@<Character |k| is not allowed in PostScript output@>) ) { 4919 mp_ps_print_char(mp, '\\'); 4920 mp_ps_print_char(mp, '0'+(k / 64)); 4921 mp_ps_print_char(mp, '0'+((k / 8) % 8)); 4922 mp_ps_print_char(mp, '0'+(k % 8)); 4923 } else { 4924 if ( (k=='(')||(k==')')||(k=='\\') ) 4925 mp_ps_print_char(mp, '\\'); 4926 mp_ps_print_char(mp, k); 4927 } 4928 } 4929 mp_ps_print_char(mp, ')'); 4930} 4931 4932@ @<Declarations@>= 4933static void mp_ps_string_out (MP mp, const char *s, size_t l) ; 4934 4935@ This is a define because the function does not use its |mp| argument. 4936 4937@d mp_is_ps_name(M,A) mp_do_is_ps_name(A) 4938 4939@c 4940static boolean mp_do_is_ps_name (char *s) { 4941 ASCII_code k; /* the character being checked */ 4942 while ((k=(ASCII_code)*s++)) { 4943 if ( (k<=' ')||(k>'~') ) return false; 4944 if ( (k=='(')||(k==')')||(k=='<')||(k=='>')|| 4945 (k=='{')||(k=='}')||(k=='/')||(k=='%') ) return false; 4946 } 4947 return true; 4948} 4949 4950@ @<Declarations@>= 4951static void mp_ps_name_out (MP mp, char *s, boolean lit) ; 4952 4953@ @c 4954void mp_ps_name_out (MP mp, char *s, boolean lit) { 4955 ps_room(strlen(s)+2); 4956 mp_ps_print_char(mp, ' '); 4957 if ( mp_is_ps_name(mp, s) ) { 4958 if ( lit ) mp_ps_print_char(mp, '/'); 4959 mp_ps_print(mp, s); 4960 } else { 4961 mp_ps_string_out(mp, s, strlen(s)); 4962 if ( ! lit ) mp_ps_print(mp, "cvx "); 4963 mp_ps_print(mp, "cvn"); 4964 } 4965} 4966 4967 4968@ These special comments described in the {\sl PostScript Language Reference 4969Manual}, 2nd.~edition are understood by some \ps-reading programs. 4970We can't normally output ``conforming'' \ps\ because 4971the structuring conventions don't allow us to say ``Please make sure the 4972following characters are downloaded and define the \.{fshow} macro to access 4973them.'' 4974 4975The exact bounding box is written out if |mp_prologues<0|, although this 4976is not standard \ps, since it allows \TeX\ to calculate the box dimensions 4977accurately. (Overfull boxes are avoided if an illustration is made to 4978match a given \.{\char`\\hsize}.) 4979 4980@<Declarations@>= 4981static void mp_print_initial_comment(MP mp,mp_edge_object *hh, int prologues); 4982 4983@ @c 4984void mp_print_initial_comment(MP mp,mp_edge_object *hh, int prologues) { 4985 int t; /* scaled */ 4986 char *s; 4987 mp_ps_print(mp, "%!PS"); 4988 if ( prologues>0 ) 4989 mp_ps_print(mp, "-Adobe-3.0 EPSF-3.0"); 4990 mp_ps_print_nl(mp, "%%BoundingBox: "); 4991 if ( hh->minx>hh->maxx) { 4992 mp_ps_print(mp, "0 0 0 0"); 4993 } else if ( prologues<0 ) { 4994 mp_ps_pair_out(mp, hh->minx,hh->miny); 4995 mp_ps_pair_out(mp, hh->maxx,hh->maxy); 4996 } else { 4997 mp_ps_pair_out(mp, floor(hh->minx),floor(hh->miny)); 4998 mp_ps_pair_out(mp, -floor(-hh->maxx),-floor(-hh->maxy)); 4999 } 5000 mp_ps_print_nl(mp, "%%HiResBoundingBox: "); 5001 if ( hh->minx>hh->maxx ) { 5002 mp_ps_print(mp, "0 0 0 0"); 5003 } else { 5004 mp_ps_pair_out(mp, hh->minx,hh->miny); 5005 mp_ps_pair_out(mp, hh->maxx,hh->maxy); 5006 } 5007 mp_ps_print_nl(mp, "%%Creator: MetaPost "); 5008 s = mp_metapost_version(); 5009 mp_ps_print(mp, s); 5010 mp_xfree(s); 5011 mp_ps_print_nl(mp, "%%CreationDate: "); 5012 mp_ps_print_int(mp, round_unscaled(internal_value(mp_year))); 5013 mp_ps_print_char(mp, '.'); 5014 mp_ps_print_dd(mp, round_unscaled(internal_value(mp_month))); 5015 mp_ps_print_char(mp, '.'); 5016 mp_ps_print_dd(mp, round_unscaled(internal_value(mp_day))); 5017 mp_ps_print_char(mp, ':'); 5018 t = round_unscaled(internal_value(mp_time)); 5019 mp_ps_print_dd(mp, t / 60); 5020 mp_ps_print_dd(mp, t % 60); 5021 mp_ps_print_nl(mp, "%%Pages: 1"); 5022} 5023 5024@ The most important output procedure is the one that gives the \ps\ version of 5025a \MP\ path. 5026 5027@(mplibps.h@>= 5028#ifndef MPLIBPS_H 5029#define MPLIBPS_H 1 5030#include "mplib.h" 5031@<Internal Postscript header information@> 5032#endif 5033 5034 5035@ @<Types...@>= 5036#define gr_left_type(A) (A)->data.types.left_type 5037#define gr_right_type(A) (A)->data.types.right_type 5038#define gr_x_coord(A) (A)->x_coord 5039#define gr_y_coord(A) (A)->y_coord 5040#define gr_left_x(A) (A)->left_x 5041#define gr_left_y(A) (A)->left_y 5042#define gr_right_x(A) (A)->right_x 5043#define gr_right_y(A) (A)->right_y 5044#define gr_next_knot(A) (A)->next 5045#define gr_originator(A) (A)->originator 5046 5047@ If we want to duplicate a knot node, we can say |copy_knot|: 5048 5049@c 5050static mp_gr_knot mp_gr_copy_knot (MP mp, mp_gr_knot p) { 5051 mp_gr_knot q; /* the copy */ 5052 q = mp_xmalloc(mp, 1, sizeof (struct mp_gr_knot_data)); 5053 memcpy(q,p,sizeof (struct mp_gr_knot_data)); 5054 gr_next_knot(q)=NULL; 5055 return q; 5056} 5057 5058@ The |copy_path| routine makes a clone of a given path. 5059 5060@c 5061static mp_gr_knot mp_gr_copy_path (MP mp, mp_gr_knot p) { 5062 mp_gr_knot q, pp, qq; /* for list manipulation */ 5063 if (p==NULL) 5064 return NULL; 5065 q=mp_gr_copy_knot(mp, p); 5066 qq=q; 5067 pp=gr_next_knot(p); 5068 while ( pp!=p ) { 5069 gr_next_knot(qq)=mp_gr_copy_knot(mp, pp); 5070 qq=gr_next_knot(qq); 5071 pp=gr_next_knot(pp); 5072 } 5073 gr_next_knot(qq)=q; 5074 return q; 5075} 5076 5077@ When a cyclic list of knot nodes is no longer needed, it can be recycled by 5078calling the following subroutine. 5079 5080@<Declarations@>= 5081void mp_do_gr_toss_knot_list (mp_gr_knot p) ; 5082 5083@ 5084@d mp_gr_toss_knot_list(B,A) mp_do_gr_toss_knot_list(A) 5085 5086@c 5087void mp_do_gr_toss_knot_list (mp_gr_knot p) { 5088 mp_gr_knot q; /* the node being freed */ 5089 mp_gr_knot r; /* the next node */ 5090 if (p==NULL) 5091 return; 5092 q=p; 5093 do { 5094 r=gr_next_knot(q); 5095 mp_xfree(q); q=r; 5096 } while (q!=p); 5097} 5098 5099 5100 5101@ @c 5102static void mp_gr_ps_path_out (MP mp, mp_gr_knot h) { 5103 mp_gr_knot p, q; /* for scanning the path */ 5104 double d; /* a temporary value */ 5105 boolean curved; /* |true| unless the cubic is almost straight */ 5106 ps_room(40); 5107 mp_ps_print_cmd(mp, "newpath ","n "); 5108 mp_ps_pair_out(mp, gr_x_coord(h),gr_y_coord(h)); 5109 mp_ps_print_cmd(mp, "moveto","m"); 5110 p=h; 5111 do { 5112 if ( gr_right_type(p)==mp_endpoint ) { 5113 if ( p==h ) mp_ps_print_cmd(mp, " 0 0 rlineto"," 0 0 r"); 5114 return; 5115 } 5116 q=gr_next_knot(p); 5117 @<Start a new line and print the \ps\ commands for the curve from 5118 |p| to~|q|@>; 5119 p=q; 5120 } while (p!=h); 5121 mp_ps_print_cmd(mp, " closepath"," p"); 5122} 5123 5124@ @<Start a new line and print the \ps\ commands for the curve from...@>= 5125curved=true; 5126@<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>; 5127mp_ps_print_ln(mp); 5128if ( curved ){ 5129 mp_ps_pair_out(mp, gr_right_x(p),gr_right_y(p)); 5130 mp_ps_pair_out(mp, gr_left_x(q),gr_left_y(q)); 5131 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q)); 5132 mp_ps_print_cmd(mp, "curveto","c"); 5133} else if ( q!=h ){ 5134 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q)); 5135 mp_ps_print_cmd(mp, "lineto","l"); 5136} 5137 5138@ Two types of straight lines come up often in \MP\ paths: 5139cubics with zero initial and final velocity as created by |make_path| or 5140|make_envelope|, and cubics with control points uniformly spaced on a line 5141as created by |make_choices|. 5142 5143@d bend_tolerance (131/65536.0) /* allow rounding error of $2\cdot10^{-3}$ */ 5144 5145@<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>= 5146if ( gr_right_x(p)==gr_x_coord(p) ) 5147 if ( gr_right_y(p)==gr_y_coord(p) ) 5148 if ( gr_left_x(q)==gr_x_coord(q) ) 5149 if ( gr_left_y(q)==gr_y_coord(q) ) curved=false; 5150d=gr_left_x(q)-gr_right_x(p); 5151if ( fabs(gr_right_x(p)-gr_x_coord(p)-d)<=bend_tolerance ) 5152 if ( fabs(gr_x_coord(q)-gr_left_x(q)-d)<=bend_tolerance ) 5153 { d=gr_left_y(q)-gr_right_y(p); 5154 if ( fabs(gr_right_y(p)-gr_y_coord(p)-d)<=bend_tolerance ) 5155 if ( fabs(gr_y_coord(q)-gr_left_y(q)-d)<=bend_tolerance ) curved=false; 5156 } 5157 5158@ The colored objects use a struct with anonymous fields to express the color parts: 5159 5160@<Internal Postscript header information@>= 5161typedef struct { 5162 double a_val, b_val, c_val, d_val; 5163} mp_color; 5164 5165@ The exported form of a dash pattern is simpler than the internal 5166format, it is closely modelled to the PostScript model. The array of 5167dashes is ended by a single negative value, because this is not 5168allowed in PostScript. 5169 5170@<Internal Postscript header information@>= 5171typedef struct { 5172 double offset; 5173 double *array; 5174} mp_dash_object ; 5175 5176 5177@ 5178@d mp_gr_toss_dashes(A,B) mp_do_gr_toss_dashes(B) 5179 5180@<Declarations@>= 5181static void mp_do_gr_toss_dashes(mp_dash_object *dl); 5182 5183@ @c 5184void mp_do_gr_toss_dashes(mp_dash_object *dl) { 5185 if (dl==NULL) 5186 return; 5187 mp_xfree(dl->array); 5188 mp_xfree(dl); 5189} 5190 5191 5192@ @c 5193static mp_dash_object *mp_gr_copy_dashes(MP mp, mp_dash_object *dl) { 5194 mp_dash_object *q = NULL; 5195 (void)mp; 5196 if (dl==NULL) 5197 return NULL; 5198 q = mp_xmalloc(mp, 1, sizeof (mp_dash_object)); 5199 memcpy (q,dl,sizeof(mp_dash_object)); 5200 if (dl->array != NULL) { 5201 size_t i = 0; 5202 while (*(dl->array+i) != -1) i++; 5203 q->array = mp_xmalloc(mp, i, sizeof (double)); 5204 memcpy(q->array,dl->array, (i*sizeof(double))); 5205 } 5206 return q; 5207} 5208 5209 5210@ Now for outputting the actual graphic objects. First, set up some 5211structures and access macros. 5212 5213@d gr_has_color(A) (gr_type((A))<mp_start_clip_code) 5214 5215@<Types...@>= 5216#define gr_type(A) (A)->type 5217#define gr_link(A) (A)->next 5218#define gr_color_model(A) (A)->color_model 5219#define gr_red_val(A) (A)->color.a_val 5220#define gr_green_val(A) (A)->color.b_val 5221#define gr_blue_val(A) (A)->color.c_val 5222#define gr_cyan_val(A) (A)->color.a_val 5223#define gr_magenta_val(A) (A)->color.b_val 5224#define gr_yellow_val(A) (A)->color.c_val 5225#define gr_black_val(A) (A)->color.d_val 5226#define gr_grey_val(A) (A)->color.a_val 5227#define gr_path_p(A) (A)->path_p 5228#define gr_htap_p(A) ((mp_fill_object *)A)->htap_p 5229#define gr_pen_p(A) (A)->pen_p 5230#define gr_ljoin_val(A) (A)->ljoin 5231#define gr_lcap_val(A) ((mp_stroked_object *)A)->lcap 5232#define gr_miterlim_val(A) (A)->miterlim 5233#define gr_pre_script(A) (A)->pre_script 5234#define gr_post_script(A) (A)->post_script 5235#define gr_dash_p(A) ((mp_stroked_object *)A)->dash_p 5236#define gr_size_index(A) ((mp_text_object *)A)->size_index 5237#define gr_text_p(A) ((mp_text_object *)A)->text_p 5238#define gr_text_l(A) ((mp_text_object *)A)->text_l 5239#define gr_font_n(A) ((mp_text_object *)A)->font_n 5240#define gr_font_name(A) ((mp_text_object *)A)->font_name 5241#define gr_font_dsize(A) ((mp_text_object *)A)->font_dsize 5242#define gr_width_val(A) ((mp_text_object *)A)->width 5243#define gr_height_val(A) ((mp_text_object *)A)->height 5244#define gr_depth_val(A) ((mp_text_object *)A)->depth 5245#define gr_tx_val(A) ((mp_text_object *)A)->tx 5246#define gr_ty_val(A) ((mp_text_object *)A)->ty 5247#define gr_txx_val(A) ((mp_text_object *)A)->txx 5248#define gr_txy_val(A) ((mp_text_object *)A)->txy 5249#define gr_tyx_val(A) ((mp_text_object *)A)->tyx 5250#define gr_tyy_val(A) ((mp_text_object *)A)->tyy 5251 5252@ @<Internal Postscript header information@>= 5253#define GRAPHIC_BODY \ 5254 int type; \ 5255 struct mp_graphic_object * next 5256 5257typedef struct mp_graphic_object { 5258 GRAPHIC_BODY; 5259} mp_graphic_object; 5260 5261typedef struct mp_text_object { 5262 GRAPHIC_BODY; 5263 char *pre_script; 5264 char *post_script; 5265 mp_color color; 5266 unsigned char color_model; 5267 unsigned char size_index; 5268 char *text_p; 5269 size_t text_l; 5270 char *font_name ; 5271 double font_dsize ; 5272 unsigned int font_n ; 5273 double width ; 5274 double height ; 5275 double depth ; 5276 double tx ; 5277 double ty ; 5278 double txx ; 5279 double txy ; 5280 double tyx ; 5281 double tyy ; 5282} mp_text_object; 5283 5284typedef struct mp_fill_object { 5285 GRAPHIC_BODY; 5286 char *pre_script; 5287 char *post_script; 5288 mp_color color; 5289 unsigned char color_model; 5290 unsigned char ljoin ; 5291 mp_gr_knot path_p; 5292 mp_gr_knot htap_p; 5293 mp_gr_knot pen_p; 5294 double miterlim ; 5295} mp_fill_object; 5296 5297typedef struct mp_stroked_object { 5298 GRAPHIC_BODY; 5299 char *pre_script; 5300 char *post_script; 5301 mp_color color; 5302 unsigned char color_model; 5303 unsigned char ljoin ; 5304 unsigned char lcap ; 5305 mp_gr_knot path_p; 5306 mp_gr_knot pen_p; 5307 double miterlim ; 5308 mp_dash_object *dash_p; 5309} mp_stroked_object; 5310 5311typedef struct mp_clip_object { 5312 GRAPHIC_BODY; 5313 mp_gr_knot path_p; 5314} mp_clip_object; 5315 5316typedef struct mp_bounds_object { 5317 GRAPHIC_BODY; 5318 mp_gr_knot path_p; 5319} mp_bounds_object; 5320 5321typedef struct mp_special_object { 5322 GRAPHIC_BODY; 5323 char *pre_script; 5324} mp_special_object ; 5325 5326typedef struct mp_edge_object { 5327 struct mp_graphic_object * body; 5328 struct mp_edge_object * next; 5329 char * filename; 5330 MP parent; 5331 double minx, miny, maxx, maxy; 5332 double width, height, depth, ital_corr; 5333 int charcode; 5334} mp_edge_object; 5335 5336@ @<Exported function headers@>= 5337mp_graphic_object *mp_new_graphic_object(MP mp, int type); 5338 5339@ @c 5340mp_graphic_object *mp_new_graphic_object (MP mp, int type) { 5341 mp_graphic_object *p; 5342 size_t size ; 5343 switch (type) { 5344 case mp_fill_code: size = sizeof(mp_fill_object); break; 5345 case mp_stroked_code: size = sizeof(mp_stroked_object); break; 5346 case mp_text_code: size = sizeof(mp_text_object); break; 5347 case mp_start_clip_code: size = sizeof(mp_clip_object); break; 5348 case mp_start_bounds_code: size = sizeof(mp_bounds_object); break; 5349 case mp_special_code: size = sizeof(mp_special_object); break; 5350 default: size = sizeof(mp_graphic_object); break; 5351 } 5352 p = (mp_graphic_object *)mp_xmalloc(mp,1,size); 5353 memset(p,0,size); 5354 gr_type(p) = type; 5355 return p; 5356} 5357 5358@ We need to keep track of several parameters from the \ps\ graphics state. 5359@^graphics state@> 5360This allows us to be sure that \ps\ has the correct values when they are 5361needed without wasting time and space setting them unnecessarily. 5362 5363@d gs_red mp->ps->gs_state->red_field 5364@d gs_green mp->ps->gs_state->green_field 5365@d gs_blue mp->ps->gs_state->blue_field 5366@d gs_black mp->ps->gs_state->black_field 5367@d gs_colormodel mp->ps->gs_state->colormodel_field 5368@d gs_ljoin mp->ps->gs_state->ljoin_field 5369@d gs_lcap mp->ps->gs_state->lcap_field 5370@d gs_adj_wx mp->ps->gs_state->adj_wx_field 5371@d gs_miterlim mp->ps->gs_state->miterlim_field 5372@d gs_dash_p mp->ps->gs_state->dash_p_field 5373@d gs_dash_init_done mp->ps->gs_state->dash_done_field 5374@d gs_previous mp->ps->gs_state->previous_field 5375@d gs_width mp->ps->gs_state->width_field 5376 5377@<Types...@>= 5378typedef struct _gs_state { 5379 double red_field ; 5380 double green_field ; 5381 double blue_field ; 5382 double black_field ; 5383 /* color from the last \&{setcmykcolor} or \&{setrgbcolor} or \&{setgray} command */ 5384 quarterword colormodel_field ; 5385 /* the current colormodel */ 5386 quarterword ljoin_field ; 5387 quarterword lcap_field ; 5388 /* values from the last \&{setlinejoin} and \&{setlinecap} commands */ 5389 quarterword adj_wx_field ; 5390 /* what resolution-dependent adjustment applies to the width */ 5391 double miterlim_field ; 5392 /* the value from the last \&{setmiterlimit} command */ 5393 mp_dash_object * dash_p_field ; 5394 /* edge structure for last \&{setdash} command */ 5395 boolean dash_done_field ; /* to test for initial \&{setdash} */ 5396 struct _gs_state * previous_field ; 5397 /* backlink to the previous |_gs_state| structure */ 5398 double width_field ; 5399 /* width setting or $-1$ if no \&{setlinewidth} command so far */ 5400} _gs_state; 5401 5402 5403@ @<Glob...@>= 5404struct _gs_state * gs_state; 5405 5406@ @<Set init...@>= 5407mp->ps->gs_state=NULL; 5408 5409@ @<Dealloc variables@>= 5410mp_xfree(mp->ps->gs_state); 5411 5412@ To avoid making undue assumptions about the initial graphics state, these 5413parameters are given special values that are guaranteed not to match anything 5414in the edge structure being shipped out. On the other hand, the initial color 5415should be black so that the translation of an all-black picture will have no 5416\&{setcolor} commands. (These would be undesirable in a font application.) 5417Hence we use |c=0| when initializing the graphics state and we use |c<0| 5418to recover from a situation where we have lost track of the graphics state. 5419 5420@d mp_void (mp_node)(null+1) /* a null pointer different from |null| */ 5421 5422@c static void mp_gs_unknown_graphics_state (MP mp, int c) { 5423 struct _gs_state *p; /* to shift graphic states around */ 5424 if ( (c==0)||(c==-1) ) { 5425 if ( mp->ps->gs_state==NULL ) { 5426 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state)); 5427 gs_previous=NULL; 5428 } else { 5429 while ( gs_previous!=NULL ) { 5430 p = gs_previous; 5431 mp_xfree(mp->ps->gs_state); 5432 mp->ps->gs_state=p; 5433 } 5434 } 5435 gs_red=c; gs_green=c; gs_blue=c; gs_black=c; 5436 gs_colormodel=mp_uninitialized_model; 5437 gs_ljoin=3; 5438 gs_lcap=3; 5439 gs_miterlim=0.0; 5440 gs_dash_p=NULL; 5441 gs_dash_init_done=false; 5442 gs_width=-1.0; 5443 } else if ( c==1 ) { 5444 p= mp->ps->gs_state; 5445 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state)); 5446 memcpy(mp->ps->gs_state,p,sizeof(struct _gs_state)); 5447 gs_previous = p; 5448 } else if ( c==2 ) { 5449 p = gs_previous; 5450 mp_xfree(mp->ps->gs_state); 5451 mp->ps->gs_state=p; 5452 } 5453} 5454 5455 5456@ When it is time to output a graphical object, |fix_graphics_state| ensures 5457that \ps's idea of the graphics state agrees with what is stored in the object. 5458 5459@<Declarations@>= 5460static void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) ; 5461 5462@ @c 5463void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) { 5464 /* get ready to output graphical object |p| */ 5465 mp_gr_knot pp, path_p; /* for list manipulation */ 5466 mp_dash_object *hh; 5467 double wx,wy,ww; /* dimensions of pen bounding box */ 5468 quarterword adj_wx; /* whether pixel rounding should be based on |wx| or |wy| */ 5469 double tx,ty; /* temporaries for computing |adj_wx| */ 5470 if ( gr_has_color(p) ) 5471 @<Make sure \ps\ will use the right color for object~|p|@>; 5472 if ( (gr_type(p)==mp_fill_code)||(gr_type(p)==mp_stroked_code) ) { 5473 if (gr_type(p)==mp_fill_code) { 5474 pp = gr_pen_p((mp_fill_object *)p); 5475 path_p = gr_path_p((mp_fill_object *)p); 5476 } else { 5477 pp = gr_pen_p((mp_stroked_object *)p); 5478 path_p = gr_path_p((mp_stroked_object *)p); 5479 } 5480 if ( pp!=NULL ) 5481 if ( pen_is_elliptical(pp) ) { 5482 @<Generate \ps\ code that sets the stroke width to the 5483 appropriate rounded value@>; 5484 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>; 5485 @<Decide whether the line cap parameter matters and set it if necessary@>; 5486 @<Set the other numeric parameters as needed for object~|p|@>; 5487 } 5488 } 5489 if ( mp->ps->ps_offset>0 ) mp_ps_print_ln(mp); 5490} 5491 5492@ @<Decide whether the line cap parameter matters and set it if necessary@>= 5493if ( gr_type(p)==mp_stroked_code ) { 5494 mp_stroked_object *ts = (mp_stroked_object *)p; 5495 if ( (gr_left_type(gr_path_p(ts))==mp_endpoint)||(gr_dash_p(ts)!=NULL) ) 5496 if ( gs_lcap!=(quarterword)gr_lcap_val(ts) ) { 5497 ps_room(13); 5498 mp_ps_print_char(mp, ' '); 5499 mp_ps_print_char(mp, '0'+gr_lcap_val(ts)); 5500 mp_ps_print_cmd(mp, " setlinecap"," lc"); 5501 gs_lcap=(quarterword)gr_lcap_val(ts); 5502 } 5503} 5504 5505@ 5506@d set_ljoin_miterlim(p) 5507 if ( gs_ljoin!=(quarterword)gr_ljoin_val(p) ) { 5508 ps_room(14); 5509 mp_ps_print_char(mp, ' '); 5510 mp_ps_print_char(mp, '0'+gr_ljoin_val(p)); 5511 mp_ps_print_cmd(mp, " setlinejoin"," lj"); 5512 gs_ljoin=(quarterword)gr_ljoin_val(p); 5513 } 5514 if ( gs_miterlim!=gr_miterlim_val(p) ) { 5515 ps_room(27); 5516 mp_ps_print_char(mp, ' '); 5517 mp_ps_print_double(mp, gr_miterlim_val(p)); 5518 mp_ps_print_cmd(mp, " setmiterlimit"," ml"); 5519 gs_miterlim=gr_miterlim_val(p); 5520 } 5521 5522@<Set the other numeric parameters as needed for object~|p|@>= 5523if ( gr_type(p)==mp_stroked_code ) { 5524 mp_stroked_object *ts = (mp_stroked_object *)p; 5525 set_ljoin_miterlim(ts); 5526} else { 5527 mp_fill_object *ts = (mp_fill_object *)p; 5528 set_ljoin_miterlim(ts); 5529} 5530 5531@ 5532@d set_color_objects(pq) 5533 object_color_model = pq->color_model; 5534 object_color_a = pq->color.a_val; 5535 object_color_b = pq->color.b_val; 5536 object_color_c = pq->color.c_val; 5537 object_color_d = pq->color.d_val; 5538 5539@<Make sure \ps\ will use the right color for object~|p|@>= 5540{ 5541 int object_color_model; 5542 double object_color_a, object_color_b, object_color_c, object_color_d ; 5543 if (gr_type(p) == mp_fill_code) { 5544 mp_fill_object *pq = (mp_fill_object *)p; 5545 set_color_objects(pq); 5546 } else if (gr_type(p) == mp_stroked_code) { 5547 mp_stroked_object *pq = (mp_stroked_object *)p; 5548 set_color_objects(pq); 5549 } else { 5550 mp_text_object *pq = (mp_text_object *)p; 5551 set_color_objects(pq); 5552 } 5553 5554 if ( object_color_model==mp_rgb_model) { 5555 if ( (gs_colormodel!=mp_rgb_model)||(gs_red!=object_color_a)|| 5556 (gs_green!=object_color_b)||(gs_blue!=object_color_c) ) { 5557 gs_red = object_color_a; 5558 gs_green = object_color_b; 5559 gs_blue = object_color_c; 5560 gs_black = -1.0; 5561 gs_colormodel=mp_rgb_model; 5562 { ps_room(36); 5563 mp_ps_print_char(mp, ' '); 5564 mp_ps_print_double(mp, gs_red); mp_ps_print_char(mp, ' '); 5565 mp_ps_print_double(mp, gs_green); mp_ps_print_char(mp, ' '); 5566 mp_ps_print_double(mp, gs_blue); 5567 mp_ps_print_cmd(mp, " setrgbcolor", " R"); 5568 } 5569 } 5570 } else if ( object_color_model==mp_cmyk_model) { 5571 if ( (gs_red!=object_color_a)||(gs_green!=object_color_b)|| 5572 (gs_blue!=object_color_c)||(gs_black!=object_color_d)|| 5573 (gs_colormodel!=mp_cmyk_model) ) { 5574 gs_red = object_color_a; 5575 gs_green = object_color_b; 5576 gs_blue = object_color_c; 5577 gs_black = object_color_d; 5578 gs_colormodel=mp_cmyk_model; 5579 { ps_room(45); 5580 mp_ps_print_char(mp, ' '); 5581 mp_ps_print_double(mp, gs_red); 5582 mp_ps_print_char(mp, ' '); 5583 mp_ps_print_double(mp, gs_green); 5584 mp_ps_print_char(mp, ' '); 5585 mp_ps_print_double(mp, gs_blue); 5586 mp_ps_print_char(mp, ' '); 5587 mp_ps_print_double(mp, gs_black); 5588 mp_ps_print_cmd(mp, " setcmykcolor"," C"); 5589 } 5590 } 5591 } else if ( object_color_model==mp_grey_model ) { 5592 if ( (gs_red!=object_color_a)||(gs_colormodel!=mp_grey_model) ) { 5593 gs_red = object_color_a; 5594 gs_green = -1.0; 5595 gs_blue = -1.0; 5596 gs_black = -1.0; 5597 gs_colormodel=mp_grey_model; 5598 { ps_room(16); 5599 mp_ps_print_char(mp, ' '); 5600 mp_ps_print_double(mp, gs_red); 5601 mp_ps_print_cmd(mp, " setgray"," G"); 5602 } 5603 } 5604 } else if ( object_color_model==mp_no_model ) { 5605 gs_colormodel=mp_no_model; 5606 } 5607} 5608 5609@ In order to get consistent widths for horizontal and vertical pen strokes, we 5610want \ps\ to use an integer number of pixels for the \&{setwidth} parameter. 5611@:setwidth}{\&{setwidth}command@> 5612We set |gs_width| to the ideal horizontal or vertical stroke width and then 5613generate \ps\ code that computes the rounded value. For non-circular pens, the 5614pen shape will be rescaled so that horizontal or vertical parts of the stroke 5615have the computed width. 5616 5617Rounding the width to whole pixels is not likely to improve the appearance of 5618diagonal or curved strokes, but we do it anyway for consistency. The 5619\&{truncate} command generated here tends to make all the strokes a little 5620@:truncate}{\&{truncate} command@> 5621thinner, but this is appropriate for \ps's scan-conversion rules. Even with 5622truncation, an ideal with of $w$~pixels gets mapped into $\lfloor w\rfloor+1$. 5623It would be better to have $\lceil w\rceil$ but that is ridiculously expensive 5624to compute in \ps. 5625 5626@<Generate \ps\ code that sets the stroke width...@>= 5627@<Set |wx| and |wy| to the width and height of the bounding box for 5628 |pen_p(p)|@>; 5629@<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more 5630 important and set |adj_wx| and |ww| accordingly@>; 5631if ( (ww!=gs_width) || (adj_wx!=gs_adj_wx) ) { 5632 if ( adj_wx != 0 ) { 5633 ps_room(13); 5634 mp_ps_print_char(mp, ' '); mp_ps_print_double(mp, ww); 5635 mp_ps_print_cmd(mp, 5636 " 0 dtransform exch truncate exch idtransform pop setlinewidth"," hlw"); 5637 } else { 5638 if (number_positive (internal_value(mp_procset)) ) { 5639 ps_room(13); 5640 mp_ps_print_char(mp, ' '); 5641 mp_ps_print_double(mp, ww); 5642 mp_ps_print(mp, " vlw"); 5643 } else { 5644 ps_room(15); 5645 mp_ps_print(mp, " 0 "); mp_ps_print_double(mp, ww); 5646 mp_ps_print(mp, " dtransform truncate idtransform setlinewidth pop"); 5647 } 5648 } 5649 gs_width = ww; 5650 gs_adj_wx = adj_wx; 5651} 5652 5653@ @<Set |wx| and |wy| to the width and height of the bounding box for...@>= 5654if ( (gr_right_x(pp)==gr_x_coord(pp)) && (gr_left_y(pp)==gr_y_coord(pp)) ) { 5655 wx = fabs(gr_left_x(pp) - gr_x_coord(pp)); 5656 wy = fabs(gr_right_y(pp) - gr_y_coord(pp)); 5657} else { 5658 double a, b; 5659 a = gr_left_x(pp)-gr_x_coord(pp); 5660 b = gr_right_x(pp)-gr_x_coord(pp); 5661 wx = sqrt(a*a + b*b); 5662 a = gr_left_y(pp)-gr_y_coord(pp); 5663 b = gr_right_y(pp)-gr_y_coord(pp); 5664 wy = sqrt(a*a + b*b); 5665} 5666 5667@ The path is considered ``essentially horizontal'' if its range of 5668$y$~coordinates is less than the $y$~range |wy| for the pen. ``Essentially 5669vertical'' paths are detected similarly. This code ensures that no component 5670of the pen transformation is more that |aspect_bound*(ww+1)|. 5671 5672@d aspect_bound 10.0/65536.0 /* ``less important'' of |wx|, |wy| cannot exceed the other by 5673 more than this factor */ 5674 5675@d do_x_loc 1 5676@d do_y_loc 2 5677 5678@<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more...@>= 5679tx=1.0/65536.0; ty=1.0/65536.0; 5680if ( mp_gr_coord_rangeOK(path_p, do_y_loc, wy) ) tx=aspect_bound; 5681else if ( mp_gr_coord_rangeOK(path_p, do_x_loc, wx) ) ty=aspect_bound; 5682if ( wy / ty>=wx / tx ) { ww=wy; adj_wx=0; } 5683else { ww=wx; adj_wx=1; } 5684 5685@ This routine quickly tests if path |h| is ``essentially horizontal'' or 5686``essentially vertical,'' where |zoff| is |x_loc(0)| or |y_loc(0)| and |dz| is 5687allowable range for $x$ or~$y$. We do not need and cannot afford a full 5688bounding-box computation. 5689 5690@<Declarations@>= 5691static boolean mp_gr_coord_rangeOK (mp_gr_knot h, 5692 quarterword zoff, double dz); 5693 5694@ @c 5695boolean mp_gr_coord_rangeOK (mp_gr_knot h, 5696 quarterword zoff, double dz) { 5697 mp_gr_knot p; /* for scanning the path form |h| */ 5698 double zlo,zhi; /* coordinate range so far */ 5699 double z; /* coordinate currently being tested */ 5700 if (zoff==do_x_loc) { 5701 zlo=gr_x_coord(h); 5702 zhi=zlo; 5703 p=h; 5704 while ( gr_right_type(p)!=mp_endpoint ) { 5705 z=gr_right_x(p); 5706 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>; 5707 p=gr_next_knot(p); z=gr_left_x(p); 5708 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>; 5709 z=gr_x_coord(p); 5710 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>; 5711 if ( p==h ) break; 5712 } 5713 } else { 5714 zlo=gr_y_coord(h); 5715 zhi=zlo; 5716 p=h; 5717 while ( gr_right_type(p)!=mp_endpoint ) { 5718 z=gr_right_y(p); 5719 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>; 5720 p=gr_next_knot(p); z=gr_left_y(p); 5721 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>; 5722 z=gr_y_coord(p); 5723 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>; 5724 if ( p==h ) break; 5725 } 5726 } 5727 return true; 5728} 5729 5730@ @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>= 5731if ( z<zlo ) zlo=z; 5732else if ( z>zhi ) zhi=z; 5733if ( zhi-zlo>dz ) return false 5734 5735@ Filling with an elliptical pen is implemented via a combination of \&{stroke} 5736and \&{fill} commands and a nontrivial dash pattern would interfere with this. 5737@:stroke}{\&{stroke} command@> 5738@:fill}{\&{fill} command@> 5739Note that we don't use |delete_edge_ref| because |gs_dash_p| is not counted as 5740a reference. 5741 5742@<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>= 5743if ( gr_type(p)==mp_fill_code || gr_dash_p(p) == NULL) { 5744 hh=NULL; 5745} else { 5746 hh=gr_dash_p(p); 5747} 5748if ( hh==NULL ) { 5749 if ( gs_dash_p!=NULL || gs_dash_init_done == false) { 5750 mp_ps_print_cmd(mp, " [] 0 setdash"," rd"); 5751 gs_dash_p=NULL; 5752 gs_dash_init_done=true; 5753 } 5754} else if ( ! mp_gr_same_dashes(gs_dash_p,hh) ) { 5755 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>; 5756} 5757 5758@ The original code had a check here to ensure that the result from 5759|mp_take_scaled| did not go out of bounds. 5760 5761@<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>= 5762{ gs_dash_p=hh; 5763 if ( (gr_dash_p(p)==NULL) || (hh==NULL) || (hh->array==NULL)) { 5764 mp_ps_print_cmd(mp, " [] 0 setdash"," rd"); 5765 } else { 5766 int i; 5767 ps_room(28); 5768 mp_ps_print(mp, " ["); 5769 for (i=0; *(hh->array+i) != -1;i++) { 5770 ps_room(13); 5771 mp_ps_print_double(mp, *(hh->array+i)); 5772 mp_ps_print_char(mp, ' ') ; 5773 } 5774 ps_room(22); 5775 mp_ps_print(mp, "] "); 5776 mp_ps_print_double(mp, hh->offset); 5777 mp_ps_print_cmd(mp, " setdash"," sd"); 5778 } 5779} 5780 5781@ @<Declarations@>= 5782static boolean mp_gr_same_dashes (mp_dash_object *h, mp_dash_object *hh) ; 5783 5784@ This function test if |h| and |hh| represent the same dash pattern. 5785 5786@c 5787boolean mp_gr_same_dashes (mp_dash_object *h, mp_dash_object *hh) { 5788 boolean ret=false; 5789 int i = 0; 5790 if ( h==hh ) ret=true; 5791 else if ( (h==NULL)||(hh==NULL) ) ret=false; 5792 else if ( h->offset!=hh->offset ) ret=false; 5793 else if ( h->array == hh->array ) ret=true; 5794 else if ( h->array == NULL || hh->array == NULL) ret=false; 5795 else { @<Compare |dash_list(h)| and |dash_list(hh)|@>; } 5796 return ret; 5797} 5798 5799@ @<Compare |dash_list(h)| and |dash_list(hh)|@>= 5800{ 5801 while (*(h->array+i)!=-1 && 5802 *(hh->array+i)!=-1 && 5803 *(h->array+i) == *(hh->array+i)) i++; 5804 if (i>0) { 5805 if (*(h->array+i)==-1 && *(hh->array+i) == -1) 5806 ret=true; 5807 } 5808} 5809 5810@ When stroking a path with an elliptical pen, it is necessary to transform 5811the coordinate system so that a unit circular pen will have the desired shape. 5812To keep this transformation local, we enclose it in a 5813$$\&{gsave}\ldots\&{grestore}$$ 5814block. Any translation component must be applied to the path being stroked 5815while the rest of the transformation must apply only to the pen. 5816If |fill_also=true|, the path is to be filled as well as stroked so we must 5817insert commands to do this after giving the path. 5818 5819@<Declarations@>= 5820static void mp_gr_stroke_ellipse (MP mp, mp_graphic_object *h, boolean fill_also) ; 5821 5822@ 5823@c void mp_gr_stroke_ellipse (MP mp, mp_graphic_object *h, boolean fill_also) { 5824 /* generate an elliptical pen stroke from object |h| */ 5825 double txx,txy,tyx,tyy; /* transformation parameters */ 5826 mp_gr_knot p; /* the pen to stroke with */ 5827 double d1,det; /* for tweaking transformation parameters */ 5828 double s; /* also for tweaking transformation paramters */ 5829 boolean transformed; /* keeps track of whether gsave/grestore are needed */ 5830 transformed=false; 5831 @<Use |pen_p(h)| to set the transformation parameters and give the initial 5832 translation@>; 5833 @<Tweak the transformation parameters so the transformation is nonsingular@>; 5834 if (gr_type(h)==mp_fill_code) { 5835 mp_gr_ps_path_out(mp, gr_path_p((mp_fill_object *)h)); 5836 } else { 5837 mp_gr_ps_path_out(mp, gr_path_p((mp_stroked_object *)h)); 5838 } 5839 if ( number_zero (internal_value(mp_procset))) { 5840 if ( fill_also ) mp_ps_print_nl(mp, "gsave fill grestore"); 5841 @<Issue \ps\ commands to transform the coordinate system@>; 5842 mp_ps_print(mp, " stroke"); 5843 if ( transformed ) mp_ps_print(mp, " grestore"); 5844 } else { 5845 if ( fill_also ) mp_ps_print_nl(mp, "B"); else mp_ps_print_ln(mp); 5846 if ( (txy!=0.0)||(tyx!=0.0) ) { 5847 mp_ps_print(mp, " ["); 5848 mp_ps_pair_out(mp, txx,tyx); 5849 mp_ps_pair_out(mp, txy,tyy); 5850 mp_ps_print(mp, "0 0] t"); 5851 } else if ((txx!=unity)||(tyy!=unity) ) { 5852 mp_ps_print(mp, " "); 5853 mp_ps_pair_out(mp,txx,tyy); 5854 mp_ps_print(mp, " s"); 5855 }; 5856 mp_ps_print(mp, " S"); 5857 if ( transformed ) mp_ps_print(mp, " Q"); 5858 } 5859 mp_ps_print_ln(mp); 5860} 5861 5862@ @<Use |pen_p(h)| to set the transformation parameters and give the...@>= 5863if (gr_type(h)==mp_fill_code) { 5864 p=gr_pen_p((mp_fill_object *)h); 5865} else { 5866 p=gr_pen_p((mp_stroked_object *)h); 5867} 5868txx=gr_left_x(p); 5869tyx=gr_left_y(p); 5870txy=gr_right_x(p); 5871tyy=gr_right_y(p); 5872if ( (gr_x_coord(p)!=0.0)||(gr_y_coord(p)!=0.0) ) { 5873 mp_ps_print_nl(mp, ""); 5874 mp_ps_print_cmd(mp, "gsave ","q "); 5875 mp_ps_pair_out(mp, gr_x_coord(p), gr_y_coord(p)); 5876 mp_ps_print(mp, "translate "); 5877 txx-=gr_x_coord(p); 5878 tyx-=gr_y_coord(p); 5879 txy-=gr_x_coord(p); 5880 tyy-=gr_y_coord(p); 5881 transformed=true; 5882} else { 5883 mp_ps_print_nl(mp, ""); 5884} 5885@<Adjust the transformation to account for |gs_width| and output the 5886 initial \&{gsave} if |transformed| should be |true|@> 5887 5888@ 5889 5890@d mp_make_double(A,B,C) ((B)/(C)) 5891@d mp_take_double(A,B,C) ((B)*(C)) 5892 5893@<Adjust the transformation to account for |gs_width| and output the...@>= 5894if ( gs_width!=unity ) { 5895 if ( gs_width==0.0 ) { 5896 txx=unity; tyy=unity; 5897 } else { 5898 txx=mp_make_double(mp, txx,gs_width); 5899 txy=mp_make_double(mp, txy,gs_width); 5900 tyx=mp_make_double(mp, tyx,gs_width); 5901 tyy=mp_make_double(mp, tyy,gs_width); 5902 } 5903} 5904if ( (txy!=0.0)||(tyx!=0.0)||(txx!=unity)||(tyy!=unity) ) { 5905 if ( (! transformed) ){ 5906 mp_ps_print_cmd(mp, "gsave ","q "); 5907 transformed=true; 5908 } 5909} 5910 5911@ @<Issue \ps\ commands to transform the coordinate system@>= 5912if ( (txy!=0.0)||(tyx!=0.0) ){ 5913 mp_ps_print_ln(mp); 5914 mp_ps_print_char(mp, '['); 5915 mp_ps_pair_out(mp, txx,tyx); 5916 mp_ps_pair_out(mp, txy,tyy); 5917 mp_ps_print(mp, "0 0] concat"); 5918} else if ( (txx!=unity)||(tyy!=unity) ){ 5919 mp_ps_print_ln(mp); 5920 mp_ps_pair_out(mp, txx,tyy); 5921 mp_ps_print(mp, "scale"); 5922} 5923 5924@ The \ps\ interpreter will probably abort if it encounters a singular 5925transformation matrix. The determinant must be large enough to ensure that 5926the printed representation will be nonsingular. Since the printed 5927representation is always within $2^{-17}$ of the internal |scaled| value, the 5928total error is at most $4T_{\rm max}2^{-17}$, where $T_{\rm max}$ is a bound on 5929the magnitudes of |txx/65536|, |txy/65536|, etc. 5930 5931The |aspect_bound*(gs_width+1)| bound on the components of the pen 5932transformation allows $T_{\rm max}$ to be at most |2*aspect_bound|. 5933 5934@<Tweak the transformation parameters so the transformation is nonsingular@>= 5935det=mp_take_double(mp, txx,tyy) - mp_take_double(mp, txy,tyx); 5936d1=4*(aspect_bound+1/65536.0); 5937if ( fabs(det)<d1 ) { 5938 if ( det>=0 ) { d1=d1-det; s=1; } 5939 else { d1=-d1-det; s=-1; }; 5940 d1=d1*unity; 5941 if ( fabs(txx)+fabs(tyy)>=fabs(txy)+fabs(tyy) ) { 5942 if ( fabs(txx)>fabs(tyy) ) tyy=tyy+(d1+s*fabs(txx)) / txx; 5943 else txx=txx+(d1+s*fabs(tyy)) / tyy; 5944 } else { 5945 if ( fabs(txy)>fabs(tyx) ) tyx=tyx+(d1+s*fabs(txy)) / txy; 5946 else txy=txy+(d1+s*fabs(tyx)) / tyx; 5947 } 5948} 5949 5950@ Here is a simple routine that just fills a cycle. 5951 5952@<Declarations@>= 5953static void mp_gr_ps_fill_out (MP mp, mp_gr_knot p); 5954 5955@ @c 5956void mp_gr_ps_fill_out (MP mp, mp_gr_knot p) { /* fill cyclic path~|p| */ 5957 mp_gr_ps_path_out(mp, p); 5958 mp_ps_print_cmd(mp, " fill"," F"); 5959 mp_ps_print_ln(mp); 5960} 5961 5962@ A text node may specify an arbitrary transformation but the usual case 5963involves only shifting, scaling, and occasionally rotation. The purpose 5964of |choose_scale| is to select a scale factor so that the remaining 5965transformation is as ``nice'' as possible. The definition of ``nice'' 5966is somewhat arbitrary but shifting and $90^\circ$ rotation are especially 5967nice because they work out well for bitmap fonts. The code here selects 5968a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the 5969non-shifting part of the transformation matrix. It is careful to avoid 5970additions that might cause undetected overflow. 5971 5972@<Declarations@>= 5973static double mp_gr_choose_scale (MP mp, mp_graphic_object *p) ; 5974 5975@ @c double mp_gr_choose_scale (MP mp, mp_graphic_object *p) { 5976 /* |p| should point to a text node */ 5977 double a,b,c,d,ad,bc; /* temporary values */ 5978 double r; 5979 a=gr_txx_val(p); 5980 b=gr_txy_val(p); 5981 c=gr_tyx_val(p); 5982 d=gr_tyy_val(p); 5983 if ( a<0 ) negate(a); 5984 if ( b<0 ) negate(b); 5985 if ( c<0 ) negate(c); 5986 if ( d<0 ) negate(d); 5987 ad=(a-d)/2.0; 5988 bc=(b-c)/2.0; 5989 a = (d+ad); 5990 b = ad; 5991 d = sqrt(a*a + b*b); 5992 a = (c+bc); 5993 b = bc; 5994 c = sqrt(a*a + b*b); 5995 r = sqrt(c*c + d*d); 5996 return r; 5997} 5998 5999@ The potential overflow here is caused by the fact the returned value 6000has to fit in a |name_type|, which is a quarterword. 6001 6002@d fscale_tolerance (65/65536.0) /* that's $.001\times2^{16}$ */ 6003 6004@<Declarations@>= 6005static quarterword mp_size_index (MP mp, font_number f, double s) ; 6006 6007@ @c 6008quarterword mp_size_index (MP mp, font_number f, double s) { 6009 mp_node p,q; /* the previous and current font size nodes */ 6010 int i; /* the size index for |q| */ 6011 p=NULL; 6012 q=mp->font_sizes[f]; 6013 i=0; 6014 while ( q!=null ) { 6015 if ( fabs(s-sc_factor(q))<=fscale_tolerance ) 6016 return (quarterword)i; 6017 else 6018 { p=q; q=mp_link(q); incr(i); }; 6019 if ( i==max_quarterword ) 6020 mp_overflow(mp, "sizes per font",max_quarterword); 6021@:MetaPost capacity exceeded sizes per font}{\quad sizes per font@> 6022 } 6023 q=(mp_node)mp_xmalloc(mp, 1, font_size_size); 6024 mp_link(q) = NULL; 6025 sc_factor(q)=s; 6026 if ( i==0 ) mp->font_sizes[f]=q; else mp_link(p)=q; 6027 return (quarterword)i; 6028} 6029 6030@ @<Declarations@>= 6031static double mp_indexed_size (MP mp,font_number f, quarterword j); 6032 6033@ @c 6034double mp_indexed_size (MP mp,font_number f, quarterword j) { /* return scaled */ 6035 mp_node p; /* a font size node */ 6036 int i; /* the size index for |p| */ 6037 p=mp->font_sizes[f]; 6038 i=0; 6039 if ( p==null ) mp_confusion(mp, "size"); 6040 while ( (i!=j) ) { 6041 incr(i); 6042 /* clang: dereference null pointer 'p' */ assert(p); 6043 p=mp_link(p); 6044 if ( p==null ) mp_confusion(mp, "size"); 6045 } 6046 /* clang: dereference null pointer 'p' */ assert(p); 6047 return sc_factor(p); 6048} 6049 6050@ @<Declarations@>= 6051static void mp_clear_sizes (MP mp) ; 6052 6053@ @c void mp_clear_sizes (MP mp) { 6054 font_number f; /* the font whose size list is being cleared */ 6055 mp_node p; /* current font size nodes */ 6056 for (f=null_font+1;f<=mp->last_fnum;f++) { 6057 while ( mp->font_sizes[f]!=null ) { 6058 p=mp->font_sizes[f]; 6059 mp->font_sizes[f]=mp_link(p); 6060 mp_xfree(p); 6061 } 6062 } 6063} 6064 6065@ There may be many sizes of one font and we need to keep track of the 6066characters used for each size. This is done by keeping a linked list of 6067sizes for each font with a counter in each text node giving the appropriate 6068position in the size list for its font. 6069 6070@d font_size_size sizeof(struct mp_font_size_node_data) /* size of a font size node */ 6071 6072@<Types...@>= 6073typedef struct mp_font_size_node_data { 6074 NODE_BODY; 6075 double sc_factor_; /* scaled */ 6076} mp_font_size_node_data; 6077typedef struct mp_font_size_node_data* mp_font_size_node; 6078 6079 6080@ @<Declarations@>= 6081static void mp_apply_mark_string_chars(MP mp, mp_edge_object *h, int next_size); 6082 6083@ @c 6084void mp_apply_mark_string_chars(MP mp, mp_edge_object *h, int next_size) { 6085 mp_graphic_object * p; 6086 p=h->body; 6087 while ( p!= NULL ) { 6088 if ( gr_type(p)==mp_text_code ) { 6089 if ( gr_font_n(p)!=null_font ) { 6090 if ( gr_size_index(p)==(unsigned char)next_size ) 6091 mp_mark_string_chars(mp, gr_font_n(p),gr_text_p(p),gr_text_l(p)); 6092 } 6093 } 6094 p=gr_link(p); 6095 } 6096} 6097 6098@ @<Unmark all marked characters@>= 6099for (f=null_font+1;f<=mp->last_fnum;f++) { 6100 if ( mp->font_sizes[f]!=null ) { 6101 mp_unmark_font(mp, f); 6102 mp->font_sizes[f]=null; 6103 } 6104} 6105 6106@ @<Scan all the text nodes and mark the used ...@>= 6107p=hh->body; 6108while ( p!=null ) { 6109 if ( gr_type(p)==mp_text_code ) { 6110 f = gr_font_n(p); 6111 if (f!=null_font ) { 6112 switch (prologues) { 6113 case 2: 6114 case 3: 6115 mp->font_sizes[f] = mp_void; 6116 mp_mark_string_chars(mp, f, gr_text_p(p),gr_text_l(p)); 6117 if (mp_has_fm_entry(mp,f,NULL) ) { 6118 if (mp->font_enc_name[f]==NULL ) 6119 mp->font_enc_name[f] = mp_fm_encoding_name(mp,f); 6120 mp_xfree(mp->font_ps_name[f]); 6121 mp->font_ps_name[f] = mp_fm_font_name(mp,f); 6122 } 6123 break; 6124 case 1: 6125 mp->font_sizes[f]=mp_void; 6126 break; 6127 default: 6128 gr_size_index(p)=(unsigned char)mp_size_index(mp, f,mp_gr_choose_scale(mp, p)); 6129 if ( gr_size_index(p)==0 ) 6130 mp_mark_string_chars(mp, f, gr_text_p(p),gr_text_l(p)); 6131 } 6132 } 6133 } 6134 p=gr_link(p); 6135} 6136 6137 6138@ 6139@d pen_is_elliptical(A) ((A)==gr_next_knot((A))) 6140 6141@<Exported function ...@>= 6142int mp_gr_ship_out (mp_edge_object *hh, int prologues, int procset, int standalone) ; 6143 6144@ @c 6145int mp_gr_ship_out (mp_edge_object *hh, int qprologues, int qprocset,int standalone) { 6146 mp_graphic_object *p; 6147 double ds,scf; /* design size and scale factor for a text node */ 6148 font_number f; /* for loops over fonts while (un)marking characters */ 6149 boolean transformed; /* is the coordinate system being transformed? */ 6150 int prologues, procset; 6151 MP mp = hh->parent; 6152 if (standalone) { 6153 mp->jump_buf = malloc(sizeof(jmp_buf)); 6154 if (mp->jump_buf == NULL || setjmp(*(mp->jump_buf))) 6155 return 0; 6156 } 6157 if (mp->history >= mp_fatal_error_stop ) return 1; 6158 if (qprologues<0) 6159 prologues = (int)((unsigned)number_to_scaled (internal_value(mp_prologues))>>16); 6160 else 6161 prologues=qprologues; 6162 if (qprocset<0) 6163 procset = (int)((unsigned)number_to_scaled (internal_value(mp_procset))>>16); 6164 else 6165 procset=qprocset; 6166 mp_open_output_file(mp); 6167 mp_print_initial_comment(mp, hh, prologues); 6168 /* clang: never read: p = hh->body; */ 6169 @<Unmark all marked characters@>; 6170 if ( prologues==2 || prologues==3 ) { 6171 mp_reload_encodings(mp); 6172 } 6173 @<Scan all the text nodes and mark the used characters@>; 6174 if ( prologues==2 || prologues==3 ) { 6175 mp_print_improved_prologue(mp, hh, prologues, procset); 6176 } else { 6177 mp_print_prologue(mp, hh, prologues, procset); 6178 } 6179 mp_gs_unknown_graphics_state(mp, 0); 6180 p = hh->body; 6181 while ( p!=NULL ) { 6182 if ( gr_has_color(p) ) { 6183 @<Write |pre_script| of |p|@>; 6184 } 6185 mp_gr_fix_graphics_state(mp, p); 6186 switch (gr_type(p)) { 6187 case mp_fill_code: 6188 if ( gr_pen_p((mp_fill_object *)p)==NULL ) { 6189 mp_gr_ps_fill_out(mp, gr_path_p((mp_fill_object *)p)); 6190 } else if ( pen_is_elliptical(gr_pen_p((mp_fill_object *)p)) ) { 6191 mp_gr_stroke_ellipse(mp, p,true); 6192 } else { 6193 mp_gr_ps_fill_out(mp, gr_path_p((mp_fill_object *)p)); 6194 mp_gr_ps_fill_out(mp, gr_htap_p(p)); 6195 } 6196 if ( gr_post_script((mp_fill_object *)p)!=NULL ) { 6197 mp_ps_print_nl (mp, gr_post_script((mp_fill_object *)p)); 6198 mp_ps_print_ln(mp); 6199 } 6200 break; 6201 case mp_stroked_code: 6202 if ( pen_is_elliptical(gr_pen_p((mp_stroked_object *)p)) ) 6203 mp_gr_stroke_ellipse(mp, p,false); 6204 else { 6205 mp_gr_ps_fill_out(mp, gr_path_p((mp_stroked_object *)p)); 6206 } 6207 if ( gr_post_script((mp_stroked_object *)p)!=NULL ) { 6208 mp_ps_print_nl (mp, gr_post_script((mp_stroked_object *)p)); 6209 mp_ps_print_ln(mp); 6210 } 6211 break; 6212 case mp_text_code: 6213 if ( (gr_font_n(p)!=null_font) && (gr_text_l(p)>0) ) { 6214 if ( prologues>0 ) 6215 scf=mp_gr_choose_scale(mp, p); 6216 else 6217 scf=mp_indexed_size(mp, gr_font_n(p), (quarterword)gr_size_index(p)); 6218 @<Shift or transform as necessary before outputting text node~|p| at scale 6219 factor~|scf|; set |transformed:=true| if the original transformation must 6220 be restored@>; 6221 mp_ps_string_out(mp, gr_text_p(p),gr_text_l(p)); 6222 mp_ps_name_out(mp, mp->font_name[gr_font_n(p)],false); 6223 @<Print the size information and \ps\ commands for text node~|p|@>; 6224 mp_ps_print_ln(mp); 6225 } 6226 if ( gr_post_script((mp_text_object *)p)!=NULL ) { 6227 mp_ps_print_nl (mp, gr_post_script((mp_text_object *)p)); mp_ps_print_ln(mp); 6228 } 6229 break; 6230 case mp_start_clip_code: 6231 mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "gsave ","q "); 6232 mp_gr_ps_path_out(mp, gr_path_p((mp_clip_object *)p)); 6233 mp_ps_print_cmd(mp, " clip"," W"); 6234 mp_ps_print_ln(mp); 6235 if ( number_positive (internal_value(mp_restore_clip_color)) ) 6236 mp_gs_unknown_graphics_state(mp, 1); 6237 break; 6238 case mp_stop_clip_code: 6239 mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "grestore","Q"); 6240 mp_ps_print_ln(mp); 6241 if ( number_positive (internal_value(mp_restore_clip_color)) ) 6242 mp_gs_unknown_graphics_state(mp, 2); 6243 else 6244 mp_gs_unknown_graphics_state(mp, -1); 6245 break; 6246 case mp_start_bounds_code: 6247 case mp_stop_bounds_code: 6248 break; 6249 case mp_special_code: 6250 { 6251 mp_special_object *ps = (mp_special_object *)p; 6252 mp_ps_print_nl (mp, gr_pre_script(ps)); 6253 mp_ps_print_ln (mp); 6254 } 6255 break; 6256 } /* all cases are enumerated */ 6257 p=gr_link(p); 6258 } 6259 mp_ps_print_cmd(mp, "showpage","P"); mp_ps_print_ln(mp); 6260 mp_ps_print(mp, "%%EOF"); mp_ps_print_ln(mp); 6261 (mp->close_file)(mp,mp->output_file); 6262 if ( prologues<=0 ) 6263 mp_clear_sizes(mp); 6264 return 1; 6265} 6266 6267@ @<Internal Postscript header information@>= 6268int mp_ps_ship_out (mp_edge_object *hh, int prologues, int procset) ; 6269 6270@ @c 6271int mp_ps_ship_out (mp_edge_object *hh, int prologues, int procset) { 6272 return mp_gr_ship_out (hh, prologues, procset, (int)true); 6273} 6274 6275 6276 6277 6278 6279@ 6280@d do_write_prescript(a,b) { 6281 if ( (gr_pre_script((b *)a))!=NULL ) { 6282 mp_ps_print_nl (mp, gr_pre_script((b *)a)); 6283 mp_ps_print_ln(mp); 6284 } 6285} 6286 6287@<Write |pre_script| of |p|@>= 6288{ 6289 if (gr_type(p)==mp_fill_code) { do_write_prescript(p,mp_fill_object); } 6290 else if (gr_type(p)==mp_stroked_code) { do_write_prescript(p,mp_stroked_object); } 6291 else if (gr_type(p)==mp_text_code) { do_write_prescript(p,mp_text_object); } 6292} 6293 6294@ @<Print the size information and \ps\ commands for text node~|p|@>= 6295ps_room(18); 6296mp_ps_print_char(mp, ' '); 6297ds=(mp->font_dsize[gr_font_n(p)]+8) / 16; 6298mp_ps_print_double(mp, (mp_take_double(mp, ds,scf)/65536.0)); 6299mp_ps_print(mp, " fshow"); 6300if ( transformed ) 6301 mp_ps_print_cmd(mp, " grestore"," Q") 6302 6303 6304 6305@ @<Shift or transform as necessary before outputting text node~|p| at...@>= 6306transformed=(gr_txx_val(p)!=scf)||(gr_tyy_val(p)!=scf)|| 6307 (gr_txy_val(p)!=0)||(gr_tyx_val(p)!=0); 6308if ( transformed ) { 6309 mp_ps_print_cmd(mp, "gsave [", "q ["); 6310 mp_ps_pair_out(mp, mp_make_double(mp, gr_txx_val(p),scf), 6311 mp_make_double(mp, gr_tyx_val(p),scf)); 6312 mp_ps_pair_out(mp, mp_make_double(mp, gr_txy_val(p),scf), 6313 mp_make_double(mp, gr_tyy_val(p),scf)); 6314 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p)); 6315 mp_ps_print_cmd(mp, "] concat 0 0 moveto","] t 0 0 m"); 6316} else { 6317 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p)); 6318 mp_ps_print_cmd(mp, "moveto","m"); 6319} 6320mp_ps_print_ln(mp) 6321 6322@ @<Internal Postscript header information@>= 6323void mp_gr_toss_objects ( mp_edge_object *hh) ; 6324void mp_gr_toss_object (mp_graphic_object *p) ; 6325 6326@ @c 6327void mp_gr_toss_object (mp_graphic_object *p) { 6328 mp_fill_object *tf; 6329 mp_stroked_object *ts; 6330 mp_text_object *tt; 6331 switch (gr_type(p)) { 6332 case mp_fill_code: 6333 tf = (mp_fill_object *)p; 6334 mp_xfree(gr_pre_script(tf)); 6335 mp_xfree(gr_post_script(tf)); 6336 mp_gr_toss_knot_list(mp,gr_pen_p(tf)); 6337 mp_gr_toss_knot_list(mp,gr_path_p(tf)); 6338 mp_gr_toss_knot_list(mp,gr_htap_p(p)); 6339 break; 6340 case mp_stroked_code: 6341 ts = (mp_stroked_object *)p; 6342 mp_xfree(gr_pre_script(ts)); 6343 mp_xfree(gr_post_script(ts)); 6344 mp_gr_toss_knot_list(mp,gr_pen_p(ts)); 6345 mp_gr_toss_knot_list(mp,gr_path_p(ts)); 6346 if (gr_dash_p(p)!=NULL) 6347 mp_gr_toss_dashes (mp,gr_dash_p(p)); 6348 break; 6349 case mp_text_code: 6350 tt = (mp_text_object *)p; 6351 mp_xfree(gr_pre_script(tt)); 6352 mp_xfree(gr_post_script(tt)); 6353 mp_xfree(gr_text_p(p)); 6354 mp_xfree(gr_font_name(p)); 6355 break; 6356 case mp_start_clip_code: 6357 mp_gr_toss_knot_list(mp,gr_path_p((mp_clip_object *)p)); 6358 break; 6359 case mp_start_bounds_code: 6360 mp_gr_toss_knot_list(mp,gr_path_p((mp_bounds_object *)p)); 6361 break; 6362 case mp_stop_clip_code: 6363 case mp_stop_bounds_code: 6364 break; 6365 case mp_special_code: 6366 mp_xfree(gr_pre_script((mp_special_object *)p)); 6367 break; 6368 } /* all cases are enumerated */ 6369 mp_xfree(p); 6370} 6371 6372 6373@ @c 6374void mp_gr_toss_objects (mp_edge_object *hh) { 6375 mp_graphic_object *p, *q; 6376 p = hh->body; 6377 while ( p!=NULL ) { 6378 q = gr_link(p); 6379 mp_gr_toss_object(p); 6380 p=q; 6381 } 6382 mp_xfree(hh->filename); 6383 mp_xfree(hh); 6384} 6385 6386@ @<Internal Postscript header information@>= 6387mp_graphic_object *mp_gr_copy_object (MP mp, mp_graphic_object *p) ; 6388 6389@ @c 6390mp_graphic_object * 6391mp_gr_copy_object (MP mp, mp_graphic_object *p) { 6392 mp_fill_object *tf; 6393 mp_stroked_object *ts; 6394 mp_text_object *tt; 6395 mp_clip_object *tc; 6396 mp_bounds_object *tb; 6397 mp_special_object *tp; 6398 mp_graphic_object *q = NULL; 6399 switch (gr_type(p)) { 6400 case mp_fill_code: 6401 tf = (mp_fill_object *)mp_new_graphic_object(mp, mp_fill_code); 6402 gr_pre_script(tf) = mp_xstrdup(mp, gr_pre_script((mp_fill_object *)p)); 6403 gr_post_script(tf) = mp_xstrdup(mp, gr_post_script((mp_fill_object *)p)); 6404 gr_path_p(tf) = mp_gr_copy_path(mp,gr_path_p((mp_fill_object *)p)); 6405 gr_htap_p(tf) = mp_gr_copy_path(mp,gr_htap_p(p)); 6406 gr_pen_p(tf) = mp_gr_copy_path(mp,gr_pen_p((mp_fill_object *)p)); 6407 q = (mp_graphic_object *)tf; 6408 break; 6409 case mp_stroked_code: 6410 ts = (mp_stroked_object *)mp_new_graphic_object(mp, mp_stroked_code); 6411 gr_pre_script(ts) = mp_xstrdup(mp, gr_pre_script((mp_stroked_object *)p)); 6412 gr_post_script(ts) = mp_xstrdup(mp, gr_post_script((mp_stroked_object *)p)); 6413 gr_path_p(ts) = mp_gr_copy_path(mp,gr_path_p((mp_stroked_object *)p)); 6414 gr_pen_p(ts) = mp_gr_copy_path(mp,gr_pen_p((mp_stroked_object *)p)); 6415 gr_dash_p(ts) = mp_gr_copy_dashes(mp,gr_dash_p(p)); 6416 q = (mp_graphic_object *)ts; 6417 break; 6418 case mp_text_code: 6419 tt = (mp_text_object *)mp_new_graphic_object(mp, mp_text_code); 6420 gr_pre_script(tt) = mp_xstrdup(mp, gr_pre_script((mp_text_object *)p)); 6421 gr_post_script(tt) = mp_xstrdup(mp, gr_post_script((mp_text_object *)p)); 6422 gr_text_p(tt) = mp_xstrldup(mp, gr_text_p(p), gr_text_l(p)); 6423 gr_text_l(tt) = gr_text_l(p); 6424 gr_font_name(tt) = mp_xstrdup(mp, gr_font_name(p)); 6425 q = (mp_graphic_object *)tt; 6426 break; 6427 case mp_start_clip_code: 6428 tc = (mp_clip_object *)mp_new_graphic_object(mp, mp_start_clip_code); 6429 gr_path_p(tc) = mp_gr_copy_path(mp,gr_path_p((mp_clip_object *)p)); 6430 q = (mp_graphic_object *)tc; 6431 break; 6432 case mp_start_bounds_code: 6433 tb = (mp_bounds_object *)mp_new_graphic_object(mp, mp_start_bounds_code); 6434 gr_path_p(tb) = mp_gr_copy_path(mp,gr_path_p((mp_bounds_object *)p)); 6435 q = (mp_graphic_object *)tb; 6436 break; 6437 case mp_special_code: 6438 tp = (mp_special_object *)mp_new_graphic_object(mp, mp_special_code); 6439 gr_pre_script(tp) = mp_xstrdup(mp, gr_pre_script((mp_special_object *)p)); 6440 q = (mp_graphic_object *)tp; 6441 break; 6442 case mp_stop_clip_code: 6443 q = mp_new_graphic_object(mp, mp_stop_clip_code); 6444 break; 6445 case mp_stop_bounds_code: 6446 q = mp_new_graphic_object(mp, mp_stop_bounds_code); 6447 break; 6448 } /* all cases are enumerated */ 6449 return q; 6450} 6451 6452