初歩のシェルスクリプトで遊ぶ[ぬかみそフォントの制作サポート(9)]

VSつきの文字を抜き出す

$ cat ivstest.txt 
ななかまど
辻󠄀
辻󠄀辻味噌󠄀味噌

$ cat ivstest.txt |./ttedit_20210327_02.sh --html
ななかまど
辻󠄀
辻󠄀辻味噌󠄀味噌

という感じに、異体字セレクタつきのテキストデータを入れると、異体字セレクタだけをHTMLの数値参照に変換したり、異体字セレクタつきの文字だけ抜き出したり、異体字セレクタつきの文字だけ反転表示したり、とできます。
不満の残るスクリプトだけれど、とりあえず無いよりはマシです。

TTEdit/OTEditがデフォルトで作るフォントは、今まで異体字セレクタは使えなかったはず、なんだけれども。OTEditの新しい、文字数の多いやつだと、IVSが使えるみたい。そのへんを扱うための、道具作りです。

#!/bin/bash
#!/usr/bin/zsh

# =======================================================================================
# ■ VSつきテキスト文字とVSなし文字を分離,VSを可視化,VSをHTML数値参照へ変換_[bash版]
# 
# ▼ 異体字セレクタ (U+E0100〜U+E01EF, VS17~VS256) 検出
# 
# iconvとodで16進数に変換表示して、「00 0e 01 00」〜「00 0e 01 ef」を探す方式
# 
# 
# ・1文字1行4バイト、
# ・VSは文字の直後に付加して1文字1行8バイト
# 
# 00 00 30 4b
# 00 00 30 7e
# 00 00 30 69
# 00 00 00 0d
# 00 00 00 0a
# 00 00 8f bb 00 0e 01 00
# 00 00 00 0d
# 00 00 00 0a
# 
# 
# 4バイトの行だけ抜き出せば、VSなしの文字のみ抜き出せる。
# 8バイトの行のみ抜き出せば、VSつきの文字のみ抜き出せる。
# 8バイトの行の後半4バイトに「&#x ;」を付加すると、数値参照に変換できる。
# 8バイトの行の前後にエスケープシーケンスを付加すると、色反転表示ができる。
# 
# 
# ▼ 入力
# stdinにテキストデータ
# 
# ▼ 出力
# stdout
# 
# 
# ▼ 必要なコマンド
# iconv
# od
# 
# =======================================================================================


#exitParm='0'
outputType='view'





case "${1}" in
( --base ) # VSが無い文字のみ抜き出す
outputType='base'

;;
( --vs )  # VSがついている文字のみ抜き出す
outputType='vs'

;;
( --html ) # VSをhtmlの数値参照に書き換える
outputType='html'

;;
( * )      # VSつきの文字を反転表示
outputType='view'

;;
esac


iconv -f utf8 -t utf32be |
od -tx1 -An -v -w4 |
{
while read Byte3 Byte2 Byte1 Byte0 ; do 

printf -v Byte0dec '%d' "0x${Byte0}"


if test "${Byte3}${Byte2}${Byte1}" = '000e01' &&
   test ${Byte0dec} -ge 0    &&
   test ${Byte0dec} -le 239  ; then

printf ' %s %s %s %s\n' ${Byte3} ${Byte2} ${Byte1} ${Byte0}

else

printf '\n %s %s %s %s' ${Byte3} ${Byte2} ${Byte1} ${Byte0}

fi

done 
echo

} | sed -e '/^$/d' | {

case "${outputType}" in
# ----------------------------------------------
( base )
# ----------------------------------------------
sed -n -e '
/^ .. .. .. ..$/{
p
}' |
while read B3 B2 B1 B0 ; do

  printf "\U${B3}${B2}${B1}${B0}"

done

;;
# ----------------------------------------------
( vs )
# ----------------------------------------------
sed -n -e '
/^ .. .. .. .. 00 0e 01 ..$/{
p
}' |
while read B7 B6 B5 B4 B3 B2 B1 B0 ; do

  printf "\U${B7}${B6}${B5}${B4}\U${B3}${B2}${B1}${B0}"

done
;;
# ----------------------------------------------
( html )
# ----------------------------------------------

while read B7 B6 B5 B4 B3 B2 B1 B0 ; do
if test "${B3}${B2}${B1}" = '000e01' ; then

printf "\U${B7}${B6}${B5}${B4}"
printf '&#x%s;' "e01${B0}"


else
printf "\U${B7}${B6}${B5}${B4}"

fi
done

;;
# ----------------------------------------------
( view )
# ----------------------------------------------

# 1行8バイトの行のみ、前後に文字反転エスケープシーケンスを
# \uで叩き込む
# \033[7m	 1b 5b 37 6d
# \033[0m	 1b 5b 30 6d


while read B7 B6 B5 B4 B3 B2 B1 B0 ; do
case "${B3}" in
(  '' )
  printf "\U${B7}${B6}${B5}${B4}"

;;
(  *  )
  printf '\u1b\u5b\u37\u6d'
  printf "\U${B7}${B6}${B5}${B4}\U${B3}${B2}${B1}${B0}"
  printf '\u1b\u5b\u30\u6d'
;;
esac
done

esac
}
#!/bin/sh
#!/usr/bin/yash
#!/usr/bin/ksh
#!/bin/bash

