Output a playable crossword grid
Postscript 905 797 677 675 629 608 330 320 308

{G N}/Times-Roman .3 2 22 1 30/.{&lt;920&gt;dup 1 4 3 roll put cvx 
exec}def/${//. 73 .}def[/R{99&lt;a51f3e7d75&gt;$}/G{&lt;1fab75&gt;$ 
R(uN)${0 R{1(X)$ 0 1 -1 5 4 roll 35 eq{4&lt;1980&gt;$}if&lt;81&gt;$ 
1 add}(I)$(u)$ 0 -1&lt;ad&gt;$}&lt;834d&gt;$}/X{{exit}if}/N{-.9
.7&lt;ad&gt;${&lt;1fab70&gt;$ X}loop{{(&gt;nk)$(  )&lt;31a0&gt;$}&lt;a3&gt;$
X}loop}&gt;&gt;&lt;0d38388b369bad8e3f&gt;$


This program is written as a "protocol prolog" so you just cat it together with the grid and number files (in that order, separated by blank lines) and pipe the whole mess to ghostscript or Distiller or a PS printer. Appended to the reference version below is a NYT puzzle (From Nov. 5, 2011) with numbers and one answer I'm pretty sure of (Saturdays is hard!).

The new revision uses these two procedures to execute binary-encoded
system names from strings.

/.{
    &lt;920&gt;  % two-byte binary-encoded name template with 0x92 prefix
    dup 1 4 3 roll put  % insert number into string
    cvx exec  % and execute it
}def
/${
    //.   %the /. procedure body defined above
    73 .  %"forall" (by code number)
}def


Indented and (somewhat) commented.

/Times-Roman .3 2 22 1 30
/.{&lt;920&gt;dup 1 4 3 roll put cvx exec}def/${//. 73 .}def
[
/R{99&lt;a51f3e7d75&gt;$}    %currentfile 99 string readline pop 
/G{&lt;1fab75&gt;$ %currentfile token pop 
    R (uN)$ %&lt;754e&gt;$ %pop gsave
    {   
        0 R { 
            1 (X)$ %index
            0 1 -1 5 4 roll
            35 eq{ 
                4&lt;1980&gt;$ %copy rectfill
            }if 
            &lt;81&gt;$ %rectstroke
            1 add 
        }(I)$
        (u)$ % 73 . %forall pop 
        0 -1&lt;ad&gt;$ %translate
    }&lt;834d&gt;$ %repeat grestore
}
/X{{exit}if}
/N{
    -.9 .7&lt;ad&gt;$ %translate
    %{ currentfile token not {exit} if } loop
    {&lt;1fab70&gt;$
        X %{exit}if
    }loop
    {   
        %dup type/integertype ne{exit}if
        {
            (&gt;nk)$ %&lt;3e6e6b&gt;$ %exch neg moveto
            (  )&lt;31a0&gt;$ %cvs show
        }&lt;a3&gt;$ %stopped
        X %{exit}if
    }loop
}
&gt;&gt;
&lt;0d38388b369bad8e&gt;$
%begin dup dup scale div setlinewidth translate selectfont
{G N}exec


Data files.

15 15
     #   #     


       #     ##
##   #   #     
    #     #    
   #    #      
       #       
      #    #   
    #     #    
    K#   #   ##
##  I  #       
    L          
    N          
    S#   #     

#i m n   figure(number), row, col
1 1 1
2 1 2
3 1 3
4 1 4
5 1 5
6 1 7
7 1 8
8 1 9
9 1 11
10 1 12
11 1 13
12 1 14
13 1 15
14 2 1
15 2 6
16 2 10
17 3 1
18 4 1
19 4 9
20 5 3
21 5 7
22 5 8
23 5 11
24 5 14
25 5 15
26 6 1
27 6 2
28 6 6
29 6 10
30 6 12
31 7 1
32 7 5
33 7 10
34 7 11
35 8 1
36 8 4
37 8 9
38 9 1
39 9 8
40 9 13
41 10 1
42 10 6
43 10 7
44 10 12
45 11 1
46 11 5
47 11 7
48 11 11
49 12 3
50 12 6
51 12 9
52 12 10
53 12 14
54 12 15
55 13 1
56 13 2
57 13 8
58 14 1
59 15 1
60 15 7
60 15 11


It should look okay from a printer, but on-screen it needs a little help.
This 19-char procedure and 9 chars to invoke it on all user-space points, helps make evenly-spaced lines look more even. So 308 + 19 + 9 = 337, used to generate this image.

/F{&lt;ac893e893e5f&gt;$} % transform round exch round exch itransform




Postscript 608

This earlier version (from revision 8) uses a completely different approach, reusing the main-line code as a "lexicon" from which longer tokens can be indexed using strings.

&lt;&lt;-1{{30 700 translate 0 0 moveto
currentfile 2{(@A1*)*}repeat exch string
3 2 roll{('BO1)*{( )(@#)*
4(,.N:;&lt;%)*&lt;&lt;( ){p}/#{(1:;&gt;%)*}0{(;,'.)*
6 -26(CE%)*}&gt;&gt;(IJ'B)* known not{p
0}if(LG #C)*}forall(#;*1 D%)*}repeat
p/Times-Roman 8 selectfont 99
string{('BO)*{(@)* length(#=)*{p}{(@#L)*
35(=)*{p}{cvx(GID ?'H*ID ?KHF%)*(   )cvs(E)*}e}e}{showpage
exit}e}loop exit{( @#M# FMF#M)*
closepath}currentpoint stroke eq fill
mul dup token copy rmoveto sub show
neg exec add 1 index 9 get rlineto
put readline}loop}0 1 index 0 get{1
index 1 add}forall pop/*{{32 sub load
exec}forall}/e{ifelse}/p{pop}&gt;&gt;begin(23,?4)*&lt;1f&gt;*


It was written using this commented version which illustrates the encoding of the lexicon. The first token 30 is commented space therefore ( )* is a synonym for 30. Not very beneficial for 30, but for longer tokens this is(was) a big win (until deeper encoding possibilities are(were) discovered).

&lt;&lt;-1{{
%space  !    "         # $ %      &amp;           '(        )     %*    +      , - .   %/
 30     700  translate 0 0 moveto currentfile 2{(@A1*)*}repeat exch string 3 2 roll
{('BO1)*{( )(@#)* 4(,.N:;&lt;%)*&lt;&lt;( ){p}/#{(1:;&gt;%)*}0{(;,'.)* 6 -26(CE%)*}&gt;&gt;(IJ'B)*
known not{p 0}if(LG #C)*}forall(#;*1 D%)*}
%0      1   2          3 4          5  6     7
 repeat p /Times-Roman 8 selectfont 99 string{('BO)*{(@)* length(#=)*{p}{
(@#L)* 35(=)*{p}{cvx(GID ?'H*ID ?KHF%)*(   )cvs(E)*}e}e}{showpage clear exit}e}
%8    9   :                         %;            &lt;      =    &gt;    ?
 loop exit{( @#M# FMF#M)* closepath} currentpoint stroke eq fill mul
%@   A     B    C       D   E    F   G    H   I J     K L   M       N   O
 dup token copy rmoveto sub show neg exec add 1 index 9 get rlineto put readline
}loop}0 1 index 0 get{1 index 1 add}forall
pop/*{{32 sub load exec}forall}/e{ifelse}/p{pop}&gt;&gt;begin(23,?4)*&lt;1f&gt;*