Files: 7f59fc06d2300c374b6d0ee81788ce84dd588c69 / freecheck
22658 bytesRaw
1 | #!/usr/bin/perl |
2 | |
3 | #--------------- |
4 | # |
5 | # FreeCheck - a free check printing application released |
6 | # under the GNU General Public Licene. |
7 | # |
8 | # Copyright (C) 2000 Eric Sandeen (eric_sandeen@bigfoot.com) |
9 | # |
10 | # This program is free software; you can redistribute it and/or modify |
11 | # it under the terms of the GNU General Public License as published by |
12 | # the Free Software Foundation; either version 2 of the License, or |
13 | # (at your option) any later version. |
14 | # |
15 | # This program is distributed in the hope that it will be useful, |
16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | # GNU General Public License for more details. |
19 | # |
20 | # You should have received a copy of the GNU General Public License |
21 | # along with this program; if not, write to the Free Software |
22 | # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
23 | # |
24 | #--------------- |
25 | |
26 | $version = "0.2"; |
27 | $ConfigFile = "freecheck.cfg"; |
28 | |
29 | use Getopt::Long; |
30 | |
31 | # This tells us how to format the strings from the cfg file |
32 | # so we can print it as a PostScript definition |
33 | # The key read will replace "value" for each of these |
34 | # Strings are enclosed in parentheses (String) (Foobar) |
35 | # Fonts are preceded by a "/" /FontName /Arial |
36 | # Digits are fine as they are Digit 123 |
37 | # Booleans are fine too Bool true |
38 | # But to be safe, do digits and bools as subroutines: |
39 | # Subroutines are in {} {subr} {3 mul} |
40 | |
41 | %Formats = qw( |
42 | # Globals |
43 | MICRFontName /value |
44 | MICRFontSize {value} |
45 | TransitSymbol (value) |
46 | OnUsSymbol (value) |
47 | AmountSymbol (value) |
48 | DashSymbol (value) |
49 | MICRVerTweak {value} |
50 | MICRHorTweak {value} |
51 | # Account |
52 | CheckNumber {value} |
53 | PrintCheckBody {value} |
54 | PrintMICRLine {value} |
55 | NumPages {value} |
56 | Name1 (value) |
57 | Name2 (value) |
58 | Address1 (value) |
59 | Address2 (value) |
60 | CityStateZip (value) |
61 | PhoneNumber (value) |
62 | BankName (value) |
63 | BankAddr1 (value) |
64 | BankAddr2 (value) |
65 | BankCityStateZip (value) |
66 | AuxOnUs (value) |
67 | Routing (value) |
68 | OnUs (value) |
69 | Fraction (value) |
70 | PrintVOID {value} |
71 | # Styles |
72 | StandardFontName /value |
73 | StandardFontSize {value} |
74 | CheckNumDigits {value} |
75 | CheckNumFont /value |
76 | CheckNumSize {value} |
77 | MemoLineHeight {value} |
78 | SignatureLineHeight {value} |
79 | BankInfoHeight {value} |
80 | AmountLineHeight {value} |
81 | PayeeLineHeight {value} |
82 | DateLineHeight {value} |
83 | # Check Blank Types |
84 | CheckHeight {value} |
85 | CheckWidth {value} |
86 | CheckHorOffset {value} |
87 | CheckVerOffset {value} |
88 | ChecksPerPage {value} |
89 | LeftMargin {value} |
90 | RightMargin {value} |
91 | TopMargin {value} |
92 | ); |
93 | |
94 | # Parse command line options and deal with them: |
95 | |
96 | GetOptions ("account:s", # Account definition file |
97 | "checknum:i", # Check number optional (overrides acct file) |
98 | "pages:i", # Number of pages to print |
99 | "checkstyle:s", # Check style (defaults to "normal_style.ps" |
100 | "checktype:s", # Check blank definition |
101 | "nomicr", # Prevents MICR line from printing (body only) |
102 | "nobody", # Prevents body from printing (MICR line only) |
103 | "showaccounts", # Show available accounts |
104 | "showstyles", # Show available check styles |
105 | "showblanks", # Show available check blanks |
106 | "test", # Don't increment check n, and print VOID |
107 | "cgi:s", # Accept big string from CGI script (or similar) |
108 | "help") |
109 | |
110 | or Show_Usage(); |
111 | |
112 | if ($opt_help) { |
113 | Show_Usage(); |
114 | } |
115 | |
116 | # Some defaults... |
117 | if (!$opt_cgi) { |
118 | $opt_account = "sample"; |
119 | $opt_checktype = "MVG3001"; |
120 | $opt_checkstyle = "normal"; |
121 | } |
122 | |
123 | # Pull the config file into a string... |
124 | $config_file = read_file($ConfigFile); |
125 | |
126 | # See what sections are available |
127 | Get_Sections(); |
128 | |
129 | # If we're missing the [Global] section, or if a requested section |
130 | # cannot be found, die. |
131 | if (!$global_found) { |
132 | die ("No [Global] section found in config file\n"); |
133 | } |
134 | |
135 | if ($accounts !~ /${opt_account}/i) { |
136 | die ("Account $opt_account not found in config file\n"); |
137 | } |
138 | |
139 | if ($checkblanks !~ /$opt_checktype/i) { |
140 | die ("Check type $opt_checktype not found in config file\n"); |
141 | } |
142 | |
143 | if ($checkstyles !~ /$opt_checkstyle/i) { |
144 | die ("Style $opt_checkstyle not found in config file\n"); |
145 | } |
146 | |
147 | # Show list of available sections, if requested |
148 | if ($opt_showaccounts || $opt_showstyles || $opt_showblanks) { |
149 | print "\nFreeCheck v$version\n"; |
150 | if ($opt_showaccounts) { |
151 | print "Accounts:\n"; |
152 | foreach (split(/\s+/,$accounts)) { |
153 | print "\t$_\n"; |
154 | } |
155 | } |
156 | if ($opt_showstyles) { |
157 | print "Check Styles:\n"; |
158 | foreach (split(/\s+/,$checkstyles)) { |
159 | print "\t$_\n"; |
160 | } |
161 | } |
162 | if ($opt_showblanks) { |
163 | print "Check Types:\n"; |
164 | foreach (split(/\s+/,$checkblanks)) { |
165 | print "\t$_\n"; |
166 | } |
167 | } |
168 | die("\n"); |
169 | } |
170 | |
171 | # Go through the config and fill up a hash with PostScript defines... |
172 | Parse_Config($config_file); |
173 | |
174 | # Overwrite anything we got from the config file with what was on the |
175 | # Command Line (if anything...) |
176 | |
177 | if ($opt_checknum) { |
178 | $Definitions{"CheckNumber"} = $opt_checknum; |
179 | } |
180 | |
181 | if ($opt_pages) { |
182 | $Definitions{"NumPages"} = $opt_pages; |
183 | } |
184 | |
185 | if ($opt_nomicr) { |
186 | $Definitions{"PrintMICRLine"} = "false"; |
187 | } |
188 | |
189 | if ($opt_nobody) { |
190 | $Definitions{"PrintCheckBody"} = "false"; |
191 | } |
192 | |
193 | # This probably isn't in the config file (although it might be...) |
194 | # so cover both possibilites (true/false) |
195 | if ($opt_test) { |
196 | $Definitions{"PrintVOID"} = "true"; |
197 | } else { |
198 | $Definitions{"PrintVOID"} = "false"; |
199 | } |
200 | |
201 | # the --cgi option lets us pass in name value pairs in a string. |
202 | # This will overwrite anything we got from the config file, or |
203 | # from the other command line options (--cgi has the last word) |
204 | # Parse as follows: |
205 | # name is the first word, everything following it is the value |
206 | # each line contains one name/value pair. |
207 | |
208 | while ( $opt_cgi =~ /(^\w+)\s+?(.*$)/mcg ) { |
209 | $Definitions{$1} = $2; |
210 | } |
211 | |
212 | ################## |
213 | # Error Checking # |
214 | ################## |
215 | |
216 | $error = ""; |
217 | |
218 | # Make sure that MICR line is only numbers and symbols |
219 | |
220 | if ( $Definitions{"Routing"} !~ /^R[0-9]+R$/ ) { |
221 | $error = $error . "Error - Routing number must be numeric, with an \"R\" on each end\n"; |
222 | } |
223 | |
224 | if ( $Definitions{"AuxOnUs"} !~ /^[0-9\-CPS]*$/ ) { |
225 | $error = $error . "Error - Auxiliary On-Us field may only be numeric, with \"-\", and MICR symbols (C,P,S)\n"; |
226 | } |
227 | |
228 | if ( $Definitions{"OnUs"} !~ /^[0-9\-CPS]+$/ ) { |
229 | $error = $error . "Error - On-Us field may only be numeric, with \"-\", and MICR symbols (C,P,S)\n"; |
230 | } |
231 | |
232 | if ( $Definitions{"CheckNumber"} !~ /^[0-9]+$/ ) { |
233 | $error = $error . "Error - Check number must be numeric \n"; |
234 | } |
235 | |
236 | if ( $Definitions{"NumPages"} !~ /^[0-9]+$/ ) { |
237 | $error = $error . "Error - Number of pages must be numeric\n"; |
238 | } |
239 | |
240 | if ( $Definitions{"Fraction"} !~ /^[0-9]{2}\s*\-\s*[0-9]{1,4}\s*\/\s*[0-9]{3,4}$/ ) { |
241 | $error = $error . "Error - Routing fraction must be numeric, with a \"-\" in the numerator\n"; |
242 | } |
243 | |
244 | # Get routing symbol and institution number from routing number |
245 | $RoutingSymbol = substr($Definitions{"Routing"},1,4); |
246 | $Institution = substr($Definitions{"Routing"},5,4); |
247 | |
248 | # Strip any leading zeros... |
249 | # Only should be one on routing... |
250 | $RoutingSymbol =~ s/^0//; |
251 | # One or more on institution |
252 | $Institution =~ s/^0+//; |
253 | |
254 | # Fraction format: |
255 | # |
256 | # 2 digits, a "-", institution number (no leading zeros) |
257 | # ------------------------------------------------------ |
258 | # routing number (no leading zeros) |
259 | |
260 | |
261 | if ( $Definitions{"Fraction"} !~ /^[0-9]{2}\-${Institution}\/${RoutingSymbol}$/ ) { |
262 | $error = $error . "Error - Routing fraction does not match routing number\n"; |
263 | } |
264 | |
265 | |
266 | # Test the MICR line for correctness |
267 | if ( length ($Definitions{"Routing"}) != 11 ) { |
268 | $error = $error . "Error - Routing number must be exactly 9 digits long, with |
269 | an \"R\" on each end\n"; |
270 | } |
271 | |
272 | # Test the MICR checksum |
273 | # Don't forget the real routing number is sandwiched between "Rs" |
274 | |
275 | unless ( ( $Definitions{"Routing"} !~ /^R[0-9]+R$/) || ( length ($Definitions{"Routing"}) != 11 ) ){ |
276 | |
277 | $CheckSum = 0; |
278 | |
279 | @CheckSumMults = (3, 7, 1, 3, 7, 1, 3, 7); |
280 | for ($Digit = 1; $Digit < 9; $Digit++) { |
281 | $CheckSum = $CheckSum + |
282 | $CheckSumMults[$Digit-1] * substr($Definitions{"Routing"}, $Digit, 1); |
283 | } |
284 | |
285 | $CheckSum = 10 - ($CheckSum % 10); |
286 | |
287 | # Kludge alert... |
288 | if ($CheckSum == 10) { |
289 | $CheckSum = 0; |
290 | } |
291 | |
292 | if ( $CheckSum ne substr($Definitions{"Routing"}, 9, 1) ) { |
293 | $error = $error . "Error - Routing Number Checksum Incorrect\n"; |
294 | } |
295 | } |
296 | |
297 | |
298 | # die() if we got errors |
299 | if ( $error && !$opt_test ) { |
300 | print $error; |
301 | die("Errors Encountered\n"); |
302 | } |
303 | |
304 | # Print PostScript |
305 | |
306 | # Initial stuff: |
307 | |
308 | print "%!\n"; |
309 | print "/inch {72 mul} def\n"; |
310 | |
311 | # Go through $Definitions and print them out PostScript-Like |
312 | Print_Defs(); |
313 | |
314 | # Then print the main body |
315 | Print_Body(); |
316 | |
317 | # Update the config file with the new check number, if it's not just a test |
318 | if (!$opt_test && !$opt_cgi) { |
319 | $next_check_number = $Definitions{"CheckNumber"} |
320 | + ($Definitions{"NumPages"} * $Definitions{"ChecksPerPage"}); |
321 | |
322 | $config_file = Replace_Val($config_file, "Account", $opt_account, |
323 | "CheckNumber", $next_check_number); |
324 | write_file ("freecheck.cfg", $config_file); |
325 | } |
326 | |
327 | ############### |
328 | # Subroutines # |
329 | ############### |
330 | |
331 | # read_file and write_file shamelessly stolen from the File::Slurp module |
332 | # Short enough, and I didn't want to require a non-standard module |
333 | |
334 | sub read_file |
335 | { |
336 | my ($file) = @_; |
337 | |
338 | local(*F); |
339 | my $r; |
340 | my (@r); |
341 | |
342 | open(F, "<$file") || die "open $file: $!"; |
343 | @r = <F>; |
344 | close(F); |
345 | |
346 | return @r if wantarray; |
347 | return join("",@r); |
348 | } |
349 | |
350 | sub write_file |
351 | { |
352 | my ($f, @data) = @_; |
353 | |
354 | local(*F); |
355 | |
356 | open(F, ">$f") || die "open >$f: $!"; |
357 | (print F @data) || die "write $f: $!"; |
358 | close(F) || die "close $f: $!"; |
359 | return 1; |
360 | } |
361 | |
362 | # Wow, this is ugly! Anybody have a better suggestion? |
363 | sub Parse_Config { |
364 | local ($config_file) = ($_[0]); |
365 | # Find each section we're looking for... |
366 | while ($config_file =~ /^\[\s*( |
367 | Global | |
368 | Account\s+${opt_account} | |
369 | Style\s+${opt_checkstyle} | |
370 | CheckBlank\s+${opt_checktype} |
371 | )\s*\]/xmgci) { |
372 | # and get the lines under it one by one |
373 | while ($config_file =~ /(^.+$)/mgc) { |
374 | $line = $+; |
375 | # If this line is a comment, skip it |
376 | if ($line =~ /^#/) { |
377 | next; |
378 | } |
379 | # If the line we just found is a new section..."[...]" |
380 | if ($line =~ /^\[.+\]/) { |
381 | # and it is another section we're looking for |
382 | # Grab the next line, and keep going |
383 | if ($line =~ /\[\s*( |
384 | Global | |
385 | Account\s+${opt_account} | |
386 | Style\s+${opt_checkstyle} | |
387 | CheckBlank\s+${opt_checktype} |
388 | )\s*]/xi) { |
389 | # Grab the next line, and keep going |
390 | next; |
391 | } else { |
392 | # Not a section we need, so break out |
393 | # of the loop |
394 | last; |
395 | } |
396 | } |
397 | |
398 | ($key, $val) = split (/\s*=\s*/,$line); |
399 | # Need to strip trailing whitespace... |
400 | $val =~ s/\s*$//; |
401 | $Definitions{$key} = $val; |
402 | } # line-by-line while |
403 | } # section match conditional |
404 | } |
405 | |
406 | sub Replace_Val { |
407 | local ($string, $section, $name, $key, $value) = |
408 | ($_[0], $_[1], $_[2], $_[3], $_[4]); |
409 | # We want to get "[section name] ... key = value" and replace it |
410 | # with the new value. |
411 | |
412 | # s - "." matches ANYTHING including newline |
413 | # m - ^ and $ match after and before any newline |
414 | # in this case, ".+?" means the minimum number of <anything> i.e. end |
415 | # when we find the first instance of $key after [section name] |
416 | $string =~ |
417 | s/(^\[\s*$section\s+$name\s*\].+?^${key}\s*=\s*).*?$/$+$value/smi; |
418 | $string; |
419 | } |
420 | # Given a section type, list all the section names of that type |
421 | sub Get_Sections { |
422 | local $section; |
423 | while ($config_file =~ /^\[\s*( |
424 | Global | |
425 | Account.+ | |
426 | Style.+ | |
427 | CheckBlank.+ |
428 | )\s*\]/xmgci) { |
429 | $section = $+; |
430 | if ( $section =~/CheckBlank\s+(.+)/i ) { |
431 | $checkblanks = "$+ $checkblanks"; |
432 | } elsif ( $section =~/Style\s+(.+)/i ) { |
433 | $checkstyles = "$+ $checkstyles"; |
434 | } elsif ( $section =~/Account\s+(.+)/i ) { |
435 | $accounts = "$+ $accounts"; |
436 | } elsif ( $section =~/Global/i ) { |
437 | $global_found = "true"; |
438 | } |
439 | } |
440 | } |
441 | |
442 | sub Show_Usage { |
443 | print "\nFreeCheck v.$version - a Free Check printing Utility\n\n"; |
444 | print "Usage: freecheck <options>:\n"; |
445 | print "\n"; |
446 | print "options:\n"; |
447 | print " --account <filename> account to use (default \"$opt_account\")\n"; |
448 | print " --checknum <integer> starting check number (overrides cfg)\n"; |
449 | print " --pages <integer> number of pages to print (overrides cfg)\n"; |
450 | print " --checkstyle <filename> check style to use (default \"$opt_checkstyle\")\n"; |
451 | print " --checktype <filename> blank check type to use (default \"$opt_checktype\")\n"; |
452 | print " --nomicr do not print MICR line\n"; |
453 | print " --nobody do not print check body\n"; |
454 | print " --showaccounts show all configured accounts\n"; |
455 | print " --showstyles show all configured check styles\n"; |
456 | print " --showblanks show all configured check blanks\n"; |
457 | print " --help print this message\n"; |
458 | print " --test print but don't increment check number\n"; |
459 | print " and print VOID on the check\n"; |
460 | print " --cgi accept string from CGI script (see docs)\n"; |
461 | print "\nconfig file \"freecheck.cfg\" must be in the same directory,\n"; |
462 | print "as the freecheck executable (this will change in the future...)\n"; |
463 | die "\n"; |
464 | } |
465 | |
466 | sub Print_Defs { |
467 | # Go through each def in the hash table, and print according to the |
468 | # formatting hash |
469 | while ( ($key, $val) = each (%Definitions) ) { |
470 | print "/$key\t"; |
471 | $_ = $Formats{$key}; |
472 | s/value/$val/; |
473 | print; |
474 | print " def\n"; |
475 | } |
476 | } |
477 | |
478 | sub Print_Body { |
479 | |
480 | # This is the main body of the postscript file, that acts on all of the |
481 | # definitions we got from the config file. |
482 | |
483 | print <<END_OF_POSTSCRIPT |
484 | |
485 | % Other Constants: |
486 | |
487 | % Size of the rectangular box for the amount (digits) |
488 | /AmountBoxWidth {1 inch} def |
489 | /AmountBoxHeight {0.25 inch} def |
490 | |
491 | /LineWidth {0.3} def |
492 | |
493 | % Max number of digits in check number, and allocate string |
494 | /CheckNumDigits 4 def |
495 | /CheckNumberString CheckNumber log floor 1 add cvi string def |
496 | |
497 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
498 | % Helpful Printing Routines % |
499 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
500 | |
501 | % Shows a line, then does a "carriage return / line feed" |
502 | % But only if the string exists (more than 0 chars) |
503 | % (How do we get the current font size (height)?) |
504 | |
505 | /ShowAndCR { |
506 | % A couple copies of the string (now 3 on stack) |
507 | dup dup |
508 | length 0 gt { % First copy |
509 | show % Second copy |
510 | stringwidth pop neg 0 rmoveto % Third copy & move back |
511 | neg 0 exch rmoveto % line down |
512 | } if |
513 | } def |
514 | |
515 | % Fix up the MICR line components (replace placeholders with MICR |
516 | % characters) |
517 | % Argh... surely there's a better way - anyone? use "forall?" |
518 | |
519 | /FixMICR { |
520 | |
521 | /CheckNumStart -1 def |
522 | /CheckNumEnd -1 def |
523 | /CheckNumInOnUs false def |
524 | /CheckNumInAuxOnUs false def |
525 | |
526 | % Get starting and ending positions for check number in |
527 | % (Aux)OnUs field |
528 | % (This will break if check number is entered in both fields) |
529 | |
530 | OnUs length 1 sub -1 0 { |
531 | dup % dups the index |
532 | OnUs exch get (C) 0 get eq { |
533 | /CheckNumInOnUs true def |
534 | % If end number not yet defined, define it |
535 | CheckNumEnd 0 lt { |
536 | /CheckNumEnd exch def |
537 | } { |
538 | /CheckNumStart exch def |
539 | } ifelse |
540 | |
541 | } { |
542 | pop |
543 | } ifelse |
544 | } for |
545 | |
546 | AuxOnUs length 1 sub -1 0 { |
547 | dup % dups the index |
548 | AuxOnUs exch get (C) 0 get eq { |
549 | /CheckNumInAuxOnUs true def |
550 | % If end number not yet defined, define it |
551 | CheckNumEnd 0 lt { |
552 | /CheckNumEnd exch def |
553 | } { |
554 | /CheckNumStart exch def |
555 | } ifelse |
556 | |
557 | } { |
558 | pop |
559 | } ifelse |
560 | } for |
561 | |
562 | |
563 | % Replace "R" in routing number with actual transit number symbol |
564 | % That's it - should be no spaces, dashes, or anything but digits |
565 | Routing length 1 sub -1 0 { |
566 | dup % dups the index |
567 | Routing exch get (R) 0 get eq { |
568 | Routing exch TransitSymbol 0 get put |
569 | } { |
570 | pop |
571 | } ifelse |
572 | } for |
573 | |
574 | % Replace "S" with space character in AuxOnUs |
575 | AuxOnUs length 1 sub -1 0 { |
576 | dup |
577 | AuxOnUs exch get (S) 0 get eq { |
578 | AuxOnUs exch ( ) 0 get put |
579 | } { |
580 | pop |
581 | } ifelse |
582 | } for |
583 | |
584 | % Replace "-" with dash character in AuxOnUs |
585 | AuxOnUs length 1 sub -1 0 { |
586 | dup |
587 | AuxOnUs exch get (-) 0 get eq { |
588 | AuxOnUs exch DashSymbol 0 get put |
589 | } { |
590 | pop |
591 | } ifelse |
592 | } for |
593 | |
594 | % Replace "P" with OnUs character in AuxOnUs |
595 | AuxOnUs length 1 sub -1 0 { |
596 | dup |
597 | OnUs exch get (P) 0 get eq { |
598 | OnUs exch OnUsSymbol 0 get put |
599 | } { |
600 | pop |
601 | } ifelse |
602 | } for |
603 | |
604 | % Replace "S" with space character in OnUs |
605 | OnUs length 1 sub -1 0 { |
606 | dup |
607 | OnUs exch get (S) 0 get eq { |
608 | OnUs exch ( ) 0 get put |
609 | } { |
610 | pop |
611 | } ifelse |
612 | } for |
613 | |
614 | % Replace "-" with dash character in OnUs |
615 | OnUs length 1 sub -1 0 { |
616 | dup |
617 | OnUs exch get (-) 0 get eq { |
618 | OnUs exch DashSymbol 0 get put |
619 | } { |
620 | pop |
621 | } ifelse |
622 | } for |
623 | |
624 | % Replace "P" with OnUs character in OnUs |
625 | OnUs length 1 sub -1 0 { |
626 | dup |
627 | OnUs exch get (P) 0 get eq { |
628 | OnUs exch OnUsSymbol 0 get put |
629 | } { |
630 | pop |
631 | } ifelse |
632 | } for |
633 | |
634 | } def |
635 | |
636 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
637 | % Feature Printing Routines % |
638 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
639 | |
640 | /DrawMemoLine { |
641 | LeftMargin MemoLineHeight CheckHeight mul moveto |
642 | 2.5 inch 0 inch rlineto |
643 | -2.5 inch 0 inch rmoveto |
644 | 0 2 rmoveto |
645 | (for) show |
646 | } def |
647 | |
648 | /DrawSignatureLine { % Expects height of signature line |
649 | % and right edge of check for |
650 | % beginning position |
651 | |
652 | CheckWidth SignatureLineHeight CheckHeight mul moveto |
653 | RightMargin neg 0 rmoveto |
654 | -2.5 inch 0 rmoveto |
655 | 2.5 inch 0 inch rlineto |
656 | |
657 | } def |
658 | |
659 | /DrawAmountLine { |
660 | CheckWidth AmountLineHeight CheckHeight mul moveto |
661 | RightMargin neg 0 rmoveto |
662 | (DOLLARS) stringwidth pop neg 0 rmoveto |
663 | (DOLLARS) show |
664 | (DOLLARS) stringwidth pop neg 0 rmoveto |
665 | -2 0 rmoveto |
666 | LeftMargin AmountLineHeight CheckHeight mul lineto |
667 | } def |
668 | |
669 | /DrawAccountHolderInfo { |
670 | LeftMargin CheckHeight moveto |
671 | 0 TopMargin neg rmoveto |
672 | 0 StandardFontSize neg rmoveto |
673 | |
674 | StandardFontSize Name1 ShowAndCR |
675 | StandardFontSize Name2 ShowAndCR |
676 | |
677 | StandardFontName findfont |
678 | StandardFontSize 1 sub scalefont |
679 | setfont |
680 | |
681 | StandardFontSize 1 sub Address1 ShowAndCR |
682 | StandardFontSize 1 sub Address2 ShowAndCR |
683 | StandardFontSize 1 sub CityStateZip ShowAndCR |
684 | StandardFontSize 1 sub PhoneNumber ShowAndCR |
685 | |
686 | StandardFontName findfont |
687 | StandardFontSize 1 add scalefont |
688 | setfont |
689 | } def |
690 | |
691 | /DrawDateLine { |
692 | 0.6 CheckWidth mul DateLineHeight CheckHeight mul moveto |
693 | (Date) show |
694 | 1 inch 0 rlineto |
695 | } def |
696 | |
697 | /DrawBankInfo { |
698 | LeftMargin BankInfoHeight CheckHeight mul moveto |
699 | |
700 | StandardFontSize BankName ShowAndCR |
701 | |
702 | StandardFontName findfont |
703 | StandardFontSize 1 sub scalefont |
704 | setfont |
705 | |
706 | StandardFontSize 1 sub BankAddr1 ShowAndCR |
707 | StandardFontSize 1 sub BankAddr2 ShowAndCR |
708 | StandardFontSize 1 sub BankCityStateZip ShowAndCR |
709 | |
710 | StandardFontName findfont |
711 | StandardFontSize 1 add scalefont |
712 | setfont |
713 | } def |
714 | |
715 | /DrawPayeeLine { |
716 | |
717 | LeftMargin PayeeLineHeight CheckHeight mul moveto |
718 | (ORDER OF) show |
719 | (ORDER OF) stringwidth pop neg StandardFontSize rmoveto |
720 | (PAY TO THE) show |
721 | 0 StandardFontSize neg rmoveto |
722 | 4 0 rmoveto |
723 | currentpoint mark |
724 | |
725 | CheckWidth PayeeLineHeight CheckHeight mul moveto |
726 | RightMargin neg 0 rmoveto |
727 | AmountBoxWidth neg 0 rmoveto |
728 | |
729 | 0 AmountBoxHeight rlineto |
730 | AmountBoxWidth 0 rlineto |
731 | 0 AmountBoxHeight neg rlineto |
732 | AmountBoxWidth neg 0 rlineto |
733 | |
734 | -4 0 rmoveto |
735 | |
736 | /Helvetica-Bold findfont |
737 | 14 scalefont |
738 | setfont |
739 | |
740 | (\$) stringwidth pop neg 0 rmoveto |
741 | (\$) show |
742 | (\$) stringwidth pop neg 0 rmoveto |
743 | |
744 | -4 0 rmoveto |
745 | cleartomark |
746 | lineto |
747 | |
748 | StandardFontName findfont |
749 | StandardFontSize scalefont |
750 | setfont |
751 | |
752 | } def |
753 | |
754 | /DrawMICR { |
755 | % 0.25 high, 5.6875 from right edge should be in the middle |
756 | % of the tolerance band |
757 | CheckWidth 0.25 inch moveto |
758 | -5.6875 inch 0 inch rmoveto |
759 | MICRHorTweak MICRVerTweak rmoveto |
760 | % Now we're at the nominal start of the routing number |
761 | |
762 | MICRFontName findfont |
763 | MICRFontSize scalefont |
764 | setfont |
765 | |
766 | % Number of digits in the CheckNumberString |
767 | /CheckNumDigit CheckNumberString length 1 sub def |
768 | |
769 | CheckNumInAuxOnUs { |
770 | CheckNumEnd -1 CheckNumStart { |
771 | CheckNumDigit 0 ge { |
772 | AuxOnUs exch CheckNumberString CheckNumDigit get put |
773 | /CheckNumDigit CheckNumDigit 1 sub def |
774 | } { |
775 | AuxOnUs exch (0) 0 get put |
776 | } ifelse |
777 | } for |
778 | } if |
779 | |
780 | |
781 | AuxOnUs stringwidth pop neg 0 rmoveto |
782 | AuxOnUs show |
783 | |
784 | Routing show |
785 | |
786 | CheckNumInOnUs { |
787 | CheckNumEnd -1 CheckNumStart { |
788 | CheckNumDigit 0 ge { |
789 | OnUs exch CheckNumberString CheckNumDigit get put |
790 | /CheckNumDigit CheckNumDigit 1 sub def |
791 | } { |
792 | OnUs exch (0) 0 get put |
793 | } ifelse |
794 | } for |
795 | } if |
796 | |
797 | OnUs show |
798 | |
799 | StandardFontName findfont |
800 | StandardFontSize scalefont |
801 | setfont |
802 | } def |
803 | |
804 | /DrawCheckNumber { |
805 | CheckWidth CheckHeight moveto |
806 | RightMargin neg TopMargin neg rmoveto |
807 | CheckNumFont findfont |
808 | CheckNumSize scalefont |
809 | setfont |
810 | |
811 | CheckNumberString stringwidth pop neg 0 rmoveto |
812 | 0 -14 rmoveto |
813 | CheckNumberString show |
814 | |
815 | StandardFontName findfont |
816 | StandardFontSize scalefont |
817 | setfont |
818 | } def |
819 | |
820 | /DrawFraction { |
821 | 0.6 CheckWidth mul CheckHeight moveto |
822 | 0 TopMargin neg rmoveto |
823 | 0 StandardFontSize neg rmoveto |
824 | Fraction show |
825 | } def |
826 | |
827 | /DrawStub { |
828 | CheckHorOffset 2 inch ge { |
829 | save |
830 | newpath |
831 | CheckHorOffset neg 0 translate |
832 | StandardFontName findfont |
833 | StandardFontSize 1 sub scalefont |
834 | setfont |
835 | /StubSpacing {CheckHeight 6 div} def |
836 | CheckHorOffset 2 div StubSpacing 5 mul moveto |
837 | CheckNumberString show |
838 | 0.3 inch StubSpacing 4 mul moveto |
839 | (Date ) show |
840 | CheckHorOffset 0.3 inch sub StubSpacing 4 mul lineto |
841 | 0.3 inch StubSpacing 3 mul moveto |
842 | (Payee ) show |
843 | CheckHorOffset 0.3 inch sub StubSpacing 3 mul lineto |
844 | 0.3 inch StubSpacing 2 mul moveto |
845 | (Amount ) show |
846 | CheckHorOffset 0.3 inch sub StubSpacing 2 mul lineto |
847 | 0.3 inch StubSpacing 1 mul moveto |
848 | (Memo ) show |
849 | CheckHorOffset 0.3 inch sub StubSpacing 1 mul lineto |
850 | stroke |
851 | restore |
852 | } if |
853 | } def |
854 | |
855 | /DrawVOID { |
856 | save |
857 | StandardFontName findfont |
858 | 50 scalefont |
859 | setfont |
860 | newpath |
861 | CheckWidth 2 div 1 inch moveto |
862 | 30 rotate |
863 | (V O I D) stringwidth pop 0 moveto |
864 | (V O I D) true charpath |
865 | stroke |
866 | restore |
867 | } def |
868 | |
869 | /DrawCheck { |
870 | |
871 | % Convert CheckNumber integer to a string |
872 | CheckNumber CheckNumberString cvs |
873 | |
874 | PrintCheckBody { |
875 | DrawBankInfo |
876 | DrawAccountHolderInfo |
877 | DrawMemoLine |
878 | DrawSignatureLine |
879 | DrawAmountLine |
880 | DrawPayeeLine |
881 | DrawCheckNumber |
882 | DrawFraction |
883 | DrawDateLine |
884 | DrawStub |
885 | } if |
886 | |
887 | PrintMICRLine { |
888 | DrawMICR |
889 | } if |
890 | |
891 | PrintVOID { |
892 | % Draw border around check, and print "VOID" for testing |
893 | 0 0 moveto |
894 | CheckWidth 0 lineto |
895 | CheckWidth CheckHeight lineto |
896 | 0 CheckHeight lineto |
897 | |
898 | 0 0 lineto |
899 | |
900 | DrawVOID |
901 | } if |
902 | |
903 | } def |
904 | |
905 | |
906 | /CurrentPage 1 def |
907 | |
908 | % Replace symbol placeholders with actual glyphs |
909 | % Also get starting and ending position for check number |
910 | FixMICR |
911 | |
912 | NumPages -1 1 { |
913 | /CheckNumber CheckNumber ChecksPerPage add def |
914 | CheckHorOffset CheckVerOffset translate |
915 | |
916 | StandardFontName findfont |
917 | StandardFontSize scalefont |
918 | setfont |
919 | |
920 | LineWidth setlinewidth |
921 | |
922 | % Loop through printing checks, starting with the bottom one |
923 | |
924 | ChecksPerPage -1 1 { |
925 | /CheckNumber CheckNumber 1 sub def |
926 | newpath |
927 | DrawCheck |
928 | stroke |
929 | 0 CheckHeight translate |
930 | } for |
931 | |
932 | showpage |
933 | /CheckNumber CheckNumber ChecksPerPage add def |
934 | /CurrentPage CurrentPage 1 add def |
935 | |
936 | } for |
937 | |
938 | |
939 | END_OF_POSTSCRIPT |
940 | ; |
941 | |
942 | } |
943 |
Built with git-ssb-web