# =======================================================================================
# ■ VSつきテキスト文字とVSなし文字を分離,VSを可視化,VSをHTML数値参照へ変換[xxd不要版]
# 
# ▼ 異体字セレクタ (U+E0100〜U+E01EF, VS17~VS256) 検出
# iconvでutf32beに変換し、odでダンプリスト表示して、
# 「00 0e 01 00」〜「00 0e 01 ef」を探す方式、の一種。
# 
# ・「xxd -r -p」を使わずに済ませるため、odコマンドでは8進数に変換する。
#    8進数ならprintfでバイナリを作成できるため。
# 
# 
# ・1文字1行4バイト、
# ・VSは文字の直後に付加して1文字1行8バイト
# 
# 00 00 30 4b
# 00 00 30 7e
# 00 00 30 69
# 00 00 00 0d
# 00 00 00 0a
# 00 00 8f bb 00 0e 01 00
# 00 00 00 0d
# 00 00 00 0a
# 
# 
# 4バイトの行だけ抜き出せば、VSなしの文字のみ抜き出せる。
# 8バイトの行のみ抜き出せば、VSつきの文字のみ抜き出せる。
# 8バイトの行の後半4バイトに「&#x ;」を付加すると、数値参照に変換できる。
# 8バイトの行の前後にエスケープシーケンスを付加すると、色反転表示ができる。
# 
# 
# ▼ 入力
# stdinにテキストデータ
# 
# ▼ 出力
# stdout
# 
# 
# ▼ 必要なコマンド
# iconv
# od
# 
# =======================================================================================


#exitParm='0'
outputType='view'


# 動作切り替え指定
case "${1}" in
( --base ) # VSが無い文字のみ抜き出す
outputType='base'

;;
( --vs )  # VSがついている文字のみ抜き出す
outputType='vs'

;;
( --html ) # VSをhtmlの数値参照に書き換える
outputType='html'

;;
( * )      # VSつきの文字を反転表示
outputType='view'

;;
esac



xxdFakeSh(){ (
# 入力は 3桁の8進数 複数バイト入力可能

IFS_BAK="${IFS}"

while read octalByteLine ; do

set -- ${octalByteLine}

IFS='\'
printf '\'"${*}"
IFS="${IFS_BAK}"

done

) }

