#!@PERL@ use strict; use warnings; use lib '@prefix@/lib/perl5'; use Encode; use Cairo; use Pango; use Getopt::Long; my $VERSION = '@VERSION@'; use Remind::PDF; my $media_to_size = { "Letter" => [ 612, 792], "Tabloid" => [ 792, 1224], "Ledger" => [1224, 792], "Legal" => [ 612, 1008], "Statement" => [ 396, 612], "Executive" => [ 540, 720], "A3" => [ 842, 1190], "A4" => [ 595, 842], "A5" => [ 420, 595], "B4" => [ 729, 1032], "B5" => [ 519, 729], "Folio" => [ 612, 936], "Quarto" => [ 612, 780], "10x14" => [ 720, 1008], }; my $help = 0; my $settings = { landscape => 0, numbers_on_left => 0, small_calendars => 0, fill_entire_page => 0, media => 'Letter', width => 0, height => 0, title_font => 'Sans', header_font => 'Sans', daynum_font => 'Sans Bold Oblique', entry_font => 'Sans', small_cal_font => 'Sans', title_size => 14, header_size => 12, daynum_size => 14, entry_size => 8, border_size => 4, line_thickness => 1, margin_top => 36, margin_bottom => 36, margin_left => 36, margin_right => 36, verbose => 0, }; my $me = $0; $me =~ s/^.*\///; set_default_media(); sub usage { print <<"EOF"; $me (version $VERSION): Convert Remind -pp output to a PDF calendar. Usage: remind -pp [options] filename | $me [options] > out.pdf Options: --landscape, -l Print in landscape orientation --small-calendars=N Choose location for small calendars -cN Synonym for --small-calendars=N --left-numbers, -x Print day numbers on the left --fill-page, -e Fill the entire page --media=MEDIA, -mMEDIA Size for specified media --width=W, -wW Specify media width in 1/72nds of an inch --height=H, -hH Specify media height in 1/72nds of an inch --title-font=FONT Specify font for calendar title --header-font=FONT Specify font for weekday names --daynum-font=FONT Specify font for day numbers --entry-font=FONT Specify font for calendar entries --small-cal-font=FONT Specify font for small calendars --title-size=S Specify size of font for calendar title in points --header-size=S Specify size of font for weekday names --daynum-size=S Specify size of font for day numbers --entry-size=S Specify size of font for calendar entries --border-size=S Specify size of gaps between items in 1/72nds of an inch --line-thickness=S Specify line thickness in 1/72nds of an inch --margin-top=S Specify top margin size in 1/72nds of an inch --margin-bottom=S Specify bottom margin size in 1/72nds of an inch --margin-left=S Specify left margin size in 1/72nds of an inch --margin-right=S Specify right margin size in 1/72nds of an inch --verbose, -v Print progress messages --help Display this help EOF } Getopt::Long::Configure('bundling_values'); my $ret = GetOptions('landscape|l' => \$settings->{landscape}, 'small-calendars|c=i' => \$settings->{small_calendars}, 'left-numbers|x' => \$settings->{numbers_on_left}, 'fill-page|e' => \$settings->{fill_entire_page}, 'media|m=s' => \$settings->{media}, 'width|w=i' => \$settings->{width}, 'height|h=i' => \$settings->{height}, 'title-font=s' => \$settings->{title_font}, 'header-font=s' => \$settings->{header_font}, 'daynum-font=s' => \$settings->{daynum_font}, 'entry-font=s' => \$settings->{entry_font}, 'small-cal-font=s' => \$settings->{small_cal_font}, 'title-size=f' => \$settings->{title_size}, 'header-size=f' => \$settings->{header_size}, 'daynum-size=f' => \$settings->{daynum_size}, 'entry-size=f' => \$settings->{entry_size}, 'border-size=f' => \$settings->{border_size}, 'line-thickness=f' => \$settings->{line_thickness}, 'margin-top=f' => \$settings->{margin_top}, 'margin-bottom=f' => \$settings->{margin_bottom}, 'margin-left=f' => \$settings->{margin_left}, 'margin-right=f' => \$settings->{margin_right}, 'verbose|v' => \$settings->{verbose}, 'help' => \$help ); if (!$ret) { usage(); exit(1); } if ($help) { usage(); exit(0); } if ($settings->{width} <= 0 || $settings->{height} <= 0) { my $size = $media_to_size->{ucfirst($settings->{media})}; if (!$size) { if (lc($settings->{media}) ne 'help') { print STDERR "Unknown media " . $settings->{media} . "\n"; } set_default_media(); printf("%-12s Size in 1/72 in\n", "Valid media:"); foreach my $m (sort { $a cmp $b } (keys(%$media_to_size))) { if ($m eq $settings->{media}) { print "* "; } else { print " "; } printf("%-12s %4d x %4d\n", $m, $media_to_size->{$m}->[0], $media_to_size->{$m}->[1]); } exit(1); } $settings->{width} = $size->[0]; $settings->{height} = $size->[1]; } if ($settings->{landscape}) { my $tmp = $settings->{width}; $settings->{width} = $settings->{height}; $settings->{height} = $tmp; } # Don't read from a terminal if (-t STDIN) { print STDERR "I can't read data from a terminal. Please run like this:\n"; print STDERR " remind -pp [options] filename | $me [options] > out.pdf\n"; exit(1); } my $done_one = 0; my $errored_out = 0; my $surface = Cairo::PdfSurface->create_for_stream(sub { print $_[1] unless $errored_out; }, undef, $settings->{width}, $settings->{height}); # set_metadata not available in older versions of Cairo eval { $surface->set_metadata('title', 'Calendar'); }; eval { $surface->set_metadata('author', 'Remind (https://dianne.skoll.ca/projects/remind/)'); }; eval { $surface->set_metadata('creator', 'rem2pdf (https://dianne.skoll.ca/projects/remind/)'); }; eval { $surface->set_metadata('subject', 'Calendar'); }; my $cr = Cairo::Context->create($surface); $cr->set_line_width($settings->{line_thickness}); while(1) { my ($obj, $err) = Remind::PDF->create_from_stream(*STDIN, {color => 1, shade => 1, moon => 1, pango => 1, week => 1,}); if (!$obj) { if (!$done_one) { $errored_out = 1; print STDERR "$me: $err\n"; exit(1); } last; } $done_one = 1; $obj->render($cr, $settings); } $surface->finish(); sub set_default_media { my $paper; $paper = $ENV{PAPERSIZE}; if ($paper && set_media(ucfirst($paper))) { return 1; } if ($ENV{PAPERCONF}) { if (set_media_from_file($ENV{PAPERCONF})) { return 1; } } if (set_media_from_file('/etc/papersize')) { return 1; } return set_media('Letter'); } sub set_media { my ($m) = @_; return 0 unless $media_to_size->{$m}; $settings->{media} = $m; return 1; } sub set_media_from_file { my ($fn) = @_; if (!open(IN, '<', $fn)) { return 0; } while() { chomp; s/^\s+//; s/\s+$//; next if ($_ eq ''); next if ($_ =~ /^#/); my $m = $_; close(IN); return set_media($m); } close(IN); return 0; } __END__ =head1 NAME rem2pdf - draw a PDF calendar from Remind output =head1 SYNOPSIS remind -pp [options] file | rem2pdf [options] > output.pdf =head1 DESCRIPTION B reads the standard input, which should be the results of running B with the B<-p>, B<-pp> or B<-ppp> options. It emits PDF code that draws a calendar to standard output. B uses the Pango text formatting library (L) and the Cairo graphics library (L) to produce its output. The CPAN modules Pango (L) and Cairo (L) are prerequisites. B assumes that its input stream is valid UTF-8. If this is not the case, it may render output incorrectly or even fail to render output at all. =head1 OPTIONS =over =item --landscape, -l Print the calendar in landscape orientation. Essentially, this swaps the width and height of the output media. =item --small-calendars=I, -cI Control the inclusion of small calendars for the previous and next month. Possible values for I are: =over =item Z<>0 Do not draw any small calendares =item Z<>1 Place the small calendars at the bottom-right if there is room; otherwise, place them at the top-left. =item Z<>2 Place the small calendars at the top-left if there is room; otherwise, place them at the bottom-right. =item Z<>3 Place the previous month's small calendar at the top-left and the next month's at the bottom-right if there is room; otherwise, follow I=1. A moment's thought reveals that an option which splits the calendars if there is room and otherwise follows I=2 yields the same results. =back =item --left-numbers, -x Draw the day numbers in the top-left corner of each day's box rather than the default top-right. =item --fill-page, -e Make the calendar fill the available space on the page. =item --media=I, -mI Specify the paper size (Letter, A4, etc.) For a list of valid media sizes, run: rem2pdf --media=help The default media size will be marked with an asterisk. =item --width=I, -wI, --height=I, -hI Rather than specifying a named media size, directly specify the width and height of the output in 1/72ths of an inch. You must specify both width and height for the options to be respected. =item --title-font=I Specify the font used for the calendar title. It can be any font that the Pango library on your system can use. The default is Sans. If you choose a font with spaces in its name, you may need to quote this argument. =item --header-font=I Specify the font used for the weekday names. The default is Sans. =item --daynum-font=I Specify the font used for the day numbers. The default is Sans Bold Oblique. =item --entry-font=I Specify the font used for calendar entries. The default is Sans. =item --small-cal-font=I Specify the font used for the small next- and previous-month calendars. The default is Sans. =item --title-size=I Specify the size of the title font in 1/72ths of an inch. The default is 14. This size, and indeed all following sizes, may be specified as floating-point numbers. =item --header-size=I Specify the size of the header font in 1/72ths of an inch. The default is 14. =item --daynum-size=I Specify the size of the day number font in 1/72ths of an inch. The default is 14. =item --entry-size=I Specify the size of the calendar entry font in 1/72ths of an inch. The default is 8. =item --border-size=I Specify the size of the blank border between the contents of a calendar box and the centre of the lines surrounding it, in 1/72ths of an inch. The default is 4. =item --line-thickness=I Specify the thickness of the lines drawn on the calendar. The default is 1. =item --margin-top=I The size of the margin at the top of the page in 1/72ths of an inch. The default is 36. =item --margin-bottom=I The size of the margin at the bottom of the page in 1/72ths of an inch. The default is 36. =item --margin-left=I The size of the margin at the left of the page in 1/72ths of an inch. The default is 36. =item --margin-right=I The size of the margin at the right of the page in 1/72ths of an inch. The default is 36. =item --verbose, -v Print (on STDERR) the name of the month and year for each month that is rendered. =back =head1 USAGE To use B, pipe the output of B with one of the B<-p>, B<-pp> or B<-ppp> options into B. The PDF output will be sent to standard output. So for example, to print a 12-month calendar for the year 2030, use: remind -pp12 /dev/null Jan 2030 | rem2pdf -e -l -c=3 | lpr You can concatenate multiple B runs. For example, the following will produce a PDF calendar for January through March of 2023, and June of 2023 (for a total of four pages); (remind -pp3 Jan 2023 /dev/null ; \ remind -p June 2023 /dev/null) | rem2pdf -e -l -c=3 > cal.pdf =head1 FORMATTED TEXT B supports a B reminder type called B. This lets you format text using the Pango markup language, described at L. Here are some examples: REM Mon SPECIAL PANGO Bold and italic REM Tue SPECIAL PANGO Fancy REM Wed SPECIAL PANGO Bold red Other back-ends such as B and B will ignore PANGO special reminders. Neither B nor B will check the markup to ensure it is syntactically correct. If you use invalid Pango markup, the Pango library will print a warning and B will not render any output for the invalid reminder. =head1 ABSOLUTELY-POSITIONED TEXT If your B special reminder starts with C<@I,I> where I and I are floating-point numbers, then the Pango marked-up test is positioned absolutely with respect to the day's box (and is not counted when calculating the box's height.) A positive I value positions the left edge of the text I points to the right of the left side of the calendar box, while a negative I value positions the right edge of the text I points to the left of the right side of the calendar box. A positive I value positions the top edge of the text I points below the top of the calendar box, while a negative I value positions the bottom edge of the text I points above the bottom of the calendar box. If you use absolutely-positioned text, it's up to you to make sure it doesn't overlap other text; B takes no special precautions to prevent this. As an example, this places Sunrise and Sunset times at the bottom left of each calendar box: REM SPECIAL PANGO @1,-1 Rise [sunrise($U)] Set [sunset($U)] (Note that Pango expresses font sizes in 1024's of a point, so a size of 4800 works out to about 4.6 points.) =head1 AUTHOR B was written by Dianne Skoll =head1 HOME PAGE L =head1 SEE ALSO B, B, B, B