2 # $MirOS: X11/extras/bdfctool/bdfctool.sh,v 1.12 2013/05/17 21:51:40 tg Exp $
4 # Copyright © 2012, 2013
5 # Thorsten Glaser <tg@mirbsd.org>
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.
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.
26 while getopts "acdeFh" ch; do
30 (c|d|e|+e) mode=$ch ;;
40 if [[ $mode = ?(h) ]] || [[ $mode != e && $uascii != -1 ]] || \
41 [[ $mode != d && $ufast != 0 ]]; then
42 print -ru2 "Usage: ${0##*/} -c | -d [-F] | -e [-a] | +e"
43 [[ $mode = h ]]; exit $?
47 if [[ $mode = e ]]; then
48 if (( uascii == 1 )); then
49 set -A BITv -- '.' '#' '|'
51 set -A BITv -- ' ' '䷀' '▌'
53 while IFS= read -r line; do
55 if [[ $line = 'e '* ]]; then
60 if IFS= read -r line; then
64 print -ru2 "E: Unexpected end of 'e' command" \
71 if [[ $line != 'c '* ]]; then
76 if (( (w = f[2]) > 32 || w < 1 )); then
77 print -ru2 "E: width ${f[2]} not in 1‥32 at line $lno"
82 elif (( w <= 16 )); then
84 elif (( w <= 24 )); then
89 (( shiftbits = 32 - w ))
98 for ch in "${bmp[@]}"; do
100 if [[ $ch != +([0-9A-F]) ]]; then
101 print -ru2 "E: char '$ch' at #$chl in line $lno not hex"
105 if (( ${#ch} != 8 )); then
106 print -ru2 "E: char '$ch' at #$chl in line $lno not valid"
109 typeset -Uui2 -Z$uw bbin=16#$ch
110 (( bbin >>= shiftbits ))
114 print -r -- $b${BITv[2]}
120 Fdef= # currently valid 'd' line
121 set -A Fhead # lines of file header, including comments intersparsed
122 set -A Fprop # lines of file properties, same
123 set -A Gprop # glyph property line (from Fdef), per glyph
124 set -A Gdata # glyph data line, per glyph
125 set -A Gcomm # glyph comments (if any) as string, per glyph
126 set -A Fcomm # lines of comments at end of file
130 function parse_bdfc_file {
134 while IFS= read -r line; do
136 if [[ $line = C ]]; then
137 Fprop+=("${last[@]}")
140 elif [[ $line = '=bdfc 1' ]]; then
144 [[ $line = \' || $line = "' "* ]] && continue
145 if [[ $line = h* ]]; then
146 Fhead+=("${last[@]}")
147 elif [[ $line = p* ]]; then
148 Fprop+=("${last[@]}")
150 print -ru2 "E: invalid line #$lno: '$line'"
155 Fprop+=("${last[@]}")
159 function parse_bdfc_edit {
160 local w shiftbits uw line r i
162 if (( (w = f[2]) <= 8 )); then
163 (( shiftbits = 8 - w ))
165 elif (( w <= 16 )); then
166 (( shiftbits = 16 - w ))
168 elif (( w <= 24 )); then
169 (( shiftbits = 24 - w ))
172 (( shiftbits = 32 - w ))
176 if (( (i = f[3]) < 1 || i > 999 )); then
177 print -ru2 "E: nonsensical number of lines '${f[3]}' in" \
178 "line $lno, U+${ch#16#}"
183 if ! IFS= read -r line; then
184 print -ru2 "E: Unexpected end of 'e' command" \
185 "at line $lno, U+${ch#16#}"
194 if [[ $linx != +([01])'|' || ${#linx} != $((w + 1)) ]]; then
195 print -ru2 "E: U+${ch#16#} (line #$lno) bitmap line" \
196 $((f[3] - i)) "invalid: '$line'"
200 typeset -Uui16 -Z$uw bhex=2#$linx
201 (( bhex <<= shiftbits ))
208 function parse_bdfc_glyph {
212 while IFS= read -r line; do
214 if [[ $line = . ]]; then
215 Fcomm+=("${last[@]}")
219 if [[ $line = \' || $line = "' "* ]]; then
224 if [[ ${f[0]} = d ]]; then
228 if [[ ${f[0]} != [ce] ]]; then
229 print -ru2 "E: invalid line #$lno: '$line'"
232 if [[ $Fdef != 'd '* ]]; then
233 print -ru2 "E: char at line $lno without defaults set"
236 if [[ ${f[1]} != [0-9A-F][0-9A-F][0-9A-F][0-9A-F] ]]; then
237 print -ru2 "E: invalid encoding '${f[1]}' at line $lno"
240 typeset -Uui16 -Z7 ch=16#${f[1]}
241 if (( ${#f[*]} < 4 || ${#f[*]} > 5 )); then
242 print -ru2 "E: invalid number of fields on line $lno" \
243 "at U+${ch#16#}: ${#f[*]}: '$line'"
246 if (( f[2] < 1 || f[2] > 32 )); then
247 print -ru2 "E: width ${f[2]} not in 1‥32 at line $lno"
250 [[ ${f[4]} = "uni${ch#16#}" ]] && unset f[4]
251 if [[ ${f[0]} = e ]]; then
254 if (( f[2] <= 8 )); then
255 x='+([0-9A-F][0-9A-F]:)'
256 elif (( f[2] <= 16 )); then
257 x='+([0-9A-F][0-9A-F][0-9A-F][0-9A-F]:)'
258 elif (( f[2] <= 24 )); then
259 x='+([0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F]:)'
261 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]:)'
263 if eval [[ '${f[3]}:' != "$x" ]]; then
264 print -ru2 "E: invalid hex encoding for" \
265 "U+${ch#16#}, line $lno: '${f[3]}'"
270 for line in "${last[@]}"; do
271 Gcomm[ch]+=$line$'\n'
276 Fcomm+=("${last[@]}")
280 function parse_bdfc {
283 (0) parse_bdfc_file ;;
284 (1) parse_bdfc_glyph ;;
288 print -ru2 "E: internal error (at line $lno), shouldn't happen"
293 while IFS= read -r line; do
300 Fhead+=("' ${line#COMMENT[ ]}")
302 (STARTPROPERTIES\ +([0-9]))
312 while IFS= read -r line; do
319 Fprop+=("' ${line#COMMENT[ ]}")
330 if (( numprop )); then
331 print -ru2 "E: expected ${f[1]} properties, got" \
332 "$((f[1] - numprop)) in line $lno"
335 while IFS= read -r line; do
342 Fprop+=("' ${line#COMMENT[ ]}")
348 print -ru2 "E: expected CHARS not '$line' in line $lno"
361 while IFS= read -r line; do
368 cc+=("' ${line#COMMENT[ ]}")
376 (SWIDTH\ +([0-9-])\ +([0-9-]))
379 (DWIDTH\ +([0-9-])\ +([0-9-]))
382 (BBX\ +([0-9])\ +([0-9])\ +([0-9-])\ +([0-9-]))
386 if [[ -z $cn ]]; then
387 print -ru2 "E: missing STARTCHAR in line $lno"
390 if [[ -z $ce ]]; then
391 print -ru2 "E: missing ENCODING in line $lno"
394 if [[ -z $cs ]]; then
395 print -ru2 "E: missing SWIDTH in line $lno"
398 if [[ -z $cd ]]; then
399 print -ru2 "E: missing DWIDTH in line $lno"
402 if [[ -z $cb ]]; then
403 print -ru2 "E: missing BBX in line $lno"
406 typeset -Uui16 -Z7 ch=10#${ce[1]}
407 if (( ch < 0 || ch > 0xFFFF )); then
408 print -ru2 "E: encoding ${ce[1]} out of" \
409 "bounds in line $lno"
412 Gprop[ch]="d ${cs[1]} ${cs[2]} ${cd[1]} ${cd[2]} ${cb[3]} ${cb[4]}"
413 set -A f c ${ch#16#} ${cb[1]} - ${cn[1]}
414 [[ ${f[4]} = "uni${ch#16#}" ]] && unset f[4]
415 if (( f[2] <= 8 )); then
416 ck='[0-9A-F][0-9A-F]'
417 elif (( f[2] <= 16 )); then
418 ck='[0-9A-F][0-9A-F][0-9A-F][0-9A-F]'
419 elif (( f[2] <= 24 )); then
420 ck='[0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F]'
422 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]'
424 if (( (numlines = cb[2]) )); then
427 while IFS= read -r linu; do
429 if eval [[ '$linu' != "$ck" ]]; then
430 print -ru2 "E: invalid hex encoding" \
431 "for U+${ch#16#} (dec. $((ch)))" \
432 "on line $lno: '$linu'"
436 (( --numlines )) || break
443 if ! IFS= read -r line || [[ $line != ENDCHAR ]]; then
444 print -ru2 "E: expected ENDCHAR after line $lno"
449 [[ -n $cc ]] && for line in "${cc[@]}"; do
450 Gcomm[ch]+=$line$'\n'
463 print -ru2 "E: unexpected '$line' in line $lno"
469 for line in "${cn[*]}" "${ce[*]}" "${cs[*]}" "${cd[*]}" "${cb[*]}"; do
470 [[ -n $line ]] || continue
471 print -ru2 "E: unexpected '$line' between last char and ENDFONT"
474 if (( numchar != ${#Gdata[*]} )); then
475 print -ru2 "E: expected $numchar glyphs, got ${#Gdata[*]}"
478 while IFS= read -r line; do
485 Fcomm+=("' ${line#COMMENT[ ]}")
488 print -ru2 "E: unexpected '$line' past ENDFONT" \
496 if [[ $mode = c ]]; then
497 if ! IFS= read -r line; then
498 print -ru2 "E: read error at BOF"
502 if [[ $line = 'STARTFONT 2.1' ]]; then
504 elif [[ $line = '=bdfc 1' ]]; then
507 print -ru2 "E: not BDF or bdfc at BOF: '$line'"
513 for line in '=bdfc 1' "${Fhead[@]}" "${Fprop[@]}"; do
518 for x in ${!Gdata[*]}; do
519 if [[ ${Gprop[x]} != "$Fdef" ]]; then
523 print -r -- "${Gcomm[x]}${Gdata[x]}"
525 for line in "${Fcomm[@]}"; do
532 if [[ $mode = +e ]]; then
533 while IFS= read -r line; do
535 if [[ $line = \' || $line = "' "* ]]; then
540 if [[ ${f[0]} != [ce] ]]; then
541 print -ru2 "E: invalid line #$lno: '$line'"
544 if [[ ${f[1]} != [0-9A-F][0-9A-F][0-9A-F][0-9A-F] ]]; then
545 print -ru2 "E: invalid encoding '${f[1]}' at line $lno"
548 typeset -Uui16 -Z7 ch=16#${f[1]}
549 if (( ${#f[*]} < 4 || ${#f[*]} > 5 )); then
550 print -ru2 "E: invalid number of fields on line $lno" \
551 "at U+${ch#16#}: ${#f[*]}: '$line'"
554 if (( f[2] < 1 || f[2] > 32 )); then
555 print -ru2 "E: width ${f[2]} not in 1‥32 at line $lno"
558 [[ ${f[4]} = "uni${ch#16#}" ]] && unset f[4]
559 if [[ ${f[0]} = e ]]; then
562 if (( f[2] <= 8 )); then
563 x='+([0-9A-F][0-9A-F]:)'
564 elif (( f[2] <= 16 )); then
565 x='+([0-9A-F][0-9A-F][0-9A-F][0-9A-F]:)'
566 elif (( f[2] <= 24 )); then
567 x='+([0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F]:)'
569 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]:)'
571 if eval [[ '${f[3]}:' != "$x" ]]; then
572 print -ru2 "E: invalid hex encoding for" \
573 "U+${ch#16#}, line $lno: '${f[3]}'"
577 print -r -- "${f[@]}"
582 if [[ $mode != d ]]; then
583 print -ru2 "E: cannot happen (control flow issue in ${0##*/}:$LINENO)"
587 if ! IFS= read -r line; then
588 print -ru2 "E: read error at BOF"
592 if [[ $line != '=bdfc 1' ]]; then
593 print -ru2 "E: not bdfc at BOF: '$line'"
598 if ! T=$(mktemp /tmp/bdfctool.XXXXXXXXXX); then
599 print -u2 E: cannot make temporary file
602 # quickly parse bdfc header
604 while IFS= read -r line; do
605 [[ $line = C ]] && break
607 [[ $line = \' || $line = "' "* ]] && continue
608 if [[ $line = h* ]]; then
609 Fhead+=("${last[@]}")
611 Fprop+=("${last[@]}")
615 Fprop+=("${last[@]}")
617 # parse entire bdfc file into memory
621 # analyse data for BDF
623 for line in "${Fprop[@]}"; do
624 [[ $line = p* ]] && let ++numprop
626 (( ufast )) || numchar=${#Gdata[*]}
629 print 'STARTFONT 2.1'
630 for line in "${Fhead[@]}"; do
631 if [[ $line = h* ]]; then
632 print -r -- "${line#h}"
634 print -r -- "COMMENT${line#\'}"
638 print STARTPROPERTIES $((numprop))
639 for line in "${Fprop[@]}"; do
640 if [[ $line = p* ]]; then
643 last+=("COMMENT${line#\'}")
646 for line in "${last[@]}"; do
652 for line in "${last[@]}"; do
657 # directly transform font data
659 while IFS= read -r line; do
660 [[ $line = . ]] && break
661 if [[ $line = \' || $line = "' "* ]]; then
666 if [[ ${f[0]} = d ]]; then
667 set -A xprop -- $line
670 typeset -Uui16 -Z7 ch=16#${f[1]}
671 for line in "${last[@]}"; do
672 print -r -- "COMMENT${line#\'}"
676 set -A bmp -- ${f[3]}
679 STARTCHAR ${f[4]:-uni${ch#16#}}
681 SWIDTH ${xprop[1]} ${xprop[2]}
682 DWIDTH ${xprop[3]} ${xprop[4]}
683 BBX ${f[2]} ${#bmp[*]} ${xprop[5]} ${xprop[6]}
686 for line in "${bmp[@]}"; do
692 Fcomm+=("${last[@]}")
693 print CHARS $((numchar))
697 print CHARS $((numchar))
698 for x in ${!Gdata[*]}; do
700 set -A xcomm -- ${Gcomm[x]}
702 for line in "${xcomm[@]}"; do
703 print -r -- "COMMENT${line#\'}"
705 set -A xprop -- ${Gprop[x]}
706 set -A f -- ${Gdata[x]}
708 set -A bmp -- ${f[3]}
710 typeset -Uui16 -Z7 ch=16#${f[1]}
712 STARTCHAR ${f[4]:-uni${ch#16#}}
714 SWIDTH ${xprop[1]} ${xprop[2]}
715 DWIDTH ${xprop[3]} ${xprop[4]}
716 BBX ${f[2]} ${#bmp[*]} ${xprop[5]} ${xprop[6]}
719 for line in "${bmp[@]}"; do
725 for line in "${Fcomm[@]}"; do
726 print -r -- "COMMENT${line#\'}"