out2hex2utf32beFunc(){
# 8進数を16進数に変換し、
# 16進数を「10」の位の数字と「01」の位の数字に分け、
# utf32beの8進数ダンプリスト形式で出力する。

Hex0=`printf '%02x' "0${1}"`

utf32_0='\060' ; utf32_1='\061'
utf32_2='\062' ; utf32_3='\063'
utf32_4='\064' ; utf32_5='\065'
utf32_6='\066' ; utf32_7='\067'
utf32_8='\070' ; utf32_9='\071'
utf32_a='\141' ; utf32_b='\142'
utf32_c='\143' ; utf32_d='\144'
utf32_e='\145' ; utf32_f='\146'

Hex0_10=${Hex0%?} # 16進数10の位の文字
Hex0_01=${Hex0#?} # 16進数01の位の文字

eval 'printf "\000\000\000${utf32_'${Hex0_10}'}\000\000\000${utf32_'${Hex0_01}'}"'

# (補注)サブシェル動作も可能
}


# 1.入力テキストをutf32beに変換
# 2.バイナリ8進数表記、1行4バイト(一文字)に並び替え
# 3.4バイトずつデータを調査して、VSではないならば、改行してから改行なし出力
#   「(改行)
#     入力4バイト16進数値をそのまま出力(改行なし)」
# 4.VSならば、そのまま出力して改行
# 5.空行を削除

iconv -f utf8 -t utf32be |
od -to1 -An -v -w4 |
{
while read Byte3 Byte2 Byte1 Byte0 ; do 

if test "${Byte3}${Byte2}${Byte1}" = '000016001' &&
   test ${Byte0} -ge 0    &&
   test ${Byte0} -le 357  ; then

printf ' %s %s %s %s\n' ${Byte3} ${Byte2} ${Byte1} ${Byte0}

else

printf '\n %s %s %s %s' ${Byte3} ${Byte2} ${Byte1} ${Byte0}

fi

done 
echo

} | sed -e '/^$/d' | {

# ^ 00 00 72 59 00 0e 01 00$
# ^ 00 00 30 7f$
# 
# データを整形しておく。odコマンドの出力に合わせる。
# 「半角空白-8進数1バイト」の繰り返しにすること。
# 行頭に半角空白が必須。


case "${outputType}" in
# ----------------------------------------------
( base )
# ----------------------------------------------
sed -n -e '
/^ ... ... ... ...$/{
p
}' | xxdFakeSh | iconv -f utf32be -t utf8


;;
# ----------------------------------------------
( vs )
# ----------------------------------------------
sed -n -e '
/^ ... ... ... ... 000 016 001 ...$/{
p
}' | xxdFakeSh | iconv -f utf32be -t utf8

;;
# ----------------------------------------------
( html )
# ----------------------------------------------

# &#x	 000 000 000 046 000 000 000 043 000 000 000 170
# e01	 000 000 000 145 000 000 000 060 000 000 000 061
# ;  	 000 000 000 073


while read B7 B6 B5 B4 B3 B2 B1 B0 ; do

if test "${B3}${B2}${B1}" = '000016001' ; then

# 基底文字を出力
printf '\'${B7}'\'${B6}'\'${B5}'\'${B4}

# HTML数値参照を出力
# 外部コマンドを使わない実装
printf '\000\000\000\046\000\000\000\043\000\000\000\170' # &#x
printf '\000\000\000\145\000\000\000\060\000\000\000\061' # e01
out2hex2utf32beFunc ${B0}
printf '\000\000\000\073'                                 # ;

# HTML数値参照を出力
# 外部コマンドを使う実装
#B0hex=`printf '%02x' "0${B0}"`
#printf '&#x%s;' "e01${B0hex}" | iconv -f utf8 -t utf32be

else
printf '\'${B7}'\'${B6}'\'${B5}'\'${B4}

fi

done | iconv -f utf32be -t utf8

;;
# ----------------------------------------------
( view )
# ----------------------------------------------

# 1行8バイトの行のみ、前後に文字反転エスケープシーケンスを
# バイナリ表記で叩き込む
# \033[7m	  000 000 000 033 000 000 000 133 000 000 000 067 000 000 000 155
# \033[0m	  000 000 000 033 000 000 000 133 000 000 000 060 000 000 000 155
sed -n -e '
/^ ... ... ... ... ... ... ... ...$/{
i\
 000 000 000 033 000 000 000 133 000 000 000 067 000 000 000 155
p
i\
 000 000 000 033 000 000 000 133 000 000 000 060 000 000 000 155
d
}
p
' | xxdFakeSh  | iconv -f utf32be -t utf8

;;
esac
}


終了コードを設定したいのだけれど、どうやったら簡単にできるのか、いいのが思いつかん。パイプは難しい。
異体字セレクタがあれば0、無ければ1、とかにしたいんだが。異体字セレクタ文字を抜き出して出力が無ければ1だ、っつうのは、どうもなぁ。