update from MirBSD CVS
[shellsnippets/shellsnippets.git] / mksh / bdfctool.sh
1 #!/bin/mksh
2 # $MirOS: X11/extras/bdfctool/bdfctool.sh,v 1.17 2015/02/26 03:05:33 tg Exp $
3 #-
4 # Copyright © 2007, 2008, 2009, 2010, 2012, 2013, 2015
5 #       Thorsten “mirabilos” Glaser <tg@mirbsd.org>
6 #
7 # Provided that these terms and disclaimer and all copyright notices
8 # are retained or reproduced in an accompanying document, permission
9 # is granted to deal in this work without restriction, including un‐
10 # limited rights to use, publicly perform, distribute, sell, modify,
11 # merge, give away, or sublicence.
12 #
13 # This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
14 # the utmost extent permitted by applicable law, neither express nor
15 # implied; without malicious intent or gross negligence. In no event
16 # may a licensor, author or contributor be held liable for indirect,
17 # direct, other damage, loss, or other issues arising in any way out
18 # of dealing in the work, even if advised of the possibility of such
19 # damage or existence of a defect, except proven that it results out
20 # of said person’s immediate fault when using the work as intended.
21
22 set -o noglob
23
24 uascii=-1
25 ufast=0
26 oform=0
27 while getopts "acdeFGgh" ch; do
28         case $ch {
29         (a) uascii=1 ;;
30         (+a) uascii=0 ;;
31         (c|d|e|+e) mode=$ch oform=0 ;;
32         (F) ufast=1 oform=0 ;;
33         (G) oform=4 ;;
34         (g) oform=3 ;;
35         (h) mode=$ch ;;
36         (*) mode= ;;
37         }
38 done
39 shift $((OPTIND - 1))
40 (( $# )) && mode=
41
42 if [[ $mode = ?(h) ]] || [[ $mode != e && $uascii != -1 ]] || \
43     [[ $mode != d && $ufast$oform != 00 ]]; then
44         print -ru2 "Usage: ${0##*/} -c | -d [-FGg] | -e [-a] | +e"
45         [[ $mode = h ]]; exit $?
46 fi
47
48 # check padding on input, currently
49 (( chkpad = (oform == 3 || oform == 4) ))
50
51 # disable -F if -g
52 (( ufast = (oform == 3 || oform == 4) ? 0 : ufast ))
53
54 lno=0
55 if [[ $mode = e ]]; then
56         if (( uascii == 1 )); then
57                 set -A BITv -- '.' '#' '|'
58         else
59                 set -A BITv -- ' ' '䷀' '▌'
60         fi
61         while IFS= read -r line; do
62                 (( ++lno ))
63                 if [[ $line = 'e '* ]]; then
64                         set -A f -- $line
65                         i=${f[3]}
66                         print -r -- "$line"
67                         while (( i-- )); do
68                                 if IFS= read -r line; then
69                                         print -r -- "$line"
70                                         continue
71                                 fi
72                                 print -ru2 "E: Unexpected end of 'e' command" \
73                                     "at line $lno"
74                                 exit 2
75                         done
76                         (( lno += f[3] ))
77                         continue
78                 fi
79                 if [[ $line != 'c '* ]]; then
80                         print -r -- "$line"
81                         continue
82                 fi
83                 set -A f -- $line
84                 if (( (w = f[2]) > 32 || w < 1 )); then
85                         print -ru2 "E: width ${f[2]} not in 1‥32 at line $lno"
86                         exit 2
87                 fi
88                 if (( w <= 8 )); then
89                         adds=000000
90                 elif (( w <= 16 )); then
91                         adds=0000
92                 elif (( w <= 24 )); then
93                         adds=00
94                 else
95                         adds=
96                 fi
97                 (( shiftbits = 32 - w ))
98                 (( uw = 2 + w ))
99                 IFS=:
100                 set -A bmp -- ${f[3]}
101                 IFS=$' \t\n'
102                 f[0]=e
103                 f[3]=${#bmp[*]}
104                 print -r -- "${f[*]}"
105                 chl=0
106                 for ch in "${bmp[@]}"; do
107                         (( ++chl ))
108                         if [[ $ch != +([0-9A-F]) ]]; then
109                                 print -ru2 "E: char '$ch' at #$chl in line $lno not hex"
110                                 exit 2
111                         fi
112                         ch=$ch$adds
113                         if (( ${#ch} != 8 )); then
114                                 print -ru2 "E: char '$ch' at #$chl in line $lno not valid"
115                                 exit 2
116                         fi
117                         typeset -Uui2 -Z$uw bbin=16#$ch
118                         (( bbin >>= shiftbits ))
119                         b=${bbin#2#}
120                         b=${b//0/${BITv[0]}}
121                         b=${b//1/${BITv[1]}}
122                         print -r -- $b${BITv[2]}
123                 done
124         done
125         exit 0
126 fi
127
128 Fdef=           # currently valid 'd' line
129 set -A Fhead    # lines of file header, including comments intersparsed
130 set -A Fprop    # lines of file properties, same
131 set -A Gprop    # glyph property line (from Fdef), per glyph
132 set -A Gdata    # glyph data line, per glyph
133 set -A Gcomm    # glyph comments (if any) as string, per glyph
134 set -A Fcomm    # lines of comments at end of file
135
136 state=0
137
138 function parse_bdfc_file {
139         local last
140
141         set -A last
142         while IFS= read -r line; do
143                 (( ++lno ))
144                 if [[ $line = C ]]; then
145                         Fprop+=("${last[@]}")
146                         state=1
147                         return
148                 elif [[ $line = '=bdfc 1' ]]; then
149                         set -A hFBB
150                         continue
151                 fi
152                 last+=("$line")
153                 case $line {
154                 (\'|\'\ *)
155                         continue
156                         ;;
157                 (hFONTBOUNDINGBOX\ +([0-9])\ +([0-9])\ +([0-9-])\ +([0-9-]))
158                         set -A hFBB -- $line
159                         Fhead+=("${last[@]}")
160                         ;;
161                 (h*)
162                         Fhead+=("${last[@]}")
163                         ;;
164                 (p*)
165                         Fprop+=("${last[@]}")
166                         ;;
167                 (*)
168                         print -ru2 "E: invalid line $lno: '$line'"
169                         exit 2
170                         ;;
171                 }
172                 set -A last
173         done
174         Fprop+=("${last[@]}")
175         (( chkpad )) && if [[ -z $hFBB ]]; then
176                 print -ru2 "E: missing FONTBOUNDINGBOX header"
177                 exit 2
178         fi
179         state=2
180 }
181
182 function parse_bdfc_edit {
183         local w shiftbits uw line r i
184
185         if (( (w = f[2]) <= 8 )); then
186                 (( shiftbits = 8 - w ))
187                 (( uw = 5 ))
188         elif (( w <= 16 )); then
189                 (( shiftbits = 16 - w ))
190                 (( uw = 7 ))
191         elif (( w <= 24 )); then
192                 (( shiftbits = 24 - w ))
193                 (( uw = 9 ))
194         else
195                 (( shiftbits = 32 - w ))
196                 (( uw = 11 ))
197         fi
198
199         if (( (i = f[3]) < 1 || i > 999 )); then
200                 print -ru2 "E: nonsensical number of lines '${f[3]}' in" \
201                     "line $lno, U+${ch#16#}"
202                 exit 2
203         fi
204
205         while (( i-- )); do
206                 if ! IFS= read -r line; then
207                         print -ru2 "E: Unexpected end of 'e' command" \
208                             "at line $lno, U+${ch#16#}"
209                         exit 2
210                 fi
211                 (( ++lno ))
212                 linx=${line// /.}
213                 linx=${linx//䷀/#}
214                 linx=${linx//▌/|}
215                 linx=${linx//[ .]/0}
216                 linx=${linx//[#*]/1}
217                 if [[ $linx != +([01])'|' || ${#linx} != $((w + 1)) ]]; then
218                         print -ru2 "E: U+${ch#16#} (line $lno) bitmap line" \
219                             $((f[3] - i)) "invalid: '$line'"
220                         exit 2
221                 fi
222                 linx=${linx%'|'}
223                 typeset -Uui16 -Z$uw bhex=2#$linx
224                 (( bhex <<= shiftbits ))
225                 r+=${bhex#16#}:
226         done
227         f[3]=${r%:}
228         f[0]=c
229 }
230
231 function parse_bdfc_glyph {
232         local last
233
234         set -A last
235         while IFS= read -r line; do
236                 (( ++lno ))
237                 if [[ $line = . ]]; then
238                         Fcomm+=("${last[@]}")
239                         state=0
240                         return
241                 fi
242                 if [[ $line = \' || $line = "' "* ]]; then
243                         last+=("$line")
244                         continue
245                 fi
246                 set -A f -- $line
247                 if [[ ${f[0]} = d ]]; then
248                         Fdef="${f[*]}"
249                         (( chkpad )) && if [[ ${f[5]},${f[6]} != ${hFBB[3]},${hFBB[4]} ]]; then
250                                 print -ru2 "E: d line $lno does not match FONTBOUNDINGBOX … ${hFBB[3]} ${hFBB[4]}"
251                                 exit 2
252                         fi
253                         continue
254                 fi
255                 if [[ ${f[0]} != [ce] ]]; then
256                         print -ru2 "E: invalid line $lno: '$line'"
257                         exit 2
258                 fi
259                 if [[ $Fdef != 'd '* ]]; then
260                         print -ru2 "E: char at line $lno without defaults set"
261                         exit 2
262                 fi
263                 if [[ ${f[1]} != [0-9A-F][0-9A-F][0-9A-F][0-9A-F] ]]; then
264                         print -ru2 "E: invalid encoding '${f[1]}' at line $lno"
265                         exit 2
266                 fi
267                 typeset -Uui16 -Z7 ch=16#${f[1]}
268                 if (( ${#f[*]} < 4 || ${#f[*]} > 5 )); then
269                         print -ru2 "E: invalid number of fields on line $lno" \
270                             "at U+${ch#16#}: ${#f[*]}: '$line'"
271                         exit 2
272                 fi
273                 if (( f[2] < 1 || f[2] > 32 )); then
274                         print -ru2 "E: width ${f[2]} not in 1‥32 at line $lno"
275                         exit 2
276                 fi
277                 (( chkpad )) && if [[ ${f[2]} != ${hFBB[1]} ]]; then
278                         print -ru2 "E: c line $lno width ${f[2]} does not match FONTBOUNDINGBOX ${hFBB[1]}"
279                         exit 2
280                 fi
281                 [[ ${f[4]} = "uni${ch#16#}" ]] && unset f[4]
282                 if [[ ${f[0]} = e ]]; then
283                         parse_bdfc_edit
284                 else
285                         if (( f[2] <= 8 )); then
286                                 x='+([0-9A-F][0-9A-F]:)'
287                         elif (( f[2] <= 16 )); then
288                                 x='+([0-9A-F][0-9A-F][0-9A-F][0-9A-F]:)'
289                         elif (( f[2] <= 24 )); then
290                                 x='+([0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F]:)'
291                         else
292                                 x='+([0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F]:)'
293                         fi
294                         if eval [[ '${f[3]}:' != "$x" ]]; then
295                                 print -ru2 "E: invalid hex encoding for" \
296                                     "U+${ch#16#}, line $lno: '${f[3]}'"
297                                 exit 2
298                         fi
299                 fi
300                 if (( chkpad )); then
301                         x=${f[3]//[!:]}
302                         if (( (${#x} + 1) != hFBB[2] )); then
303                                 print -ru2 "E: c line $lno height $((${#x} + 1)) does not match FONTBOUNDINGBOX ${hFBB[2]}"
304                                 exit 2
305                         fi
306                 fi
307                 Gdata[ch]="${f[*]}"
308                 for line in "${last[@]}"; do
309                         Gcomm[ch]+=$line$'\n'
310                 done
311                 set -A last
312                 Gprop[ch]=$Fdef
313         done
314         Fcomm+=("${last[@]}")
315         state=2
316 }
317
318 function parse_bdfc {
319         while :; do
320                 case $state {
321                 (0) parse_bdfc_file ;;
322                 (1) parse_bdfc_glyph ;;
323                 (2) return 0 ;;
324                 }
325         done
326         print -ru2 "E: internal error (at line $lno), shouldn't happen"
327         exit 255
328 }
329
330 function parse_bdf {
331         set -A hFBB
332         while IFS= read -r line; do
333                 (( ++lno ))
334                 case $line {
335                 (COMMENT)
336                         Fhead+=("'")
337                         ;;
338                 (COMMENT@([      ])*)
339                         Fhead+=("' ${line#COMMENT[       ]}")
340                         ;;
341                 (STARTPROPERTIES\ +([0-9]))
342                         break
343                         ;;
344                 (FONTBOUNDINGBOX\ +([0-9])\ +([0-9])\ +([0-9-])\ +([0-9-]))
345                         set -A hFBB -- $line
346                         Fhead+=("h$line")
347                         ;;
348                 (*)
349                         Fhead+=("h$line")
350                         ;;
351                 }
352         done
353         (( chkpad )) && if [[ -z $hFBB ]]; then
354                 print -ru2 "E: missing FONTBOUNDINGBOX header"
355                 exit 2
356         fi
357         set -A f -- $line
358         numprop=${f[1]}
359         while IFS= read -r line; do
360                 (( ++lno ))
361                 case $line {
362                 (COMMENT)
363                         Fprop+=("'")
364                         ;;
365                 (COMMENT@([      ])*)
366                         Fprop+=("' ${line#COMMENT[       ]}")
367                         ;;
368                 (ENDPROPERTIES)
369                         break
370                         ;;
371                 (*)
372                         Fprop+=("p$line")
373                         let --numprop
374                         ;;
375                 }
376         done
377         if (( numprop )); then
378                 print -ru2 "E: expected ${f[1]} properties, got" \
379                     "$((f[1] - numprop)) in line $lno"
380                 exit 2
381         fi
382         while IFS= read -r line; do
383                 (( ++lno ))
384                 case $line {
385                 (COMMENT)
386                         Fprop+=("'")
387                         ;;
388                 (COMMENT@([      ])*)
389                         Fprop+=("' ${line#COMMENT[       ]}")
390                         ;;
391                 (CHARS\ +([0-9]))
392                         break
393                         ;;
394                 (*)
395                         print -ru2 "E: expected CHARS not '$line' in line $lno"
396                         exit 2
397                         ;;
398                 }
399         done
400         set -A f -- $line
401         numchar=${f[1]}
402         set -A cc
403         set -A cn
404         set -A ce
405         set -A cs
406         set -A cd
407         set -A cb
408         while IFS= read -r line; do
409                 (( ++lno ))
410                 case $line {
411                 (COMMENT)
412                         cc+=("'")
413                         ;;
414                 (COMMENT@([      ])*)
415                         cc+=("' ${line#COMMENT[  ]}")
416                         ;;
417                 (STARTCHAR\ *)
418                         set -A cn -- $line
419                         ;;
420                 (ENCODING\ +([0-9]))
421                         set -A ce -- $line
422                         ;;
423                 (SWIDTH\ +([0-9-])\ +([0-9-]))
424                         set -A cs -- $line
425                         ;;
426                 (DWIDTH\ +([0-9-])\ +([0-9-]))
427                         set -A cd -- $line
428                         ;;
429                 (BBX\ +([0-9])\ +([0-9])\ +([0-9-])\ +([0-9-]))
430                         set -A cb -- $line
431                         (( chkpad )) && if [[ ${cb[1]},${cb[2]},${cb[3]},${cb[4]} != ${hFBB[1]},${hFBB[2]},${hFBB[3]},${hFBB[4]} ]]; then
432                                 print -ru2 "E: BBX in line $lno does not match FONTBOUNDINGBOX ${hFBB[1]} ${hFBB[2]} ${hFBB[3]} ${hFBB[4]}"
433                                 exit 2
434                         fi
435                         ;;
436                 (BITMAP)
437                         if [[ -z $cn ]]; then
438                                 print -ru2 "E: missing STARTCHAR in line $lno"
439                                 exit 2
440                         fi
441                         if [[ -z $ce ]]; then
442                                 print -ru2 "E: missing ENCODING in line $lno"
443                                 exit 2
444                         fi
445                         if [[ -z $cs ]]; then
446                                 print -ru2 "E: missing SWIDTH in line $lno"
447                                 exit 2
448                         fi
449                         if [[ -z $cd ]]; then
450                                 print -ru2 "E: missing DWIDTH in line $lno"
451                                 exit 2
452                         fi
453                         if [[ -z $cb ]]; then
454                                 print -ru2 "E: missing BBX in line $lno"
455                                 exit 2
456                         fi
457                         typeset -Uui16 -Z7 ch=10#${ce[1]}
458                         if (( ch < 0 || ch > 0xFFFF )); then
459                                 print -ru2 "E: encoding ${ce[1]} out of" \
460                                     "bounds in line $lno"
461                                 exit 2
462                         fi
463                         Gprop[ch]="d ${cs[1]} ${cs[2]} ${cd[1]} ${cd[2]} ${cb[3]} ${cb[4]}"
464                         set -A f c ${ch#16#} ${cb[1]} - ${cn[1]}
465                         [[ ${f[4]} = "uni${ch#16#}" ]] && unset f[4]
466                         if (( f[2] <= 8 )); then
467                                 ck='[0-9A-F][0-9A-F]'
468                         elif (( f[2] <= 16 )); then
469                                 ck='[0-9A-F][0-9A-F][0-9A-F][0-9A-F]'
470                         elif (( f[2] <= 24 )); then
471                                 ck='[0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F]'
472                         else
473                                 ck='[0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F]'
474                         fi
475                         if (( (numlines = cb[2]) )); then
476                                 bmps=
477                                 typeset -u linu
478                                 while IFS= read -r linx; do
479                                         (( ++lno ))
480                                         linu=$linx
481                                         while eval [[ '$linu' != "$ck" ]]; do
482                                                 if [[ $linu = *00 ]]; then
483                                                         linu=${linu%00}
484                                                         continue
485                                                 fi
486                                                 print -ru2 "E: invalid hex encoding" \
487                                                     "for U+${ch#16#} (dec. $((ch)))" \
488                                                     "on line $lno: '$linx'"
489                                                 exit 2
490                                         done
491                                         bmps+=$linu:
492                                         (( --numlines )) || break
493                                 done
494                                 f[3]=${bmps%:}
495                         else
496                                 f[2]=1
497                                 f[3]=00
498                         fi
499                         if ! IFS= read -r line || [[ $line != ENDCHAR ]]; then
500                                 print -ru2 "E: expected ENDCHAR after line $lno"
501                                 exit 2
502                         fi
503                         (( ++lno ))
504                         Gdata[ch]="${f[*]}"
505                         [[ -n $cc ]] && for line in "${cc[@]}"; do
506                                 Gcomm[ch]+=$line$'\n'
507                         done
508                         set -A cc
509                         set -A cn
510                         set -A ce
511                         set -A cs
512                         set -A cd
513                         set -A cb
514                         ;;
515                 (ENDFONT)
516                         break
517                         ;;
518                 (*)
519                         print -ru2 "E: unexpected '$line' in line $lno"
520                         exit 2
521                         ;;
522                 }
523         done
524         Fcomm+=("${cc[@]}")
525         for line in "${cn[*]}" "${ce[*]}" "${cs[*]}" "${cd[*]}" "${cb[*]}"; do
526                 [[ -n $line ]] || continue
527                 print -ru2 "E: unexpected '$line' between last char and ENDFONT"
528                 exit 2
529         done
530         if (( numchar != ${#Gdata[*]} )); then
531                 print -ru2 "E: expected $numchar glyphs, got ${#Gdata[*]}"
532                 exit 2
533         fi
534         while IFS= read -r line; do
535                 (( ++lno ))
536                 case $line {
537                 (COMMENT)
538                         Fcomm+=("'")
539                         ;;
540                 (COMMENT@([      ])*)
541                         Fcomm+=("' ${line#COMMENT[       ]}")
542                         ;;
543                 (*)
544                         print -ru2 "E: unexpected '$line' past ENDFONT" \
545                             "in line $lno"
546                         exit 2
547                         ;;
548                 }
549         done
550 }
551
552 if [[ $mode = c ]]; then
553         if ! IFS= read -r line; then
554                 print -ru2 "E: read error at BOF"
555                 exit 2
556         fi
557         lno=1
558         if [[ $line = 'STARTFONT 2.1' ]]; then
559                 parse_bdf
560         elif [[ $line = '=bdfc 1' ]]; then
561                 parse_bdfc
562         else
563                 print -ru2 "E: not BDF or bdfc at BOF: '$line'"
564                 exit 2
565         fi
566
567         # write .bdfc stream
568
569         for line in '=bdfc 1' "${Fhead[@]}" "${Fprop[@]}"; do
570                 print -r -- "$line"
571         done
572         print C
573         Fdef=
574         for x in ${!Gdata[*]}; do
575                 if [[ ${Gprop[x]} != "$Fdef" ]]; then
576                         Fdef=${Gprop[x]}
577                         print -r -- $Fdef
578                 fi
579                 print -r -- "${Gcomm[x]}${Gdata[x]}"
580         done
581         for line in "${Fcomm[@]}"; do
582                 print -r -- "$line"
583         done
584         print .
585         exit 0
586 fi
587
588 if [[ $mode = +e ]]; then
589         while IFS= read -r line; do
590                 (( ++lno ))
591                 if [[ $line = \' || $line = "' "* ]]; then
592                         print -r -- "$line"
593                         continue
594                 fi
595                 set -A f -- $line
596                 if [[ ${f[0]} != [ce] ]]; then
597                         print -ru2 "E: invalid line $lno: '$line'"
598                         exit 2
599                 fi
600                 if [[ ${f[1]} != [0-9A-F][0-9A-F][0-9A-F][0-9A-F] ]]; then
601                         print -ru2 "E: invalid encoding '${f[1]}' at line $lno"
602                         exit 2
603                 fi
604                 typeset -Uui16 -Z7 ch=16#${f[1]}
605                 if (( ${#f[*]} < 4 || ${#f[*]} > 5 )); then
606                         print -ru2 "E: invalid number of fields on line $lno" \
607                             "at U+${ch#16#}: ${#f[*]}: '$line'"
608                         exit 2
609                 fi
610                 if (( f[2] < 1 || f[2] > 32 )); then
611                         print -ru2 "E: width ${f[2]} not in 1‥32 at line $lno"
612                         exit 2
613                 fi
614                 [[ ${f[4]} = "uni${ch#16#}" ]] && unset f[4]
615                 if [[ ${f[0]} = e ]]; then
616                         parse_bdfc_edit
617                 else
618                         if (( f[2] <= 8 )); then
619                                 x='+([0-9A-F][0-9A-F]:)'
620                         elif (( f[2] <= 16 )); then
621                                 x='+([0-9A-F][0-9A-F][0-9A-F][0-9A-F]:)'
622                         elif (( f[2] <= 24 )); then
623                                 x='+([0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F]:)'
624                         else
625                                 x='+([0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F]:)'
626                         fi
627                         if eval [[ '${f[3]}:' != "$x" ]]; then
628                                 print -ru2 "E: invalid hex encoding for" \
629                                     "U+${ch#16#}, line $lno: '${f[3]}'"
630                                 exit 2
631                         fi
632                 fi
633                 print -r -- "${f[@]}"
634         done
635         exit 0
636 fi
637
638 if [[ $mode != d ]]; then
639         print -ru2 "E: cannot happen (control flow issue in ${0##*/}:$LINENO)"
640         exit 255
641 fi
642
643 if ! IFS= read -r line; then
644         print -ru2 "E: read error at BOF"
645         exit 2
646 fi
647 lno=1
648
649 if (( ufast )); then
650         if [[ $line != '=bdfc 1' ]]; then
651                 print -ru2 "E: not bdfc at BOF: '$line'"
652                 exit 2
653         fi
654         if ! T=$(mktemp /tmp/bdfctool.XXXXXXXXXX); then
655                 print -u2 E: cannot make temporary file
656                 exit 4
657         fi
658         # quickly parse bdfc header
659         set -A last
660         while IFS= read -r line; do
661                 [[ $line = C ]] && break
662                 last+=("$line")
663                 [[ $line = \' || $line = "' "* ]] && continue
664                 if [[ $line = h* ]]; then
665                         Fhead+=("${last[@]}")
666                 else
667                         Fprop+=("${last[@]}")
668                 fi
669                 set -A last
670         done
671         Fprop+=("${last[@]}")
672 elif [[ $line = 'STARTFONT 2.1' ]]; then
673         # parse entire BDF file into memory
674         parse_bdf
675 elif [[ $line = '=bdfc 1' ]]; then
676         # parse entire bdfc file into memory
677         parse_bdfc
678 else
679         print -ru2 "E: not BDF or bdfc at BOF: '$line'"
680         exit 2
681 fi
682
683 # analyse data for BDF
684 numprop=0
685 for line in "${Fprop[@]}"; do
686         [[ $line = p* ]] && let ++numprop
687 done
688 (( ufast )) || numchar=${#Gdata[*]}
689
690 # handle diverging and non-ufast output formats
691 case $oform {
692 (3)
693         # little-endian .gdf
694         function out_int32 {
695                 typeset -Uui16 value=$1
696                 typeset -Uui8 ba bb bc bd
697
698                 (( bd = (value >> 24) & 0xFF ))
699                 (( bc = (value >> 16) & 0xFF ))
700                 (( bb = (value >> 8) & 0xFF ))
701                 (( ba = value & 0xFF ))
702                 print -n "\\0${ba#8#}\\0${bb#8#}\\0${bc#8#}\\0${bd#8#}"
703         }
704         ;|
705 (4)
706         # big-endian .gdf
707         function out_int32 {
708                 typeset -Uui16 value=$1
709                 typeset -Uui8 ba bb bc bd
710
711                 (( ba = (value >> 24) & 0xFF ))
712                 (( bb = (value >> 16) & 0xFF ))
713                 (( bc = (value >> 8) & 0xFF ))
714                 (( bd = value & 0xFF ))
715                 print -n "\\0${ba#8#}\\0${bb#8#}\\0${bc#8#}\\0${bd#8#}"
716         }
717         ;|
718 (3|4)
719         # do some input analysis for .gdf output
720         if [[ -z $hFBB ]]; then
721                 print -ru2 "E: missing FONTBOUNDINGBOX header"
722                 exit 2
723         fi
724         set -A f -- ${!Gdata[*]}
725         typeset -i firstch=${f[0]} lastch=${f[${#f[*]} - 1]}
726         nullch=
727         x=$((hFBB[1] * hFBB[2]))
728         while (( x-- )); do
729                 nullch+=\\0
730         done
731         if (( hFBB[1] <= 8 )); then
732                 adds=000000
733         elif (( hFBB[1] <= 16 )); then
734                 adds=0000
735         elif (( hFBB[1] <= 24 )); then
736                 adds=00
737         else
738                 adds=
739         fi
740         # write .gdf stream
741         out_int32 $((# lastch - firstch + 1))
742         out_int32 $((# firstch))
743         out_int32 $((# hFBB[1]))
744         out_int32 $((# hFBB[2]))
745         typeset -i curch
746         ((# curch = firstch - 1 ))
747         while ((# ++curch <= lastch )); do
748                 set -A f -- ${Gdata[curch]}
749                 if [[ -z $f ]]; then
750                         print -n "$nullch"
751                         continue
752                 fi
753                 IFS=:
754                 set -A bmp -- ${f[3]}
755                 IFS=$' \t\n'
756                 s=
757                 for line in "${bmp[@]}"; do
758                         typeset -Uui2 bbin=16#$line$adds
759                         x=${hFBB[1]}
760                         while (( x-- )); do
761                                 s+=\\0$(( (bbin & 0x80000000) ? 377 : 0 ))
762                                 (( bbin <<= 1 ))
763                         done
764                 done
765                 print -n "$s"
766         done
767         exit 0
768         ;;
769 }
770
771 # write BDF stream
772 print 'STARTFONT 2.1'
773 for line in "${Fhead[@]}"; do
774         if [[ $line = h* ]]; then
775                 print -r -- "${line#h}"
776         else
777                 print -r -- "COMMENT${line#\'}"
778         fi
779 done
780 set -A last
781 print STARTPROPERTIES $((numprop))
782 for line in "${Fprop[@]}"; do
783         if [[ $line = p* ]]; then
784                 last+=("${line#p}")
785         else
786                 last+=("COMMENT${line#\'}")
787                 continue
788         fi
789         for line in "${last[@]}"; do
790                 print -r -- "$line"
791         done
792         set -A last
793 done
794 print ENDPROPERTIES
795 for line in "${last[@]}"; do
796         print -r -- "$line"
797 done
798 if (( ufast )); then
799         numchar=0
800         # directly transform font data
801         set -A last
802         while IFS= read -r line; do
803                 [[ $line = . ]] && break
804                 if [[ $line = \' || $line = "' "* ]]; then
805                         last+=("$line")
806                         continue
807                 fi
808                 set -A f -- $line
809                 if [[ ${f[0]} = d ]]; then
810                         set -A xprop -- $line
811                         continue
812                 fi
813                 typeset -Uui16 -Z7 ch=16#${f[1]}
814                 for line in "${last[@]}"; do
815                         print -r -- "COMMENT${line#\'}"
816                 done
817                 set -A last
818                 IFS=:
819                 set -A bmp -- ${f[3]}
820                 IFS=$' \t\n'
821                 cat <<-EOF
822                         STARTCHAR ${f[4]:-uni${ch#16#}}
823                         ENCODING $((ch))
824                         SWIDTH ${xprop[1]} ${xprop[2]}
825                         DWIDTH ${xprop[3]} ${xprop[4]}
826                         BBX ${f[2]} ${#bmp[*]} ${xprop[5]} ${xprop[6]}
827                         BITMAP
828                 EOF
829                 for line in "${bmp[@]}"; do
830                         print $line
831                 done
832                 print ENDCHAR
833                 let ++numchar
834         done >"$T"
835         Fcomm+=("${last[@]}")
836         print CHARS $((numchar))
837         cat "$T"
838         rm -f "$T"
839 else
840         print CHARS $((numchar))
841         for x in ${!Gdata[*]}; do
842                 IFS=$'\n'
843                 set -A xcomm -- ${Gcomm[x]}
844                 IFS=$' \t\n'
845                 for line in "${xcomm[@]}"; do
846                         print -r -- "COMMENT${line#\'}"
847                 done
848                 set -A xprop -- ${Gprop[x]}
849                 set -A f -- ${Gdata[x]}
850                 IFS=:
851                 set -A bmp -- ${f[3]}
852                 IFS=$' \t\n'
853                 typeset -Uui16 -Z7 ch=16#${f[1]}
854                 cat <<-EOF
855                         STARTCHAR ${f[4]:-uni${ch#16#}}
856                         ENCODING $((ch))
857                         SWIDTH ${xprop[1]} ${xprop[2]}
858                         DWIDTH ${xprop[3]} ${xprop[4]}
859                         BBX ${f[2]} ${#bmp[*]} ${xprop[5]} ${xprop[6]}
860                         BITMAP
861                 EOF
862                 for line in "${bmp[@]}"; do
863                         print $line
864                 done
865                 print ENDCHAR
866         done
867 fi
868 for line in "${Fcomm[@]}"; do
869         print -r -- "COMMENT${line#\'}"
870 done
871 print ENDFONT
872 exit 0