/***************************************************************/ /* */ /* REM2PS.C */ /* */ /* Print a PostScript calendar. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #include "version.h" #include "config.h" #include "dynbuf.h" #include #include #include #include #include #include "rem2ps.h" #include "json.h" #define NEW(type) (malloc(sizeof(type))) #define SPECIAL_NORMAL 0 #define SPECIAL_POSTSCRIPT 1 #define SPECIAL_PSFILE 2 #define SPECIAL_MOON 3 #define SPECIAL_COLOR 4 #define SPECIAL_WEEK 5 #define SPECIAL_SHADE 6 #define SPECIAL_UNKNOWN 7 /* Array holding how specials sort */ static int SpecialSortOrder[] = { 0, /* NORMAL */ 1, /* POSTSCRIPT */ 1, /* PSFILE */ 2, /* MOON */ 0, /* COLOR */ 4, /* WEEK */ 5 /* SHADE */ }; typedef struct calentry { struct calentry *next; int special; char *entry; int daynum; } CalEntry; typedef struct { char const *name; int xsize, ysize; } PageType; char DayName[7][33]; char const *SmallCalLoc[] = { "", "bt", "tb", "sbt", }; #define NUMSMALL ((int) (sizeof(SmallCalLoc)/sizeof(SmallCalLoc[0]))) char const *SmallLocation; int SmallCol1, SmallCol2; PageType Pages[] = { {"Letter", 612, 792}, /* 8.5 x 11 in. */ {"Tabloid", 792, 1224}, /* 11 x 17 in. */ {"Ledger", 1224, 792}, /* 17 x 11 in. */ {"Legal", 612, 1008}, /* 8.5 x 14 in. */ {"Statement", 396, 612}, /* 5.5 x 8.5 in. */ {"Executive", 540, 720}, /* 7.5 x 10 in. */ {"A3", 842, 1190}, {"A4", 595, 842}, {"A5", 420, 595}, {"B4", 729, 1032}, {"B5", 519, 729}, {"Folio", 612, 936}, {"Quarto", 612, 780}, {"10x14", 720, 1008}, {"-custom-", 0, 0} }; PageType DefaultPage[1] = { DEFAULT_PAGE }; #define NUMPAGES (sizeof(Pages)/sizeof(Pages[0])) CalEntry *CurEntries = NULL; CalEntry *PsEntries[32]; PageType *CurPage; char PortraitMode; char DaynumRight; char NoSmallCal; char UseISO; char const *HeadFont="Helvetica"; char const *TitleFont="Helvetica"; char const *DayFont="Helvetica-BoldOblique"; char const *EntryFont="Helvetica"; char const *SmallFont="Helvetica"; char const *LineWidth = "1"; char const *HeadSize="14"; char const *TitleSize="14"; char const *DaySize="14"; char const *EntrySize="8"; char const *BorderSize = "6"; char const *UserProlog = NULL; int validfile = 0; int CurDay; int MaxDay; int DayNum; int WkDayNum; int FirstWkDay; int MondayFirst; int LeftMarg, RightMarg, TopMarg, BotMarg; int FillPage; int Verbose = 0; void Init (int argc, char *argv[]); void Usage (char const *s); void DoPsCal (void); int DoQueuedPs (void); void DoSmallCal (char const *m, int days, int first, int col, int which); void WriteProlog (void); void WriteCalEntry (void); void WriteOneEntry (CalEntry *c); void GetSmallLocations (void); char const *EatToken(char const *in, char *out, int maxlen); static void put_escaped_string(char const *s) { while(*s) { if (*s == '\\' || *s == '(' || *s == ')') { putchar('\\'); } putchar(*s); s++; } } /***************************************************************/ /* */ /* StrCmpi */ /* */ /* Compare strings, case insensitive. */ /* */ /***************************************************************/ int StrCmpi(char const *s1, char const *s2) { int r; while (*s1 && *s2) { r = toupper(*s1) - toupper(*s2); if (r) return r; s1++; s2++; } return toupper(*s1) - toupper(*s2); } /***************************************************************/ /* */ /* Parse the new-style JSON intermediate format */ /* */ /***************************************************************/ static CalEntry * JSONToCalEntry(DynamicBuffer *buf) { CalEntry *c; json_value *val; val = json_parse(DBufValue(buf), DBufLen(buf)); if (!val) { fprintf(stderr, "Unable to parse JSON line `%s'\n", DBufValue(buf)); exit(EXIT_FAILURE); } if (val->type != json_object) { fprintf(stderr, "Expecting JSON object; found `%s'\n", DBufValue(buf)); exit(EXIT_FAILURE); } c = NEW(CalEntry); if (!c) { fprintf(stderr, "malloc failed - aborting.\n"); exit(EXIT_FAILURE); } c->next = NULL; c->special = SPECIAL_NORMAL; int got_date = 0, got_body = 0; size_t i; for (i=0; iu.object.length; i++) { char const *nm = val->u.object.values[i].name; json_value *v = val->u.object.values[i].value; char const *s; if (!strcmp(nm, "date")) { if (v->type == json_string) { s = v->u.string.ptr; c->daynum = (s[8] - '0') * 10 + s[9] - '0'; got_date = 1; } } else if (!strcmp(nm, "body")) { if (v->type == json_string) { s = v->u.string.ptr; c->entry = malloc(strlen(s)+1); if (!c->entry) { fprintf(stderr, "malloc failed - aborting.\n"); exit(EXIT_FAILURE); } strcpy(c->entry, s); got_body = 1; } } else if (!strcmp(nm, "passthru")) { if (v->type == json_string) { s = v->u.string.ptr; if (!StrCmpi(s, "PostScript")) { c->special = SPECIAL_POSTSCRIPT; } else if (!StrCmpi(s, "SHADE")) { c->special = SPECIAL_SHADE; } else if (!StrCmpi(s, "MOON")) { c->special = SPECIAL_MOON; } else if (!StrCmpi(s, "WEEK")) { c->special = SPECIAL_WEEK; } else if (!StrCmpi(s, "PSFile")) { c->special = SPECIAL_PSFILE; } else if (!StrCmpi(s, "COLOUR") || !StrCmpi(s, "COLOR")) { c->special = SPECIAL_COLOR; } else { c->special = SPECIAL_UNKNOWN; } } } } json_value_free(val); if (!got_body || !got_date) { fprintf(stderr, "Could not parse line `%s'\n", DBufValue(buf)); exit(EXIT_FAILURE); } return c; } /***************************************************************/ /* */ /* Parse the old-style REM2PS intermediate format */ /* */ /***************************************************************/ static CalEntry * TextToCalEntry(DynamicBuffer *buf) { char const *startOfBody; char passthru[PASSTHRU_LEN+1]; CalEntry *c = NEW(CalEntry); if (!c) { fprintf(stderr, "malloc failed - aborting.\n"); exit(EXIT_FAILURE); } c->next = NULL; c->special = SPECIAL_NORMAL; c->daynum = (DBufValue(buf)[8] - '0') * 10 + DBufValue(buf)[9] - '0'; /* Skip the tag, duration and time */ startOfBody = DBufValue(buf)+10; /* Eat the passthru */ startOfBody = EatToken(startOfBody, passthru, PASSTHRU_LEN); /* Eat the tag */ startOfBody = EatToken(startOfBody, NULL, 0); /* Eat the duration */ startOfBody = EatToken(startOfBody, NULL, 0); /* Eat the time */ startOfBody = EatToken(startOfBody, NULL, 0); c->entry = malloc(strlen(startOfBody) + 1); if (!c->entry) { fprintf(stderr, "malloc failed - aborting.\n"); exit(EXIT_FAILURE); } strcpy(c->entry, startOfBody); /* Save the type of SPECIAL */ if (!StrCmpi(passthru, "PostScript")) { c->special = SPECIAL_POSTSCRIPT; } else if (!StrCmpi(passthru, "SHADE")) { c->special = SPECIAL_SHADE; } else if (!StrCmpi(passthru, "MOON")) { c->special = SPECIAL_MOON; } else if (!StrCmpi(passthru, "WEEK")) { c->special = SPECIAL_WEEK; } else if (!StrCmpi(passthru, "PSFile")) { c->special = SPECIAL_PSFILE; } else if (!StrCmpi(passthru, "COLOUR") || !StrCmpi(passthru, "COLOR")) { c->special = SPECIAL_COLOR; } else if (StrCmpi(passthru, "*")) { c->special = SPECIAL_UNKNOWN; } return c; } /***************************************************************/ /* */ /* MAIN PROGRAM */ /* */ /***************************************************************/ int main(int argc, char *argv[]) { /* If stdin is a tty - probably wrong. */ DynamicBuffer buf; DBufInit(&buf); Init(argc, argv); if (isatty(0)) { Usage("Input should not come from a terminal"); } int first_line = 1; /* Search for a valid input file */ while (!feof(stdin)) { DBufGets(&buf, stdin); if (first_line && (!strcmp(DBufValue(&buf), "["))) { fprintf(stderr, "Rem2PS: It appears that you have invoked Remind with the -ppp option.\n Please use either -p or -pp, but not -ppp.\n"); exit(EXIT_FAILURE); } first_line = 0; if (!strcmp(DBufValue(&buf), PSBEGIN) || !strcmp(DBufValue(&buf), PSBEGIN2)) { if (!validfile) { if (Verbose) { fprintf(stderr, "Rem2PS: Version %s Copyright 1992-2024 by Dianne Skoll\n\n", VERSION); fprintf(stderr, "Generating PostScript calendar\n"); } } validfile++; DoPsCal(); } } if (!validfile) { fprintf(stderr, "Rem2PS: Couldn't find any calendar data - are you\n"); fprintf(stderr, " sure you fed me input produced by remind -p ...?\n"); exit(EXIT_FAILURE); } printf("%%%%Trailer\n"); printf("%%%%Pages: %d\n", validfile); if (Verbose) fprintf(stderr, "Rem2PS: Done\n"); return 0; } /***************************************************************/ /* */ /* DoPsCal - emit PostScript for the calendar. */ /* */ /***************************************************************/ void DoPsCal(void) { char month[40], year[40]; char prevm[40], nextm[40]; int days, wkday, prevdays, nextdays; int sfirst; int i; int firstcol; DynamicBuffer buf; CalEntry *c, *d, *p; char *s; /* Read the month and year name, followed by # days in month and 1st day of month */ DBufInit(&buf); DBufGets(&buf, stdin); sscanf(DBufValue(&buf), "%39s %39s %d %d %d", month, year, &days, &wkday, &MondayFirst); /* Replace underscores in month name with spaces */ s = month; while(*s) { if (*s == '_') *s = ' '; s++; } /* Get day names */ DBufGets(&buf, stdin); sscanf(DBufValue(&buf), "%32s %32s %32s %32s %32s %32s %32s", DayName[0], DayName[1], DayName[2], DayName[3], DayName[4], DayName[5], DayName[6]); /* Replace underscores in day names with spaces */ for (i=0; i<7; i++) { s = DayName[i]; while(*s) { if (*s == '_') *s = ' '; s++; } } /* We write the prolog here because it's only at this point that MondayFirst is set correctly. */ if (validfile == 1) { WriteProlog(); } DBufGets(&buf, stdin); sscanf(DBufValue(&buf), "%39s %d", prevm, &prevdays); DBufGets(&buf, stdin); sscanf(DBufValue(&buf), "%39s %d", nextm, &nextdays); /* Replace underscores with spaces in names of next/prev month */ s = prevm; while(*s) { if (*s == '_') *s = ' '; s++; } s = nextm; while(*s) { if (*s == '_') *s = ' '; s++; } DBufFree(&buf); MaxDay = days; FirstWkDay = wkday; /* Print a message for the user */ if (Verbose) fprintf(stderr, " %s %s\n", month, year); printf("%%%%Page: %c%c%c%c%c %d\n", month[0], month[1], month[2], year[2], year[3], validfile); printf("%%%%PageBoundingBox: 0 0 %d %d\n", CurPage->xsize, CurPage->ysize); /* Emit PostScript to do the heading */ if (!PortraitMode) printf("90 rotate 0 XSIZE neg translate\n"); printf("/SAVESTATE save def (%s) (%s) PreCal SAVESTATE restore\n", month, year); printf("(%s %s) doheading\n", month, year); /* Figure out the column of the first day in the calendar */ if (MondayFirst) { firstcol = wkday-1; if (firstcol < 0) firstcol = 6; } else { firstcol = wkday; } /* Calculate the minimum box size */ if (!FillPage) { printf("/MinBoxSize ytop MinY sub 7 div def\n"); } else { if ((days == 31 && firstcol >= 5) || (days == 30 && firstcol == 6)) printf("/MinBoxSize ytop MinY sub 6 div def\n"); else if (days == 28 && firstcol == 0 && NoSmallCal) printf("/MinBoxSize ytop MinY sub 4 div def\n"); else printf("/MinBoxSize ytop MinY sub 5 div def\n"); } printf("/ysmalltop ytop def\n"); /* Do each entry */ CurEntries = NULL; CurDay = 1; WkDayNum = wkday; while(1) { if (feof(stdin)) { fprintf(stderr, "Input from REMIND is corrupt!\n"); exit(EXIT_FAILURE); } DBufGets(&buf, stdin); if (!strcmp(DBufValue(&buf), PSEND) || !strcmp(DBufValue(&buf), PSEND2)) { DBufFree(&buf); break; } /* Ignore lines beginning with '#' */ if (DBufValue(&buf)[0] == '#') { DBufFree(&buf); continue; } if (DBufValue(&buf)[0] == '{') { /* Starts with '{', so assume new-style JSON format */ c = JSONToCalEntry(&buf); } else { /* Assume it's the old-style rem2ps intermediate format */ c = TextToCalEntry(&buf); } /* If it's an unknown special, ignore */ if (c->special == SPECIAL_UNKNOWN) { DBufFree(&buf); free(c); c = NULL; continue; } if (c->daynum != CurDay) { for(; CurDaydaynum; CurDay++) { WriteCalEntry(); WkDayNum = (WkDayNum + 1) % 7; } } if (c->special == SPECIAL_POSTSCRIPT || c->special == SPECIAL_SHADE || c->special == SPECIAL_MOON || c->special == SPECIAL_WEEK || c->special == SPECIAL_PSFILE) { if (!PsEntries[c->daynum]) { PsEntries[c->daynum] = c; } else { d = PsEntries[c->daynum]; p = NULL; /* Slot it into the right place */ while (d->next && (SpecialSortOrder[c->special] <= SpecialSortOrder[d->special])) { p = d; d = d->next; } if (SpecialSortOrder[c->special] <= SpecialSortOrder[d->special]) { c->next = d->next; d->next = c; } else { if (p) { p->next = c; } else { PsEntries[c->daynum] = c; } c->next = d; } } } else { /* Put on linked list */ if (!CurEntries) { CurEntries = c; } else { d = CurEntries; while(d->next) d = d->next; d->next = c; } } } for(; CurDay<=days; CurDay++) { WriteCalEntry(); WkDayNum = (WkDayNum + 1) % 7; } /* If wkday < 2, set ysmall. If necessary (only for feb) increase cal size. */ printf("/ysmallbot ylast def\n"); /* Now draw the vertical lines */ GetSmallLocations(); for (i=0; i<=7; i++) { printf("%d xincr mul MinX add ymin %d xincr mul MinX add topy L\n", i, i); } /* print the small calendars */ if (!NoSmallCal) { sfirst = wkday - (prevdays % 7); if (sfirst < 0) sfirst += 7; DoSmallCal(prevm, prevdays, sfirst, SmallCol1, 1); sfirst = wkday + (days % 7); if (sfirst >6) sfirst -= 7; DoSmallCal(nextm, nextdays, sfirst, SmallCol2, 2); } /* Do it! */ printf("showpage\n"); } /***************************************************************/ /* */ /* WriteProlog - write the PostScript prologue */ /* */ /***************************************************************/ void WriteProlog(void) { int i; int x = CurPage->xsize; int y = CurPage->ysize; char const *isostuff; FILE *fp; int nread; char buffer[512]; if (!PortraitMode) { i = x; x = y; y = i; } if (UseISO) isostuff = "reencodeISO"; else isostuff = "copyFont"; /* Write the document structuring stuff */ printf("%%!PS-Adobe-2.0\n"); printf("%%%%DocumentFonts: %s", HeadFont); if (strcmp(TitleFont, HeadFont)) printf(" %s", TitleFont); if (strcmp(TitleFont, DayFont) && strcmp(HeadFont, DayFont)) printf(" %s", DayFont); if (strcmp(EntryFont, HeadFont) && strcmp(TitleFont, EntryFont) && strcmp(EntryFont, DayFont)) printf(" %s", EntryFont); if (!NoSmallCal && strcmp(SmallFont, HeadFont) && strcmp(SmallFont, DayFont) && strcmp(TitleFont, SmallFont) && strcmp(SmallFont, EntryFont)) printf(" %s", SmallFont); putchar('\n'); printf("%%%%Creator: Rem2PS\n"); printf("%%%%Pages: (atend)\n"); printf("%%%%Orientation: %s\n", PortraitMode ? "Portrait" : "Landscape"); printf("%%%%EndComments\n"); if (PortraitMode) { printf("<< /PageSize [%d %d] >> setpagedevice\n", x, y); } else { /* They were swapped up above, so swap them back or we'll get rotated output */ printf("<< /PageSize [%d %d] >> setpagedevice\n", y, x); } for (i=0; PSProlog1[i]; i++) puts(PSProlog1[i]); if (!MondayFirst) printf("[(%s) (%s) (%s) (%s) (%s) (%s) (%s)]\n", DayName[0], DayName[1], DayName[2], DayName[3], DayName[4], DayName[5], DayName[6]); else printf("[(%s) (%s) (%s) (%s) (%s) (%s) (%s)]\n", DayName[1], DayName[2], DayName[3], DayName[4], DayName[5], DayName[6], DayName[0]); for (i=0; PSProlog2[i]; i++) puts(PSProlog2[i]); printf("/HeadFont /%s %s\n", HeadFont, isostuff); if (!NoSmallCal) printf("/SmallFont /%s %s\n", SmallFont, isostuff); printf("/DayFont /%s %s\n", DayFont, isostuff); printf("/EntryFont /%s %s\n", EntryFont, isostuff); printf("/TitleFont /%s %s\n", TitleFont, isostuff); printf("/HeadSize %s def\n", HeadSize); printf("/DaySize %s def\n", DaySize); printf("/EntrySize %s def\n", EntrySize); printf("/TitleSize %s def\n", TitleSize); printf("/XSIZE %d def\n", CurPage->xsize); printf("/MinX %d def\n", LeftMarg); printf("/MinY %d def\n", BotMarg); printf("/MaxX %d def\n", x-RightMarg); printf("/MaxY %d def\n", y-TopMarg); printf("/Border %s def\n", BorderSize); printf("/LineWidth %s def\n", LineWidth); printf("%s setlinewidth\n", LineWidth); /* Check if smallfont is fixed pitch */ if (!NoSmallCal) { printf("/SmallFont findfont /FontInfo get /isFixedPitch get\n"); /* Define SmallString used to set smallfont size */ printf("{/SmallString (WW ) def}\n"); printf("{/SmallString (WW) def}\nifelse\n"); } /* Do the user-supplied prolog file, if any */ if (UserProlog) { fp = fopen(UserProlog, "r"); if (!fp) { fprintf(stderr, "Could not open prologue file `%s'\n", UserProlog); } else { while(1) { nread = fread(buffer, sizeof(char), 512, fp); if (!nread) break; fwrite(buffer, sizeof(char), nread, stdout); } fclose(fp); } } printf("%%%%EndProlog\n"); } /***************************************************************/ /* */ /* WriteCalEntry - write all entries for one day */ /* */ /***************************************************************/ void WriteCalEntry(void) { CalEntry *c = CurEntries; CalEntry *d; int begin, end, i, HadQPS; /* Move to appropriate location */ printf("/CAL%d {\n", CurDay); if (!MondayFirst) printf("Border ytop %d xincr mul MinX add xincr\n", WkDayNum); else printf("Border ytop %d xincr mul MinX add xincr\n", (WkDayNum ? WkDayNum-1 : 6)); /* Set up the text array */ printf("[\n"); CurEntries = NULL; while(c) { WriteOneEntry(c); free(c->entry); d = c->next; free(c); c = d; } printf("]\n"); /* Print the day number */ printf("(%d) %d\n", CurDay, (int) DaynumRight); /* Do it! */ printf("DoCalBox\n"); /* Update ymin */ printf("/y exch def y ymin lt {/ymin y def} if\n"); printf("} def\n"); /* If WkDayNum is a Sunday or Monday, depending on MondayFirst, move to next row. Also handle the queued PS and PSFILE reminders */ if ((!MondayFirst && WkDayNum == 6) || (MondayFirst && WkDayNum == 0) || CurDay == MaxDay) { HadQPS = 0; if (MondayFirst) begin = CurDay - (WkDayNum ? WkDayNum-1 : 6); else begin = CurDay - WkDayNum; if (begin < 1) begin = 1; end = CurDay; for (i=begin; i<=end; i++) { if (PsEntries[i]) { HadQPS = 1; break; } } /* Avoid problems with blotching if PS printer has roundoff errors */ if (HadQPS) printf("1 setgray\n"); for (i=begin; i<=end; i++) { printf("CAL%d\n", i); } if (HadQPS) printf("0 setgray\n"); printf("/y ytop MinBoxSize sub def y ymin lt {/ymin y def} if\n"); /* Draw the line at the bottom of the row */ printf("MinX ymin MaxX ymin L\n"); /* Update ytop */ printf("/ylast ytop def\n"); printf("/ytop ymin def\n"); (void) DoQueuedPs(); /* Re-do the calendar stuff if there was any included PS code */ if (HadQPS) { printf("/ytop ylast def\n"); for (i=begin; i<=end; i++) { printf("CAL%d\n", i); } printf("/y ytop MinBoxSize sub def y ymin lt {/ymin y def} if\n"); printf("MinX ymin MaxX ymin L\n"); printf("/ylast ytop def\n"); printf("/ytop ymin def\n"); } } } /***************************************************************/ /* */ /* WriteOneEntry - write an entry for one day */ /* */ /***************************************************************/ void WriteOneEntry(CalEntry *c) { int ch, i; char const *s = c->entry; printf(" ["); /* Chew up leading spaces */ while(isspace((unsigned char) *s)) s++; /* Skip three decimal numbers for COLOR special */ if (c->special == SPECIAL_COLOR) { for (i=0; i<3; i++) { while(*s && !isspace(*s)) s++; while(*s && isspace(*s)) s++; } } putchar('('); while(*s) { /* Use the "unsigned char" cast to fix problem on Solaris 2.5 */ /* which treated some latin1 characters as white space. */ ch = (unsigned char) *s++; if (ch == '\\' || ch == '(' || ch == ')') putchar('\\'); if (!isspace(ch)) putchar(ch); else { putchar(')'); while(isspace((unsigned char)*s)) s++; if (!*s) { goto finish; } putchar('('); } } printf(")\n"); finish: if (c->special == SPECIAL_COLOR) { int r, g, b; if (sscanf(c->entry, "%d %d %d", &r, &g, &b) == 3) { if (r < 0) r = 0; else if (r > 255) r = 255; if (g < 0) g = 0; else if (g > 255) g = 255; if (b < 0) b = 0; else if (b > 255) b = 255; printf("(gsave %f %f %f setrgbcolor)(grestore)", r / 255.0, g / 255.0, b / 255.0); } else { /* Punt... unrecognized color is black */ printf("()()"); } } else { printf("()()"); } printf("]\n"); } /***************************************************************/ /* */ /* Init - set up parameters */ /* */ /***************************************************************/ void Init(int argc, char *argv[]) { char const *s; char const *t; int i=1; size_t j; int k; int offset; PortraitMode = 1; NoSmallCal = 0; LeftMarg = 36; RightMarg = 36; TopMarg = 36; BotMarg = 36; UseISO = 0; FillPage = 0; MondayFirst = 0; SmallLocation = "bt"; DaynumRight = 1; for(j=0; j<32; j++) PsEntries[i] = NULL; CurPage = DefaultPage; /* Letter size by default */ while (i < argc) { s = argv[i]; i++; if (*s++ != '-') Usage("Options must begin with `-'"); switch(*s++) { case 'p': if (i == argc) Usage("Prologue filename must be supplied"); UserProlog = argv[i++]; break; case 's': if (i == argc) Usage("Size must be supplied"); t = argv[i++]; while(*s) { switch(*s++) { case 'h': HeadSize = t; break; case 'e': EntrySize = t; break; case 'd': DaySize = t; break; case 't': TitleSize = t; break; default: Usage("Size must specify h, t, e, or d"); } } break; case 'f': if (i == argc) Usage("Font must be supplied"); t = argv[i++]; while(*s) { switch(*s++) { case 'h': HeadFont = t; break; case 'e': EntryFont = t; break; case 'd': DayFont = t; break; case 's': SmallFont = t; break; case 't': TitleFont = t; break; default: Usage("Font must specify s, h, t, e, or d"); } } break; case 'v': Verbose = 1; break; case 'm': if (i == argc) Usage("Media must be supplied"); t = argv[i++]; CurPage = NULL; for (j=0; jxsize = (int) (w * 72.0); CurPage->ysize = (int) (h * 72.0); } else if (sscanf(t, "%lfx%lfcm", &w, &h) == 2) { CurPage = &Pages[NUMPAGES-1]; CurPage->xsize = (int) ((double) w * 28.346457); CurPage->ysize = (int) ((double) w * 28.346457); } } if (!CurPage) { fprintf(stderr, "\nUnknown media specified.\n"); fprintf(stderr, "\nAvailable media types:\n"); for (j=0; j=0 && kentry+fnoff))) fnoff++; switch(e->special) { case SPECIAL_POSTSCRIPT: /* Send PostScript through */ printf("%s\n", e->entry+fnoff); break; case SPECIAL_PSFILE: /* PostScript from a file */ fp = fopen(e->entry+fnoff, "r"); if (!fp) { fprintf(stderr, "Could not open PostScript file `%s'\n", e->entry+1); } else { while(1) { nread = fread(buffer, sizeof(char), 512, fp); if (!nread) break; fwrite(buffer, sizeof(char), nread, stdout); } fclose(fp); } break; case SPECIAL_SHADE: /* Shading */ num = sscanf(e->entry+fnoff, "%d %d %d", &r, &g, &b); if (num == 1) { g = r; b = r; } else if (num != 3) { fprintf(stderr, "Rem2PS: Malformed SHADE special\n"); break; } if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) { fprintf(stderr, "Rem2PS: Illegal values for SHADE\n"); break; } printf("/_A LineWidth 2 div def _A _A moveto\n"); printf("BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto\n"); printf("_A BoxHeight _A sub lineto closepath\n"); printf("%g %g %g setrgbcolor fill 0.0 setgray\n", r/255.0, g/255.0, b/255.0); break; case SPECIAL_WEEK: /* Week number */ printf("gsave Border Border 2 div moveto /EntryFont findfont EntrySize 1.2 div scalefont setfont ("); s = e->entry+fnoff; while(*s && isspace(*s)) { s++; } put_escaped_string(s); printf(") show grestore\n"); break; case SPECIAL_MOON: /* Moon phase */ num = sscanf(e->entry+fnoff, "%d %d %d", &phase, &moonsize, &fontsize); /* See if we have extra stuff */ extra = e->entry+fnoff; /* Skip phase */ while(*extra && !isspace(*extra)) extra++; while(*extra && isspace(*extra)) extra++; /* Skip moon size */ while(*extra && !isspace(*extra)) extra++; while(*extra && isspace(*extra)) extra++; /* Skip font size */ while(*extra && !isspace(*extra)) extra++; while(*extra && isspace(*extra)) extra++; if (num == 1) { moonsize = -1; fontsize = -1; } else if (num == 2) { fontsize = -1; } else if (num != 3) { fprintf(stderr, "Rem2PS: Badly formed MOON special\n"); break; } if (phase < 0 || phase > 3) { fprintf(stderr, "Rem2PS: Illegal MOON phase %d\n", phase); break; } if (moonsize < 0) { size = "DaySize 2 div"; } else { sprintf(buffer, "%d", moonsize); size = buffer; } /* Store the starting X coordinate in "moonstartx" */ if (DaynumRight) { printf("Border %s add /moonstartx exch def", size); } else { printf("xincr Border sub %s sub ", size); if (*extra) { if (fontsize < 0) { fsize = "EntrySize"; } else { sprintf(fbuffer, "%d", fontsize); fsize = fbuffer; } printf("/EntryFont findfont %s scalefont setfont (", fsize); put_escaped_string(extra); printf(") stringwidth pop sub Border sub "); } printf("/moonstartx exch def\n"); } printf(" gsave 0 setgray newpath "); printf("moonstartx BoxHeight Border sub %s sub\n", size); printf(" %s 0 360 arc closepath\n", size); switch(phase) { case 0: printf("fill\n"); break; case 2: printf("stroke\n"); break; case 1: printf("stroke\nnewpath "); printf("moonstartx BoxHeight Border sub %s sub\n", size); printf("%s 90 270 arc closepath fill\n", size); break; default: printf("stroke\nnewpath "); printf("moonstartx BoxHeight Border sub %s sub\n", size); printf("%s 270 90 arc closepath fill\n", size); break; } /* Anything left? */ if (*extra) { printf("moonstartx %s add Border add BoxHeight border sub %s sub %s sub moveto\n", size, size, size); if (fontsize < 0) { fsize = "EntrySize"; } else { sprintf(fbuffer, "%d", fontsize); fsize = fbuffer; } printf("/EntryFont findfont %s scalefont setfont (", fsize); put_escaped_string(extra); printf(") show\n"); } printf("grestore\n"); break; } /* Free the entry */ free(e->entry); n = e->next; free(e); e = n; } if (PsEntries[i]) printf("\n SAVESTATE restore\n"); PsEntries[i] = NULL; } return HadPS; } /***************************************************************/ /* */ /* GetSmallLocations */ /* */ /* Set up the locations for the small calendars. */ /* */ /***************************************************************/ void GetSmallLocations(void) { char c; char const *s = SmallLocation; int colfirst, collast; /* Figure out the first and last columns */ colfirst = FirstWkDay; collast = (FirstWkDay+MaxDay-1) % 7; if (MondayFirst) { colfirst = colfirst ? colfirst - 1 : 6; collast = collast ? collast - 1 : 6; } NoSmallCal = 0; while((c = *s++) != 0) { switch(c) { case 'b': /* Adjust Feb. if we want it on the bottom */ if (MaxDay == 28 && colfirst == 0) { printf("/ysmallbot ymin def /ymin ysmallbot MinBoxSize sub def\n"); printf("MinX ymin MaxX ymin L\n"); printf("/ysmall1 ysmallbot def /ysmall2 ysmallbot def\n"); SmallCol1 = 5; SmallCol2 = 6; return; } if (collast <= 4) { printf("/ysmall1 ysmallbot def /ysmall2 ysmallbot def\n"); SmallCol1 = 5; SmallCol2 = 6; return; } break; case 't': if (colfirst >= 2) { printf("/ysmall1 ysmalltop def /ysmall2 ysmalltop def\n"); SmallCol1 = 0; SmallCol2 = 1; return; } break; case 's': if (colfirst >= 1 && collast<=5) { printf("/ysmall1 ysmalltop def /ysmall2 ysmallbot def\n"); SmallCol1 = 0; SmallCol2 = 6; return; } break; } } NoSmallCal = 1; return; } /***************************************************************/ /* */ /* EatToken */ /* */ /* Read a space-delimited token into an output buffer. */ /* */ /***************************************************************/ char const *EatToken(char const *in, char *out, int maxlen) { int i = 0; /* Skip space before token */ while(*in && isspace(*in)) in++; /* Eat the token */ while(*in && !isspace(*in)) { if (i < maxlen) { if (out) *out++ = *in; i++; } in++; } if (out) *out = 0; return in; }