# mdevSGML_s2w ( ver 0.1 ) : # This program converts Medical Device SGML to it's Word data form. # Written by prepress-tips 2009.1.27 # Contact: prepress-tips@users.sourceforge.jp # This program is under the same licensing terms as Perl # ( the Artistic License 1.0 or the GNU GPL ). # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. use Encode qw/encode decode/; # 処理の概要は ‥ # - 起動 # 開始メッセージを表示する。 msg( 'mdevSGML_s2w ( ver 0.1 )'); $fn, $fol; { # 入力ファイル名 # 入力ファイル名 @ARGV > 0 || err( ' ファイル名を指定してください。' ); $fn = $ARGV[0]; -f $fn || err( ' ファイルがありません。' ); $fn =~ /^((?:\\|[\00-\x7f\xa0-\xdf]|..)*\\)([^\\]+\.sgml?)$/i || err( ' sgmlファイルを指定してください。' ); $fol = $1; msg( " $2" ); } $opt; { # オプション # オプション $opt = @ARGV > 1 ? $ARGV[1] : "" ; $opt eq "" || msg( " option : $opt" ); } $in, @in; # xml・sgml入力 @out; # 出力xml =pod - 起動時オプション perl mdevSGML_s2w.pl 入力sgml [bds] b : ブロックの詳細出力あり d : デバグ用出力あり s : サンプルxmlの整形出力あり =cut # - サンプルxml 入力 { #+ サンプルxmlを読む。 # サンプルxmlを読む。 # サンプルxmlを 'mdevSGML_sample.xml' から読む。 getF_xml( 'mdevSGML_sample.xml' ); # xml中の不要なタブを削除する。 $in =~ s/\t+(?=<[^\?\/])//g; $in =~ s/(<\/[^>]*>[\x0d\x0a]*)\t+/$1/g; $in =~ s/(\/>\x0a?)\t+/$1/g; # タブと改行をエスケープする。 $in =~ s/(\\)+[tn]/\\$&/g; $in =~ s/\t/\\t/g; $in =~ s/\x0d?\x0a/\\n/g; # タグ+テキストの形に分ける。 $in =~ s/<[^<>]*(?=<)/$&\t/g; $in =~ s/([^\x0a\t])(<)/$1\x0a$2/g; $in =~ s/\t//g; # 不要な改行を削除する。 $in =~ s,>(\\n)+,>\\n,g; $in =~ s,(\\n)+\x0a,\x0a,g; # サンプルxmlを整形する。 # 入力xml・sgmlを配列に変える。 ( @in = split "\x0a", $in ); # タブでインデントする。 my $ind = 0; my $n = 0; for( @in ) { my $i = $ind; /^<\// && ( $i--, $ind-- ); /^<\w/ && $ind++; /\/>[^>]*$/ && $ind--; $_ = ( "\t" x $i ).$_; } # 終了タグを1行にまとめる( まとめられる場合 )。 my $i; for( $i = 0; $i + 1 < @in; $i++ ) { $in[ $i ] =~ /^(?:\t)*<(?![\?\/])([^\s>]+)/ || next; my $tag = $1; $in[ $i + 1 ] =~ /^(?:\t)*(<\/$tag>)/ && ( $in[ $i ] .= $1, $in[ $i + 1 ] = "" ); } @in = map do { $_ eq "" ? () : $_ }, @in; # DocumentPropertiesの不要タグ を省く。 my @t = ( TotalTime, LastPrinted, Created, LastSaved, Version, Pages, Words, Characters, Bytes, Lines, Paragraphs, CharactersWithSpaces, ); my $t = "(".( join "|", @t ).")"; @in = map do { if( // .. /<\/o:DocumentProperties>/ ) { s/().*?(<\/o:LastAuthor>.*)/$1 $2/; s/().*?(<\/o:Revision>.*)/${1}1$2/; /^\t*/ ? () : $_ ; } else { $_; } }, @in; # サンプルxmlの整形出力あり ならば サンプルxmlを整形出力する。 if( $opt =~ /s/i ) { # 出力xmlにサンプルxmlを挿入する。 @out = @in; # タブと改行をアンエスケープする。 for( @out ) { s/\\t/\t/g; s/\\n/\x0a/g; s/\\((\\)+[tn])/$1/g; } msg( ' out: sample_xml.txt' ); # メッセージ # xmlを 'sample_xml.txt' に出力する。 putF_xml( 'sample_xml.txt' ); msg( ' out: sample_lv1.txt' ); # メッセージ # xmlの第1階層までを 'sample_lv1.txt' に出力する。 putF_Lv( 1, 'sample_lv1.txt' ); } } # - サンプルxml ブロックに分割 @parts; { # ブロック @parts = ( 'wordDocument', 'DocumentProperties', 'CustomDocumentProperties', 'fonts', 'lists', 'styles', 'docSuppData', 'shapeDefaults', 'docPr', 'body', ); } %delim; { # 項目に分けるタグ %delim = ( 'DocumentProperties' => 'o:DocumentProperties', 'CustomDocumentProperties' => 'o:CustomDocumentProperties', 'fonts' => 'w:font', 'lists' => 'w:listDef', 'styles' => 'w:style', 'docSuppData' => 'w:docSuppData', 'shapeDefaults' => 'w:shapeDefaults', 'docPr' => 'w:docPr', 'body' => 'w:body', ); } { #+ サンプルxmlをブロックに分ける。 # サンプルxmlをブロックに分ける。 # for( @parts ) { ブロック $_ を読む。 } for( @parts ) { get_block( $_ ) } # wordDocumentブロックを分割する。 @wordDocument_end = ( pop @wordDocument ); push @parts, 'wordDocument_end'; # bodyを除く各ブロックを項目に分ける。 # for( @parts ) { $_ eq 'body' || ブロック $_ を項目に分ける。 } for( @parts ) { $_ eq 'body' || sep_block( $_ ) } # bodyを除く各ブロックの各項目にタイトルを付ける。 # for( @parts ) { $_ eq 'body' || ブロック $_ の各項目にタイトルを付ける。 } for( @parts ) { $_ eq 'body' || titled( $_ ) } # bodyブロックを項目に分ける。 my $d = '(w:body|wx:sect|w:p|w:tbl|w:tr|w:tc)'; my @b = (); my $t = ""; for( @body ) { $t =~ /<\/$d>/ || /<$d[\s>]/ || ( $t .= "\x0a$_\\n", next ); push @b, $t; $t = "\x0a$_\\n"; } if( @b ) { push @b, $t; shift @b; } @body = @b; # bodyブロックを分割する。 @body_end = ( join '', 'body end', pop @body ); my $i = 0; for( @parts ) { /^body$/ && last; $i++; } splice( @parts, $i + 1, 0, 'body_end' ); # bodyの各項目にタイトルを付ける。 for( @body ) { /^\x0a/ || next; /^\x0a/ || next; my $s = /^\x0a(.*)\x0a/ ? $1 : $_ ; $s =~ /<\/?(?:\w*:)?([\w:]+)/; my $t = $1; my $m = ( $s =~ /\sw:name="([^"]*)"/ ) ? $1 : ( /]/ ) ? "- start .. end" : "- start" ; $t eq 'p' && /]/ && ( $t .= ' sectPr' ); $t eq 'p' && ( $m = join '', /(.*)<\/w:t>/g ); $t eq 'p' && ( $m .= "\t\t".chk_para( $_ ), $m =~ s/\t\t$// ); $_ = "$t $m$_"; } } # - サンプルxml ブロックを加工 %styleId; # スタイルIDのテーブル @para_gaij; # 機種既存文字の段落 @style_table; # 表組のスタイル { #+ ブロックを加工する。 # stylesブロックから 表組のスタイルを読む。 @style_table = (); my $n = 'Table Grid'; for( @styles ) { // && push @s, $i; // && push @p, $i; $i++; } @ins_mark = ( '〓hidden〓', '〓top〓', '〓body〓' ); @s > 1 && @p && ( @i = ( $s[ 0 ], $p[ 0 ], $s[ @s - 1 ] ) ); # 最初の wx:sect, 最初の w:p, 最後の wx:sect my @m = @ins_mark; for( reverse @i ) { splice( @body, $_ + 1, 0, "\t" ); } } } @unknown; # bodyの未知タグ・未知属性 $dTag, $dProp; # 省略するタグと属性 sub putF_title { # bodyを除く各ブロックのタイトルを( fn )に出力する。 my $fn = $_[0]; # ファイル名 # ファイル $fn に '' を出力する。 putF( $fn, '' ); # for( @parts ) { $_ eq 'body' || ブロック $_ のタイトルを $fn に追記する。 } for( @parts ) { $_ eq 'body' || addF_index( $_, $fn ) } } sub putF_body_title { # bodyのタイトルを( fn )に出力する。 my $fn = $_[0]; # ファイル名 # ファイル $fn に '' を出力する。 putF( $fn, '' ); # ブロック 'body' のタイトルを $fn に追記する。 addF_index( 'body', $fn ); } # - 出力xml ひな形作成 { #+ 出力xmlのひな形を作る。 # 出力xmlにブロックを挿入する。 @out = (); # for( @parts ) { 出力xmlにブロック $_ を挿入する。 } for( @parts ) { ins_parts( $_ ) } # ブロック 'body' を 'xml_body_1st.txt' に出力する。 } # - 入力sgml 入力 { #+ sgmlを読む。 # 入力sgmlを読む。 ( $in = join '', getF( $fn ) ); # 入力sgmlを タグ+テキスト の形に分ける。 # タブと改行をエスケープする。 $in =~ s/(\\)+[tn]/\\$&/g; $in =~ s/\t/\\t/g; $in =~ s/\x0d?\x0a/\\n/g; # 半角&をエスケープする。 $in =~ s/\&/&/g; # 強制改行をタグに変える。 $in =~ s/\&enter;/
/g; # 終了タグを補う。 my $tag = '(graphic|br)'; $in =~ s/<($tag)(\s[^>]*)?>/$&<\/$1>/gi; # DOCTYPE宣言をエスケープする。 while( $in =~ s/(]*)<(![^>]*)>/$1<$2>/i ) {}; # タグ+テキストの形に分ける。 $in =~ s/<[^<>]*(?=<)/$&\t/g; $in =~ s/([^\x0a\t])(<)/$1\x0a$2/g; $in =~ s/\t//g; # 入力sgmlの書式を揃える。 # 属性の記述を統一する。 $in =~ s,(<[\w-]+)\s+,$1 ,g; while( $in =~ s,(<[\w-]+[^>]*)\s+=,$1=,i ) {}; while( $in =~ s,(<[\w-]+[^>]*)=\s+,$1=,i ) {}; # 定型の属性を削除する。 my @tag =( 'Warning', 'Contraindication-and-Prohibitions', ); my $col = '(red|black)'; for ( @tag ) { $in =~ s,<$_ boxline="yes" boxcolor="rd" color="$col">,<$_>,gi; } # 不要な属性を削除する。 $in =~ s,,,gi; $in =~ s,,,gi; # 不要な改行を削除する。 $in =~ s,>(\\n)+,>\\n,g; $in =~ s,(\\n)+\x0a,\x0a,g; # variablelabelタグの属性を追加する。 my @a = ( 'Company-identifier', 'Download', 'The-permission-number-of-business-condition', 'Name-of-manufacturer', 'Address-of-manufacturer', 'The-recognition-number-of-business-condition', 'Phonenumber-of-manufacturer', 'Name-of-oversea-manufacturer', 'Address-of-oversea-manufacturer', 'The-authorization-number-of-business-condition', 'Phonenumber-of-oversea-manufacturer', 'The-company-name-of-specification-into-English', 'Address-of-specification-into-English', 'The-country-code', 'Name-of-a-country', ); my $a = "(?:".( join "|", @a ).")"; $in =~ s/(<$a>\s*)/$1 onswitch="on"$2/gi; # variablelabelタグを属性に変える。 $in =~ s/(>)\s*(])/$1$2/gi; $in =~ s/(<\/variablelabel>)\s*(?!<)/$1/gi; $in =~ s/\s*<\/variablelabel>//gi; $in =~ s/]*>\s*<\/variablelabel>//gi; $in =~ s/<([^>]*)>\s*<(variablelabel)>([^<]*\S)\s*<\/\2>/<$1 $2-off="$3">/gi; $in =~ s/<([^>]*)>\s*<(variablelabel)\s[^>]*>([^<]*\S)\s*<\/\2>/<$1 $2="$3">/gi; # 不要の itemタグと detailタグを削除する。 $in =~ s/<(item|detail)(\s+variablelabel-off="[^"]*")?>\s*<\/\1>\s*//gi; # 変換できない serialnoタグを表示する。 my @t =( $in =~ /]).*?>\s*((?:.|\s)*?)<\/serialno>/gi ); @t = map do { s/\x0a//g; /^\d*\s*$/ ? () : $_ }, @t; @t && msg( ' 変換できない serialnoタグ:', map do { " $_" }, @t ); # serialnoタグを次のタグの属性に変える。 $in =~ s/\s*//gi; $in =~ s/)\s*(?=<)/$1/gi; my ( $as, $ae ) = ( '\s*\s*\s*', '\s*<\/bold>\s*<\/chr>\s*' ); ( $as, $ae ) = map do { "(?:$_)?"; }, ( $as, $ae ); $in =~ s/<(serialno)(.*?)>$as([^<\s]*)$ae<\/serialno><([\w-]+)/<$4 $1="$3"/gi; # linkタグを属性に変える。 $in =~ s/(>)\s*(])/$1$2/gi; $in =~ s/(<\/link>)\s*(?!<)/$1/gi; $in =~ s/\s*<\/link>//gi; $in =~ s/<([^>]*)>\s*<(link)>([^<]*\S)\s*<\/\2>/<$1 $2="$3">/gi; # detail内のtableを分離する。 my $at = '('.( join '|', qw( chem chr graphic br url ) ).')'; my $s = ''; while( $in =~ /(])[^>]*>(?:.|\s)*?<\/tblblock>)/ ) { my ( $p, $q, $r ) = ( $`, '', $& ); $in = $'; while( $p =~ /^((?:.|\s)*)(<(?![\s!-])[^<]*)$/ ) { ( $p, $q ) = ( $1, $2.$q ); $q =~ /^<\/?$at[\s>]/ || last; } $q =~ /^<\// && ( $s .= "$p$q$r", next ); $s .= $p; $q =~ /^<(\S+)/; my $t = $1; $s .= ( $q =~/^<$t(\s+variablelabel-off="[^"]*")?>\s*$/ ) ? "$q$r" : "$q\x0a$r" ; my $x = "\x0a<$t>"; $in =~ /^(\s*)<\/$t>/ && ( $in = $', $x = $1 ); $s .= $x; } $in = $s.$in; # simpletableのセル数を数える。 while( $in =~ // ) { my ( $p, $q, $r ) = ( $`, $&, $' ); $r =~ /((?:.|\s)*?)<\/simptblrow>/; my $s = $1; my @r = ( $s =~ /])([^>]*)>/g ); @r = map do { /cspan="0*(\d+)"/ ? $1 : 1 }, @r; my $c = 0; for( @r ) { $c += $_; } $in = $p.''.$r; } # デバグ用出力あり ならば msg( ' out: chk_*.txt' ); if( $opt =~ /d/i ) { msg( ' out: chk_*.txt' ); } # デバグ用出力あり ならば 入力xml・sgmlを 'chk_sgm1.txt' に出力する。 if( $opt =~ /d/i ) { putF( 'chk_sgm1.txt', $in ); } } # - 変換規則 入力 @s2w; { # 変換規則を読む。 # 変換規則を 'mdevSGML_s2w.txt' から読む。 getF_s2w( 'mdevSGML_s2w.txt' ); } %tag2name; { # タグをタグ名に置換するテーブルを作る。 # タグをタグ名に置換するテーブルを作る。 %tag2name = (); for( @s2w ) { /^( )*(.*?)\s+\/\s+(.*\S)/ || next; $tag2name{ lc( "<$3>" ) } = ( $2 eq "" ) ? $3 : $2 ; } } @tag; { # 変換規則から タグのリストを作る。 # 変換規則から タグのリストを作る。 my @t = map do { ! /^\t/ && /\s\/\s+(.*\S)/ ? $1 : () }, @s2w; my %t = map do { ( lc( "<$_>" ) => 1 ); }, @t; @tag = sort keys %t; } %tag2num; { # タグのリストから タグをタグ番号に置換するテーブルを作る。 # タグのリストから タグをタグ番号に置換するテーブルを作る。 %tag2num = (); my $n = 1; for( @tag ) { $tag2num{ $_ } = sprintf "%03d", $n++; } # デバグ用出力あり ならば タグをタグ番号に置換するテーブルを 'chk_tag2num.txt' に出力する。 if( $opt =~ /d/i ) { putF_tag2num( 'chk_tag2num.txt' ) } } %path2xml; { # sgmlのタグ列を xmlのタグに置換するテーブル # 変換規則内のタグを タグ番号に変える。 for ( @s2w ) { /^(?!\t)((?: )*).*\/\s*(.*\S)\s*$/ || next; my ( $s, $t ) = ( $1, $2 ); $_ = $s.'<'.$tag2num{ lc( "<$t>" ) }.'>'; } # 変換規則内の全角空白によるインデントを タグ列に変える。 my @t = (); for( @s2w ) { /^\t/ && next; my @s = / /g; my @n = /<[^>]+>/g; splice( @t, scalar @s, @t - @s, @n ); $_ = join "", @t; } # 変換規則内の 行頭がタブで始まる行を 前の行に連結する。 my @w = map do { /^\t/ ? $_ : $_."\t" ; }, @s2w; my $s2w = join "\x0a", @w; $s2w =~ s/\x0a+\t\t//g; @s2w = split "\x0a", $s2w; # sgmlのタグ列を xmlのタグに置換するテーブル を作る。 %path2xml = map do { /\t+/ ? ( $` => [ $' ] ) : () ; }, @s2w; # 開始タグ・中区切・終了タグ・属性の それぞれに対応する部分に分ける。 for( keys %path2xml ) { my $s = @{$path2xml{ $_ }}[0]; my ( $st, $sp, $et ) = ( "", "", "" ); my @p = (); while( $s =~ /〓.*?(〓|:|$)/ ) { $st .= $`; my $p = $&; $s = $'; $1 eq '〓' && ( $st .= $p, next ); $s =~ /(〓|$)/; push @p, $p.$`; $s = $&.$'; } $st .= $s; $st =~ /(\.\.\.)(.*?)\.\.\./ && ( $st = $`.$1.$', $sp = $2 ); $st =~ /\.\.\./ && ( $st = $`, $et = $' ); for( $st, $sp, $et ) { s/\t//g; } $path2xml{ $_ } = [ $st, $sp, $et, $_, @p ]; } # 属性の置換テーブルを作る。 for( keys %path2xml ) { my ( $t, @p ) = splice( @{$path2xml{ $_ }}, 3 ); $t =~ /^(<\d+>)*<\/?(\d+)>/; my $sn = $tag2name{ $tag[ $2 - 1 ] }; @{$path2xml{ $_ }}[0] =~ /-style:(.*?)-/ && ( $sn = $1, @{$path2xml{ $_ }}[0] = $`.$' ); my $p = { 'variablelabel' => [ '.*=>$&' ], 'variablelabel.style' => [ $sn ], }; for( @p ) { /^〓[@]?([@]?.*?):/ || next; my ( $k, $v ) = ( $1, $' ); $v =~ /\t-style:(.*?)-/ && ( $v = $', $p->{ $k.'.style' } = [ $1 ] ); @v = ( $v =~ /\t.*?=>\t*[^\t]*/g ); @v = map do { s/^\t//; s/\$nul\s*$//; /\t*=>\t*/; "$`=>$'"; }, @v; $p->{ $k } = [ @v ]; } push @{$path2xml{ $_ }}, $p; } # デバグ用出力あり ならば 変換規則を 'chk_cnv.txt' に出力する。 if( $opt =~ /d/i ) { putF( 'chk_cnv.txt', join "\x0a", @s2w, "" ); } # デバグ用出力あり ならば xmlのタグに置換するテーブルを 'chk_path2xml.txt' に出力する。 if( $opt =~ /d/i ) { putF_path2xml( 'chk_path2xml.txt' ) } } # - 入力sgml 変換の準備 { #+ 変換の準備をする。 # 入力sgmlを タグ列+テキスト の形に変える。 # 入力xml・sgmlを配列に変える。 ( @in = split "\x0a", $in ); # タブでインデントする。 my $ind = 0; my $n = 0; for( @in ) { my $i = $ind; /^<\// && ( $i--, $ind-- ); /^<\w/ && $ind++; /\/>[^>]*$/ && $ind--; $_ = ( "\t" x $i ).$_; } # 未知のタグを確認する。 my @u = map do { /<\w[^>]*>/g; }, @in; @u = map do { /^<[^\s>]*/; defined( $tag2num{ lc( "$&>" ) } ) ? () : $_ }, @u; my %u = map do { ( $_ => 1 ) }, @u; @u && msg( ' 未知のタグがありました。', map do { " $_" }, sort keys %u ); # タグを タグ番号に置換する。 for ( @in ) { s/<(\w[^\s>]*)\s*/<$tag2num{ lc( "<$1>" ) }>//; s/<\/(\w[^>]*)>/<\/$tag2num{ lc( "<$1>" ) }>/; } # 終了タグの終わりの文字列を確認する。 my @u = map do { /^( )*(<\d+>)*<\/\d+>(.*?>)?(\s|\\n)*/ && $' ne "" ? $_ : () }, @in; @u && msg( ' 終了タグの終わりに文字列がありました。', map do { " $_" }, @u ); # インデントをタグ列に変える。 my @t = (); for( @in ) { /^(\t)*(<[^>]+>)*/; my ( $t, $r ) = ( $&, $' ); my @s = ( $t =~/\t/g ); my @n = ( $t =~ /<[^>]+>/g ); splice( @t, scalar @s, @t - @s, @n ); $_ = join "", @t, $r; } # 入力xml・sgmlを文字列に戻す。 ( $in = join "\x0a", @in, "" ); # デバグ用出力あり ならば 入力xml・sgmlを 'chk_sgm2.txt' に出力する。 if( $opt =~ /d/i ) { putF( 'chk_sgm2.txt', $in ); } } @cat, %cat; { # 類別 と 類別のテーブル # 類別を 'mdevSGML_cat.txt' から読む。 getF_cat( 'mdevSGML_cat.txt' ); # 類別のテーブルを作る。 %cat = (); map do { s/^\s*//; s/\s*$//; /\t+/ && ( $cat{ $` } = [ $' ] ); }, @cat; } @country, %country; { # 国名 と 国名のテーブル # 国名を 'mdevSGML_country.txt' から読む。 getF_country( 'mdevSGML_country.txt' ); # 国名のテーブルを作る。 %country = (); map do { s/^\s*//; s/\s*$//; /\t+/ && ( $country{ $` } = [ $' ] ); }, @country; } # - 出力xml 変換 @hidden; { # 表示されない情報 # 表示されない情報 @hidden = (); } @hide; { # 表示する情報かどうかの判断の保持 # 表示する情報かどうかの判断の保持 @hide = (); } $top_prop; { # 冒頭の情報の抽出結果 # 冒頭の情報の抽出結果 $top_prop = {}; } @pare; { # タグの属性の保持 # タグの属性の保持 @pare = (); } $w_w; { # 表組のセル幅の保持 # 表組のセル幅の保持 $w_w = 0; } @span_p, @span_n; { # 表組のrowspanの保持 # 表組のrowspanの保持 @span_p = (); @span_n = (); } %atr_tag; { # 文字属性タグの変換テーブル # 文字属性タグの変換テーブル my %a = ( 'bold' => '', 'italic' => '', 'under' => '', 'sup' => '', 'sub' => '', 'chem' => '', 'div' => '', 'nom' => '', 'den' => '', 'han' => '', 'gaiji' => '', ); %atr_tag = (); for( keys %a ) { $atr_tag{ $tag2num{ "<$_>" } } = $a{ $_ }; }; } %cols; { # 色の変換テーブル # 色の変換テーブル %cols = ( 'red' => 'FF0000', ); } @not_conv; { # 変換されなかった情報 # 変換されなかった情報 @not_conv = (); } { #+ 変換する。 # bodyブロックを 初期化する。 @body = (); # 入力sgmlに タグ列をxmlのタグに置換する処理 を繰り返す。 for( @in ) { my $t, $p, $r; { # タグ列・属性・テキストに分離する。 # タグ列・属性・テキストに分離する。 /[^>]*$/; ( $t, $r ) = ( $`, $& ); $t =~ /^(<\d+>)*<\/?\d+>/; $t = $&; $p = $'; } my $c; { # タグ列に対応する変換テーブルを読む。 # タグ列に対応する変換テーブルを読む。 $c = path_conv( $t ); # タグ列に対応する変換テーブルがないとき 次へ。 $c || next; } my $st, $sp, $et, $pr; { # 開始タグ・タグ間・終了タグに対応するxmlのタグ と 属性の変換テーブル を取り出す。 # 開始タグ・タグ間・終了タグに対応するxmlのタグ と 属性の変換テーブル を取り出す。 ( $st, $sp, $et, $pr, ) = map do { s/^\t+//; $_; }, @$c; } my $isStart; { # 開始タグか? # 開始タグか? $isStart = ( $t =~ /<\d+>$/ ); } my $hide; { # 表示する情報か? # 表示する情報か? $hide = ( $st =~ /-to:hx-/ ) ? @hide && $hide[ 0 ] : $st =~/-to:h-/ ; if( $isStart ) { unshift @hide, $hide; } else { shift @hide; } $st =~ s/-to:hx?-//; } my $head; { # 冒頭の情報か? # 冒頭の情報か? $head = ( $st =~ /-to:tx-/ ) ? @head && $head[ 0 ] : $st =~/-to:t-/ ; if( $isStart ) { unshift @head, $head; } else { shift @head; } $st =~ s/-to:tx?-//; } my $prop, $pare; { # タグの属性・親タグの属性 # タグの属性・親タグの属性 $isStart && unshift @pare, prop_get( $p ); $prop = $pare[ 0 ]; $pare = @pare > 1 ? $pare[ 1 ] : {} ; $isStart || shift @pare; } my $isPara, $inPara; { # 段落か? # 段落か? $isPara = ( $st =~ /-para-/ ); $st =~ s/-para-//; } my $free; { # 自由書式か? # 自由書式か? $free = ( $st =~ /-f-/ ); $st =~ s/-f-//; } # 冒頭の情報を抽出する。 $head && $t =~ /<(\d+)>$/ && defined( $tag[ $1 - 1 ] ) && do { my $tn = $tag[ $1 - 1 ]; $tn =~ /^<(item|detail)>$/ && ( $t =~ /<(\d+)><\d+>$/, $tn = $tag[ $1 - 1 ].$tn ); $tn =~ /^<(year-month|version)>$/ && do { my $n = 1; while( defined( $top_prop->{ "$1/$n" } ) ) { $n++ } $tn = "$1/$n"; }; my $f = $tn; $f =~ s/^$//; $f =~ s/> $r }; for( keys %$prop ){ $p->{ $_ } = $prop->{ $_ }; } $top_prop->{ $f } = $p; }; # タグの属性を補正する。 $isStart && ( $st = prop_conv( $st, $prop, $pr, $pare ) ); ! $isStart && ( $et = prop_conv( $et, $prop, $pr, $pare ) ); # 表組のセル幅を調べる。 my $tr = $tag2num{ "" }; $t =~ /<$tr>$/ && do { my $c = ( $p =~ /[<\s]columns="(\d+)"/ && $1 ) ? $1 : 1 ; my $width = 1186 * 4; $w_w = int( $width / $c + 0.5 ); }; # 表組のrowspanを調べる。 my $tr = $tag2num{ "" }; $t =~ /<$tr>$/ && do { @span_p = @span_n; @span_n = (); for( @span_p ) { $$_[0]--; } }; # テキストを補正する( テキストと文字属性 )。 $isStart && $st =~ /^(-\w+-)*\$nul$/ && ( $r = "" ); ! $isStart && $r =~ /^\s*$/ && ( $r = "" ); $prop->{ '...' } = $r; $isStart && defined( $pr->{ '...' } ) && $r ne "" && ( $r = prop_alt( '...', $prop, $pr ) ); my $sn = @{$pr->{ 'variablelabel.style' }}[0]; $isPara && $isStart && $sn ne "" && pStyle( $sn, $p ) ne "" && ( $inPara = 1 ); $inPara && $r ne "" && ( $r = join '', ( '', rStyle( $t ), '', $r, '', '', ) ); $inPara && $isStart && defined( $prop->{ 'serialno' } ) && ( $r = rList( $t, $prop ).$r ); # テキストを補正する( 強制改行 )。 my $b = '<'.$tag2num{ "
" }.'>'; $inPara && $r eq "" && $t =~ /$b$/ && ( $r = join '', ( '', '', $r, '', ) ); # テキストを補正する( 画像 )。 my $g = '<'.$tag2num{ "" }.'>'; my ( $w, $h ); $inPara && $r eq "" && $t =~ /$g$/ && $p =~ // && ( ( $w, $h ) = getS( $1 ), $r = join '', ( '', '', '', '', '', '', '', ) ); # テキストを補正する( 段落属性 )。 $inPara && $isPara && ! $isStart && ( $r .= '
', $inPara = 0 ); $inPara && $isPara && ( $r = join '', ( '', pStyle( $sn, $p ), $r, ) ); $inPara && $isPara && $isStart && defined( $prop->{ 'serialno' } ) && $r =~ s/<\/w:pPr>/$&/; # テキストを補正する( 表組 )。 my $tb = $tag2num{ "" }; $t =~ /<$tb>$/ && ( $r = join '', ( '', '', '', '', '', $r, ) ); $t =~ /<\/$tb>$/ && ( $r .= '' ); my $tr = $tag2num{ "" }; $t =~ /<$tr>$/ && ( $r = join '', ( '', $r, ) ); $t =~ /<\/$tr>$/ && ( $r .= '' ); my $tc = $tag2num{ "" }; $t =~ /<$tc>$/ && ( $r = join '', ( chk_rowspan( $p ), '', '', '', '', ( $p =~ /[<\s]rspan="0*(\d+)"/ && 1 < $1 ? '' : () ), ( $p =~ /[<\s]cspan="0*(\d+)"/ && 1 < $1 ? '' : () ), ( $p =~ /[<\s]valign="bottom"/ ? '' : $p =~ /[<\s]valign="top"/ ? () : '' ), '', $r, ) ); $t =~ /<\/$tc>$/ && ( $r .= '' ); # 変換されなかった情報を確認する。 $r ne '' && ! $head && ! ( $r =~ /<\/?[\w:-]+[>\s]/ ) && ( ( push @not_conv, [ $t, $r ] ), $r = ''); # 変換結果を保存する。 $r = cp932_to_utf8( $isStart ? "$st$r" : "$r$et" ); $r ne '' && $hide && push @hidden, "\t$r"; $r ne '' && ! $hide && ! $head && push @body, "\t$r"; # テキストを追加する( 自由書式のとき )。 $free && ! $isStart && push @body, cp932_to_utf8( join '', ( '', pStyle( '自由書式' ), '', rStyle( '' ), ' ', '', '' ) ); } # 機種既存文字の段落を hidden に追加する。 push @hidden, @para_gaij; # 行の高さを補正する。 my $s = join "\x0a", @body; my $body = ''; while( $s =~ /(.|\s)*?<\/w:p>/ ) { my $q = $&; $s = $'; $body .= $`; my @q = ( $q =~ /(]*>)/g ); @q = map do { /style=".*?;height:([\d.]+)pt"/ ? $1 : () }, @q; my $h = 0; for( @q ) { $_ > $h && ( $h = $_ ); } $h = int( $h + 0.5 ); @q && $q =~ s/<\/w:pPr>/$&/; $body .= $q; } $body .= $s; @body = split "\x0a", $body; # セルの高さを補正する。 my $s = join "\x0a", @body; my $body = ''; while( $s =~ /](?:.|\s)*?<\/w:tr>/ ) { my $tc = $&; $s = $'; $body .= $`; my @tc = ( $tc =~ /(](?:.|\s)*?<\/w:pPr>)/g ); @tc = map do { // ? $1 : () }, @tc; my $h = 0; for( @tc ) { $_ > $h && ( $h = $_ ); } $h = int( $h + 0.5 ); $h *= 20; @tc && $h > 0 && do { $tc =~ // || $tc =~ s/])[^>]*>/$&<\/w:trPr>/; $tc =~ s/<\/w:trPr>/$&/; $tc =~ s///g; }; $body .= $tc; } $body .= $s; @body = split "\x0a", $body; # 変換されなかった情報を表示する。 my @at = qw( chr bold italic under sup sub chem div nom den han gaiji graphic br ); my $at = '(?:'.( join '|', @at ).')'; for( @not_conv ) { $$_[0] =~ /<([^>]*)>(?:<$at(?=[\s>])[^>]*>|<\/[^>]*>)*$/; $1 > 0 && ( $$_[0] = $tag[ $1 - 1 ] ); } @not_conv = map do{ " $$_[0]:$$_[1]" }, @not_conv; @not_conv && msg( ' 変換されなかった情報:', @not_conv ); # デバグ用出力あり ならば msg( ' out: xml_*.txt' ); if( $opt =~ /d/i ) { msg( ' out: xml_*.txt' ); } # デバグ用出力あり ならば ブロック 'hidden' を 'xml_hidden.txt' に出力する。 if( $opt =~ /d/i ) { putF_part( 'hidden', 'xml_hidden.txt' ) } # デバグ用出力あり ならば ブロック 'body' を 'xml_body.txt' に出力する。 if( $opt =~ /d/i ) { putF_part( 'body', 'xml_body.txt' ) } } # - 出力xml 冒頭の情報 @top; # 冒頭の情報 $top_cnv; # 冒頭の情報の変換テーブル { #+ 冒頭の情報を作成する。 # 冒頭の情報を 'mdevSGML_s2w_top.txt' から読む。 getF_top( 'mdevSGML_s2w_top.txt' ); # 冒頭の情報の変換テーブルを作る。 my $t = join "\x0a", @top; $t =~ /(^|\x0a)\t*〓.*?:/ && ( ( @top = split "\x0a", $` ), $t = $&.$' ); @top = map do { /^\t\t/ ? $' : () }, @top; my @t = (); while( $t =~ /(?:^|\x0a)(\t*〓.*?:(?:.|\s)*?)(\x0a\t*〓.*?:|$)/ ) { push @t, $1; $t = $2.$'; } @t = map do { s/(^|\x0a)\t\t/$1/g; $_; }, @t; $top_conv = {}; for( @t ) { /^〓(.*?):/ || next; my ( $k, $v ) = ( $1, $' ); $v =~ /\t(-.*-)/ && ( $v = $', $top_cnv->{ $k.'.style' } = [ $1 ] ); my @v = ( $v =~ /\t.*?=>\t*[^\t]*/g ); @v = map do { /\t*=>\t*/; "$`=>$'"; }, @v; for( @v ) { s/^\t//; s/\$nul\s*$//; s/\s*$//; } $top_cnv->{ $k } = [ @v ]; } # 冒頭の情報を変換する。 my $top = join "\x0a", @top; while( $top =~ /〓(.*?)〓/ ) { my $k = $1; my ( $pre, $post ) = ( $`, $' ); my $v = ( $k =~ /^\#/ ) ? '' : ( $k =~ /\@/ ) ? $top_prop->{ $` }->{ $' } : $top_prop->{ $k }->{ '...' } ; my @c = defined( $top_cnv->{ $k } ) ? @{$top_cnv->{ $k }} : () ; for( @c ) { /=>/ || next; my ( $c, $r ) = ( $`, $' ); $v =~ /^$c$/i || next; my @v = ( $&, $1, $2, $3, $4, $5, $6, $7, $8, $9 ); $r =~ s/\$&/$v[ 0 ]/g; for( 1 .. 9 ) { $r =~ s/\$$_/$v[ $_ ]/g; } $r =~ s/cat\(\s*(\S+)\s*\)/@{$cat{ $1 }}[0]/g; $v = $r; last; } $v ne '' && ( $v = join '', ( '', rStyle( '' ), '', $v, '', '', ) ); my $s = defined( $top_cnv->{ $k.'.style' } ) ? @{$top_cnv->{ $k.'.style' }}[0] : ''; if( $s =~ /-style:(.*?)-/ ) { $v = join '', ( '', pStyle( $1, $s ), $v, '', ); } else { $pre =~ /(<\/w:p>)(\s*)$/ && ( $pre = $`.$2, $v .= $1 ); } $top = $pre.cp932_to_utf8( $v ).$post; } @top = split "\x0a", $top; # デバグ用出力あり ならば ブロック 'top' を 'xml_top.txt' に出力する。 if( $opt =~ /d/i ) { putF_part( 'top', 'xml_top.txt' ) } } # - 出力xml 出力 { #+ 出力する。 # hidden, top, body を挿入マークと置換する。 my $i = 0; my %i = (); my @m = @ins_mark; for( @out ) { // && do { for( @m ) { $_ eq $1 && ( $i{ $_ } = $i ); } }; $i++; } sub i { $i{ $b } <=> $i{ $a } } for( sort i @m ) { /〓(.*?)〓/ && splice( @out, $i{ $_ }, 1, @{$1} ); } # タブと改行をアンエスケープする。 for( @out ) { s/\\t/\t/g; s/\\n/\x0a/g; s/\\((\\)+[tn])/$1/g; } # xmlを出力する。 putF_xml(); # デバグ用出力あり ならば xmlを 'xml.txt' に出力する。 if( $opt =~ /d/i ) { putF_xml( 'xml.txt' ); } } # 処理の詳細 ‥ # - 開始 # - サンプルxml・入力sgml sub getF_xml { # サンプルxmlを( fn )から読む。 -f $_[0] || err( ' xmlファイルがありません。' ); $in = join '', getF( $_[0] ); } # - 類別・国名 sub getF_cat { # 類別を( fn )から読む。 -f $_[0] || err( ' 類別のファイルがありません: '.$_[0] ); @cat = getF( $_[0] ); } sub getF_country { # 国名を( fn )から読む。 -f $_[0] || err( ' 国名のファイルがありません: '.$_[0] ); @country = getF( $_[0] ); } # - エスケープ と アンエスケープ # - タグの整形 タグ列 # - タグの整形 属性 # - ブロック sub get_block { # ブロック( array )を読む。 my $t = $_[0]; $t eq 'wordDocument' && return get_wordDocument(); my $s = ( $delim{ $_[0] } =~ /^\w+:/ ) ? $& : "" ; @{"$_[0]"} = map do { /^(\t)*<$s$t[\s>]/ .. /^(\t)*<\/$s$t[\s>]/ ? $_ : () ; }, @in; } sub sep_block { # ブロック( array )を項目に分ける。 my @w = (); my $d = $delim{ $_[0] }; defined( $d ) || return; my $s = ( $delim{ $_[0] } =~ /^\w+:/ ) ? "$&$_[0]" : "" ; $s ne $d && ( $d = "($d|$s)" ); my $t = ""; for( @{$_[0]} ) { $t =~ /<\/$d>/ || /<$d[\s>]/ || ( $t .= "\x0a$_\\n", next ); push @w, $t; $t = "\x0a$_\\n"; } if( @w ) { push @w, $t; shift @w; } @{$_[0]} = @w; } sub titled { # ブロック( array )の各項目にタイトルを付ける。 for( @{$_[0]} ) { /^\x0a/ || next; my $s = /^\x0a(.*)\x0a/ ? $1 : $_ ; $s =~ /<\/?(?:\w*:)?([\w:]+)/; my $t = $1; my $m = ( $s =~ /\sw:name="([^"]*)"/ ) ? $1 : ( /]/ ) ? "- start .. end" : "- start" ; $_ = "$t $m$_"; } } sub putF_part { # ブロック( array )を( fn )に出力する。 putF( $_[1], join "\x0a", @{$_[0]}, "" ); } sub addF_index { # ブロック( array )のタイトルを( fn )に追記する。 my $n = $_[0]; my @t = map do { "\t".utf8_to_cp932( /\x0a/ ? $` : $_ ) }, @{$_[0]}; unshift @t, "$n /"; $n eq 'body' && ( @t = map do { s/^.*?(?=\t\t)/$&\x0a/; $_; }, @t ); addF( $_[1], join "\x0a", @t, "" ); } # - ブロック wordDocument sub get_wordDocument { # wordDocumentブロックを読む。 my @w = map do { /^/ ? $_ : () ; }, @in; my $e = join "\x0a\t", "wordDocument end", pop @w; my $s = join "\x0a\t", "wordDocument start", @w; $s =~ s/(\t)(\t)*/$1/g; $s .= "\\n"; @wordDocument = ( $s, $e ); } # - ブロック body sub chk_para { # パラグラフの属性を調べる。 my @u = (); for( $_[0] =~ /<(?![\/])([^>]+)>/g ) { /^\s*([^\s\/]+)\s*/; my ( $t, $p ) = ( $1, $' ); $p =~ s/\s*\/\s*$//; push @u, $p eq '' ? $t : $t." ".$p ; } my @k = qw( b-cs spacing ind jc color ); my $k = '('.( join '|', @k ).')'; @u = map do { /$k/ ? utf8_to_cp932( $_ ) : () }, @u; @u = sort { $a cmp $b } @u; my $n = ''; @u = map do { my $p = $n; $n = $_; $p eq $n ? () : $n; }, @u; join "\t", @u; } sub chk_body { # bodyの未知タグ・未知属性を調べる。 my @u = (); for( @body ) { for( /<(?![\/])([^>]+)>/g ) { /^\s*([^\s\/]+)\s*/; my ( $t, $p ) = ( $1, $' ); $p =~ s/\s*\/\s*$//; push @u, $p eq '' ? $t : $t."\t".$p ; } } my @k = qw( w:body wx:sect w:sectPr w:pStyle w:rPr w:rStyle w:t w:tbl w:tblPr w:tr w:trPr w:tc w:tcPr ); my $k = '('.( join '|', @k ).')'; @u = map do { /^$k(\s|$)/ ? () : utf8_to_cp932( $_ ) }, @u; @u = sort { $a cmp $b } @u; my $n = ''; @u = map do { my $p = $n; $n = $_; $p eq $n ? () : $n; }, @u; @unknown = map do { s/\t/\x0a$&/; $_; }, @u; } # - ブロック styles # - ブロック 項目・タグ・属性の削除 sub pass1 { # ( array )から( del )タグのある項目を省く。 my $d = $_[1]; @{$_[0]} = map do { /<$d[\s>]/ ? () : $_ }, @{$_[0]}; } sub pass2 { # ( array )から( del )タグのない項目を省く。 my $d = $_[1]; @{$_[0]} = map do { /<$d[\s>]/ ? $_ : () }, @{$_[0]}; } sub delTag { # ( array )の各項目で( del )タグを省く。 my $d = $_[1]; for( @{$_[0]} ) { s/\x0a\t*<\/?$d[\s>].*//g; } } sub delProp { # ( array )の各項目で( tag )タグの( del )属性を省く。 my $t = $_[1]; my $d = $_[2]; for( @{$_[0]} ) { s/(<$t)(\s)/$1~$2/g; while( /(<$t)~(\s[^>]*)/ ) { my $p = $`.$1; my$q = $2; my $r = $'; $q =~ s/\s$d\s*=\s*(['"]).*?\1//g; $_ = $p.$q.$r; } } } # - 出力xml sub ins_parts { # 出力xmlにブロック( block )を挿入する。 for( @{$_[0]} ) { /\t/ || next; my $v = $'; $v =~ s/(^|\x0a)\t*//g; push @out, $v; } } sub putF_xml { # xmlを出力する。 my $fn = defined( $_[0] ) && $_[0] =~ /\S/ ? $_[0] : $ARGV[0] ; $fn =~ /\.sgm$/i && ( $fn =~ s/\.sgm$/.xml/i ); putF( $fn, join "\x0a", @out ); } sub putF_Lv { # xmlの第( n )階層までを( fn )に出力する。 my @x = map do { /^(\t){0,$_[0]}<(?!\/)/ ? $_ : () }, @in; putF( $_[1], join "\x0a", @x, "" ); } # - xmlへの変換 sub path_conv { # ( path )を xmlのタグに置換するテーブル で置換する。 my $t = $_[0]; $t =~ s/<\//]+>/ ) { $t = $'; defined( $path2xml{ $t } ) && return $path2xml{ $t }; } return undef; } sub prop_get { # ( tag )の属性を読む。 my $t = $_[0]; $t =~ s/^<\s*//; $t =~ s/\s*>$//; my @t = $t =~ /([\w-]+\s*=\s*'[^']*'|[\w-]+\s*=\s*"[^"]*"|[\w-]+\s*\s*=\S+)\s*/g; my $h = {}; map do { /\s*=\s*/; my ( $k, $v ) = ( $`, $' ); $v =~ /^('|")(.*)\1$/ && ( $v = $2 ); $h->{ $k } = $v; }, @t; $h; } sub prop_conv { # ( xml )に属性( h )を置換テーブル( p )( pr )で埋め込む。 my ( $t, $h, $p, $pr, ) = @_; $t =~ s/\$nul//g; while( $t =~ /〓(?![^@]*(?:〓|$))([^@]*)[@]([@]?[\w-]+)(.*?)(〓|$)/ ) { my ( $s, $cs, $k, $cr, $r ) = ( $`, $1, $2, $3, $' ); $k =~ /^[@]/ && ( $h->{ $k } = $pr->{ $' } ); my $c = prop_alt( $k, $h, $p ); $c eq "" || ( $c = "$cs$c$cr" ); $t = "$s$c$r"; } $t; } sub prop_alt { # 属性( prop )を置換テーブル( p )で置換する。 my ( $k, $h, $p, ) = @_; defined( $p->{ $k } ) || return ""; my @a = @{$p->{ $k }}; my $u = ""; @a = map do { /^=>/ && ( $u = $' ); /^=>/ ? () : $_; }, @a; my $r = $h->{ $k }; defined( $r ) || return $u; for( @a ) { /=>/; my ( $f, $v ) = ( $`, $' ); $r =~ /^$f$/i || next; my @v = ( $&, $1, $2, $3, $4, $5, $6, $7, $8, $9 ); $v =~ s/\$&/$v[ 0 ]/g; for( 1 .. 9 ) { $v =~ s/\$$_/$v[ $_ ]/g; } $v =~ s/cat\(\s*(\S+)\s*\)/@{$cat{ $1 }}[0]/g; $v =~ s/country\(\s*(\S+)\s*\)/@{$country{ $1 }}[0]/g; $v =~ s/\@([\w-]+)(?=\s|$)/$h->{ $1 }/g; my @t = (); my $sn = $k eq '...' ? @{$p->{ 'variablelabel.style' }}[0] : @{$p->{ $k.'.style' }}[0]; while( $v =~ /para\(\s*-style:(.*?)-\s*(.*\S)\s*\)/ ) { push @t, [ $`, $sn ]; push @t, [ $2, $1 ]; $v = $'; } push @t, [ $v, $sn ]; $v = join '', map do { my $r = ""; @$_[0] ne "" && @$_[1] ne "" && join '', ( '', pStyle( @$_[1], $p ), '', rStyle( '' ), '', @$_[0], '', '', '', ); }, @t; return $v; } return $u; } # - xmlへの変換 属性を調べる sub pStyle { # 段落属性( sn )( p )を調べる。 my ( $sn, $p, ) = @_; defined( $p ) || ( $p = '' ); $sn = pStyle_sel( $sn, $p ); defined( $styleId{ $sn } ) && $styleId{ $sn } ne "" || return ""; join '', ( '', '', ( $p =~ /-(right|center)-/ || $p =~ /[<\s]align="(right|center)"/ ? '' : () ), '' ); } sub pStyle_sel { # 段落属性( sn )を( p )で選ぶ。 my ( $sn, $p, ) = @_; if( $sn =~ /\@variablelabel=/ ) { my $v = ( $p =~ /[<\s]variablelabel(?:-off)?="([^"]*)"/ ) ? $1 : '' ; $v = quotemeta_ja( $v ); $sn =~ s/\@variablelabel=$v?(.*?):/$1/; $sn =~ s/\@variablelabel=.*??.*?://g; } $sn; } sub rStyle { # 文字属性( tags )を調べる。 my ( $tags, ) = @_; join '', ( '', '', '', ( map do { $tags =~ /<$_>/ ? $atr_tag{ $_ } : () }, keys %atr_tag ), ( rColor() ), '', ); } sub rColor { # 色属性を調べる。 my $c = ""; for( @pare ) { defined( $_->{ 'color' } ) && ( $c = $_->{ 'color' }, last ); } $c = ( $c eq "" ) ? undef : $cols{ $c } ; defined( $c ) ? '' : '' ; } sub rLevel { # ( tags )のレベルを調べる。 my ( $tags, ) = @_; my @l = map do { $tag2num{ '' } }, 1 .. 5; my $l = '<('.( join '|', @l ).')>'; my $lv = 1; $tags =~ /^.*$l/ && $tag[ $1 - 1 ] =~ // && ( $lv = $1 ); my $s = ( $lv == 4 ) ? 'a' : $lv == 5 ? 'i' : 1 ; ( $lv, $s ); } sub ListStyle { # 順序番号の文字属性( tags )を調べる。 my ( $tags, ) = @_; my $wt = '<'.$tag2num{ "" }.'>'; join '', ( '', '', '', ( $tags =~ /$wt/ ? '' : '' ), '', ); } sub rList { # 順序番号を調べる( tags )( prop )。 my ( $tags, $prop, ) = @_; my $sn = $prop->{ 'serialno' }; defined( $sn ) && $sn ne '' || return ""; my $it = '<'.$tag2num{ "" }.'>'; my $ln = ( $tags =~ /$it$/ ) ? 'ln1' : 'ln2' ; my ( $lv, $lv_s ) = rLevel( $tags ); my $l = "LISTNUM \"$ln\" \\l $lv".( $sn == $lv_s ? " \\s 1" : "" ); my $rs = ListStyle( $tags ); join '', ( '', $rs, '', '', '', $rs, ' ', '', '', $rs, '', $l, '', '', '', $rs, ' ', '', '', $rs, '', '', '', ); } # - xmlへの変換 冒頭の情報 sub getF_top { # 冒頭の情報を( fn )から読む。 -f $_[0] || err( ' 冒頭の情報のファイルがありません: '.$_[0] ); @top = map do { s/\x0d?\x0a$//; /\S/ ? $_ : () ; }, getF( $_[0] ); } # - xmlへの変換 表組 sub chk_rowspan { # 前の行のrowspanを調べる( p )。 my $tc = $tag2num{ "" }; my $rs = ( $p =~ /[<\s]rspan="0*(\d+)"/ ) ? $1 : 1 ; my $cs = ( $p =~ /[<\s]cspan="0*(\d+)"/ ) ? $1 : 1 ; push @span_n, [ $rs, $cs ]; my $r = ''; while( $cs ) { $cs--; while( @span_p ) { my $sp = shift @span_p; $$sp[0] || last; $r .= join '', ( '', '', '', '', ( 1 < $$sp[1] ? '' : () ), '', '', '', pStyle( '表内_項目' ), '', '', ); } } $r; } # - xmlへの変換 強制改行・画像 sub getS { # 画像( fn )の幅・高さを調べる。 my $fn = $_[0]; my $width = 230; my @s = ( $width, $width ); my $buf = ''; my $d = '[\x00-\xff]'; -f $fol.$fn && open( IN, '<'.$fol.$fn ) && read( IN, $buf, 10 ) == 10 || ( msg( ' 画像ファイルを読めません( '.$fn.' )。' ), return @s ); $fn =~ /\.gif$/i && $buf =~ /^GIF...($d$d)($d$d)/ && ( $s[0] = unpack( 'v', $1 ), $s[1] = unpack( 'v', $2 ) ); my $pos = 2; my $ok = 1; my @w = (); $fn =~ /\.jpg$/i && $buf =~ /^\xff\xd8\xff(\xe0)($d$d)JFIF/ && do { while( $1 ne "\xc0" && $1 ne "\xc0" ) { $pos += 2 + unpack( 'n', $2 ); seek( IN, $pos, 0 ); read( IN, $buf, 9 ) == 9 || ( $ok = 0, last ); $buf =~ /^\xff($d)($d$d)$d($d$d)($d$d)/; @w = ( $4, $3 ); } $ok && ( @s = map do { unpack( 'n', $_ ) }, @w ); }; close( IN ); my $sc = 0.6; @s = map do { $_ * $sc }, @s; my $k = 1; for( @s ) { $_ / $width > $k && ( $k = $_ / $width ) } $k > 1 && ( @s = map do { int( $_ / $k + 0.5 ) }, @s ); @s; } # - 変換規則 sub getF_s2w { # 変換規則を( fn )から読む。 -f $_[0] || err( ' 変換規則のファイルがありません: '.$_[0] ); @s2w = map do { s/\x0d?\x0a$//; /\S/ ? $_ : () ; }, getF( $_[0] ); } sub putF_tag2num { # タグをタグ番号に置換するテーブルを( fn )に出力する。 my @t = map do { "$tag2num{ $_ }:$_" }, sort keys %tag2num; putF( $_[0], join "\x0a", @t, "" ); } # - 変換規則 path2xml sub putF_path2xml { # xmlのタグに置換するテーブルを( fn )に出力する。 sub sort_path { my $sa = $a; $sa =~ s/<\/(\d*)>/<$1>\//g; my $sb = $b; $sa =~ s/<\/(\d*)>/<$1>\//g; $sa cmp $sb; } my @k = sort sort_path keys %path2xml; @k = map do { "$_:\x0a".( join "", map do { "\t$_\x0a" }, @{$path2xml{ $_ }} ); }, @k; putF( $_[0], join "\x0a", @k, "" ); } # - 補助の定型ルーチン sub utf8_to_cp932 { # utf8文字列( str )を cp932文字列に変換する encode( "cp932", decode( "utf8", $_[0] ) ); } sub cp932_to_utf8 { # cp932文字列( str )を utf8文字列に変換する encode( "utf8", decode( "cp932", $_[0] ) ); } sub quotemeta_ja { # 日本語文字列( str )のquotemeta join '', map do{ s/(.)([\x40\x5b-\x60\x7b-\x7f])/$1\\$2/; $_ ; }, ( $_[0] =~ /([\x00-\x7f\xa0-\xdf]|..)/g ); } sub getF { # ファイル( name )を読む。 open( IN, '<'.$_[0] ) || err( 'オープンエラー:'.$_[0] ); my @buf = ; close( IN ); @buf; } sub putF { # ファイル( name )に( string )を出力する。 if( open( OUT, '>'.$_[0] ) ) { print OUT $_[1]; close( OUT ); } else { err( 'オープンエラー:'.$_[0] ); } } sub addF { # ファイル( name )に( string )を追記する。 if( open( OUT, '>>'.$_[0] ) ) { print OUT $_[1]; close( OUT ); } else { err( 'オープンエラー:'.$_[0] ); } } sub err { # メッセージ( array )を表示して エラー終了する。 msg( @_ ); exit( 1 ); } sub msg { # メッセージ( array )を表示する。 print map do { $_."\x0a" }, @_; } # - 構文 # - ライセンス # ~ スクリプトの冒頭に記述。 # - ライブラリ # ~ スクリプトの冒頭に記述。