home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume17 / calentool / part10 / pcal.c
Encoding:
C/C++ Source or Header  |  1991-04-06  |  17.0 KB  |  601 lines

  1. /*
  2.  * $Header: pcal.c,v 2.5 91/03/27 16:46:14 billr Exp $
  3.  */
  4. #include "ct.h"        /* for NO_PRINTER define */
  5.  
  6. #ifndef NO_PRINTER
  7. #ifndef RASTER_ONLY
  8. /*
  9.  * pcal - print pretty PostScript image of a month calendar
  10.  *
  11.  * Pieces extracted from the pcal program by Ken Keirnan and modified
  12.  * slightly by Bill Randle, Tektronix, Inc. <billr@saab.CNA.TEK.COM>.
  13.  * 
  14.  * "Pcal" is a program to print PostScript calendars for any month and year.
  15.  * Pcal is the combined effort of several people, most notably Patrick Wood
  16.  * of Pipeline Associates, Inc. for the original PostScript code and Bill
  17.  * Vogel of AT&T for the calendar file mechanism.  My part was simple
  18.  * translation to a "C" program, the addition of a couple options and a more
  19.  * generalized date searching routine (oh yes, and a manual page :-).
  20.  * 
  21.  * The original calendar PostScript was Copyright (c) 1987 by Patrick Wood
  22.  * and Pipeline Associates, Inc. with permission to modify and redistribute.
  23.  * 
  24.  * Ken Keirnan
  25.  * Pacific Bell
  26.  * San Ramon, CA.
  27.  *
  28.  * Changes and additions Copyright (C) 1989, 1991 Tektronix, Inc.
  29.  *    All Rights Reserved
  30.  * Permission is hereby granted to use and modify the modifications in source
  31.  * or binary form as long as they are not sold for profit and this copyright
  32.  * notice remains intact.
  33.  *
  34.  * Modified by PM Lashley, KLA Instruments, Inc.
  35.  * Fixes and extentions:                1..2 June, 1989
  36.  *    1. Notes were extending beyond the right and bottom edges of the day.
  37.  *       They are now line-wrapped with indentation.
  38.  *    3. Bottom justify notes for each day.  Truncate if there are too many
  39.  *       to fit.
  40.  *    3. If month starts on a Wednesday or later, put the previous/next
  41.  *       month inserts in the first two day boxes instead of the last two.
  42.  *    4. Center the month inserts vertically in the day box.
  43.  *    5. When possible, draw the calendar in a 5x7 grid instead of a 6x7
  44.  *       grid.  Each day will be taller, and accomodate more note text.
  45.  *    6. Added various comments to the Postscript code array.
  46.  *    7. If a periodic event was removed for given occurance, that event
  47.  *       would be printed twice when it should not be printed at all.
  48.  *    8. Replaced the constant "Helvetica-Narrow" with PS_NOTE_FONT for
  49.  *       sites which do not have the Helvetica-Narrow font but do have
  50.  *       some preference other than the default.
  51.  *
  52.  * Left to do:
  53.  *    1. If a 31 day month starts on Tuesday, the previous month should
  54.  *       be in the upper left, and the next month in the lower right.
  55.  *       This may be more trouble than it is worth.
  56.  *
  57.  * Notes:
  58.  *    1. The Postscript code layout is a compromise between readability
  59.  *       and compaction.  All comments and vertical spacing are in the
  60.  *       c source only to keep the output file size small.
  61.  *
  62.  * Further changes and additions Copyright (C) 1989 PM Lashley
  63.  *    All Rights Reserved
  64.  * Permission is hereby granted to use and modify the modifications in source
  65.  * or binary form as long as they are not sold for profit and this copyright
  66.  * notice remains intact.
  67.  *
  68.  * Several more additions by Hakan Kallberg (hk@simulina.se) to support
  69.  * printing Monday first in the week and natural language support for
  70.  * Swedish.
  71.  *
  72.  */
  73.  
  74. #include <stdio.h>
  75. #include <time.h>
  76.  
  77. extern struct tm current, First;
  78. extern struct dayslot *slots;
  79. extern int get_day_appts();
  80. extern int monday_first;
  81. extern int n_slots;
  82. extern int deleted();
  83. extern char *p_lang_header[];
  84.  
  85. /*
  86.  * pmonfirst - replace strings for entries in pheader for monday first
  87.  * calendars.
  88.  */
  89. static char *pmonfirst[] = {
  90.     "  [ (Monday) (Tuesday) (Wednesday) ",
  91.     "    (Thursday) (Friday) (Saturday) (Sunday) ]",
  92.     "    day start add 1 add 7 mod 0 eq {",
  93.     "    day start add 1 add 7 mod 1 eq {",
  94.     "  1 sub dup -1 eq { pop 6 } if"
  95. };
  96.  
  97. /*
  98.  * pheader - provides the PostScript routines
  99.  */
  100. static char *prolog[] = {
  101.     "%!",
  102.     "%%Creator: Pipeline Associates",
  103.     "%%Title: calentool's month-at-a-glance",
  104.     "%%Modifed: Ken Keirnan, Bill Randle, PM Lashley, Richard Wolff and Hakan Kallberg",
  105.     "%%DocumentFonts: Times-Bold Helvetica-Bold Helvetica-Narrow",
  106.     "%%Bounding Box: 0 0 500 700",
  107.     "%%Pages: 1",
  108.     "%%EndComments",
  109.     "/titlefont /Times-Bold def",
  110.     "/dayfont /Helvetica-Bold def",
  111.     (char *)0
  112. };
  113.  
  114. static char *pheader[] = {
  115.     "/month_names [ (January) (February) (March) (April) (May) (June) (July)",
  116.     "  (August) (September) (October) (November) (December) ] def",
  117.     "/prtnum { 3 string cvs show} def",
  118.     /*
  119.      * -  -weeks-  int
  120.      *
  121.      * Pushes the number of week lines (rows) necessary for the current month.
  122.      */
  123.     "/weeks {",
  124.     "  startday ndays add 35 gt { 6 } { 5 } ifelse",
  125.     "} def",
  126.  
  127.     /*
  128.      * -  -monthHeight-  int
  129.      *
  130.      * Pushes the height of an individual day box for the current month.
  131.      */
  132.     "/monthHeight { weeks 5 eq { 96 } { 80 } ifelse } def",
  133.  
  134.     /*
  135.      * Draw the day names and the grid for the current month
  136.      */
  137.     "/drawgrid {",
  138.     "  dayfont findfont 10 scalefont setfont",
  139.     "  0 1 6 {",
  140.     "    dup dup 100 mul 40 moveto",
  141.     "    [  (Sunday) (Monday) (Tuesday) (Wednesday)",
  142.     "      (Thursday) (Friday) (Saturday) ]",
  143.     "    exch get",
  144.     "    100 center",
  145.     "    100 mul 35 moveto",
  146.     "    1.0 setlinewidth",
  147.     "    0 1 weeks 1 sub {",
  148.     "      gsave",
  149.     "        100 0 rlineto",
  150.     "        0 monthHeight neg rlineto",
  151.     "        -100 0 rlineto",
  152.     "        closepath stroke",
  153.     "      grestore",
  154.     "      0 monthHeight neg rmoveto",
  155.     "    } for",
  156.     "  } for",
  157.     "} def",
  158.  
  159.     /*
  160.      * Draw in the day numbers for each day of the current month.
  161.      *
  162.      * Sunday (and possibly Saturday) will be gray.
  163.      */
  164.     "/drawnums {",
  165.     "  dayfont findfont 30 scalefont setfont",
  166.     "  /start startday def",
  167.     "  /days ndays def",
  168.     "  start 100 mul 5 add 10 rmoveto",
  169.     "  1 1 days {",
  170.     "    /day exch def",
  171.     "    gsave",
  172. #ifndef SATBLK
  173.     "      day start add 7 mod 0 eq {",
  174.     "        submonth 0 eq { .8 setgray } if",
  175.     "      } if",
  176. #endif
  177.     "      day start add 7 mod 1 eq {",
  178.     "        submonth 0 eq { .8 setgray } if",
  179.     "      } if",
  180.     "      day prtnum",
  181.     "    grestore",
  182.     "    day start add 7 mod 0 eq",
  183.     "    { currentpoint exch pop monthHeight sub 5 exch moveto }",
  184.     "    { 100 0 rmoveto }",
  185.     "    ifelse",
  186.     "  } for",
  187.     "} def",
  188.  
  189.     /*
  190.      * Gray out the day boxes before the first day of the month and after
  191.      * the last day of the month.  If this is not a submonth, leave two
  192.      * blank for the previous and next month miniature calendars.
  193.      */
  194.     "/drawfill {",
  195.     "  /start startday def",
  196.     "  1.0 setlinewidth",
  197.     "  submonth 1 eq {",
  198.     "   0 35 rmoveto",
  199.     "   /grayWidth start 100 mul def",
  200.     "   /lastday 7 def",
  201.     "  } {",
  202.     "   start 3 ge {",
  203.     "     200 35 rmoveto",
  204.     "     /grayWidth start 2 sub 100 mul def",
  205.     "     /lastday 7 def",
  206.     "   } {",
  207.     "     0 35 rmoveto",
  208.     "     /grayWidth start 100 mul def",
  209.     "     /lastday 5 def",
  210.     "   } ifelse",
  211.     "  } ifelse",
  212.     "  grayWidth 0 gt {",
  213.     "   gsave",
  214.     "     .9 setgray",
  215.     "     grayWidth 0 rlineto",
  216.     "     0 monthHeight neg rlineto",
  217.     "     grayWidth neg 0 rlineto",
  218.     "     closepath fill",
  219.     "   grestore",
  220.     "  } if",
  221.     "  /endday startday ndays add 7 mod def",
  222.     "  endday 0 ne {",
  223.     "   ndays startday add 7 mod 100 mul",
  224.     "   weeks 1 sub neg monthHeight mul 35 add moveto",
  225.     "   /grayWidth lastday 100 mul currentpoint pop sub def",
  226.     "   grayWidth 0 gt {",
  227.     "    gsave",
  228.     "      .9 setgray",
  229.     "      grayWidth 0 rlineto",
  230.     "      0 monthHeight neg rlineto",
  231.     "      grayWidth neg 0 rlineto",
  232.     "      closepath fill",
  233.     "    grestore",
  234.     "   } if",
  235.     "  } if",
  236.     "} def",
  237.  
  238.     "/isleap {",
  239.     "  year 4 mod 0 eq",
  240.     "  year 100 mod 0 ne",
  241.     "  year 400 mod 0 eq or and",
  242.     "} def",
  243.  
  244.     "/days_month [ 31 28 31 30 31 30 31 31 30 31 30 31 ] def",
  245.  
  246.     /*
  247.      * -  -ndays-  int
  248.      *
  249.      * Push number of days in current month.  Account for leap February.
  250.      */
  251.     "/ndays {",
  252.     "  days_month month 1 sub get",
  253.     "  month 2 eq",
  254.     "  isleap and { 1 add } if",
  255.     "} def",
  256.  
  257.     /*
  258.      * -  -startday-  int
  259.      *
  260.      * Push the day of the week on which the first of the current month falls.
  261.      */
  262.     "/startday {",
  263.     "  /off year 2000 sub def",
  264.     "  off",
  265.     "  off 4 idiv add",
  266.     "  off 100 idiv sub",
  267.     "  off 400 idiv add",
  268.     "  6 add 7 mod 7 add",
  269.     "  /off exch def",
  270.     "  1 1 month 1 sub {",
  271.     "    /idx exch def",
  272.     "    days_month idx 1 sub get",
  273.     "    idx 2 eq",
  274.     "    isleap and",
  275.     "    { 1 add } if",
  276.     "    /off exch off add def",
  277.     "  } for",
  278.     "  off 7 mod",
  279.     "% Place holder",
  280.     "} def",
  281.  
  282.     "/center {",
  283.     "  /width exch def",
  284.     "  /str exch def width str ",
  285.     "  stringwidth pop sub 2 div 0 rmoveto str show",
  286.     "} def",
  287.  
  288.     /*
  289.      * Draw an entire month calendar.
  290.      * (Without any previous/next subcalendars.)
  291.      */
  292.     "/calendar",
  293.     "{",
  294.     "  titlefont findfont 48 scalefont setfont",
  295.     "  0 60 moveto",
  296.     "  /month_name month_names month 1 sub get def",
  297.     "  month_name show",
  298.     "  /yearstring year 10 string cvs def",
  299.     "  700 yearstring stringwidth pop sub 60 moveto",
  300.     "  yearstring show",
  301.     "  0 0 moveto",
  302.     "  drawnums",
  303.     "  0 0 moveto",
  304.     "  drawfill",
  305.     "  0 0 moveto",
  306.     "  drawgrid",
  307.     "} def",
  308.  
  309.     /*
  310.      * array-of-notes  -daytext-  -
  311.      */
  312.     "/daytext {",
  313.     "  /mytext exch def /myday exch def",
  314.     "  /bottom monthHeight 30 sub def",
  315.     "  startday myday 1 sub add dup",
  316.     "  7 mod 100 mul 5 add /LM exch def",
  317.     "  7 idiv monthHeight neg mul /ylimit exch def",
  318.     "  ylimit bottom sub /ypos exch def",
  319.     "  /RM LM 95 add def /ystart ypos def",
  320.     "  mytext {",
  321.     "    95 90 { pop pop /ystart ystart 8 add def } breakIntoLines",
  322.     "    ystart ylimit le { /ypos ystart def } if",
  323.     "  } forall",
  324.     "  /ylimit ylimit bottom sub def",
  325.     "  mytext { 95 90 { prstr } breakIntoLines } forall",
  326.     "} def",
  327.  
  328.     /*
  329.      * string maxwidth  -prstr-  -
  330.      */
  331.     "/prstr {",
  332.     "  ypos ylimit gt {",
  333.     "    RM exch sub ypos moveto show",
  334.     "    /ypos ypos 8 sub def",
  335.     "  } {",
  336.     "   pop pop",
  337.     "  } ifelse",
  338.     "} def",
  339.  
  340.     /*
  341.      * Word break string for breakIntoLines.
  342.      */
  343.     "/space ( ) def",
  344.  
  345.     /*
  346.      * string  first-width  next-width  proc  -breakIntoLines-  -
  347.      *
  348.      * Break the string into lines.  The first line will fit within
  349.      * first-width.  Later lines will fit within next-width.  For each
  350.      * line, push the string and current width then execute proc.
  351.      *
  352.      * This is a modification of the function listed in the Blue book
  353.      * (Postscript Language Tutorial and Cookbook, Adobe Systems, Inc.).
  354.      * The modifications are:
  355.      *    1.    The addition of the next-width parameter to handle indentation.
  356.      *  2.    The original would not break the line if only the last word
  357.      *        extended beyond the limit.
  358.      */
  359.     "/breakIntoLines {",
  360.     "  /proc exch def",
  361.     "  /nextlinewidth exch def",
  362.     "  /linewidth exch def",
  363.     "  /textstring exch def",
  364.     "  /breakwidth space stringwidth pop def",
  365.     "  /curwidth 0 def",
  366.     "  /lastwordbreak 0 def",
  367.     "  /startchar 0 def",
  368.     "  /restoftext textstring def",
  369.     "  {",
  370.     "   restoftext space search {",
  371.     "     /nextword exch def pop",
  372.     "     /restoftext exch def",
  373.     "      /wordwidth nextword stringwidth pop def",
  374.     "      curwidth wordwidth add linewidth gt {",
  375.     "           textstring startchar",
  376.     "        lastwordbreak startchar sub",
  377.     "        getinterval linewidth proc",
  378.     "        /startchar lastwordbreak def",
  379.     "        /curwidth wordwidth breakwidth add def",
  380.     "           /linewidth nextlinewidth def",
  381.     "      } {",
  382.     "           /curwidth curwidth wordwidth add breakwidth add def",
  383.     "     } ifelse",
  384.     "      /lastwordbreak lastwordbreak nextword length add 1 add def",
  385.     "    } {",
  386.     "     stringwidth pop curwidth add linewidth gt {",
  387.     "           textstring startchar",
  388.     "           lastwordbreak startchar sub",
  389.     "           getinterval linewidth proc",
  390.     "        /startchar lastwordbreak def",
  391.     "           /linewidth nextlinewidth def",
  392.     "     } if",
  393.     "     exit",
  394.     "   }",
  395.     "    ifelse",
  396.     "  } loop",
  397.     "  /lastchar textstring length def",
  398.     "  textstring startchar lastchar startchar sub getinterval linewidth proc",
  399.     "} def",
  400.  
  401.     "/printmonth {",
  402.     "  90 rotate",
  403.     "  50 -120 translate",
  404.     "  /submonth 0 def",
  405.     "  calendar",
  406.     "  month 1 sub 0 eq {",
  407.     "    /lmonth 12 def",
  408.     "    /lyear year 1 sub def",
  409.     "  } {",
  410.     "    /lmonth month 1 sub def",
  411.     "    /lyear year def",
  412.     "  } ifelse",
  413.     "  month 1 add 13 eq {",
  414.     "    /nmonth 1 def",
  415.     "    /nyear year 1 add def",
  416.     "  } {",
  417.     "    /nmonth month 1 add def",
  418.     "    /nyear year def",
  419.     "  } ifelse",
  420.     "  /savemonth month def",
  421.     "  /saveyear year def",
  422.     "  /submonth 1 def",
  423.     "  gsave",
  424.     "    /offset monthHeight 80 sub 2 div neg 35 add def",
  425.     "    startday 3 lt",
  426.     "    { 500 weeks 1 sub neg monthHeight mul offset add translate }",
  427.     "    { 0  offset translate }",
  428.     "    ifelse",
  429.     "   /year lyear def",
  430.     "   /month lmonth def",
  431.     "    gsave",
  432.     "      .138 .138 scale",
  433.     "      10 -120 translate",
  434.     "      calendar",
  435.     "    grestore",
  436.     "    /submonth 1 def",
  437.     "    /year nyear def",
  438.     "    /month nmonth def",
  439.     "    100 0 translate",
  440.     "    gsave",
  441.     "      .138 .138 scale",
  442.     "      10 -120 translate",
  443.     "      calendar",
  444.     "    grestore",
  445.     "    /month savemonth def",
  446.     "    /year saveyear def",
  447.     "    /submonth 0 def",
  448.     "  grestore",
  449.     "} def",
  450.   (char *)0,
  451. };
  452.  
  453. print_psmonth(fp, noteflag)
  454. FILE *fp;
  455. int noteflag;
  456. {
  457.     char      **ap;
  458.     int        i, startdow, nweeks;
  459.     char    weeknums[70], wn[5];
  460.  
  461.     
  462.     /*
  463.      * muck with prolog for Monday first (ISO) weeks.
  464.      * N.B. if the prolog changes, these indices may also change
  465.      */
  466.     if (monday_first) {
  467.     pheader[11] = pmonfirst[0];
  468.     pheader[12] = pmonfirst[1];
  469. #ifndef SATBLK
  470.     pheader[36] = pmonfirst[2];
  471.     pheader[39] = pmonfirst[3];
  472.     pheader[121] = pmonfirst[4];
  473. #else
  474.     pheader[36] = pmonfirst[3];
  475.     pheader[118] = pmonfirst[4];
  476. #endif
  477.     }
  478.  
  479.     /*
  480.      * Write out PostScript header
  481.      */
  482.     for (ap = prolog; *ap; ap++)
  483.     fprintf(fp, "%s\n", *ap);
  484.     fprintf (fp, "/daytextfont /%s def\n", PS_NOTE_FONT);
  485.  
  486. #ifdef LANGUAGE
  487.     /*
  488.      * Write out PS prolog for local languages
  489.      */
  490.     for (ap = p_lang_header; *ap; ap++)
  491.     fprintf(fp, "%s\n", *ap);
  492. #endif
  493.  
  494.     fprintf(fp, "%%EndProlog\n");
  495.  
  496.     /*
  497.      * Write out common core PostScript
  498.      */
  499.     for (ap = pheader; *ap; ap++)
  500.     fprintf(fp, "%s\n", *ap);
  501.  
  502.     /*
  503.      * Do the calendar
  504.      */
  505.     fprintf (fp, "/year %d def\n", current.tm_year+1900);
  506.     fprintf (fp, "/month %d def\n", current.tm_mon+1);
  507.     fprintf (fp, "printmonth\n");
  508.  
  509.     First = current;
  510.  
  511.     /*
  512.      *  Calculate and print the week numbers
  513.      */
  514.     current.tm_mday = 1;
  515.     fix_current_day();
  516.     if (monday_first)
  517.     startdow = (current.tm_wday == 0 ? 6 : current.tm_wday-1);
  518.     else
  519.     startdow = current.tm_wday;
  520.     nweeks = (startdow + monthlength(current.tm_mon) > 35 ? 6 : 5);
  521.     strcpy(weeknums, "/weeknums [");
  522.     for(i=0; i<nweeks; i++) {
  523.     sprintf(wn, " (%2d)", week_number());
  524.     strcat(weeknums, wn);
  525.     current.tm_mday += 7;
  526.     fix_current_day();
  527.     }
  528.     strcat( weeknums, " ] def");
  529.     fprintf(fp, "/weekn {\n");
  530.     fprintf(fp, "  %s\n", weeknums);
  531.     fprintf(fp, "  /Helvetica-Bold findfont 16 scalefont setfont\n");
  532.     fprintf(fp, "  /wy -10 def\n");
  533.     if (monday_first)
  534.     fprintf(fp, "  0 1 weeks 1 sub { 710 wy moveto\n");
  535.     else
  536.     fprintf(fp, "  0 1 weeks 1 sub { -35 wy moveto\n");
  537.     fprintf(fp, "    weeknums exch get show\n");
  538.     fprintf(fp, "    /wy wy monthHeight sub def\n");
  539.     fprintf(fp, "  } for\n");
  540.     if (monday_first)
  541.     fprintf(fp, "  705 40 moveto\n");
  542.     else
  543.     fprintf(fp, "  -40 40 moveto\n");
  544.     fprintf(fp, "  /Helvetica-Bold findfont 10 scalefont setfont\n");
  545.     fprintf(fp, "  (Week) show\n");
  546.     fprintf(fp, "} def\n");
  547.     fprintf(fp, "weekn\n");
  548.     current = First;
  549.  
  550.     /*
  551.      * Set the font here to reduce the number of complainings if
  552.      * it is not found.
  553.      */
  554.     fprintf (fp, "daytextfont findfont 6 scalefont setfont\n");
  555.  
  556.     current.tm_mday = 1;
  557.     for (i=0; i<monthlength(current.tm_mon); i++) {
  558.     fix_current_day();
  559.     (void)get_day_appts();
  560.     print_mday(fp, noteflag);
  561.     current.tm_mday++;
  562.     }
  563.     current = First;
  564.  
  565.     /*
  566.      * Write out PostScript postlog
  567.      */
  568.     fprintf(fp, "showpage\n");
  569. }
  570.  
  571. print_mday(fp, noteflag)
  572. FILE *fp;
  573. int noteflag;
  574. {
  575.     int     slotno;
  576.     struct appt_entry    *aptr, *optr;
  577.     struct dayslot *slptr;
  578.     
  579.     fprintf(fp, "%d [\n", current.tm_mday);
  580.  
  581.     for (slotno=0; slotno<n_slots; slotno++) {
  582.     /* any appts in this timeslot? */
  583.     slptr = &slots[slotno];
  584.     if (slptr->active) {
  585.         /* get printable string from each appt */
  586.         for (aptr=slptr->first; aptr;) {
  587.         if (!deleted(aptr, slptr) && 
  588.             (!noteflag || ((aptr->flags & MARKED_NOTE) != MARKED_NOTE)))
  589.             fprintf(fp, "    (%s)\n", format_appt_nd(aptr, TRUE));
  590.         /* free up memory used */
  591.         optr = aptr;
  592.         aptr = aptr->next;
  593.         free(optr);
  594.         }
  595.     }
  596.     }
  597.     fprintf(fp, "] daytext\n");
  598. }
  599. #endif /* RASTER_ONLY */
  600. #endif /* NO_PRINTER */
  601.