summaryrefslogtreecommitdiff
path: root/graphics/MF-PS/epstomf/epstomf.awk
blob: 163a4ecd14476cac0499150d333345309620374d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
# ---------------------------------------------------------------------------
# This file belongs to the EPSTOMF package.
# ---------------------------------------------------------------------------
# You are entitled to do with the file whatever you wish. If you alter a file,
# however, please remove the line containing the comment:
#
#      `This file belongs to the EPSTOMF package.'
#
# in order to avoid mess.
# ---------------------------------------------------------------------------
# AUTHORS: B. Jackowski, P. Pianowski, M. Ry\'cko
# HISTORY:
#    version 0.50: Tuesday, August 15th, 1995
#        * first less or more complete version
#    version 0.51: Monday, October 23rd, 1995
#        * comments adjusted to a new distribution
#    version 0.52: Monday, November 6th, 1995
#        * miter limit is a dimensionless quantity
#    version 0.53: Wednesday, March 19th, 1997
#        * degenerated (single-node) paths used to be processed improperly
#        * font dimens specified explicitly
#        * `plain' option added (a METAFONT code without referring to the
#          MFTOEPS package is generated if variable `plain' is set to 1)
#    version 0.54: Thursday, January 29th, 1998
#        * this and previous versions were stimulated (stipulated in fact :-)
#          by Samy Alex Za\"\i{}mi -- thanks!
#        * all kinds of end-of-line characters are accepted in
#          input files; this, however, necessitates using Gnu AWK
#        * a patch was added to cope (somehow) with PageDraw's (ver. 2.0)
#          EPSes that do not conform to the definition of a ``canonical''
#          EPS given below, nonetheless, they are ``nearly canonical''
#          and something can be done 
#        * in order to avoid annoying gftodvi's messages
#          ``Too many labels'' and ``Too many labels and/or rules''
#          the command |labels| is preceded by a percent sign if
#          suspiciously many nodes occur
# ---------------------------------------------------------------------------
# This is an AWK program for the translation of a ``canonical'' (see below)
# EPS file into METAFONT lingo; the resulting METAFONT program inputs MFTOEPS
# macros (ver. >= 0.61). EPS files created using the MFTOEPS package are
# accepted by this converter.
# ---
# The crucial features of a ``canonical'' EPS file are listed below:
#  0. General assumptions:
#     a) parameters of a command appear in one line along with the command;
#     b) only explicit numerical values are admissible (variables are not
#        interpreted);
#     c) all numbers are expressed in PostScript points (called in TeX
#        ``big points'': 1/72in), i.e., no local transformations are involved;
#     d) prior to painting a curve (fill or stroke) its parameters
#        for filling or stroking are explicitly set;
#     e) only the commands listed in items 1--11 are interpreted.
#  1. `gsave' and `grestore' are represented by `q' and `Q', respectively;
#     malformed grouping structure may yield unpredictable results.
#  2. `moveto' is represented by `m'.
#  3. `lineto' is represented by either `l' or `L'.
#  4. `curveto' is represented by either `c' or `C'.
#  5. `setlinewidth' is represented by `w'.
#  6. `setlinejoin' is represented by `j'.
#  7. `setlinecap' is represented by `J'.
#  8. `setmiterlimit' is represented by `M'.
#  9. `setdash' is represented by `d'.
# 10. Curves start with `*u' and end with `*U'; no `q'/`Q' commands are
#     expected to occur inside a curve, i.e., between `*u' and `*U' commands;
#     `q'/`Q' pairs occuring outside a curve, i.e., between `*U' and `*u'
#     commands, are ignored; actually, the latter situation should not
#     occur either.
# 11. Component paths of a multi-path curve end consistently either
#     with `f' (filling), or `s'/`S' (stroking of closed and open paths,
#     resp.), or `h W n' (clipping; in fact, only `n' is taken taken into
#     account).
# 12. Structural comments `%%BoundingBox' and `%%EndSetup' should occur at
#     the beginning of the file (in this order).
# ---
# COMMENTS:
# 1. The engine works in such a way that at first a necessary information
#    about paths is collected; during this stage only the coordinates of
#    nodes are written; after processing all the data the ``painting'' code
#    is generated; this complicates the AWK code a bit due to controlling
#    `gsave'/`grestore' nesting; a simplified approach is used: instead of
#    recording the full nesting tree, a ``backbone'' of a nesting structure
#    is reconstructed (cf. item 10 above).
# 2. A little bit more complicated (in comparison with other commands) is
#    the translation of `d' (`setdash') command; in addition to the
#    assumptions specified above, it is assumed that at least one space
#    precedes `[' and `d', and follows `]'.
# 3. Some complexity is also involved at the METAFONT level at the stage of
#    computing of a bounding box; as a default, the graphic object is shifted
#    such that the left lower corner of the bounding box coincides with
#    the origin of the coordinate system; by manual alteration of the
#    resulting METAFONT file the user can easily control the situation, e.g.,
#    vertical or horizontal shift (or both) can be prohibited, the original
#    bounding box can be preserved, etc.; one should also remember that
#    if clipping is involved, the computed bounding box may be invalid.
# 4. Due to rounding errors the original EPS file and the EPS file generated
#    from a resulting METAFONT file may differ.
# ---------------------------------------------------------------------------
BEGIN {
 skipping=1
 path_max=0
 group_max=0
 node_max=0
 xl=0; yl=0; xh=72; yh=72; # emergency default
# `q'/`Q' nesting is controled by the following three variables
# (`ignore_grestore' controls ignoring `q'/`Q' pairs between curves):
 nesting_level=0; nesting_group=0; ignore_grestore=0
 RS="\015|\012|\015\012"
}
# ---
(skipping==1) && ($0 ~ /^%%BoundingBox:/) {xl=$2; yl=$3; xh=$4; yh=$5}
# ---
(skipping==1) && $0=="%%EndSetup" {skipping=0;
 print "%%% fill fill_C draw_C clip_C set_BB find_BB close_path"
 print "%%% fill fix_fill_cmyk fix_draw_cmyk"
 print "%%% fill fix_line_width fix_line_join fix_line_cap"
 print "%%% fill fix_miter_limit fix_dash"
 print "%%% message write_preamble"
 print "%%% endchar write_postamble g_save g_restore"
 print "%%% addto ~"
 print "%%\\input mftform"
 if (!plain) print "input mftoeps;"
 print "numeric w#, h#, d#, xl#, yl#, xh#, yh#, xs#, ys#;"
 print "xl#=" xl "bp#; yl#=" yl "bp#; xh#=" xh "bp#; yh#=" yh "bp#;";
 print "if false: noxs:=0; fi % no horizontal shift, ignored by default"
 print "if false: noys:=0; fi % no vertical shift, ignored by default"
 if (!plain) {
   print "if false: oribb:=0; fi",
         "% preserving the original bounding box, ignored by default"
   print "pinbb:=0; % default: shifting the object such that |(xl,yl)=(0,0)|"
 }
 print "xs#=if known noxs: 0 else: -xl# fi;"
 print "ys#=if known noys: 0 else: -yl# fi;"
 print "w#=max(0,xh#+xs#); h#=max(0,yh#+ys#); d#=max(0,-yl#-ys#);"
 print "%"
 fn=FILENAME; gsub(/^.*[\/\\]/,"",fn); gsub(/\.[^\/\\]*$/,"",fn)
 print "font_identifier:=\"" fn "\";";
 print "font_size round(w#);"
 print "font_slant 0;"
 print "font_normal_space w#;"
 print "font_normal_stretch 0;"
 print "font_normal_shrink 0;"
 print "font_x_height h#;"
 print "font_quad w#;"
 print "font_extra_space 0;"
 print "message \"The font size = \" & decimal(designsize) & \"pt#\";"
 print "message \"\";"
 print "%"
 if (!plain) print "extra_eps_setup:=\"pixels_per_inch:=72\";",
                   "% better resolution for this case"
 print (plain ? "" : "eps_") "mode_setup; define_pixels(xs, ys);"
 print "%"
 print "def ~ text xy = \\\\ = (bp*xpart(xy)+xs,bp*ypart(xy)+ys) enddef;"
 print "%%% step C B L M"
 print "def M = M_ ( enddef;"
 print "def M_(suffix $) = z$ enddef;"
 print "def B = ) B_ ( enddef;"
 print "def B_(suffix $) = .. controls z$a and z$b .. z$ enddef;"
 print "def L = ) L_ ( enddef;"
 print "def L_(suffix $) = -- z$ enddef;"
 print "def C = ) enddef;"
 print "def close_path suffix p ="
 print " p:=p if (point 0 of p)=(point length(p) of p): & else: -- fi cycle;"
 print "enddef;"
 print "%%% labels B L M"
 print "%%% fi C"
 if (plain) {
   print "% ---"
   print "def eofill expr P ="
   print " begingroup save pp_; picture pp_;"
   print "  interim turningcheck:=0;"
   print "% the following |addto|s are supposed to fill disjoint areas:"
   print "  pp_:=nullpicture; addto pp_ contour P.t_;"
   print "  cull pp_ keeping (1,infinity); addto currentpicture also pp_;"
   print "  pp_:=nullpicture; addto pp_ contour reverse P.t_;"
   print "  cull pp_ keeping (1,infinity); addto currentpicture also pp_;"
   print "  cull currentpicture keeping(1,1);"
   print " endgroup"
   print "enddef;"
   print "def fill_C text t ="
   print " begingroup"
   print "  save rr_; picture rr_; % must not be |pp_| (see |eofill|)"
   print "  begingroup"
   print "   save currentpicture; def currentpicture = rr_ enddef; % danger!"
   print "   currentpicture:=nullpicture; for qq_:=t: eofill qq_; endfor"
   print "  endgroup;"
   print "  addto currentpicture also rr_; cullit;"
   print " endgroup"
   print "enddef;"
   print "def draw_C text t = for pp_:=t: draw pp_; endfor enddef;"
   print "def clip_C text t = enddef;"
 }
 print "% ---"
 print "beginchar("0",w#,h#,d#); path p[\\\\];"
}
# ---
skipping==0 {
 for (i=1; i<=NF; i+=1) {
# ---
  if ($i=="q") {++nesting_level; ++ignore_grestore}
# ---
  if ($i=="Q") {
   --nesting_level
   if (ignore_grestore>0) {--ignore_grestore} else {++nesting_group}
   }
# ---
  if ($i=="*u") {
   print ""; print "% BEGIN of GROUP", group_max ";"
   ignore_grestore=0; first_path[group_max]=path_max
  }
# ---
  if ($i=="k") {
   fix_fill_cmyk[group_max]=$(i-4) "," $(i-3) "," $(i-2) "," $(i-1)
  }
# ---
  if ($i=="K") {
   fix_draw_cmyk[group_max]=$(i-4) "," $(i-3) "," $(i-2) "," $(i-1)
  }
# ---
  if ($i=="w") fix_line_width[group_max]=$(i-1) "bp"
# ---
  if ($i=="j") fix_line_join[group_max]=$(i-1)
# ---
  if ($i=="J") fix_line_cap[group_max]=$(i-1)
# ---
  if ($i=="M") fix_miter_limit[group_max]=$(i-1)
# ---
  if ($i=="d") {# this is a little bit more complex case (see comment 2)
   l=i-2; # $(i-1)="d", $(i-1) is the offset
   for (j=l; $j !~ /\[/; --j) {}
# $j contains the opening bracket for `setdash'
# $l contains the closing bracket for `setdash'
   gsub(/\[/,"",$j); gsub(/\]/,"",$l) # delete both brackets
   was_something=0
   fix_dash[group_max]="("; # construct parameters for `fix_dash'
   for (k=j; k<=l; ++k) {
    if ($k != "") {
     if (was_something==1) {aux=","} else {was_something=1; aux=""}
     fix_dash[group_max]=fix_dash[group_max] aux $k "bp"
    }
   }
   fix_dash[group_max]=fix_dash[group_max] ") " $(i-1) "bp"
  }
# ---
  if ($i=="m") {
   print "% begin of path", path_max ";"
   print "%%\\BMU\\COLS"
   print "z" node_max "~" $(i-2) "," $(i-1) ";"
   node_path[node_max]=path_max; first_node[path_max]=node_max; ++node_max
  }
# ---
  if (($i=="l") || ($i=="L")) {
   print "z" node_max "~" $(i-2) "," $(i-1) ";"
   node_path[node_max]=path_max; arc_type[node_max]="L"; ++node_max
  }
# ---
  if (($i=="c") || ($i=="C")) {
   print "z" node_max "a~" $(i-6) "," $(i-5) ";"
   print "z" node_max "b~" $(i-4) "," $(i-3) ";"
   print "z" node_max "~" $(i-2) "," $(i-1) ";"
   node_path[node_max]=path_max; arc_type[node_max]="B"; ++node_max
  }
# ---
  if (($i=="f") || ($i=="s") || ($i=="S") || ($i=="n")) {
   print "%%\\EMU";
   path_group[path_max]=group_max; last_node[path_max]=node_max-1;
   group_type[group_max]=$i; # should be consistent for all paths in a group
   if (($i=="f") || ($i=="s") || ($i=="n")) is_closed_path=1
   else is_closed_path=0
   aux="p" path_max "=M" first_node[path_max]
   if (first_node[path_max]<last_node[path_max])
    for (j=first_node[path_max]+1; j<=last_node[path_max]; ++j) {
     aux=aux " " arc_type[j] j
     if (j==last_node[path_max]) aux=aux " C;"
     if (length(aux)>70) {print aux; aux=""}
    } else aux=aux " C;" # degenerated case
   if (aux!="") print aux
   if (is_closed_path==1) print "close_path p" path_max ";"
   print "% end of path", path_max ";"
   ++path_max
  }
# ---
  if ($i=="*U") {
   print "% END of GROUP", group_max ";";
   group_nesting_level[group_max]=nesting_level;
   group_nesting_group[group_max]=nesting_group;
   last_path[group_max]=path_max-1;
   ++group_max
  }
 }
}
# ---
END {
 node_max--
 path_max--
 group_max--
# ---
 if (!plain) {
   print ""
   print "if known oribb:"
   print " set_BB " xl "bp+xs," yl "bp+ys," xh "bp+xs," yh "bp+ys;"
   print "elseif known pinbb:"
   print " find_BB p0 for i:=1 upto "  path_max": , p[i] endfor;"
   print " for i:=0 upto "  node_max":"
   print "  x[i]:=x[i]-xl_crd; y[i]:=y[i]-yl_crd;"
   print "  if known x[i]a: x[i]a:=x[i]a-xl_crd; fi"
   print "  if known x[i]b: x[i]b:=x[i]b-xl_crd; fi"
   print "  if known y[i]a: y[i]a:=y[i]a-yl_crd; fi"
   print "  if known y[i]b: y[i]b:=y[i]b-yl_crd; fi"
   print " endfor"
   print " for i:=0 upto "  path_max": p[i]:=p[i] shifted -llxy; endfor"
   print " set_BB origin, urxy-llxy;"
   print "else:"
   print " find_BB p0 for i:=1 upto "  path_max": , p[i] endfor;"
   print "fi"
   print "write_preamble jobname;"
 }
# ---
# one extra `gsave'/`grestore' pair is always added by MFTOEPS, hence `-2'
 if (!plain) for (j=0; j<=group_nesting_level[0]-2; ++j) {print "g_save;"}
 for (g=0; g<=group_max; ++g) {
  if (plain) {
    if (fix_line_width[g]!="")
      print "pickup pencircle scaled " fix_line_width[g] ";"
  } else {
  if (g>0) {
     k=group_nesting_group[g]-group_nesting_group[g-1];
     # k == number of groups closed in the meantime
     for (j=1; j<=k; ++j) print "g_restore;"
     k=group_nesting_level[g-1]-k; l=group_nesting_level[g];
     # k == current nesting level, l == resulting nesting level
     for (j=1; j<=(k-l); ++j) print "g_restore;"
     for (j=-1; j>=(k-l); --j) print "g_save;"
    }
    if (fix_fill_cmyk[g]!="") print "fix_fill_cmyk " fix_fill_cmyk[g] ";"
    if (fix_draw_cmyk[g]!="") print "fix_draw_cmyk " fix_draw_cmyk[g] ";"
    if (fix_line_width[g]!="") print "fix_line_width " fix_line_width[g] ";"
    if (fix_line_join[g]!="") print "fix_line_join " fix_line_join[g] ";"
    if (fix_line_cap[g]!="") print "fix_line_cap " fix_line_cap[g] ";"
    if (fix_miter_limit[g]!="") print "fix_miter_limit " fix_miter_limit[g] ";"
    if (fix_dash[g]!="") print "fix_dash " fix_dash[g] ";"
  }
  if (group_type[g]=="f") aux="fill_C"
  if ((group_type[g]=="s") || (group_type[g]=="S")) aux="draw_C"
  if (group_type[g]=="n") aux="clip_C"
  for (j=first_path[g]; j<=last_path[g]; ++j) {
   aux=aux " p" j (j==last_path[g] ? ";" : ",")
   if (length(aux)>70) {print aux; aux=""}
  }
  if (aux!="") print aux
 }
# ---
 if ((group_max<0) && (path_max>0)) {
# something went wrong (non-conforming EPS?);
# let's undertake a desperate defense of the nearly wasted task:
  g=0; aux="fill_C" # emergency initialization
  if (group_type[g]=="f") aux="fill_C"
  if ((group_type[g]=="s") || (group_type[g]=="S")) aux="draw_C"
  if (group_type[g]=="n") aux="clip_C"
  for (j=0; j<=path_max; ++j) {
   aux=aux " p" j (j==path_max ? ";" : ",")
   if (length(aux)>70) {print aux; aux=""}
  }
  if (aux!="") print aux
 }
# ---
# one extra `gsave'/`grestore' pair is always added by MFTOEPS, hence `-2'
 for (j=0; j<=group_nesting_level[group_max]-2; ++j) print "g_restore;"
 if (!plain) print "write_postamble;"
 # not to many nodes please (otherwise gftodvi is likely to break down):
 print (node_max>800 ? "% " : "") "labels(range" 1 "thru" node_max ");"
 print "endchar;"
 print "% ---"
 print "end."
 print "%%\\end"
}