1 /* ---------------------------------------------------------------------------
2
3 writefil.c
4
5 Notes:
6
7 This file contains routines for writing the PostScript output.
8
9 Revision history:
10
11 4.11.0
12 B.Marr 2007-12-16
13
14 Allow the drawing of moon phase icons ('-m' or '-M') and
15 Julian dates ('-j' or '-J') on yearly-format calendars.
16
17 Perform various cosmetic cleanups (mostly by adding blank
18 lines) to the PostScript output.
19
20 B.Marr 2007-12-15
21
22 Fix bug whereby use of '-J' option caused garbage text
23 (PostScript commands) to appear in place of the number of days
24 remaining in the year.
25
26 Add support for new '-W' option, to specify horizontal
27 alignment of the "Month/Year" title on monthly-format
28 calendars, thanks to a patch from Todd Foster.
29
30 Remove long-obsolete external 'moon file' concept. Now, we
31 depend solely on the algorithmic determination of moon phases.
32
33 Rename some variables, structures, and/or routines to be
34 clearer about their purpose and/or to allow easier searching
35 with fewer "false positives".
36
37 4.10.0
38 B.Marr 2006-07-19
39
40 Use an international (as opposed to USA-centric) date format
41 and 2 more digits of precision in the debug output for moon
42 phases ('-ZM' option).
43
44 Add the 'pcal' website URL to the PostScript output.
45
46 Eliminate 1-use macros for EPS-like output and put the code
47 inline for clarity.
48
49 Reformatted comments and code to match my standards.
50
51 B.Marr 2006-07-12
52
53 Eliminate C++ style comments ('//').
54
55 Provide explicit casting in several spots to avoid warnings in
56 a "gcc 3.4.2 on Solaris 8" environment, based on a report from
57 David Mathog <mathog at mendel.bio.caltech.edu>.
58
59 Get rid of all the '#ifdef PROTOS' checks, which are pretty
60 much obsolete these days and just needlessly clutter up the
61 code.
62
63 4.9.0
64 B.Marr 2005-08-10
65
66 Fix a long-standing bug whereby a centered "footer"
67 specification with 'strftime()'-like date specifiers used in
68 an HTML yearly calendar was using/showing the correct date
69 values for the HTML 'title' but the wrong date values for the
70 centered header string at the start of the actual displayed
71 (HTML) content.
72
73 Eliminate the hack to support Esperanto via a custom,
74 dedicated character encoding. Esperanto is now handled
75 generically by the 'Latin3' (ISO 8859-3) character encoding.
76
77 B.Marr 2005-08-02
78
79 Per a user's request, change the separator character from a
80 space to a tab when using the '-c' option (to output text
81 lines which are compatible with the Unix 'calendar' program).
82
83 B.Marr 2005-01-04
84
85 Add support for several new character mappings (PostScript
86 encoding vectors) in order to support a wider variety of
87 languages. Rename enumerations for all encodings to be more
88 consistent (and easily searchable).
89
90 4.8.0
91 B.Marr 2004-12-04
92
93 Support new paper sizes. Support specification of paper size
94 via run-time option (command-line, etc). Create and support
95 concept of 'input' language versus 'output' language. Use
96 separate variables for X/Y scaling and X/Y translation done by
97 the program to distinguish from the X/Y scaling and X/Y
98 translation specified by the user.
99
100 B.Marr 2004-11-19
101
102 Support KOI8U character encodings (for Ukrainian language
103 support) and provide abbreviated day-of-week names, both based
104 on a patch from Volodymyr M. Lisivka. Provide support for
105 embedded EPS images (photos, icons, etc) for monthly
106 PostScript calendars. Fix bug since v4.7.1 whereby use of
107 '-q' flag required '-F 1' to prevent wrong weekday display.
108 Remove spaces embedded within tab fields. Remove Ctl-L (page
109 eject) characters from source file.
110
111 4.7.1 SF 01/06/2003 html output with one column per month
112 (cf. single_month_one_column_html)
113
114 4.7 AWR 02/09/2000 document block of code responsible for
115 forcing Letter or A4 paper tray
116
117 10/27/1999 rearrange comment block - 'sdtimage'
118 tool (possibly others) doesn't recognize
119 %%Orientation unless it precedes %%Pages
120
121 04/19/1999 support #if DIVIDE_BLANK_SPACE (cf.
122 pcaldefs.h) to print each partial
123 row of blank space at beginning/end
124 of calendar as N one-column boxes
125 instead of one N-column box
126
127 06/19/1998 support landscape- and portrait-mode
128 whole-year calendars in HTML mode
129
130 03/08/1998 write additional definitions to
131 PostScript output for use with
132 alternate (just A4 at present) paper
133 sizes
134
135 12/21/1997 clean up gcc warnings in -Wall mode
136
137 07/27/1997 revise for -H support (generate HTML
138 table as output; cf. write_htmlfile()
139 and print_html()); delete obsolete
140 FPR and PRT macros
141
142 4.6 AWR 04/30/1996 suppress "For:" and "Routing:" comments
143 if account name is "nobody" (typically
144 when pcal is run from a CGI script)
145
146 04/22/1996 use alternate (taller) date box
147 height for single-month calendars in
148 portrait mode
149
150 12/02/1995 drop asterisks from -c output (Un*x
151 "calendar" utility interprets them
152 as wildcards) unless KEEP_ASTERISKS
153 is defined
154
155 11/14/1995 if OUTLINE_BLACK is defined, display
156 -O dates as black outlines (not the
157 specified color) in RGB mode (cf.
158 pcalinit.ps)
159
160 10/03/1995 use globals for initial month, year,
161 number of months (cf. pcalglob.h,
162 pcal.c)
163
164 05/09/1995 add support for font style escape
165 sequences (.[bir]; cf. pcalinit.ps)
166
167 AH 02/03/1995 add multiple language support
168
169
170 4.5 AWR 11/15/1994 select gray/RGB independently for dates
171 and fill boxes (cf. pcalinit.ps)
172
173 04/05/1994 select real vs. dummy PostScript code
174 within write_psfile() (cf. pcalinit.ps)
175
176 11/30/1993 pre-scale all fonts used (as suggested
177 by Andrew Houghton; cf. pcalinit.ps)
178
179 11/24/1993 replace find_holidays() with
180 print_colors() (cf. drawnums{} in
181 pcalinit.ps)
182
183 11/16/1993 Add set_rgb() to handle red:green:blue
184 values for date/fill colors
185
186 09/23/1993 Support both ROMAN-8 and LATIN-8 font
187 mappings
188
189 AWR 07/09/1993 Revised PostScript comment block
190
191 AWR 03/01/1993 add optional mapping of 8-bit fonts
192
193 AWR 02/05/1993 Support -# flag (multiple copies of
194 each output page)
195
196 AWR 04/22/1992 use STRSIZ for temp buffer size
197
198 4.4 AWR 04/07/1992 revise to use new PUTCHAR and PUTSTR
199 macros (cf. pcaldefs.h)
200
201 01/20/1992 support -z and revised -[bgGO] flags
202
203 AWR 01/13/1992 support optional font size in -d and
204 -t flags; move initialization of fonts
205 and sizes here (from pcalinit.ps)
206
207 4.3 AWR 12/03/1991 add support for -s flag (specify
208 alternate date/fill box shading values)
209
210 4.2 AWR 10/08/1991 add support for -[kK] flags (change
211 position of small calendars)
212
213 10/03/1991 add find_noteboxes(); revise to print
214 text in multiple notes boxes
215
216 add support for -S flag
217
218 10/02/1991 modify def_footstring() to handle all
219 types of strings; use it to print notes
220 header (-N flag)
221
222 09/19/1991 add write_calfile(), print_dates(),
223 and new print_text() to generate
224 input for Un*x "calendar" utility;
225 renamed old print_text() as
226 print_pstext() for clarity; revised
227 to simplify setting working date
228
229 4.11 AWR 08/23/1991 revise expand_fmt() to write results
230 to string instead of stdout; revise
231 print_word() to avoid writing null
232 strings
233
234 AWR 08/21/1991 use ABBR_DAY_LEN and ABBR_MONTH_LEN
235 (cf. pcallang.h) to print abbreviated
236 day/month names
237
238 AWR 08/21/1991 add %u and %w (calculate week number
239 so that 1/1 is always week 1); support
240 %[+-]<n>[DWMY] to adjust working date
241 by +|- <n> days/weeks/months/years
242
243 4.1 AWR 08/16/1991 Support -G flag (outlined gray dates)
244
245 4.02 AWR 07/02/1991 Added "%" expansions in text strings
246 (cf. expand_fmt())
247
248 4.0 AWR 01/28/1991 Support -B, -w flags and moon file
249
250 01/15/1991 Extracted from pcal.c
251
252 */
253
254 /* ---------------------------------------------------------------------------
255
256 Header Files
257
258 */
259
260 #include <stdio.h>
261 #include <ctype.h>
262 #include <string.h>
263 #include <time.h>
264
265 #include "pcaldefs.h"
266 #include "pcallang.h"
267 #include "protos.h"
268
269 /* Some headers needed for 'user account/real name' (via 'getuid()' etc) in
270 PostScript comments */
271 #if defined (BUILD_ENV_UNIX) || defined (BUILD_ENV_DJGPP)
272 #include <pwd.h>
273 #include <unistd.h>
274 #endif
275
276 /* ---------------------------------------------------------------------------
277
278 Type, Struct, & Enum Declarations
279
280 */
281
282 /* ---------------------------------------------------------------------------
283
284 Constant Declarations
285
286 */
287
288 /* suffix for new 8-bit fonts */
289 #define NEWFONT "-8"
290
291 /* maximum number of fonts to prescale */
292 #define MAXFONT 20
293
294 /* ---------------------------------------------------------------------------
295
296 Macro Definitions
297
298 */
299
300 /* make sure printf() doesn't round "ph" up to 1.0 when printing it */
301 #define PRT_TWEAK(ph) ((ph) >= 0.9995 ? 0.0 : (ph))
302
303 /* advance working date by n days */
304 #define SET_DATE(n) do { \
305 MAKE_DATE(date, work_month, work_day + (n), work_year); \
306 normalize(&date); \
307 work_month = date.mm, work_day = date.dd, work_year = date.yy; \
308 } while (0)
309
310 /* prescale a font and add its name to list */
311 #define ADDFONT(name, size, font, isarray) do { \
312 char *p = alloc(strlen(name) + strlen(size) + 2); \
313 sprintf(allfonts[nfonts++] = p, "%s_%s", name, size); \
314 printf("/%s { %sfontsize ", p, font); \
315 if (isarray) printf("%s get ", size); \
316 printf("%sfont FontFind } def\n", font); \
317 } while (0)
318
319 /* print PostScript string definition */
320 #define PRINT_DEF(name, value) do { \
321 printf("/%s ", name); \
322 print_word(value); \
323 printf(" def\n"); \
324 } while (0)
325
326 /* add specified number of blank lines to HTML table entry */
327 #define BLANKLINES(n) do { \
328 int i; \
329 for (i = 0; i < n; i++) \
330 printf("<br> "); \
331 } while(0)
332
333 #define FOOTSTRINGS() (lfoot[0] || cfoot[0] || rfoot[0])
334
335 /* reset working date to original date */
336 #define RESET_DATE() \
337 work_month = this_month, work_day = this_day, work_year = this_year
338
339 /* 'clean' output */
340 #define PUTCHAR_CLEAN(_fc, _c, _fp) \
341 fprintf((_fp), "%c", (_c) & CHAR_MSK)
342
343 #define PUTSTR_CLEAN(_fc, _s, _fp) \
344 do { char *_p; for (_p = (_s); *_p; _p++) \
345 PUTCHAR_CLEAN(_fc, *_p, _fp); } while (0)
346
347 /* ---------------------------------------------------------------------------
348
349 Data Declarations (including externals)
350
351 */
352
353 /* order of following strings must conform to #define's in pcaldefs.h (q.v.) */
354 static char *cond[3] = {"false", "true", "(some)"};
355
356 static int this_day, this_month, this_year; /* current day */
357 static int work_day, work_month, work_year; /* working day (cf. expand_fmt()) */
358 static char *kw_note, *kw_opt, *kw_year; /* keywords for -c output */
359
360 static int debug_text; /* generate debug output */
361
362 /* ---------------------------------------------------------------------------
363
364 External Routine References & Function Prototypes
365
366 */
367
368 /* ---------------------------------------------------------------------------
369
370 ps_prtday_bw
371
372 Notes:
373
374 none
375
376 */
ps_prtday_bw(void)377 void ps_prtday_bw (void)
378 {
379 printf("%% print 'day' in 'color' (black, gray, outline, or outline-gray; cf. pcaldefs.h)\n");
380 printf("%% using single value in 'dategray' as gray shade\n");
381 printf("%%\n");
382 printf("/prtday { %% black and white version\n");
383 printf(" gsave\n");
384 printf(" day 3 string cvs %% convert day to string\n");
385 printf(" [\n");
386 printf(" { show } %% black (0)\n");
387 printf(" { dategray setgray show } %% gray (1)\n");
388 printf(" { true charpath stroke } %% outline (2)\n");
389 printf(" { true charpath gsave %% outline-gray (3)\n");
390 printf(" dategray setgray fill\n");
391 printf(" grestore stroke }\n");
392 printf(" ] color get exec %% execute operators for color\n");
393 printf(" grestore\n");
394 printf("} bind def\n\n");
395 return;
396 }
397
398 /* ---------------------------------------------------------------------------
399
400 write_psfile
401
402 Notes:
403
404 This routine writes the actual PostScript code.
405
406 The actual output of the PostScript code is straightforward. This
407 routine writes a PostScript header followed by declarations of all the
408 PostScript variables affected by command-line flags and/or language
409 dependencies. It then generates the PostScript boilerplate and finally
410 calls 'print_month()' to generate the PostScript code for each requested
411 month.
412
413 */
write_psfile(void)414 void write_psfile (void)
415 {
416 int i, nfonts, nfsize, copies, dfltsize, color_dates, color_fill;
417 char *p, tmp[STRSIZ], *allfonts[MAXFONT];
418 char time_str[50];
419 time_t curr_tyme;
420
421 #if defined (BUILD_ENV_UNIX) || defined (BUILD_ENV_DJGPP)
422 struct passwd *pw;
423 #endif
424 /* default date, title, weekday font sizes (small/medium/large) */
425 static int dsize[3] = DATEFONTSIZE;
426 static int tsize[3] = TITLEFONTSIZE;
427 static int wsize[3] = WEEKDAYFONTSIZE;
428 static int fsize[3] = FOOTFONTSIZE;
429 static int dmargin[3] = DATEMARGIN;
430 static double gwidth[3] = GRIDLINEWIDTH;
431 static char *calsize[3] = CALSIZE;
432
433 debug_text = DEBUG(DEBUG_TEXT); /* debug text output? */
434
435 /*
436 * Write out PostScript prolog (including version/datefile stamp)
437 */
438
439 /* comment block at top */
440
441 printf("%%!%s\n", PS_RELEASE); /* PostScript release */
442
443
444 /* Get the current date/time so that we can write it into the output file
445 as a timestamp... */
446 time(&curr_tyme);
447
448 /* It seems that neither MS-DOS (Borland C) nor DOS+DJGPP support the '%P'
449 (lowercase 'am'/'pm') specifier, so we'll use '%p' (uppercase 'AM'/'PM')
450 instead. */
451 #if defined (BUILD_ENV_MSDOS) || defined (BUILD_ENV_DJGPP)
452 strftime(time_str, sizeof(time_str), "%d %b %Y (%a) %I:%M:%S%p", localtime(&curr_tyme));
453 #else
454 strftime(time_str, sizeof(time_str), "%d %b %Y (%a) %I:%M:%S%P", localtime(&curr_tyme));
455 #endif
456
457 printf("%%%%CreationDate: %s\n", time_str);
458
459 printf("%%%%Creator: Generated by %s %s (%s)\n", progname, version, PCAL_WEBSITE);
460
461 /* Generate "For" and "Routing" comments if user name is known... */
462
463 #if defined (BUILD_ENV_UNIX) || defined (BUILD_ENV_DJGPP)
464 if ((pw = getpwuid(getuid())) != NULL && strcmp(pw->pw_name, "nobody" /* anonymous account */) != 0) {
465 printf("%%%%For: %s\n", pw->pw_name);
466 #ifdef BUILD_ENV_UNIX
467 /* The 'pw->pw_gecos' element ('real' user name) is not available in
468 MS-DOS or DOS+DJGPP build environments... */
469 strcpy(tmp, pw->pw_gecos);
470 if ((p = strchr(tmp, ',')) != NULL) *p = '\0';
471 printf("%%%%Routing: %s\n", tmp);
472 #endif
473 }
474 #endif
475
476
477
478
479 /* Identify the output (month/year range and input file) */
480
481 if (do_whole_year && init_month == JAN) {
482 printf("%%%%Title: calendar for %d", init_year);
483 if (final_year > init_year) printf(" - %d", final_year);
484 }
485 else {
486 char c = date_style == EUR_DATES ? '.' : '/';
487 printf("%%%%Title: calendar for %02d%c%02d", init_month, c, init_year);
488 if (nmonths > 1) printf(" - %02d%c%02d", final_month, c, final_year);
489 }
490
491 /* If a configuration file was used, show the file's name... */
492 if (*datefile) printf(" (from %s)", datefile);
493
494 printf("\n");
495
496 /* Miscellaneous other identification */
497
498 printf("%%%%Orientation: %s\n", rotate == LANDSCAPE ? "Landscape" : "Portrait");
499 printf("%%%%Pages: %d\n", do_whole_year ? nmonths / 12 : nmonths);
500 printf("%%%%PageOrder: Ascend\n");
501 printf("%%%%BoundingBox: 0 0 %d %d\n", page_dim_short_axis_pts, page_dim_long_axis_pts);
502 printf("%%%%DocumentPaperSizes: %s\n", paper_info[paper_size].name);
503 printf("%%%%ProofMode: NotifyMe\n");
504 printf("%%%%EndComments\n\n");
505
506 /* number of copies (from -#<n> flag) */
507 if ((copies = ncopy) > MAXCOPY) copies = MAXCOPY;
508 if (copies > 1) printf("/#copies %d def\n", copies);
509
510 /* paper size (force appropriate tray if known) */
511
512 /* The following lines force the printer to select the specified paper tray
513 (Letter or A4, as selected in the Makefile). I (AWR) added it to v4.7
514 to fix a problem with one particular HP printer configured for both
515 Letter and A4 size paper: under some conditions, previous A4 jobs would
516 leave the printer in A4 mode unless I forced it back to Letter mode. It
517 is probably unnecessary for most users (and is known to confuse some
518 previewers), so #ifdef it out if you like.
519 */
520 #if 1
521 strcpy(tmp, paper_info[paper_size].name);
522 tmp[0] = tolower(tmp[0]);
523 printf("statusdict /%stray known { statusdict begin %stray end } if\n", tmp, tmp);
524 #endif
525
526 /* calendar sizes: to minimize number of pre-scaled fonts, whole- year
527 calendars define 'medium' as 0 and the other sizes as -1 (not used);
528 single-month calendars define 'large' as 0, 'small' as 1, and 'medium'
529 as -1 (not used)
530 */
531
532 for (i = SMALL; i <= LARGE; i++) {
533 printf("/%s %d def\n", calsize[i], do_whole_year ? (i == MEDIUM ? 0 : -1) :
534 (i == MEDIUM ? -1 : i == SMALL));
535 }
536
537 /* font names and sizes */
538
539 /* v4.4 supports user override of note and large date/title sizes */
540 nfsize = (p = strrchr(notesfont, '/')) ? *p++ = '\0', atoi(p) : atoi(strrchr(NOTESFONT, '/') + 1);
541 dsize[LARGE] = (p = strrchr(datefont, '/')) ? *p++ = '\0', atoi(p) : atoi(strrchr(DATEFONT, '/') + 1);
542 tsize[LARGE] = (p = strrchr(titlefont, '/')) ? *p++ = '\0', atoi(p) : atoi(strrchr(TITLEFONT, '/') + 1);
543
544 /* enlarge footer strings in whole-year/portrait mode */
545 if (do_whole_year && rotate == PORTRAIT) {
546 fsize[MEDIUM] = (int)((double)fsize[MEDIUM] * 1.25);
547 }
548
549 /*
550 if 8-bit remapping has been requested (-r flag), create new fonts with
551 desired character remapping
552 */
553
554 if (mapfonts != ENC_NONE) {
555
556 /* include desired mapping */
557
558 switch (mapfonts) {
559
560 case ENC_LATIN_1: encvec_iso8859_1(); break;
561 case ENC_LATIN_2: encvec_iso8859_2(); break;
562 case ENC_LATIN_3: encvec_iso8859_3(); break;
563 case ENC_LATIN_4: encvec_iso8859_4(); break;
564 case ENC_CYRILLIC: encvec_iso8859_5(); break;
565 case ENC_ARABIC: encvec_iso8859_6(); break;
566 case ENC_GREEK: encvec_iso8859_7(); break;
567 case ENC_HEBREW: encvec_iso8859_8(); break;
568 case ENC_LATIN_5: encvec_iso8859_9(); break;
569 case ENC_LATIN_6: encvec_iso8859_10(); break;
570 case ENC_THAI: encvec_iso8859_11(); break;
571 case ENC_LATIN_7: encvec_iso8859_13(); break;
572 case ENC_LATIN_8: encvec_iso8859_14(); break;
573 case ENC_LATIN_9: encvec_iso8859_15(); break;
574 case ENC_LATIN_10: encvec_iso8859_16(); break;
575 case ENC_KOI8_R: encvec_koi8_r(); break;
576 case ENC_KOI8_U: encvec_koi8_u(); break;
577 case ENC_ROMAN8: encvec_roman8(); break;
578
579 default:
580 fprintf(stderr, "Unknown language encoding (%d) encountered!\n", mapfonts);
581 break;
582 }
583
584 /* Boilerplate PostScript code to remap for 8-bit fonts... */
585
586 printf("/alt_dict 20 dict def %% Local storage\n");
587 printf("\n");
588 printf("%% <oldfont> <newfont> remap_font => --\n");
589 printf("%%\n");
590 printf("%% create remapped font using one of the above 8-bit character remapping tables\n");
591 printf("%%\n");
592 printf("/remap_font {\n");
593 printf(" alt_dict begin\n");
594 printf(" /newName exch def\n");
595 printf(" /oldName exch def\n");
596 printf(" /oldDict oldName findfont def\n");
597 printf(" /newDict oldDict maxlength dict def\n");
598 printf(" oldDict {\n");
599 printf(" exch dup /FID ne {\n");
600 printf(" dup /Encoding eq {\n");
601 printf(" exch dup length array copy\n");
602 printf(" newDict 3 1 roll put\n");
603 printf(" } {\n");
604 printf(" exch newDict 3 1 roll put\n");
605 printf(" } ifelse\n");
606 printf(" } {\n");
607 printf(" pop pop\n");
608 printf(" } ifelse\n");
609 printf(" } forall\n");
610 printf(" newDict /FontName newName put\n");
611 printf(" 0 2 alt_mappings length 1 sub {\n");
612 printf(" dup\n");
613 printf(" alt_mappings exch get\n");
614 printf(" exch 1 add alt_mappings exch get\n");
615 printf(" newDict /Encoding get 3 1 roll put\n");
616 printf(" } for\n");
617 printf(" newName newDict definefont pop\n");
618 printf(" end\n");
619 printf("} bind def\n\n");
620
621 /* always generate code to remap title font */
622 printf("/%s /%s%s remap_font\n", titlefont, titlefont, NEWFONT);
623 strcat(titlefont, NEWFONT);
624
625 #if MAP_DATEFONT /* any text printed in date font (cf. pcaldefs.h)? */
626 /* generate code to remap date font if necessary */
627 printf("FontDirectory /%s%s known not {\n", datefont, NEWFONT);
628 printf("/%s /%s%s remap_font\n", datefont, datefont, NEWFONT);
629 printf("} if\n");
630 strcat(datefont, NEWFONT);
631 #endif
632
633 /* generate code to remap notes font if necessary */
634 printf("FontDirectory /%s%s known not {\n", notesfont, NEWFONT);
635 printf("/%s /%s%s remap_font\n", notesfont, notesfont, NEWFONT);
636 printf("} if\n");
637 strcat(notesfont, NEWFONT);
638 }
639
640 /* define title, notes, and date fonts */
641 printf("/titlefont /%s def\n", titlefont);
642 printf("/datefont /%s def\n", datefont);
643 printf("/notesfont /%s def\n", notesfont);
644
645 /* typically defined in terms of above fonts - must define last */
646 printf("/weekdayfont %s def\n", WEEKDAYFONT);
647 printf("/footfont %s def\n", FOOTFONT);
648 printf("/headingfont %s def\n", HEADINGFONT);
649
650 /* print various font sizes and line/margin widths as PostScript arrays:
651 one element for whole-year calendars; two (large, small) for single-
652 month calendars
653 */
654
655 if (do_whole_year) {
656 printf("/datemargin [ %d ] def\n", dmargin[MEDIUM]);
657 printf("/gridlinewidth [ %.1f ] def\n", gwidth[MEDIUM]);
658 printf("/titlefontsize [ %d ] def\n", tsize[MEDIUM]);
659 printf("/datefontsize [ %d ] def\n", dsize[MEDIUM]);
660 } else {
661 printf("/datemargin [ %d %d ] def\n", dmargin[LARGE], dmargin[SMALL]);
662 printf("/gridlinewidth [ %.1f %.1f ] def\n", gwidth[LARGE], gwidth[SMALL]);
663 printf("/titlefontsize [ %d %d ] def\n", tsize[LARGE], tsize[SMALL]);
664 printf("/datefontsize [ %d %d ] def\n", dsize[LARGE], dsize[SMALL]);
665 }
666
667 dfltsize = do_whole_year ? MEDIUM : LARGE;
668
669 printf("/weekdayfontsize %d def\n", wsize[dfltsize]);
670 printf("/footfontsize %d def\n", fsize[dfltsize]);
671
672 /* Now that we allow Julian dates to be displayed on yearly-format
673 calendars, we must tweak the font size of the 'notes font' accordingly,
674 since that font is also used to display the Julian dates (if enabled by
675 the user)...
676 */
677 printf("/notesfontsize %d def\n", do_whole_year ? 24 : nfsize);
678
679 printf("/headingfontsize %d def\n", HEADINGFONTSIZE);
680
681 /* pre-scale all fonts used by PostScript code; try to be smart about
682 skipping those that we know (at this point) won't be needed (whole-year
683 calendars use either 3 or 4 fonts, while single-month calendars can use
684 anywhere from 3 to 8). "FF" et. al. are indices into the font array (cf
685 pcalinit.ps) for the different font types.
686 */
687
688 printf("/FontFind { findfont exch scalefont } def\n");
689
690 nfonts = 0;
691
692 printf("/FF %d def\n", nfonts); /* footers */
693 if (FOOTSTRINGS()) ADDFONT("ff", calsize[dfltsize], "foot", FALSE);
694
695 if (do_whole_year) {
696 printf("/TF %d def\n", nfonts); /* month/year title */
697 ADDFONT("tf", calsize[MEDIUM], "title", TRUE);
698
699 printf("/DF %d def\n", nfonts); /* dates */
700 ADDFONT("df", calsize[MEDIUM], "date", TRUE);
701 }
702 else {
703 printf("/HF %d def\n", nfonts); /* 'Notes' heading */
704 if (notes_hdr[0]) ADDFONT("hf", calsize[LARGE], "heading", FALSE);
705
706 /* large/small (if used) scalings of the same font must be
707 * contiguous and appear in that order
708 */
709
710 printf("/TF %d def\n", nfonts); /* large/small title */
711 ADDFONT("tf", calsize[LARGE], "title", TRUE);
712 if (small_cal_pos != SC_NONE) ADDFONT("tf", calsize[SMALL], "title", TRUE);
713
714 printf("/DF %d def\n", nfonts); /* large/small dates */
715 ADDFONT("df", calsize[LARGE], "date", TRUE);
716 if (small_cal_pos != SC_NONE) ADDFONT("df", calsize[SMALL], "date", TRUE);
717 }
718
719 printf("/NF %d def\n", nfonts); /* text for note-boxes and/or Julian dates */
720 ADDFONT("nf", calsize[LARGE], "notes", FALSE);
721
722
723 printf("/WF %d def\n", nfonts); /* weekdays */
724 ADDFONT("wf", calsize[dfltsize], "weekday", FALSE);
725
726 /* generate the font array (automatically in sync with above) */
727
728 printf("/allfonts [\n\t");
729 for (i = 0; i < nfonts; i++) {
730 printf("%s ", allfonts[i]);
731 free(allfonts[i]);
732 }
733 printf("\n] def\n");
734
735 /*
736 Define various strings and numeric values used by Pcal
737 */
738
739 /* month names */
740
741 printf("/month_names [");
742 for (i = JAN; i <= DEC; i++) {
743 printf(i % 6 == 1 ? "\n\t" : " ");
744 (void) print_word(months_ml[output_language][i-1]);
745 }
746 printf(" ] def\n");
747
748 /* day names - abbreviate if printing entire year on page */
749
750 printf("/day_names [");
751 for (i = SUN; i <= SAT; i++) {
752 printf((i % 6 == 0 && !do_whole_year) ? "\n\t" : " ");
753 if (do_whole_year) {
754 strcpy(tmp, days_ml_short[output_language][(i + first_day_of_week) % 7]);
755 }
756 else {
757 strcpy(tmp, days_ml[output_language][(i + first_day_of_week) % 7]);
758 }
759 (void) print_word(tmp);
760 }
761 printf(" ] def\n");
762
763 /* line separator and font change strings */
764
765 PRINT_DEF("linesep", LINE_SEP);
766 PRINT_DEF("boldfont", BOLD_FONT);
767 PRINT_DEF("italicfont", ITALIC_FONT);
768 PRINT_DEF("romanfont", ROMAN_FONT);
769
770 /* page width and height (always referenced to 'portrait' mode) */
771
772 printf("/pagewidth %d def\n", page_dim_short_axis_pts);
773 printf("/pageheight %d def\n", page_dim_long_axis_pts);
774
775 /* rotation, scaling, and translation factors */
776
777 printf("/rval %d def\n", rotate);
778 printf("/xsval %.3f def\n/ysval %.3f def\n", xsval_pgm * xsval_user, ysval_pgm * ysval_user);
779 printf("/xtval %d def\n/ytval %d def\n", xtval_pgm + xtval_user, ytval_pgm + ytval_user);
780 printf("/ytop %d def\n", -TOP_OF_CAL_BOXES_PTS);
781
782 /* date box dimensions */
783
784 printf("/daywidth %d def\n", daybox_width_pts);
785 printf("/dayheight %d def\n", daybox_height_pts);
786
787 /* moon, Julian date, and box fill flags */
788
789 printf("/draw-moons %s def\n", cond[draw_moons]);
790 printf("/julian-dates %s def\n", cond[julian_dates]);
791 printf("/fill-boxes %s def\n", cond[! blank_boxes]);
792
793 /* position of small calendars */
794
795 printf("/prev_small_cal %d def\n", prev_cal_box[small_cal_pos]);
796 printf("/next_small_cal %d def\n", next_cal_box[small_cal_pos]);
797
798 /* date and fill box shading values */
799
800 strcpy(tmp, shading);
801 *(p = strchr(tmp, '/')) = '\0';
802 printf("/dategray %s def\n", set_rgb(tmp));
803 color_dates = strchr(tmp, RGB_CHAR) != NULL;
804 printf("/fillgray %s def\n", set_rgb(++p));
805 color_fill = strchr(p, RGB_CHAR) != NULL;
806
807 /* PostScript boilerplate (part 1 of 1) */
808
809 printf("/Y0 0 def %% Y-coordinate of calendar grid origin\n");
810 printf("\n");
811 printf("/gridwidth daywidth 7 mul def\n");
812 printf("/gridheight dayheight 6 mul def\n");
813 printf("/negdaywidth daywidth neg def\n");
814 printf("/negdayheight dayheight neg def\n");
815 printf("/neggridwidth gridwidth neg def\n");
816 printf("/neggridheight gridheight neg def\n");
817 printf("\n");
818 printf("/textmargin 2 def %% left/right margin for text\n");
819 printf("/notemargin 4 def %% left/right margin for notes\n");
820 printf("/charlinewidth 0.1 def %% width of outline characters\n");
821 printf("\n");
822 printf("/datewidth 2 array def %% for aligning holiday text\n");
823 printf("\n");
824 printf("/moonlinewidth 0.1 def %% width of moon icon line\n");
825
826 /*
827
828 Moon icon radius is actually larger (due to scaling, I suppose) for a
829 yearly-format calendar...
830
831 */
832 printf("/radius %d def %% radius of moon icon\n", do_whole_year ? 12 : 6);
833
834 printf("/halfperiod 0.5 def %% scale factors, etc. used by 'domoon'\n");
835 printf("/quartperiod 0.25 def\n");
836 printf("/offset radius datemargin 0 get add def\n");
837 printf("/rect radius 2 sqrt mul quartperiod div def\n");
838 printf("\n");
839 printf("/ROMAN 0 def %% font types for text in boxes\n");
840 printf("/BOLD 1 def\n");
841 printf("/ITALIC 2 def\n");
842 printf("/currfonttype ROMAN def\n");
843 printf("/boldoffset 0.5 def %% offset for overstriking bold text\n");
844 printf("\n");
845 printf("%% simulate Italic text by slanting Roman text - note that this is properly\n");
846 printf("%% called 'Oblique'\n");
847 printf("\n");
848 printf("/italicangle 12 def %% angle for slanting Italic text\n");
849 printf("/italicmatrix %% Italic font matrix\n");
850 printf(" [notesfontsize 0 notesfontsize italicangle dup sin exch cos div mul\n");
851 printf(" notesfontsize 0 0] def\n");
852 printf("\n");
853 printf("/hangingindent ( ) def %% for indenting continued text lines\n");
854 printf("\n");
855 printf("%% disable duplex mode (if supported)\n");
856 printf("statusdict (duplexmode) known { statusdict begin false setduplexmode end } if\n");
857 printf("\n");
858 printf("%%\n");
859 printf("%% Utility functions:\n");
860 printf("%%\n");
861 printf("\n");
862 printf("%% <fontposition> FontSet => --\n");
863 printf("%%\n");
864 printf("%% set current font to element <fontposition> of 'allfonts' array\n");
865 printf("%%\n");
866 printf("/FontSet {\n");
867 printf(" allfonts exch\n");
868 printf(" userdict /CurrentFontSet 2 index put\n");
869 printf(" get setfont\n");
870 printf("} bind def\n\n");
871
872 printf("%% <size> *FontSet => --\n");
873 printf("%%\n");
874 printf("%% fetch pre-scaled font (of desired calendar size) from 'allfonts' array\n");
875 printf("%%\n");
876 printf("/TitleFontSet {TF add FontSet} bind def\n");
877 printf("\n");
878 printf("/DateFontSet {DF add FontSet} bind def\n");
879 printf("\n");
880 printf("%% -- *FontSet => --\n");
881 printf("%%\n");
882 printf("%% fetch pre-scaled font (of constant size) from 'allfonts' array\n");
883 printf("%%\n");
884 printf("/WeekdayFontSet {WF FontSet} bind def\n");
885 printf("\n");
886 printf("/FootFontSet {FF FontSet} bind def\n");
887 printf("\n");
888 printf("/NotesFontSet {NF FontSet /currfonttype ROMAN def} bind def\n");
889 printf("\n");
890 printf("/HeadingFontSet {HF FontSet} bind def\n");
891 printf("\n");
892 printf("%% <string> <width> center => --\n");
893 printf("%%\n");
894 printf("%% display <string> centered horizontally in <width>\n");
895 printf("%%\n");
896 printf("/center {\n");
897 printf(" 1 index stringwidth pop sub 2 div 0 rmoveto show\n");
898 printf("} bind def\n\n");
899
900 /* These 2 routines ('left' and 'right') were added to support the '-W'
901 option (horizontal alignment of title on monthly-format calendar).
902 */
903 printf("%% <string> <width> left => --\n");
904 printf("%%\n");
905 printf("%% display <string> justified left in <width>\n");
906 printf("%%\n");
907 printf("/left {\n");
908 printf(" 1 index show\n");
909 printf("} bind def\n\n");
910 printf("%% <string> <width> right => --\n");
911 printf("%%\n");
912 printf("%% display <string> justified right in <width>\n");
913 printf("%%\n");
914 printf("/right {\n");
915 printf(" 1 index stringwidth pop sub 0 rmoveto show\n");
916 printf("} bind def\n\n");
917
918 printf("%% <str1> <str2> strcat => <string>\n");
919 printf("%%\n");
920 printf("%% concatenate <str1> and <str2>; push result onto stack\n");
921 printf("%%\n");
922 printf("/strcat {\n");
923 printf(" 2 copy\n");
924 printf(" length exch length\n");
925 printf(" dup 3 -1 roll add\n");
926 printf(" string\n");
927 printf(" dup 0 6 -1 roll putinterval\n");
928 printf(" dup 3 -1 roll 4 -1 roll putinterval\n");
929 printf("} bind def\n\n");
930
931
932 printf("%% -- nextbox => --\n");
933 printf("%%\n");
934 printf("%% move to same relative position within following day's box\n");
935 printf("%%\n");
936 printf("/nextbox {\n");
937 printf(" day startbox add 7 mod 0 eq %% end of week?\n");
938 printf(" { neggridwidth daywidth add negdayheight rmoveto } %% next row\n");
939 printf(" { daywidth 0 rmoveto } %% next col\n");
940 printf(" ifelse\n");
941 printf("} bind def\n\n");
942
943 printf("%% <box> boxpos => <x> <y>\n");
944 printf("%%\n");
945 printf("%% calculate and push coordinates of upper-left corner of <box> (0..41)\n");
946 printf("%%\n");
947 printf("/boxpos {\n");
948 printf(" dup 7 mod daywidth mul %% x-coord\n");
949 printf(" exch 7 idiv negdayheight mul Y0 add %% y-coord\n");
950 printf("} bind def\n\n");
951
952 printf("%% <day> datepos => <x> <y>\n");
953 printf("%%\n");
954 printf("%% calculate and push coordinates of upper-left corner of box for <day>\n");
955 printf("%%\n");
956 printf("/datepos {\n");
957 printf(" startbox add 1 sub dup 7 mod daywidth mul %% x-coord\n");
958 printf(" exch 7 idiv negdayheight mul Y0 add %% y-coord\n");
959 printf("} bind def\n\n");
960
961 printf("%%\n");
962 printf("%% Functions for drawing components of calendar:\n");
963 printf("%%\n");
964 printf("%% The point size of a PostScript font includes the descenders on [gjpqy],\n");
965 printf("%% but the Y-origin for printing text starts above any descenders (at the\n");
966 printf("%% bottom of the upper-case characters). The following code - and other\n");
967 printf("%% code concerned with vertical spacing - assumes that the descenders\n");
968 printf("%% occupy 1/4 of the overall point size.\n\n");
969
970 printf("%% -- drawtitle => --\n");
971 printf("%%\n");
972 printf("%% print month/year title centered at top of calendar\n");
973 printf("%%\n");
974 printf("/drawtitle {\n");
975 printf(" /fontsize titlefontsize calsize get def\n");
976 printf(" calsize TitleFontSet\n");
977 printf(" /month_name month_names month 1 sub get def\n");
978 printf(" /yearstring year 10 string cvs def\n");
979 printf(" 0 Y0 fontsize 0.25 mul add\n");
980 printf(" calsize small eq { 4 } { weekdayfontsize } ifelse\n");
981 printf(" 1.15 mul add moveto\n");
982 printf(" month_name ( ) strcat yearstring strcat gridwidth %s\n", title_align);
983 printf("} bind def\n\n");
984
985 printf("%% -- drawdaynames => --\n");
986 printf("%%\n");
987 printf("%% print weekday names centered above respective columns\n");
988 printf("%%\n");
989 printf("/drawdaynames { \n");
990 printf(" WeekdayFontSet\n");
991 printf(" 0 1 6 {\n");
992 printf(" /i exch def\n");
993 printf(" i daywidth mul Y0 weekdayfontsize 0.4 mul add moveto\n");
994 printf(" day_names i get\n");
995 printf(" daywidth center\n");
996 printf(" } for\n");
997 printf("} bind def\n\n");
998
999 printf("%% -- drawgrid => --\n");
1000 printf("%%\n");
1001 printf("%% draw the grid (6 rows x 7 columns) for the calendar\n");
1002 printf("%%\n");
1003 printf("/drawgrid {\n");
1004 printf(" gridlinewidth calsize get setlinewidth\n");
1005 printf("\n");
1006 printf(" 1 1 6 { %% inner vertical lines\n");
1007 printf(" daywidth mul Y0 moveto\n");
1008 printf(" 0 neggridheight rlineto\n");
1009 printf(" stroke\n");
1010 printf(" } for\n");
1011 printf("\n");
1012 printf(" 1 1 5 { %% inner horizontal lines\n");
1013 printf(" 0 exch negdayheight mul Y0 add moveto\n");
1014 printf(" gridwidth 0 rlineto\n");
1015 printf(" stroke\n");
1016 printf(" } for\n");
1017 printf("\n");
1018 printf(" newpath %% border (w/mitered corners)\n");
1019 printf(" 0 Y0 moveto\n");
1020 printf(" gridwidth 0 rlineto\n");
1021 printf(" 0 neggridheight rlineto\n");
1022 printf(" neggridwidth 0 rlineto\n");
1023 printf(" closepath\n");
1024 printf(" stroke\n");
1025 printf("} bind def\n\n");
1026
1027 printf("%% -- drawnums => --\n");
1028 printf("%%\n");
1029 printf("%% print dates in appropriate boxes of calendar\n");
1030 printf("%%\n");
1031 printf("/drawnums {\n");
1032 printf(" /fontsize datefontsize calsize get def\n");
1033 printf(" /margin datemargin calsize get def\n");
1034 printf(" calsize DateFontSet\n");
1035 printf(" charlinewidth setlinewidth\n");
1036 printf(" 1 datepos fontsize 0.75 mul margin add sub exch margin add exch moveto\n");
1037 printf("\n");
1038 printf(" %% calculate date widths for 'holidaytext' to use; use '2' for all\n");
1039 printf(" %% single-digit dates and '22' for all double-digit dates\n");
1040 printf(" calsize large eq {\n");
1041 printf(" datewidth 0 (2) stringwidth pop margin 2 mul add put\n");
1042 printf(" datewidth 1 (22) stringwidth pop margin 2 mul add put\n");
1043 printf(" } if\n");
1044 printf("\n");
1045 printf(" calsize small eq {\n");
1046 printf(" /color 0 def %% small calendar dates (all black)\n");
1047 printf(" 1 1 ndays {\n");
1048 printf(" /day exch def\n");
1049 printf(" prtday\n");
1050 printf(" nextbox\n");
1051 printf(" } for\n");
1052 printf(" } {\n");
1053 printf(" 1 1 ndays { %% medium/large calendar dates\n");
1054 printf(" /day exch def\n");
1055 printf(" /color date_color day get def\n");
1056 printf(" prtday\n");
1057 printf(" nextbox\n");
1058 printf(" } for\n");
1059 printf(" } ifelse\n");
1060 printf("} bind def\n\n");
1061
1062 printf("%% -- startpage => --\n");
1063 printf("%%\n");
1064 printf("%% initialize new physical page\n");
1065 printf("%%\n");
1066 printf("/startpage {\n");
1067 printf(" rval rotate\n");
1068 printf(" xsval ysval scale\n");
1069 printf(" xtval ytval translate\n");
1070 printf("} bind def\n\n");
1071
1072 printf("%% -- calendar => --\n");
1073 printf("%%\n");
1074 printf("%% draw calendar for 'month'/'year', with various features enabled/disabled\n");
1075 printf("%% according to 'calsize'\n");
1076 printf("%%\n");
1077 printf("/calendar {\n");
1078 printf(" drawtitle %% month/year\n");
1079 printf(" calsize small ne { drawdaynames } if %% weekday names\n");
1080 printf(" calsize large eq { footstrings } if %% footer strings\n");
1081 printf(" drawnums %% dates\n");
1082
1083 /*
1084
1085 On yearly-format calendars, eliminate the test 'calsize == large', so
1086 that Julian dates get drawn when enabled by user...
1087
1088 */
1089 if (do_whole_year) {
1090 printf(" julian-dates false ne { drawjnums } if %% Julian dates\n");
1091 }
1092 else {
1093 printf(" calsize large eq %% Julian dates\n");
1094 printf(" julian-dates false ne and { drawjnums } if\n");
1095 }
1096
1097 printf(" fill-boxes { drawfill } if %% fill boxes\n");
1098 printf(" drawgrid %% grid\n");
1099
1100 /*
1101
1102 On yearly-format calendars, eliminate the test 'calsize == large', so
1103 that moon icons get drawn when enabled by user...
1104
1105 */
1106 if (do_whole_year) {
1107 printf(" draw-moons false ne { drawmoons } if %% moon icons\n");
1108 }
1109 else {
1110 printf(" calsize large eq %% moon icons\n");
1111 printf(" draw-moons false ne and { drawmoons } if \n");
1112 }
1113
1114 printf(" 0 0 moveto\n");
1115 printf("} bind def\n\n");
1116
1117
1118 /* Additional PostScript code tailored to this calendar */
1119
1120 #ifdef OUTLINE_BLACK
1121 /* if OUTLINE_BLACK is defined, include an alternate prtday{} which prints
1122 outlined dates (-O) in black instead of the specified color; otherwise
1123 use the standard prtday{}
1124 */
1125
1126 if (color_dates) {
1127 printf("%% print 'day' in 'color' (black, gray, outline, or outline-gray; cf. pcaldefs.h)\n");
1128 printf("%% using three values in 'dategray' as red/green/blue levels; 'gray' will\n");
1129 printf("%% print as solid color, 'outline' as black outline; 'outline-gray' as black\n");
1130 printf("%% outline filled with color\n");
1131 printf("%%\n");
1132 printf("/prtday { %% color version\n");
1133 printf(" gsave\n");
1134 printf(" day 3 string cvs %% convert day to string\n");
1135 printf(" [\n");
1136 printf(" { show } %% black (0)\n");
1137 printf(" { dategray aload pop setrgbcolor show } %% gray (1)\n");
1138 printf(" { true charpath stroke } %% outline (2; black)\n");
1139 printf(" { true charpath gsave %% outline-gray (3)\n");
1140 printf(" dategray aload pop setrgbcolor\n");
1141 printf(" fill grestore stroke }\n");
1142 printf(" ] color get exec %% execute operators for color\n");
1143 printf(" grestore\n");
1144 printf("} bind def\n\n");
1145 }
1146 else {
1147 ps_prtday_bw();
1148 }
1149
1150 #else
1151
1152 if (color_dates) {
1153 printf("%% print 'day' in 'color' (black, gray, outline, or outline-gray; cf. pcaldefs.h)\n");
1154 printf("%% using three values in 'dategray' as red/green/blue levels; 'gray' will\n");
1155 printf("%% print as solid color, 'outline' as color outline; 'outline-gray' as black\n");
1156 printf("%% outline filled with color\n");
1157 printf("%%\n");
1158 printf("/prtday { %% color version\n");
1159 printf(" gsave\n");
1160 printf(" day 3 string cvs %% convert day to string\n");
1161 printf(" [\n");
1162 printf(" { show } %% black (0)\n");
1163 printf(" { dategray aload pop setrgbcolor show } %% gray (1)\n");
1164 printf(" { dategray aload pop setrgbcolor %% outline (2; color)\n");
1165 printf(" true charpath stroke }\n");
1166 printf(" { true charpath gsave %% outline-gray (3)\n");
1167 printf(" dategray aload pop setrgbcolor\n");
1168 printf(" fill grestore stroke }\n");
1169 printf(" ] color get exec %% execute operators for color\n");
1170 printf(" grestore\n");
1171 printf("} bind def\n\n");
1172 }
1173 else {
1174 ps_prtday_bw();
1175 }
1176
1177 #endif
1178
1179 /*
1180 pcal will generate the appropriate flavor (B&W or color) of setfill{}
1181 depending on the shading values (gray scale or RGB triple) specified by
1182 the "-s" flag
1183 */
1184 if (color_fill) {
1185 printf("%% set fill box color using three values in 'fillgray' as red/green/blue levels\n");
1186 printf("%%\n");
1187 printf("/setfill { %% color version\n");
1188 printf(" fillgray aload pop setrgbcolor\n");
1189 printf("} def\n\n");
1190 }
1191 else {
1192 printf("%% set fill box shading using single value in 'fillgray'\n");
1193 printf("%%\n");
1194 printf("/setfill { %% black and white version\n");
1195 printf(" fillgray setgray\n");
1196 printf("} def\n\n");
1197 }
1198
1199
1200 if (FOOTSTRINGS()) {
1201 /* at least one foot string */
1202 printf("%% -- footstrings => --\n");
1203 printf("%%\n");
1204 printf("%% print foot strings ([LCR]footstring) at bottom of page\n");
1205 printf("%%\n");
1206 printf("/footstrings {\n");
1207 printf(" FootFontSet\n");
1208 printf("%% /yfoot { Y0 neggridheight add footfontsize 1.25 mul sub } bind def\n");
1209 printf(" /yfoot { neggridheight 15 sub } bind def\n");
1210 printf(" 0 yfoot moveto\n");
1211 printf(" Lfootstring show\n");
1212 printf(" gridwidth Rfootstring stringwidth pop sub yfoot moveto\n");
1213 printf(" Rfootstring show\n");
1214 printf(" 0 yfoot moveto\n");
1215 printf(" Cfootstring gridwidth center\n");
1216 printf("} bind def\n\n");
1217 }
1218 else {
1219 /* no foot strings */
1220 printf("/footstrings {} bind def\n\n");
1221 }
1222
1223 if (blank_boxes) {
1224 /* blank fill boxes */
1225 printf("/drawfill {} bind def\n\n");
1226 }
1227 else {
1228 /* shaded fill boxes */
1229 printf("%% <first> <last> fillboxes => --\n");
1230 printf("%%\n");
1231 printf("%% fill empty calendar boxes in range <first>..<last> (0..41)\n");
1232 printf("%%\n");
1233 printf("/fillboxes {\n");
1234 printf(" /last exch def\n");
1235 printf(" /first exch def\n");
1236 printf("\n");
1237 printf(" first 1 last { %% loop through range of boxes\n");
1238 printf(" /box exch def\n");
1239 printf(" /fillit true def\n");
1240 printf(" calsize large eq { %% skip note and small calendar boxes\n");
1241 printf(" noteboxes { box eq { /fillit false def } if } forall\n");
1242 printf(" box prev_small_cal eq box next_small_cal eq or {\n");
1243 printf(" /fillit false def\n");
1244 printf(" } if\n");
1245 printf(" } if\n");
1246 printf(" fillit { %% move to position and fill the box\n");
1247 printf(" box boxpos moveto\n");
1248 printf(" gsave\n");
1249 printf(" setfill\n");
1250 printf(" daywidth 0 rlineto\n");
1251 printf(" 0 negdayheight rlineto\n");
1252 printf(" negdaywidth 0 rlineto\n");
1253 printf(" closepath fill\n");
1254 printf(" grestore\n");
1255 printf(" } if\n");
1256 printf(" } for\n");
1257 printf("} bind def\n\n");
1258
1259 printf("%% -- drawfill => --\n");
1260 printf("%%\n");
1261 printf("%% fill in unused boxes before and after calendar dates\n");
1262 printf("%%\n");
1263 printf("/drawfill {\n");
1264 printf(" 0 startbox 1 sub fillboxes %% fill boxes before calendar\n");
1265 printf(" startbox ndays add 41 fillboxes %% fill boxes after calendar\n");
1266 printf("} bind def\n\n");
1267 }
1268
1269 if (do_whole_year) {
1270
1271 if (rotate == LANDSCAPE) {
1272 /* medium months (landscape) */
1273 printf("/hspace gridwidth daywidth add def %% one date box space between calendars\n");
1274 printf("/hscale 0.2258 def %% 1 / (4 + 3/7)\n");
1275 printf("/vspace gridheight ytop add 135 add neg def %% vertical spacing\n");
1276 printf("/vscale .25 def %% beats me how this was calculated\n");
1277 printf("\n");
1278 printf("/printmonth {\n");
1279 printf(" /calsize medium def\n");
1280 printf("\n");
1281 printf(" posn 0 eq { %% assume first month printed on page is posn 0\n");
1282 printf(" startpage\n");
1283 printf(" footstrings\n");
1284 printf(" } if\n");
1285 printf("\n");
1286 printf(" gsave %% draw medium calendar at selected position\n");
1287 printf(" hscale vscale scale %% landscape mode - 3 rows, 4 cols\n");
1288 printf(" posn 4 mod hspace mul\n");
1289 printf(" posn 4 idiv vspace mul 45 add\n");
1290 printf(" translate\n");
1291 printf(" calendar\n");
1292 printf(" grestore\n");
1293 printf("} bind def\n\n");
1294 }
1295 else {
1296 /* medium months (portrait) */
1297 printf("/hspace gridwidth daywidth add def %% one date box space between calendars\n");
1298 printf("/hscale 0.3043 def %% 1 / (3 + 2/7)\n");
1299 printf("/vspace gridheight ytop add 135 add neg def %% vertical spacing\n");
1300 printf("/vscale .194 def %% beats me how this was calculated\n");
1301 printf("\n");
1302 printf("/printmonth { \n");
1303 printf(" /calsize medium def\n");
1304 printf("\n");
1305 printf(" posn 0 eq { %% assume first month printed on page is posn 0\n");
1306 printf(" gsave %% print foot strings at original scale\n");
1307 printf(" startpage\n");
1308 printf(" 0 25 translate %% reposition footstrings slightly higher\n");
1309 printf(" footstrings\n");
1310 printf(" grestore\n");
1311 printf("\n");
1312 printf(" /sv_ysval ysval def %% re-scale Y axis to full page height\n");
1313 printf(" /ysval xsval pageheight pagewidth div dup mul mul def\n");
1314 printf(" startpage\n");
1315 printf(" /ysval sv_ysval def\n");
1316 printf(" } if\n");
1317 printf("\n");
1318 printf(" gsave %% draw medium calendar at selected position\n");
1319 printf(" hscale vscale scale %% portrait mode - 4 rows, 3 cols\n");
1320 printf(" posn 3 mod hspace mul\n");
1321 printf(" posn 3 idiv vspace mul 155 add\n");
1322 printf(" translate\n");
1323 printf(" calendar\n");
1324 printf(" grestore\n");
1325 printf("} bind def\n\n");
1326 }
1327 }
1328 else {
1329 if (head) {
1330 /* date text */
1331 printf("%%\n");
1332 printf("%% Functions for printing text (and/or images) inside boxes:\n");
1333 printf("%%\n");
1334 printf("\n");
1335 printf("%% <day> <text> daytext => --\n");
1336 printf("%%\n");
1337 printf("%% print <text> in <day> box (below date)\n");
1338 printf("%%\n");
1339 printf("/daytext {\n");
1340 printf(" /mytext exch def /day exch def\n");
1341 printf(" NotesFontSet\n");
1342 printf(" day datepos datefontsize large get 0.75 mul datemargin large get\n");
1343 printf(" 2 mul add notesfontsize 0.75 mul add sub dup\n");
1344 printf(" /ypos exch def exch textmargin add exch moveto\n");
1345 printf(" currentpoint pop /LM exch def\n");
1346 printf(" /RM LM daywidth textmargin 2 mul sub add def\n");
1347 printf(" showtext\n");
1348 printf("} bind def\n\n");
1349
1350 printf("%% <day> <text> holidaytext => --\n");
1351 printf("%%\n");
1352 printf("%% print <text> in <day> box (to right of date)\n");
1353 printf("%%\n");
1354 printf("/holidaytext {\n");
1355 printf(" /mytext exch def /day exch def\n");
1356 printf("\n");
1357 printf(" /datesize datefontsize large get def \n");
1358 printf(" /margin datemargin large get def\n");
1359 printf(" /dwidth datewidth day 10 lt { 0 } { 1 } ifelse get def\n");
1360 printf("\n");
1361 printf(" %% display the text between the date and the moon icon (if any)\n");
1362 printf(" NotesFontSet\n");
1363 printf(" day datepos margin notesfontsize 0.75 mul add sub dup\n");
1364 printf(" /ypos exch def exch dwidth add exch moveto\n");
1365 printf(" currentpoint pop /LM exch def\n");
1366 printf(" /mwidth do-moon-p {offset radius add} {0} ifelse def %% moon width\n");
1367 printf(" /RM LM daywidth textmargin sub dwidth mwidth add sub add def\n");
1368 printf(" showtext\n");
1369 printf("} bind def\n\n");
1370
1371 printf("%% <box> <text> notetext => --\n");
1372 printf("%%\n");
1373 printf("%% print notes heading (if any) and <text> in <box> (0..41)\n");
1374 printf("%%\n");
1375 printf("/notetext {\n");
1376 printf(" /mytext exch def /box exch def\n");
1377 printf("\n");
1378 printf(" %% skip notes box heading if null\n");
1379 printf(" notesheading () eq {\n");
1380 printf(" box boxpos notemargin notesfontsize 0.75 mul add sub dup\n");
1381 printf(" } {\n");
1382 printf(" box boxpos notemargin headingfontsize 0.75 mul add sub exch\n");
1383 printf(" notemargin add exch moveto\n");
1384 printf(" HeadingFontSet\n");
1385 printf(" notesheading show\n");
1386 printf(" box boxpos notemargin headingfontsize add\n");
1387 printf(" notesfontsize add sub dup\n");
1388 printf(" } ifelse\n");
1389 printf(" /ypos exch def exch notemargin add exch moveto\n");
1390 printf("\n");
1391 printf(" %% display the notes text\n");
1392 printf(" NotesFontSet\n");
1393 printf(" /LM currentpoint pop def\n");
1394 printf(" /RM LM daywidth notemargin 2 mul sub add def\n");
1395 printf(" showtext\n");
1396 printf("} bind def\n\n");
1397
1398 printf("%% -- crlf => --\n");
1399 printf("%%\n");
1400 printf("%% simulate carriage return/line feed sequence\n");
1401 printf("%%\n");
1402 printf("/crlf {\n");
1403 printf(" ypos notesfontsize sub /ypos exch def LM ypos moveto\n");
1404 printf("} bind def\n\n");
1405
1406 printf("%% <string> prstr => --\n");
1407 printf("%%\n");
1408 printf("%% print <string> on current line if possible; otherwise print on next line\n");
1409 printf("%%\n");
1410 printf("/prstr {\n");
1411 printf(" gsave %% in case rescaling is necessary\n");
1412 printf(" dup stringwidth pop currentpoint pop add RM gt { %% too wide?\n");
1413 printf(" %% move to the next line (unless this is the initial word)\n");
1414 printf(" currentpoint pop LM ne {\n");
1415 printf(" crlf hangingindent show\n");
1416 printf(" } if\n");
1417 printf(" %% rescale in X dimension as necessary to make word fit\n");
1418 printf(" dup stringwidth pop RM currentpoint pop sub gt {\n");
1419 printf(" dup stringwidth pop RM currentpoint pop sub exch div 1\n");
1420 printf(" scale\n");
1421 printf(" } if\n");
1422 printf(" } if\n");
1423 printf(" currfonttype BOLD eq\n");
1424 printf(" { dup currentpoint 2 index show exch 0.5 add exch moveto show }\n");
1425 printf(" { show }\n");
1426 printf(" ifelse\n");
1427 printf(" currentpoint grestore moveto %% grestore alone would reset position\n");
1428 printf("} bind def\n\n");
1429
1430 printf("%% -- showtext => --\n");
1431 printf("%%\n");
1432 printf("%% print words in 'mytext', inserting line breaks where necessary (or requested)\n");
1433 printf("%%\n");
1434 printf("/showtext {\n");
1435 printf(" mytext {\n");
1436 printf(" /word exch def\n");
1437 printf(" /i 1 def\n");
1438 printf(" /n 0 def\n");
1439 printf("\n");
1440 printf(" %% look up current word in list of special formatting strings\n");
1441 printf(" [ linesep boldfont italicfont romanfont ] {\n");
1442 printf(" word eq {\n");
1443 printf(" /n i def\n");
1444 printf(" } if\n");
1445 printf(" /i i 1 add def\n");
1446 printf(" } forall\n");
1447 printf(" \n");
1448 printf(" %% n will be 0 if current word is an ordinary word, 1..4 if it\n");
1449 printf(" %% is a formatting string; execute corresponding block of code\n");
1450 printf(" [\n");
1451 printf(" { word prstr ( ) show } %% ordinary word\n");
1452 printf("\n");
1453 printf(" { crlf %% line separator (.p)\n");
1454 printf(" NotesFontSet } %% reset font type\n");
1455 printf("\n");
1456 printf(" { NotesFontSet %% font => bold (.b)\n");
1457 printf(" /currfonttype BOLD def }\n");
1458 printf("\n");
1459 printf(" { /currfonttype ITALIC def %% font => italic (.i)\n");
1460 printf(" notesfont findfont italicmatrix makefont setfont }\n");
1461 printf("\n");
1462 printf(" { NotesFontSet } %% font => roman (.r)\n");
1463 printf(" ] n get exec\n");
1464 printf(" } forall\n");
1465 printf("} bind def\n\n");
1466
1467 printf("%% <day> <text> <EPS-image-filename> <x-scaling> <y-scaling> <x-offset> <y-offset> epsimage => --\n");
1468 printf("%%\n");
1469 printf("%% print image <EPS-image-filename> in <day> box (below date)\n");
1470 printf("%%\n");
1471 printf("/epsimage {\n");
1472 printf(" /yoffset exch def /xoffset exch def\n");
1473 printf(" /yscale exch def /xscale exch def\n");
1474 printf(" /myimage exch def /mytext exch def /day exch def\n");
1475 printf(" NotesFontSet\n");
1476 printf(" day datepos datefontsize large get 0.75 mul datemargin large get\n");
1477 printf(" 2 mul add\n");
1478 printf(" sub dup\n");
1479 printf(" /ypos exch def exch textmargin add exch\n");
1480 printf(" moveto\n");
1481 printf(" currentpoint\n");
1482 printf(" \n");
1483 printf(" gsave\n");
1484 printf(" yoffset add exch xoffset add exch %% add the X,Y offsets\n");
1485 printf(" translate\n");
1486 printf(" xscale yscale scale\n");
1487 printf("\n");
1488 printf(" %% \n");
1489 printf(" %% Temporarily redefine the 'showpage' operator to null so that it's\n");
1490 printf(" %% ignored when the EPS image file is processed...\n");
1491 printf(" %%\n");
1492 printf(" 1 dict begin /showpage { } bind def\n");
1493 printf(" myimage run\n");
1494 printf(" end\n");
1495 printf(" \n");
1496 printf(" grestore\n");
1497 printf("} bind def\n\n");
1498 }
1499
1500
1501 /* single month */
1502
1503 printf("%% -- printmonth => --\n");
1504 printf("%%\n");
1505 printf("%% print full-size calendar for 'month'/'year' on single page\n");
1506 printf("%%\n");
1507 printf("/printmonth {\n");
1508 printf(" /calsize large def\n");
1509 printf(" startpage\n");
1510 printf(" calendar\n");
1511 printf(" printsmallcals %% small calendars\n");
1512 printf(" \n");
1513 printf("} bind def\n\n");
1514
1515 printf("%% scale factor (slightly < 1/7) and offset for printing small calendars\n");
1516 printf("/scscale .138 def\n");
1517 printf("/scoffset gridwidth gridwidth scscale 7 mul mul sub 2.0 div def\n");
1518 printf("\n");
1519 printf("%% -- printsmallcals => --\n");
1520 printf("%%\n");
1521 printf("%% print small calendars in boxes 'prev_small_cal' and 'next_small_cal'\n");
1522 printf("%%\n");
1523 printf("/printsmallcals {\n");
1524 printf(" /calsize small def\n");
1525 printf(" /sv_startbox startbox def\n");
1526 printf("\n");
1527 printf(" prev_small_cal 0 ge { %% previous month/year\n");
1528 printf(" /year p_year def\n");
1529 printf(" /month p_month def\n");
1530 printf(" /startbox p_startbox def\n");
1531 printf(" /ndays p_ndays def\n");
1532 printf(" gsave\n");
1533 printf(" prev_small_cal boxpos translate\n");
1534 printf(" scscale dup scale\n");
1535 printf(" scoffset ytop neg translate\n");
1536 printf(" calendar\n");
1537 printf(" grestore\n");
1538 printf(" } if\n");
1539 printf("\n");
1540 printf(" next_small_cal 0 ge { %% next month/year\n");
1541 printf(" /year n_year def\n");
1542 printf(" /month n_month def\n");
1543 printf(" /startbox n_startbox def\n");
1544 printf(" /ndays n_ndays def\n");
1545 printf(" gsave\n");
1546 printf(" next_small_cal boxpos translate\n");
1547 printf(" scscale dup scale\n");
1548 printf(" scoffset ytop neg translate\n");
1549 printf(" calendar\n");
1550 printf(" grestore\n");
1551 printf(" } if\n");
1552 printf("\n");
1553 printf(" /startbox sv_startbox def %% required for text boxes\n");
1554 printf("} bind def\n\n");
1555 }
1556
1557 if (julian_dates == NO_JULIANS) {
1558 /* no julian dates */
1559 printf("/drawjnums {} bind def\n\n");
1560 }
1561 else {
1562 /* some julian dates */
1563 printf("%% -- drawjnums => --\n");
1564 printf("%%\n");
1565 printf("%% print day-of-year (and, optionally, days remaining) for each date\n");
1566 printf("%%\n");
1567 printf("/drawjnums {\n");
1568 printf(" NotesFontSet\n");
1569 printf(" 1 datepos dayheight 3 sub sub exch daywidth 3 sub add exch moveto\n");
1570 printf("\n");
1571 printf(" 1 1 ndays {\n");
1572 printf(" /day exch def\n");
1573 printf(" /jday jdstart day add 1 sub def\n");
1574 printf(" /str jday 3 string cvs def\n");
1575 printf(" julian-dates true eq { %% print days left in year?\n");
1576 printf(" /str str ( \\050) strcat yearlen jday sub 3 string cvs\n");
1577 printf(" strcat (\\051) strcat def\n");
1578 printf(" } if\n");
1579 printf(" gsave\n");
1580 printf(" str dup stringwidth pop 0 exch sub 0 rmoveto show\n");
1581 printf(" grestore\n");
1582 printf(" nextbox\n");
1583 printf(" } for\n");
1584 printf("} bind def\n\n");
1585 }
1586
1587 if (draw_moons == NO_MOONS) {
1588 /* no moons */
1589 printf("/do-moon-p { false } bind def\n");
1590 printf("/drawmoons {} bind def\n\n");
1591 }
1592 else {
1593 /* moons on some or all days */
1594 printf("%%\n");
1595 printf("%% Moon-drawing functions:\n");
1596 printf("%%\n\n");
1597
1598 printf("%% <phase> domoon => --\n");
1599 printf("%%\n");
1600 printf("%% draw icon showing moon at <phase> (0 = new; .25 = fq; .5 = full; .75 = lq)\n");
1601 printf("%%\n");
1602 printf("/domoon {\n");
1603 printf(" /phase exch def\n");
1604 printf("\n");
1605 printf(" gsave\n");
1606 printf(" currentpoint translate\n");
1607 printf(" newpath\n");
1608 printf("\n");
1609 printf(" %% if moon is full, just draw unfilled circle\n");
1610 printf("\n");
1611 printf(" phase halfperiod .01 sub ge phase halfperiod .01 add le and {\n");
1612 printf(" 0 0 radius\n");
1613 printf(" 0 360 arc stroke\n");
1614 printf(" }\n");
1615 printf(" {\n");
1616 printf(" %% draw the line arc now; prepare (but don't draw) the fill arc\n");
1617 printf("\n");
1618 printf(" 0 0 radius %% for line and fill arcs\n");
1619 printf(" 0 0 radius \n");
1620 printf(" phase halfperiod lt { %% phase between new and full\n");
1621 printf(" 270 90 arc stroke %% (line on right, fill on left)\n");
1622 printf(" 0 radius neg moveto\n");
1623 printf(" 270 90 arcn \n");
1624 printf(" }\n");
1625 printf(" { %% phase between full and new\n");
1626 printf(" 90 270 arc stroke %% (line on left, fill on right)\n");
1627 printf(" 0 radius neg moveto\n");
1628 printf(" 270 90 arc \n");
1629 printf(" /phase phase halfperiod sub def\n");
1630 printf(" } ifelse\n");
1631 printf("\n");
1632 printf(" %% curveto uses (x0,y0) [current point], (x1,y1), (x2,y2),\n");
1633 printf(" %% and (x3,y3) as the control points for drawing a Bezier\n");
1634 printf(" %% cubic section, used here as the curve dividing the moon\n");
1635 printf(" %% icon into dark and light sections. x1 is in the range\n");
1636 printf(" %% -R*sqrt(2) <= x1 <= R*sqrt(2) and y1 is in the range\n");
1637 printf(" %% 0 <= y1 <= R; note that except in the degenerate case\n");
1638 printf(" %% where x1 = y1 = x2 = y2 = 0, the curve does not actually\n");
1639 printf(" %% pass through (x1,y1) or (x2,y2).\n");
1640 printf("\n");
1641 printf(" /x1 quartperiod phase sub rect mul def\n");
1642 printf(" /y1 x1 abs 2 sqrt div def\n");
1643 printf("\n");
1644 printf(" %% push control points for curveto\n");
1645 printf("\n");
1646 printf(" %% x0 = 0 (current\n");
1647 printf(" %% y0 = R point)\n");
1648 printf(" x1 %% x1\n");
1649 printf(" y1 %% y1\n");
1650 printf(" x1 %% x2 = x1\n");
1651 printf(" y1 neg %% y2 = -y1\n");
1652 printf(" 0 %% x3 = 0\n");
1653 printf(" radius neg %% y3 = -R\n");
1654 printf("\n");
1655 printf(" %% draw Bezier curve; fill area between curve and fill arc\n");
1656 printf("\n");
1657 printf(" curveto\n");
1658 printf(" fill\n");
1659 printf(" } ifelse\n");
1660 printf("\n");
1661 printf(" grestore\n");
1662 printf("} bind def\n\n");
1663
1664 printf("%% -- do-moon-p => <bool>\n");
1665 printf("%%\n");
1666 printf("%% determine whether or not moon icon is to be drawn on 'day'; push result\n");
1667 printf("%%\n");
1668 printf("/do-moon-p {\n");
1669 printf(" draw-moons (some) eq { %% printing quarter moons? look up day\n");
1670 printf(" /p false def\n");
1671 printf(" quarter_moons { day eq { /p true def } if } forall\n");
1672 printf(" p\n");
1673 printf(" }\n");
1674 printf(" {\n");
1675 printf(" draw-moons %% all moons or no moons\n");
1676 printf(" } ifelse\n");
1677 printf("} bind def\n\n");
1678
1679 printf("%% -- drawmoons => --\n");
1680 printf("%%\n");
1681 printf("%% main routine to draw moon icons on calendar\n");
1682 printf("%%\n");
1683 printf("/drawmoons {\n");
1684 printf(" gsave\n");
1685 printf(" moonlinewidth setlinewidth\n");
1686 printf(" 1 datepos offset sub exch daywidth add offset sub exch moveto\n");
1687 printf(" /n 0 def %% index into moon_phases\n");
1688 printf(" 1 1 ndays {\n");
1689 printf(" /day exch def\n");
1690 printf(" do-moon-p { %% draw a moon today?\n");
1691 printf(" moon_phases n get domoon\n");
1692 printf(" /n n 1 add def\n");
1693 printf(" } if\n");
1694 printf(" nextbox\n");
1695 printf(" } for\n");
1696 printf(" grestore\n");
1697 printf("} bind def\n\n");
1698 }
1699
1700 /*
1701 Write out PostScript code to print calendars...
1702 */
1703
1704 for (i = 0, this_month = init_month, this_year = init_year;
1705 i < nmonths;
1706 i++, BUMP_MONTH_AND_YEAR(this_month, this_year)) {
1707 print_month(this_month, this_year);
1708 }
1709
1710 #ifdef EPS_DSC
1711 /* generate trailer at end of PostScript output */
1712 printf("%%%%Trailer\n");
1713 printf("clear flush\n");
1714 printf("%%%%EOF\n");
1715 #endif
1716
1717 return;
1718 }
1719
1720 /* ---------------------------------------------------------------------------
1721
1722 write_calfile
1723
1724 Notes:
1725
1726 This routine writes dates in a format suitable for the old Unix
1727 "calendar" utility (and subsequent use by Pcal).
1728
1729 */
write_calfile(void)1730 void write_calfile (void)
1731 {
1732 KWD *k;
1733 int i;
1734
1735 /* look up the Pcal keywords (assumed present) for the -c output file */
1736 for (k = keywds; k->name; k++) {
1737 if (k->code == DT_NOTE) kw_note = k->name;
1738 if (k->code == DT_OPT) kw_opt = k->name;
1739 if (k->code == DT_YEAR) kw_year = k->name;
1740 }
1741
1742 /* print the date style for subsequent use by Pcal */
1743 printf("%s -%c\n", kw_opt, date_style == USA_DATES ? F_USA_DATES :
1744 F_EUR_DATES);
1745
1746 for (i = 0, this_month = init_month, this_year = init_year;
1747 i < nmonths;
1748 i++, BUMP_MONTH_AND_YEAR(this_month, this_year)) {
1749 print_dates(this_month, this_year);
1750 }
1751
1752 return;
1753 }
1754
1755 /* ---------------------------------------------------------------------------
1756
1757 single_month_html
1758
1759 Notes:
1760
1761 This routine prints the specified month and year as an HTML table.
1762
1763 */
single_month_html(int this_month,int this_year)1764 void single_month_html (int this_month, int this_year)
1765 {
1766 long holidays;
1767 int day, box, len;
1768 year_info *py;
1769 month_info *pm;
1770 register day_info *pd;
1771
1772 /* table heading with month and year */
1773 printf("<a name=_%02d%02d>\n", this_month, this_year % 100);
1774 printf("<table width=100%% border=%d>\n", BORDER);
1775 printf("<tr><th colspan=7>%s", MONTHYEAR_PRE);
1776 print_html(months_ml[output_language][this_month-1]);
1777 printf(" %d%s</th></tr>\n", this_year, MONTHYEAR_POST);
1778
1779 /* headings for weekday names */
1780 printf("<tr>");
1781 for (day = 0; day < 7; day++) {
1782 char buf[40];
1783 strcpy(buf, days_ml[output_language][(day + first_day_of_week) % 7]);
1784
1785 /* buf[ABBR_MONTH_LEN] = '\0'; */
1786
1787 printf("<th width=14%%>");
1788 print_html(buf);
1789 printf("</th>");
1790 }
1791 printf("</tr>\n");
1792
1793 /* blank space at beginning (if necessary) - divide into N one-column
1794 * boxes (#if DIVIDE_BLANK_SPACE) or print as single N-column box
1795 */
1796 printf("<tr>\n");
1797 if ((box = START_BOX(this_month, this_year)) > 0) {
1798
1799 #if DIVIDE_BLANK_SPACE
1800 int i;
1801 for (i = 0; i < box; i++) {
1802 printf("<td>");
1803 BLANKLINES(TEXTLINES);
1804 printf("</td>\n");
1805 }
1806 #else
1807 printf("<td colspan=%d>", box);
1808 BLANKLINES(TEXTLINES);
1809 printf("</td>\n");
1810 #endif
1811 }
1812
1813 /* get pointer to text information for current month */
1814 pm = (py = find_year(this_year, FALSE)) != NULL ? py->month[this_month-1] : NULL;
1815
1816 /* main loop for dates */
1817 len = LENGTH_OF(this_month, this_year);
1818 for (day = 1, holidays = pm ? pm->holidays : 0;
1819 day <= len;
1820 day++, box++, holidays >>= 1) {
1821 int nrows;
1822
1823 if (box > 0 && box % 7 == 0) printf("<tr>\n"); /* start of row */
1824
1825 printf("<td valign=top>");
1826
1827 /* special HTML code for holidays (cf. pcaldefs.h) */
1828 if (holidays & 01) printf("%s%d%s", HOLIDAY_PRE, day, HOLIDAY_POST);
1829 else printf("%d", day);
1830
1831 /* print associated text (count lines) */
1832 for (nrows = 0, pd = pm ? pm->day[day-1] : NULL;
1833 pd;
1834 pd = pd->next, nrows++) {
1835 /* Skip lines specifying an EPS image... */
1836 if (ci_strncmp(pd->text, "image:", 6) == 0) continue;
1837 printf("\n<br>");
1838 this_day = day;
1839 RESET_DATE(); /* reset working date */
1840 print_html(pd->text);
1841 }
1842
1843 /* pad first box in each row with blank lines */
1844 if (box % 7 == 0 && nrows < TEXTLINES) BLANKLINES(TEXTLINES - nrows);
1845
1846 printf("</td>\n");
1847 if (box % 7 == 6) printf("</tr>\n"); /* end of row */
1848 }
1849
1850 /* blank space at end (if necessary) - divide into N one-column
1851 * boxes (#if DIVIDE_BLANK_SPACE) or print as single N-column box
1852 */
1853 if ((box %= 7) != 0) {
1854 #if DIVIDE_BLANK_SPACE
1855 int i;
1856 for (i = box; i < 7; i++) {
1857 printf("<td>");
1858 BLANKLINES(TEXTLINES);
1859 printf("</td>\n");
1860 }
1861 #else
1862 printf("<td colspan=%d>", 7 - box);
1863 BLANKLINES(TEXTLINES);
1864 printf("</td>\n");
1865 #endif
1866 }
1867 printf("</tr>\n");
1868 printf("</table>\n");
1869
1870 /* links to other months on page */
1871 if (nmonths > 2) {
1872 int j, m, y;
1873 char c = date_style == EUR_DATES ? '.' : '/';
1874
1875 printf("<p>");
1876 for (j = 0, m = init_month, y = init_year;
1877 j < nmonths;
1878 j++, BUMP_MONTH_AND_YEAR(m, y)) {
1879 printf("<a href=#_%02d%02d>%02d%c%02d</a>%s\n",
1880 m, y % 100, m, c, y % 100,
1881 j % 12 == 11 ? "<br>" : " ");
1882 }
1883 }
1884
1885 return;
1886 }
1887
1888
1889 /* ---------------------------------------------------------------------------
1890
1891 single_month_one_column_html
1892
1893 Notes:
1894
1895 This routine prints the specified month and year as an
1896 HTML table in one column.
1897
1898 */
single_month_one_column_html(int this_month,int this_year)1899 void single_month_one_column_html (int this_month, int this_year)
1900 {
1901 long holidays;
1902 int day, box = 0, len, i;
1903 year_info *py;
1904 month_info *pm;
1905 register day_info *pd;
1906 char *text_short;
1907
1908 /* table heading with first 5 chars of the month */
1909 printf("<table border=%d>\n", BORDER);
1910 printf("<tr><th width=100px>%s</th></tr>\n", months_ml[output_language][this_month-1]);
1911
1912 /* get pointer to text information for current month */
1913 pm = (py = find_year(this_year, FALSE)) != NULL ? py->month[this_month-1] : NULL;
1914
1915 /* main loop for dates */
1916 len = LENGTH_OF(this_month, this_year);
1917 for (day = 1, holidays = pm ? pm->holidays : 0;
1918 day <= len;
1919 day++, box++, holidays >>= 1) {
1920 int nrows;
1921
1922 /* print only one column for the month */
1923 printf("<tr>\n");
1924 printf("<td valign=top>");
1925
1926 /* special HTML code for holidays (cf. pcaldefs.h) */
1927 if (holidays & 01) {
1928 printf("%s%d%s %c", HOLIDAY_PRE, day, HOLIDAY_POST,
1929 days_ml[output_language][(day + START_BOX(this_month, this_year) +
1930 first_day_of_week - 1) % 7][0]);
1931 }
1932 else {
1933 /* print Sundays in bold */
1934 if ((day + START_BOX(this_month, this_year) + first_day_of_week - 1) % 7 == 0) {
1935 printf("<B>%d %c</B>", day,
1936 days_ml[output_language][(day + START_BOX(this_month, this_year) +
1937 first_day_of_week - 1) % 7][0]);
1938 }
1939 /* print Saturdays in gray */
1940 else if ((day + START_BOX(this_month, this_year) + first_day_of_week - 1) % 7 == 6) {
1941 printf("<B><FONT color=darkgray>%d</FONT> %c</B>", day,
1942 days_ml[output_language][(day + START_BOX(this_month, this_year) +
1943 first_day_of_week - 1) % 7][0]);
1944 }
1945 else {
1946 printf("%d %c", day, days_ml[output_language]
1947 [(day + START_BOX(this_month, this_year) + first_day_of_week - 1) % 7][0]);
1948 }
1949 /* print associated text (count lines),
1950 not for holidays */
1951 for (nrows = 0, pd = pm ? pm->day[day-1] : NULL;
1952 pd;
1953 pd = pd->next, nrows++) {
1954 /* Skip lines specifying an EPS image... */
1955 if (ci_strncmp(pd->text, "image:", 6) == 0) continue;
1956 printf("\n<I>");
1957 this_day = day;
1958 RESET_DATE(); /* reset working date */
1959 text_short = pd->text;
1960 for (i=0; i < 5; i++) {
1961 if (text_short[i] == '\0') break;
1962 /* if text starts with < it is probably an HTML tag (e.g. <I>)
1963 -> skip three letters
1964 */
1965 if(text_short[0] == '<' && text_short[i+3] == '\0') break;
1966 if(text_short[0] == '<') printf("%c",text_short[i+3]);
1967 else printf("%c",text_short[i]);
1968 }
1969 printf("</I>\n");
1970 }
1971 }
1972
1973 /* TR-tag to end the row */
1974 printf("</td></TR>\n");
1975 if (box % 7 == 6) printf("</tr>\n"); /* end of row */
1976 }
1977 printf("</tr>\n");
1978 printf("</table>\n");
1979
1980 return;
1981 }
1982
1983 /* ---------------------------------------------------------------------------
1984
1985 multiple_months_html
1986
1987 Notes:
1988
1989 This routine prints an HTML table in 'ncols' (assumed to be either 3 or
1990 4) columns, one column per month starting with the specified month and
1991 year.
1992
1993 */
multiple_months_html(int first_month,int first_year,int ncols)1994 void multiple_months_html (int first_month, int first_year, int ncols)
1995 {
1996 long holidays;
1997 int day, box, len;
1998 year_info *py;
1999 month_info *pm;
2000 int i, m, d, y, w, wd1, wd2, nw;
2001
2002 /* width of dates and spaces between months */
2003 wd1 = 12 / ncols;
2004 wd2 = (100 - (ncols * (7 * wd1))) / (ncols - 1);
2005
2006 /* table heading with months and year */
2007 printf("<table width=100%% border=%d>\n", 0);
2008
2009 printf("<tr>");
2010 for (i = 0, m = first_month, y = first_year;
2011 i < ncols;
2012 i++, BUMP_MONTH_AND_YEAR(m, y)) {
2013 if (i > 0) printf("<th><br></th>");
2014 printf("<th colspan=7>%s", MONTHYEAR_W_PRE);
2015 print_html(months_ml[output_language][m-1]);
2016 printf(" %d%s</th>\n", y, MONTHYEAR_W_POST);
2017 }
2018 printf("</tr>\n");
2019
2020 /* headings for weekday names */
2021 printf("<tr>");
2022 for (i = 0; i < ncols; i++) {
2023 if (i > 0) printf("\n<th width=%d%%><br></th>", wd2);
2024 for (day = 0; day < 7; day++) {
2025 char buf[40];
2026 strcpy(buf, days_ml[output_language][(day + first_day_of_week) % 7]);
2027 #if 0
2028 buf[ncols == 3 ? 3 : 2] = '\0';
2029 #else
2030 buf[2] = '\0'; /* truncate to two characters */
2031 #endif
2032 printf("<th width=%d%%>", wd1);
2033 print_html(buf);
2034 printf("</th>");
2035 }
2036 }
2037 printf("</tr>\n");
2038
2039 #ifdef CONSISTENT_SPACING
2040 /* always print 6 weeks (to maintain consistent vertical spacing) */
2041 nw = 6;
2042 #else
2043 /* print 5 weeks unless at least one month requires a sixth week */
2044 nw = 5;
2045 for (i = 0, m = first_month, y = first_year;
2046 i < ncols;
2047 i++, BUMP_MONTH_AND_YEAR(m, y)) {
2048 if (START_BOX(m, y) + LENGTH_OF(m, y) > 35) nw = 6;
2049 }
2050 #endif
2051
2052 /* main loop to print dates (one week at a time) */
2053
2054 for (w = 0; w < nw; w++) {
2055 printf("<tr>");
2056
2057 /* print one week of dates for each month */
2058
2059 for (i = 0, m = first_month, y = first_year;
2060 i < ncols;
2061 i++, BUMP_MONTH_AND_YEAR(m, y)) {
2062 if (i > 0) printf("<td><br></td>");
2063
2064 /* get starting box, month length, holiday info */
2065 box = START_BOX(m, y);
2066 len = LENGTH_OF(m, y);
2067 pm = (py = find_year(y, FALSE)) != NULL ? py->month[m-1] : NULL;
2068 holidays = pm ? pm->holidays : 0;
2069
2070 /* loop over days in current week and month */
2071
2072 for (d = 1; d <= 7; d++) {
2073 day = (7 * w) + d - box;
2074 if (day < 1 || day > len) printf("<td><br></td>");
2075 else {
2076 printf("<td align=center>");
2077 if (holidays & (1L << (day - 1))) {
2078 printf("%s%d%s", HOLIDAY_PRE, day, HOLIDAY_POST);
2079 }
2080 else printf("%d", day);
2081
2082 printf("</td>");
2083 }
2084 }
2085 }
2086 printf("</tr>\n");
2087 }
2088
2089 /* end of table */
2090 printf("</table>\n");
2091
2092 return;
2093 }
2094
2095 /* ---------------------------------------------------------------------------
2096
2097 write_htmlfile
2098
2099 Notes:
2100
2101 This routine generates a calendar in HTML format.
2102
2103 */
write_htmlfile(void)2104 void write_htmlfile (void)
2105 {
2106 int i;
2107 static struct {
2108 char *attribute; /* attribute name */
2109 char *value; /* attribute value */
2110 int numeric; /* TRUE if value is numeric */
2111 } *pb, body_attributes[] = { /* cf. pcaldefs.h */
2112 { "bgcolor", BGCOLOR, TRUE },
2113 { "background", BACKGROUND, FALSE },
2114 { "text", TEXT, TRUE },
2115 { "link", LINK, TRUE },
2116 { "alink", ALINK, TRUE },
2117 { "vlink", VLINK, TRUE },
2118 { NULL, NULL, FALSE } /* must be last */
2119 };
2120
2121 this_day = 1; /* initialize working date */
2122 this_month = init_month;
2123 this_year = init_year;
2124 RESET_DATE();
2125
2126 /* HTML boilerplate */
2127 printf("<html>\n");
2128 printf("<head>\n");
2129
2130 /*
2131 * if center footstring (-C) was specified, use it as the title;
2132 * otherwise contrive title from beginning/ending month/year
2133 */
2134 printf("<title>");
2135 if (cfoot[0]) {
2136 print_html(cfoot);
2137 }
2138 else {
2139 if (do_whole_year && init_month == JAN) {
2140 printf("%d", init_year);
2141 if (final_year > init_year) printf(" - %d", final_year);
2142 }
2143 else {
2144 char c = date_style == EUR_DATES ? '.' : '/';
2145 printf("%d%c%02d", init_month, c, init_year % 100);
2146 if (nmonths > 1) {
2147 printf(" - %d%c%02d", final_month, c, final_year % 100);
2148 }
2149 }
2150 }
2151 printf("</title>\n");
2152 printf("</head>\n");
2153
2154 /* generate <body> definition with attributes */
2155 printf("<body");
2156 for (pb = body_attributes; pb->attribute; pb++) {
2157 char *p = pb->value;
2158 if (p && *p) {
2159 printf(" %s=%s%s\"", pb->attribute,
2160 pb->numeric && p[0] != '#' ? "\"#" : "\"",
2161 pb->value);
2162 }
2163 }
2164 printf(">\n");
2165
2166 /* repeat center footstring (if specified) as heading */
2167 if (cfoot[0]) {
2168 RESET_DATE();
2169 printf("%s", HEADING_PRE);
2170 print_html(cfoot);
2171 printf("%s\n", HEADING_POST);
2172 }
2173
2174 /* loop over all specified months, creating HTML table for each */
2175 this_month = init_month;
2176 this_year = init_year;
2177
2178 if (do_whole_year) {
2179 /* whole-year mode - generate 3- or 4-column tables */
2180 int j, nc;
2181
2182 nc = (rotate == LANDSCAPE) ? 4 : 3;
2183 for (i = 0; i < nmonths; i += nc) {
2184 if (i > 0) printf("<p>\n");
2185 multiple_months_html(this_month, this_year, nc);
2186 for (j = 0; j < nc; j++) BUMP_MONTH_AND_YEAR(this_month, this_year);
2187 }
2188 }
2189 else {
2190 /* single-month mode - generate one month per table
2191 * either in one or several columns */
2192 if (one_column) printf("<TABLE><TR>");
2193 for (i = 0; i < nmonths; i++) {
2194 if (one_column) {
2195 printf("<TD valign=\"top\">");
2196 single_month_one_column_html(this_month, this_year);
2197 printf("</TD>");
2198 }
2199 else {
2200 printf("<TD valign=\"top\" width=\"%i%%\">",100/nmonths);
2201 if (i > 0) printf("<p><br>\n");
2202 single_month_html(this_month, this_year);
2203 }
2204 BUMP_MONTH_AND_YEAR(this_month, this_year);
2205 }
2206 printf("</TR></TABLE>");
2207 }
2208
2209 /* boilerplate to terminate HTML file */
2210 printf("</body>\n");
2211 printf("</html>\n");
2212
2213 return;
2214 }
2215
2216 /*
2217 * low-level utilities for PostScript generation
2218 */
2219
2220 /* ---------------------------------------------------------------------------
2221
2222 set_rgb
2223
2224 Notes:
2225
2226 This routine converts "<r>:<g>:<b>" to [r g b] or "<gray>" to gray.
2227
2228 It returns a pointer to a static buffer containing the converted string.
2229
2230 */
set_rgb(char * s)2231 char *set_rgb (char *s)
2232 {
2233 static char buf[STRSIZ];
2234 char *p1, *p2;
2235 double val[3];
2236 int n;
2237
2238 val[0] = val[1] = val[2] = 0; /* defaults */
2239
2240 /* extract 1 - 3 floating-point values from string */
2241 for (n = 1, p1 = s; n <= 3; n++, p1 = p2 + 1) {
2242 val[n-1] = atof(p1);
2243 if ((p2 = strchr(p1, RGB_CHAR)) == NULL) break;
2244 }
2245
2246 /* single value is gray scale; assume anything else is [r g b] */
2247 if (n > 1) sprintf(buf, "[%.3f %.3f %.3f]", val[0], val[1], val[2]);
2248 else sprintf(buf, "%.3f", val[0]);
2249
2250 return buf;
2251 }
2252
2253 /* ---------------------------------------------------------------------------
2254
2255 select_color
2256
2257 Notes:
2258
2259 This routine chooses a color which contrasts with the majority of
2260 weekday colors, if the holiday color has not been explicitly selected.
2261
2262 */
select_color(void)2263 int select_color (void)
2264 {
2265 int i, min, index = BLACK;
2266 char count[NUM_COLORS];
2267
2268 for (i = 0; i < NUM_COLORS; i++) { /* clear counts */
2269 count[i] = 0;
2270 }
2271
2272 for (i = SUN; i <= SAT; i++) { /* count colors */
2273 count[(int)day_color[i]]++;
2274 }
2275
2276 /* find smallest non-zero count; set its index and value */
2277 for (i = 0, min = 99; i < NUM_COLORS; i++) {
2278 if (count[i] && count[i] < min) min = count[index = i];
2279 }
2280
2281 /* return least-used color (or pick one if only one color used) */
2282 return min == 7 ? index == BLACK ? GRAY : BLACK : index;
2283 }
2284
2285
2286 /* ---------------------------------------------------------------------------
2287
2288 ordinal_suffix
2289
2290 Notes:
2291
2292 This routine translates the %o (ordinal suffix) format specifier into
2293 the appropriate string for printing.
2294
2295 The parameter is an ordinal number, assumed positive.
2296
2297 */
ordinal_suffix(int num)2298 char *ordinal_suffix (int num)
2299 {
2300 static char buf[10];
2301 int tens, units;
2302
2303 /* Select suffix according to rules for target language. In English,
2304 the rules are as follows:
2305
2306 tens digit units digit suffix
2307 (any) 0, 4..9 "th"
2308 1 (any) "th"
2309 != 1 1 "st"
2310 != 1 2 "nd"
2311 != 1 3 "rd"
2312 */
2313 tens = (num / 10) % 10;
2314 units = num % 10;
2315 strcpy(buf, ord_suffix[(units > 3 || tens == 1) ? 0 : units]);
2316
2317 return buf;
2318 }
2319
2320 /* ---------------------------------------------------------------------------
2321
2322 expand_fmt
2323
2324 Notes:
2325
2326 This routine expands a 'strftime'-like date format specifier.
2327
2328 Pcal supports '%[aAbBdjmUWyY]' from 'strftime()' plus '%[luwDM]' and
2329 prefixes '[0o+-]' (see below).
2330
2331 The expanded string is placed in an output buffer and a pointer to the
2332 character following the end of format specifier is returned.
2333
2334 Assumes working date has been initialized (via RESET_DATE() macro) prior
2335 to first call for a given text string
2336
2337 */
expand_fmt(char * buf,char * p)2338 char *expand_fmt (char *buf, /* output buffer (filled in) */
2339 char *p) /* character following percent sign */
2340 {
2341 char c;
2342 static char *prefixes = "0o+-";
2343 int firstday, wkday;
2344 int adjust = 0, print_lz = FALSE, ordinal = FALSE, prev_num = -1;
2345 int num_present = FALSE, num_value = 0;
2346 date_str date;
2347
2348 /* For compatibility with version 4.1, still support %[+-][bBdmY] (print
2349 the next/last month-name/day/month/year). Version 4.11 introduces
2350 %[+-]<n>[DWMY], which adjusts the working date by [+-]<n>
2351 days/weeks/months/years; this method is preferred due to its greater
2352 flexibility.
2353 */
2354
2355 buf[0] = '\0'; /* initialize output to null string */
2356
2357 do { /* loop until format character found */
2358 switch (c = *p++) {
2359 case 'a': /* %a : abbreviated weekday */
2360 wkday = calc_weekday(work_month, work_day, work_year);
2361 strcpy(buf, days_ml_short[output_language][wkday]);
2362 break;
2363
2364 case 'A': /* %A : full weekday */
2365 wkday = calc_weekday(work_month, work_day, work_year);
2366 strcpy(buf, days_ml[output_language][wkday]);
2367 break;
2368
2369 case 'b': /* %b : abbreviated month name */
2370 case 'B': /* %B : full month name */
2371 strcpy(buf, months_ml[output_language][(work_month + adjust + 11) % 12]);
2372 if (c == 'b')
2373 buf[ABBR_MONTH_LEN] = '\0';
2374 break;
2375
2376 case 'd': /* %d : day of month (01-31) */
2377 prev_num = work_day;
2378 sprintf(buf, print_lz ? "%02d" : "%d", prev_num);
2379 break;
2380
2381 case 'D': /* %D : adjust working date by <N> days (NEW) */
2382 if (!num_present || num_value == 0) RESET_DATE();
2383 else SET_DATE(adjust * num_value);
2384 break;
2385
2386 case 'j': /* %j : day of year (001-366) */
2387 prev_num = DAY_OF_YEAR(work_month, work_day, work_year);
2388 sprintf(buf, print_lz ? "%03d" : "%d", prev_num);
2389 break;
2390
2391 case 'l': /* %l : days left in year (000-365) (NEW) */
2392 prev_num = YEAR_LEN(work_year) - DAY_OF_YEAR(work_month, work_day, work_year);
2393 sprintf(buf, print_lz ? "%03d" : "%d", prev_num);
2394 break;
2395
2396 case 'm': /* %m : month (01-12) */
2397 prev_num = (work_month + adjust + 11) % 12 + 1;
2398 sprintf(buf, print_lz ? "%02d" : "%d", prev_num);
2399 break;
2400
2401 case 'M': /* %M : adjust date by <N> months (NEW) */
2402 if (!num_present || num_value == 0) {
2403 RESET_DATE();
2404 }
2405 else {
2406 int len;
2407
2408 work_month += adjust * num_value;
2409 while (work_month > DEC) {
2410 work_month -= 12;
2411 work_year++;
2412 }
2413 while (work_month < JAN) {
2414 work_month += 12;
2415 work_year--;
2416 }
2417
2418 /* make sure day of new month is legal */
2419 len = LENGTH_OF(work_month, work_year);
2420 if (work_day > len) work_day = len;
2421 }
2422 break;
2423
2424 /* %u considers the week containing 1/1 to be week 1 and the next
2425 "logical Sunday" (the first day of the week as printed - cf. the
2426 -F option) to be the start of week 2; %U considers the first
2427 "logical Sunday" of the year to be the start of week 1. %w and %W
2428 behave like %u and %U respectively, but use the first "logical
2429 Monday" instead.
2430 */
2431 case 'W': /* %W : week number (00-53) */
2432 /* %W, if prefaced by [+-]N, adjusts the date by [+-]N weeks (resets
2433 if N == 0); check for this case first
2434 */
2435 if (num_present) {
2436 if (num_value == 0) RESET_DATE(); /* N = 0: reset date */
2437 else SET_DATE(7 * adjust * num_value);
2438 break;
2439 }
2440 /* fall through */
2441 case 'u': /* %u : week number (01-54) (NEW) */
2442 case 'U': /* %U : week number (00-53) */
2443 case 'w': /* %w : week number (01-54) (NEW) */
2444 firstday = ((tolower(c) == 'w' ? 15 : 14) -
2445 START_BOX(JAN, work_year)) % 7 + 1;
2446 prev_num = (DAY_OF_YEAR(work_month, work_day,
2447 work_year) - firstday + 7) / 7;
2448 if (islower((int)c) && firstday != 1) prev_num++;
2449 sprintf(buf, print_lz ? "%02d" : "%d", prev_num);
2450 break;
2451
2452 case 'y': /* %y : year w/o century (00-99) */
2453 prev_num = (work_year + adjust) % 100;
2454 sprintf(buf, "%02d", prev_num);
2455 break;
2456
2457 case 'Y': /* %Y : year w/century */
2458 /* %Y, if prefaced by [+-]N, adjusts the date by [+-]N years (resets
2459 if N == 0); check for this case first
2460 */
2461 if (num_present) {
2462 if (num_value == 0) { /* N = 0: reset date */
2463 RESET_DATE();
2464 }
2465 else {
2466 int len;
2467
2468 work_year += adjust * num_value;
2469
2470 /* make sure day is legal */
2471 len = LENGTH_OF(work_month, work_year);
2472 if (work_day > len) work_day = len;
2473 }
2474 }
2475 else {
2476 prev_num = work_year + adjust;
2477 sprintf(buf, "%d", prev_num);
2478 }
2479 break;
2480
2481 /* prefix flags [o0+-] : set flags for next pass */
2482
2483 case 'o': /* %o : ordinal suffix (NEW) */
2484 ordinal = TRUE;
2485 break;
2486
2487 case '0': /* %0 : add leading zeroes (NEW) */
2488 print_lz = TRUE;
2489 break;
2490
2491 case '+': /* %+ : increment next value (NEW) */
2492 case '-': /* %- : decrement next value (NEW) */
2493 adjust = c == '-' ? -1 : 1;
2494 if (isdigit((int)*p)) { /* get the number */
2495 num_present = TRUE;
2496 while (isdigit((int)*p)) {
2497 num_value = num_value * 10 + (*p++ - '0');
2498 }
2499 }
2500 break;
2501
2502 case '\0': /* accidental end-of-string */
2503 case ' ':
2504 return p - 1;
2505
2506 default: /* other - just copy it to output */
2507 sprintf(buf, "%c", c);
2508 break;
2509 };
2510
2511 } while (strchr(prefixes, c) != NULL);
2512
2513 /* append ordinal suffix if requested */
2514 if (ordinal && prev_num >= 0) strcat(buf, ordinal_suffix(prev_num));
2515
2516 return p;
2517 }
2518
2519 /* ---------------------------------------------------------------------------
2520
2521 print_word
2522
2523 Notes:
2524
2525 This routine prints a single word, representing punctuation and
2526 non-ASCII characters as octal literals and expanding format specifiers.
2527
2528 It returns a pointer to the character following the word (NULL if no
2529 word follows).
2530
2531 */
print_word(char * p)2532 char *print_word (char *p)
2533 {
2534 char c, buf[STRSIZ];
2535 int first = TRUE; /* flag to avoid printing null strings */
2536
2537 if (*p == '\0' || *(p += strspn(p, WHITESPACE)) == '\0') return NULL;
2538
2539 while ((c = *p) && !isspace(c & CHAR_MSK)) {
2540 if (c == '%' && p[1] != '\0') {
2541 p = expand_fmt(buf, p + 1);
2542 if (*buf && first) {
2543 printf("(");
2544 first = FALSE;
2545 }
2546 PUTSTR(isalnum, buf, stdout);
2547 }
2548 else {
2549 if (first) printf("(");
2550 first = FALSE;
2551 PUTCHAR(isalnum, c, stdout);
2552 p++;
2553 }
2554 }
2555
2556 if (!first) printf(")");
2557
2558 return p;
2559 }
2560
2561 /* ---------------------------------------------------------------------------
2562
2563 print_db_word
2564
2565 Notes:
2566
2567 This routine is the debug version of 'print_word()'.
2568
2569 It omits parentheses, does not convert punctuation to escape sequences,
2570 and writes results to stderr (not stdout).
2571
2572 */
print_db_word(char * p)2573 char *print_db_word (char *p)
2574 {
2575 char c, buf[STRSIZ];
2576
2577 if (*p == '\0' || *(p += strspn(p, WHITESPACE)) == '\0') return NULL;
2578
2579 while ((c = *p) && !isspace(c & CHAR_MSK)) {
2580 if (c == '%' && p[1] != '\0') {
2581 p = expand_fmt(buf, p + 1);
2582 PUTSTR(isprint, buf, stderr);
2583 }
2584 else {
2585 PUTCHAR(isprint, c, stderr);
2586 p++;
2587 }
2588 }
2589
2590 return p;
2591 }
2592
2593 /* ---------------------------------------------------------------------------
2594
2595 print_pstext
2596
2597 Notes:
2598
2599 This routine prints tokens in the input text (assumed separated by
2600 single blank) in PostScript format and as debugging information if
2601 requested.
2602
2603 */
print_pstext(char * p)2604 void print_pstext (char *p)
2605 {
2606 char *s = p; /* save for possible second pass */
2607
2608 while ((p = print_word(p)) != NULL) printf(" ");
2609
2610 /* repeat to generate debugging info if requested */
2611 if (debug_text) {
2612 while ((s = print_db_word(s)) != NULL) fprintf(stderr, " ");
2613 }
2614
2615 return;
2616 }
2617
2618 /* ---------------------------------------------------------------------------
2619
2620 print_text
2621
2622 Notes:
2623
2624 This routine prints text as supplied. It expands the format specifiers
2625 but does not tokenize into words or translate punctuation to escape
2626 sequences.
2627
2628 */
print_text(char * p)2629 void print_text (char *p)
2630 {
2631 char c, buf[STRSIZ];
2632
2633 while ((c = *p) != '\0') {
2634 if (c == '%' && p[1] != '\0') {
2635 p = expand_fmt(buf, p + 1);
2636 /* AH: non-ascii for -c mode should pass clean! */
2637 PUTSTR_CLEAN(isprint, buf, stdout);
2638 }
2639 else {
2640 PUTCHAR_CLEAN(isprint, c, stdout);
2641 p++;
2642 }
2643 }
2644
2645 return;
2646 }
2647
2648
2649 /* ---------------------------------------------------------------------------
2650
2651 def_footstring
2652
2653 Notes:
2654
2655 This routine prints the definition for a foot string, again converting
2656 all characters other than letters, digits, or space to octal escape and
2657 expanding format specifiers.
2658
2659 The first parameter is the definition.
2660
2661 The second parameter is the name of the string.
2662
2663 */
def_footstring(char * p,char * str)2664 void def_footstring (char *p, char *str)
2665 {
2666 char c, buf[STRSIZ];
2667
2668 this_day = 1; /* set default day in foot string */
2669 RESET_DATE(); /* reset working date */
2670
2671 printf("/%s (", str);
2672 while ((c = *p) != '\0') {
2673 if (c == '%' && p[1] != '\0') {
2674 p = expand_fmt(buf, p + 1);
2675 PUTSTR(isalnum, buf, stdout);
2676 }
2677 else {
2678 PUTCHAR(isalnum, c, stdout);
2679 p++;
2680 }
2681 }
2682 printf(") def\n");
2683
2684 return;
2685 }
2686
2687
2688 /*
2689 * Routines to extract and print data
2690 */
2691
2692
2693 /* ---------------------------------------------------------------------------
2694
2695 find_daytext
2696
2697 Notes:
2698
2699 This routine finds and prints the day (including notes) or holiday text
2700 for the specified month/year.
2701
2702 The third parameter acts as a flag to indicate if the holiday text
2703 should be printed.
2704
2705 */
find_daytext(int month,int year,int is_holiday)2706 void find_daytext (int month, int year, int is_holiday)
2707 {
2708 register int day;
2709 year_info *py;
2710 month_info *pm;
2711 register day_info *pd;
2712 int first;
2713 char *fcn = is_holiday ? "holidaytext" : "daytext";
2714 char hol = is_holiday ? '*' : ' ';
2715
2716 /* if no text for this year and month, return */
2717
2718 if ((py = find_year(year, FALSE)) == NULL || (pm = py->month[month-1]) == NULL) {
2719 return;
2720 }
2721
2722 /* walk array of day text pointers and linked lists of text */
2723
2724 for (day = 1; day <= LAST_NOTE_DAY; day++) {
2725 for (pd = pm->day[day-1], first = TRUE;
2726 pd;
2727 pd = pd->next) {
2728 if (pd->is_holiday != is_holiday) continue;
2729 /* Skip lines specifying an EPS image... */
2730 if (ci_strncmp(pd->text, "image:", 6) == 0) continue;
2731 if (first) {
2732 printf("%d [ \n", day >= FIRST_NOTE_DAY ?
2733 note_box(month, day, year) : day);
2734 }
2735 else {
2736 printf("\n");
2737 print_word(LINE_SEP); /* separate lines */
2738 printf("\n");
2739 }
2740 this_day = day >= FIRST_NOTE_DAY ? 1 : day;
2741 RESET_DATE(); /* reset working date */
2742 if (debug_text) {
2743 if (day < FIRST_NOTE_DAY) {
2744 fprintf(stderr, "%02d/%02d/%d%c ", month, day, year, hol);
2745 }
2746 else {
2747 fprintf(stderr, "%02d[%02d]%d ", month, day - FIRST_NOTE_DAY + 1, year);
2748 }
2749 }
2750 print_pstext(pd->text);
2751 if (debug_text) fprintf(stderr, "\n");
2752 first = FALSE;
2753 }
2754 if (! first) { /* wrap up call (if one made) */
2755 printf("\n] %s\n", day >= FIRST_NOTE_DAY ? "notetext" : fcn);
2756 }
2757 }
2758
2759 return;
2760 }
2761
2762 /* ---------------------------------------------------------------------------
2763
2764 find_images
2765
2766 Notes:
2767
2768 This routine finds and prints the EPS images for the specified
2769 month/year.
2770
2771 */
find_images(int month,int year)2772 void find_images (int month, int year)
2773 {
2774 register int day;
2775 year_info *py;
2776 month_info *pm;
2777 register day_info *pd;
2778
2779 /* if no text for this year and month, return */
2780
2781 if ((py = find_year(year, FALSE)) == NULL || (pm = py->month[month-1]) == NULL) {
2782 return;
2783 }
2784
2785 /* walk array of day text pointers and linked lists of text */
2786
2787 for (day = 1; day <= LAST_NOTE_DAY; day++) {
2788 for (pd = pm->day[day-1]; pd; pd = pd->next) {
2789
2790 /* Determine if this is a 'daytext' or an 'image'... */
2791 if (ci_strncmp(pd->text, "image:", 6) == 0) {
2792
2793 char temp_str[300];
2794 char *p;
2795
2796 printf("%d [()] \n", day);
2797
2798 strncpy(temp_str, pd->text, sizeof temp_str);
2799 p = strchr(pd->text, ' '); /* Isolate the EPS image filename */
2800 strncpy(temp_str, p+1, sizeof temp_str); /* Copy the remaining parameters */
2801 *p = '\0';
2802 printf("(%s) %s ", pd->text+6, temp_str);
2803
2804 this_day = 1;
2805 RESET_DATE(); /* reset working date */
2806
2807 printf("epsimage\n");
2808 }
2809 }
2810 }
2811
2812 return;
2813 }
2814
2815 /* ---------------------------------------------------------------------------
2816
2817 print_colors
2818
2819 Notes:
2820
2821 This routine prints an array specifying the color of each date in the
2822 current month (formerly calculated on the fly in drawnums{} in
2823 pcalinit.ps).
2824
2825 */
print_colors(int month,int year)2826 void print_colors (int month, int year)
2827 {
2828 register int day;
2829 year_info *py;
2830 month_info *pm;
2831 unsigned long holidays;
2832 int j, len;
2833 short color[32];
2834
2835 len = LENGTH_OF(month, year);
2836
2837 /* determine each date's color from its day of the week */
2838 for (day = 1, j = FIRST_OF(month, year);
2839 day <= len;
2840 day++, j = (j + 1) % 7) {
2841 color[day] = day_color[j];
2842 }
2843
2844 pm = (py = find_year(year, FALSE)) ? py->month[month-1] : NULL;
2845
2846 /* override weekday color for holidays */
2847 for (holidays = pm ? pm->holidays : 0, day = 1;
2848 holidays;
2849 holidays >>= 1, day++) {
2850 if (holidays & 01) color[day] = holiday_color;
2851 }
2852
2853 printf("/date_color [ -1"); /* dummy value for element 0 */
2854
2855 for (day = 1; day <= len; day++) {
2856 printf("%s %d", day % 10 == 1 ? " " : "", color[day]);
2857 }
2858 printf(" ] def\n");
2859
2860 return;
2861 }
2862
2863 /* ---------------------------------------------------------------------------
2864
2865 find_noteboxes
2866
2867 Notes:
2868
2869 This routine finds and prints the note box numbers for the specified
2870 month/year.
2871
2872 */
find_noteboxes(int month,int year)2873 void find_noteboxes (int month, int year)
2874 {
2875 register int day;
2876 year_info *py;
2877 month_info *pm;
2878
2879 /* if no text for this year and month, print empty list and return */
2880
2881 if ((py = find_year(year, FALSE)) == NULL ||
2882 (pm = py->month[month-1]) == NULL) {
2883 printf("/noteboxes [ ] def\n");
2884 return;
2885 }
2886
2887 printf("/noteboxes ["); /* start definition of list */
2888
2889 /* walk array of note text pointers, converting days to box numbers */
2890
2891 for (day = FIRST_NOTE_DAY; day <= LAST_NOTE_DAY; day++) {
2892 if (pm->day[day-1]) {
2893 printf(" %d", note_box(month, day, year));
2894 }
2895 }
2896
2897 printf(" ] def\n");
2898
2899 return;
2900 }
2901
2902 /* ---------------------------------------------------------------------------
2903
2904 print_dates
2905
2906 Notes:
2907
2908 This routine prints the dates (in "calendar" format) for the specified
2909 month and year.
2910
2911 */
print_dates(int month,int year)2912 void print_dates (int month, int year)
2913 {
2914 register int day;
2915 year_info *py;
2916 month_info *pm;
2917 register day_info *pd;
2918 unsigned long holidays;
2919 int has_holiday_text;
2920 static int save_year = 0;
2921
2922 /* if no text for this year and month, return */
2923
2924 if ((py = find_year(year, FALSE)) == NULL || (pm = py->month[month-1]) == NULL) {
2925 return;
2926 }
2927
2928 /* print the year if it has changed */
2929
2930 if (year != save_year) printf("%s %d\n", kw_year, save_year = year);
2931
2932 /* walk array of day text pointers and linked lists of text */
2933
2934 for (holidays = pm->holidays, day = 1;
2935 day < FIRST_NOTE_DAY;
2936 holidays >>= 1L, day++) {
2937 has_holiday_text = FALSE;
2938 for (pd = pm->day[day-1]; pd; pd = pd->next) {
2939 /* Bypass entries that specify an EPS image... */
2940 if (ci_strncmp(pd->text, "image:", 6) == 0) continue;
2941 if (date_style == USA_DATES) printf("%02d/%02d", month, day);
2942 else printf("%02d/%02d", day, month);
2943 #ifdef KEEP_ASTERISKS
2944 printf(pd->is_holiday ? "*\t" : "\t");
2945 #else
2946 printf("\t");
2947 #endif
2948 this_day = day;
2949 RESET_DATE(); /* reset working date */
2950 print_text(pd->text);
2951 printf("\n");
2952 has_holiday_text |= pd->is_holiday;
2953 }
2954 #ifdef KEEP_ASTERISKS
2955 /* was date flagged as holiday w/o associated text? */
2956 if ((holidays & 01) && !has_holiday_text) {
2957 if (date_style == USA_DATES) printf("%02d/%02d*\n", month, day);
2958 else printf("%02d/%02d*\n", day, month);
2959 }
2960 #endif
2961 }
2962
2963 return;
2964 }
2965
2966 /* ---------------------------------------------------------------------------
2967
2968 print_moon_info
2969
2970 Notes:
2971
2972 This routine prints the information necessary to draw moons.
2973
2974 If printing moons on all days, print the phase for each day. If
2975 printing only quarter moons, tweak the phase to an exact quarter (so the
2976 icon is printed correctly) and generate a list of the quarter-moon
2977 dates.
2978
2979 */
print_moon_info(int month,int year)2980 void print_moon_info (int month, int year)
2981 {
2982 int n, ndays, day, quarter;
2983 unsigned long qdays;
2984 double phase;
2985 static char *q[4] = {"NM", "1Q", "FM", "3Q"};
2986
2987 if (draw_moons == NO_MOONS) return;
2988
2989 /* print the phase of the moon for each day of the month */
2990
2991 printf("/moon_phases [\t\t%% from algorithm ");
2992 if (atof(time_zone) != 0.0) printf(" (UTC offset = %s)", time_zone);
2993 printf("\n\t");
2994
2995 for (n = 0, qdays = 0L, day = 1, ndays = LENGTH_OF(month, year);
2996 day <= ndays;
2997 day++) {
2998
2999 phase = find_phase(month, day, year, &quarter);
3000
3001 if (DEBUG(DEBUG_MOON)) {
3002 fprintf(stderr, "Moon phase on %04d-%02d-%02d: %.5f %s\n", year, month, day, phase,
3003 quarter != MOON_OTHER ? q[quarter] : "");
3004 }
3005
3006 /* adjust phase to exact quarter if printing only quarters */
3007 if (draw_moons == SOME_MOONS && quarter != MOON_OTHER) phase = 0.25 * quarter;
3008 if (draw_moons == ALL_MOONS || quarter != MOON_OTHER) {
3009 printf("%.3f%s", PRT_TWEAK(phase), ++n % 10 == 0 ? "\n\t" : " ");
3010 }
3011
3012 if (quarter != MOON_OTHER) qdays |= 1L << (day - 1);
3013 }
3014 printf("] def\n");
3015
3016 /* if drawing only quarter moons, print days when they occur */
3017
3018 if (draw_moons == SOME_MOONS) {
3019 printf("/quarter_moons [ ");
3020 for (day = 1; qdays; day++, qdays >>= 1) {
3021 if (qdays & 01) printf("%d ", day);
3022 }
3023 printf("] def\n");
3024 }
3025
3026 return;
3027 }
3028
3029 /* ---------------------------------------------------------------------------
3030
3031 print_julian_info
3032
3033 Notes:
3034
3035 This routine prints the information necessary to print Julian dates.
3036
3037 */
print_julian_info(int month,int year)3038 void print_julian_info (int month, int year)
3039 {
3040 if (julian_dates != NO_JULIANS) {
3041 printf("/jdstart %d def\n", DAY_OF_YEAR(month, 1, year));
3042 }
3043 if (julian_dates == ALL_JULIANS) {
3044 printf("/yearlen %d def\n", YEAR_LEN(year));
3045 }
3046 return;
3047 }
3048
3049 /* ---------------------------------------------------------------------------
3050
3051 print_month
3052
3053 Notes:
3054
3055 This routine generates a calendar for the specified month/year.
3056
3057 */
print_month(int month,int year)3058 void print_month (int month, int year)
3059 {
3060 static int nmonths = 0;
3061 #ifdef EPS_DSC
3062 static int page = 0;
3063 #endif
3064 int startbox;
3065
3066 #ifdef EPS_DSC
3067 /* start of new physical page? */
3068 if (!do_whole_year || nmonths % 12 == 0) {
3069 page++;
3070 printf("%%%%Page: %d %d\n", page, page);
3071 printf("%%%%BeginPageSetup\n");
3072 printf("clear flush\n");
3073 printf("/PageNum { %d } def\n", page);
3074 printf("/PageState save def\n");
3075 printf("%%%%EndPageSetup\n\n");
3076 }
3077 #endif
3078
3079 if (do_whole_year) {
3080 /* reset foot strings at start of each page */
3081 if (nmonths % 12 == 0) {
3082 def_footstring(lfoot, "Lfootstring");
3083 def_footstring(cfoot, "Cfootstring");
3084 def_footstring(rfoot, "Rfootstring");
3085 def_footstring(notes_hdr, "notesheading");
3086 printf("\n");
3087 }
3088 }
3089
3090 /* set up year and month */
3091 printf("/year %d def\n", year);
3092 printf("/month %d def\n", month);
3093
3094 /* move starting box to second row if conflict with small calendars */
3095 startbox = START_BOX(month, year);
3096 if (!do_whole_year &&
3097 (prev_cal_box[small_cal_pos] == startbox ||
3098 next_cal_box[small_cal_pos] == startbox)) {
3099 startbox += 7;
3100 }
3101
3102 printf("/startbox %d def\n", startbox);
3103 printf("/ndays %d def\n", LENGTH_OF(month, year));
3104
3105 find_noteboxes(month, year); /* make list of note boxes */
3106 print_colors(month, year); /* make list of date colors */
3107
3108 /* Are we printing 12 months per page or only one? */
3109 if (do_whole_year) {
3110 printf("/posn %d def\n", nmonths % 12); /* location on page */
3111
3112 print_julian_info(month, year); /* Julian date info */
3113 print_moon_info(month, year); /* moon info */
3114
3115 printf("printmonth\n\n");
3116 }
3117 else {
3118 /* reset foot strings each month (may change) */
3119 def_footstring(lfoot, "Lfootstring");
3120 def_footstring(cfoot, "Cfootstring");
3121 def_footstring(rfoot, "Rfootstring");
3122 def_footstring(notes_hdr, "notesheading");
3123
3124 /* generate information necessary for small calendars */
3125
3126 if (small_cal_pos != SC_NONE) {
3127 int m, y;
3128
3129 printf("/p_year %d def\n", y = PREV_YEAR(month, year));
3130 printf("/p_month %d def\n", m = PREV_MONTH(month, year));
3131 printf("/p_startbox %d def\n", START_BOX(m, y));
3132 printf("/p_ndays %d def\n", LENGTH_OF(m, y));
3133
3134 printf("/n_year %d def\n", y = NEXT_YEAR(month, year));
3135 printf("/n_month %d def\n", m = NEXT_MONTH(month, year));
3136 printf("/n_startbox %d def\n", START_BOX(m, y));
3137 printf("/n_ndays %d def\n", LENGTH_OF(m, y));
3138 }
3139
3140 print_julian_info(month, year); /* Julian date info */
3141 print_moon_info(month, year); /* moon info */
3142
3143 printf("printmonth\n");
3144 find_daytext(month, year, TRUE); /* holiday text */
3145 find_daytext(month, year, FALSE); /* day and note text */
3146 find_images(month, year); /* EPS images */
3147 }
3148
3149 /* end of physical page? */
3150 if (!do_whole_year || ++nmonths % 12 == 0) {
3151 #ifdef EPS_DSC
3152 printf("%%%%PageTrailer\n");
3153 printf("showpage\n");
3154 printf("clear flush\n");
3155 printf("PageState restore\n");
3156 #else
3157 printf("showpage\n");
3158 #endif
3159 }
3160 }
3161
3162 /* ---------------------------------------------------------------------------
3163
3164 print_html
3165
3166 Notes:
3167
3168 This routine prints a character string in HTML format, converting all
3169 non-printing characters to "&#NNN;" notation.
3170
3171 */
print_html(char * p)3172 void print_html (char *p)
3173 {
3174 char c, buf[STRSIZ];
3175
3176 while ((c = *p) != '\0') {
3177 if (c == '%' && p[1] != '\0') {
3178 p = expand_fmt(buf, p + 1);
3179 PUTSTR_CLEAN(isalnum, buf, stdout);
3180 }
3181 else if (c < ' ' || c > '\176') printf("&#%03d;", *p++ & CHAR_MSK);
3182 else printf("%c", *p++);
3183 }
3184 return;
3185 }
3186