diff options
Diffstat (limited to 'Master/texmf-dist/scripts/convbkmk/convbkmk.rb')
-rwxr-xr-x | Master/texmf-dist/scripts/convbkmk/convbkmk.rb | 502 |
1 files changed, 502 insertions, 0 deletions
diff --git a/Master/texmf-dist/scripts/convbkmk/convbkmk.rb b/Master/texmf-dist/scripts/convbkmk/convbkmk.rb new file mode 100755 index 00000000000..2c566fe6301 --- /dev/null +++ b/Master/texmf-dist/scripts/convbkmk/convbkmk.rb @@ -0,0 +1,502 @@ +#!/usr/bin/ruby +# -*- coding: utf-8 -*- + +=begin + += convbkmk Ver.0.08 + + 2013.05.11 + Takuji Tanaka + KXD02663 (at) nifty.ne.jp +((<URL:http://homepage3.nifty.com/ttk/comp/tex/uptex_en.html>)) + + +== Abstract + +((*convbkmk*)) is a tiny utility for making correct bookmarks in pdf files +typesetted by platex/uplatex with the hyperref package. +platex/uplatex + hyperref outputs data of bookmarks +in their internal encodings (EUC-JP, Shift_JIS or UTF-8). +On the other hand, the PostScript/PDF format requests that +the data is written in a certain syntax with UTF-16 or PDFDocEncoding. +Thus, data conversion is required to create correct bookmarks. +((*convbkmk*)) provides a function of +the encoding conversion and formatting the bookmark data. + +== Requirement + +ruby 1.8.3 or later + +== Examples + +platex (internal kanji code: euc) + hyperref + dvips : + $ platex doc00.tex + $ platex doc00.tex + $ dvips doc00.dvi + $ convbkmk.rb -e doc00.ps + $ ps2pdf doc00-convbkmk.ps + +platex (internal kanji code: sjis) + hyperref + dvipdfmx : + $ platex doc01.tex + $ platex doc01.tex + $ convbkmk.rb -s -o doc01.out + $ platex doc01.tex + $ dvipdfmx doc01.dvi + +uplatex + hyperref + dvips : + $ uplatex doc02.tex + $ uplatex doc02.tex + $ dvips doc02.dvi + $ convbkmk.rb doc02.ps + $ ps2pdf doc02-convbkmk.ps + +uplatex + hyperref + dvipdfmx : + $ uplatex doc03.tex + $ uplatex doc03.tex + $ convbkmk.rb -o doc03.out + $ uplatex doc03.tex + $ dvipdfmx doc03.dvi + +More examples are included in the uptex source archive. + +== License + +convbkmk + +Copyright (c) 2009-2013 Takuji Tanaka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +== History + +: 2009.08.02 0.00 + * Initial version. +: 2011.05.02 0.01 + * Bug fix: BOM was not correct. +: 2012.05.08 0.02 + * Bug fix: for a case of dvips with -z option and Ruby1.8. + * Add conversion of /Creator and /Producer . +: 2012.05.12 0.03 + * Suppress halfwidth -> fullwidth katakana conversion and MIME decoding in Ruby1.8. +: 2012.06.01 0.04 + * Support escape sequences: \n, \r, \t, \b, \f, \\, \ddd (octal, PDFDocEncoding) and \0xUUUU (Unicode UTF-16BE). + * Support sequences of end of line: '\' or other followed by "\n", "\r\n" or "\r" . + * Set file IO to binary mode. +: 2012.07.26 0.05 + * Add -o option to support conversion of OUT files generated by dvipdfmx. +: 2012.08.07 0.06 + * Bug fix: Ver.0.05 does not work with Ruby1.9. +: 2012.09.17 0.07 + * Bug fix: An infinite loop occurs in Ver.0.05, 0.06 with -g option in some cases. + * Add reference for PDFDocEncoding. +: 2013.05.11 0.08 + * Add -O option: overwrite output files onto input files instead of creating foo-convbkmk.ps . + * Make comments rd/rdtool friendly. + +=end + +Version = "0.08" + +require "optparse" + +if RUBY_VERSION >= "1.9" + $RUBY_M17N = true + + class String + def to_utf8(enc) + self.force_encoding(enc.current).encode('UTF-8') + end + def utf16be_to_utf8 + self.force_encoding('UTF-16BE').encode('UTF-8') + end + def utf8_to_utf16be + self.force_encoding('UTF-8').encode('UTF-16BE') + end + end +else + $RUBY_M17N = false + + require "jcode" # for method each_char + require "nkf" + class String + def to_utf8(enc) + NKF.nkf('-w -x -m0 '+enc.kconv_enc, self) + end + def utf16be_to_utf8 + NKF.nkf('-w -x -m0 --utf16-input', self) + end + def utf8_to_utf16be + NKF.nkf('-w16 -x -m0 --utf8-input', self) + end + def ascii_only? + return self !~ /[\x80-\xFF]/n + end + REPLACE_TABLE = {"\n" => "\\n", "\r" => "\\r", "\t" => "\\t", + "\b" => "\\b", "\f" => "\\f", "\e" => "\\e", "\"" => "\\\""} + def valid_encoding? + conv = '' + tmp = self.dup + while tmp.length>0 do + case tmp + when /\A\\\\/ + conv += "\\\\\\\\" + when /\A\\[nrtbf()]/, /\A\\[0-3][0-7][0-7]/, /\A\\0x[0-9A-F]{4}/i + conv += "\\"+$& + when /\A\\/ + conv += "\\\\" + when /\A[^\\]*/ + conv += $& + else + raise 'unexpected input!' + end + tmp = $' + end + conv.gsub!(/[\n\r\t\b\f\e"]/) { |s| REPLACE_TABLE[s] } + conv.gsub!(/([\000-\037\177])/) { "\\%#03o" % $1.unpack("C*") } + tmp = "\"" + conv + "\"" +# puts 's:' + self +# puts 't:' + tmp +# puts 'i:' + self.inspect + return tmp == self.inspect + end + def force_encoding(enc) + if (enc =~ /^(Shift_JIS|EUC-JP|UTF-8)/i) + $KCODE=enc + end + return self + end + end +end + +class TeXEncoding + attr_accessor :current, :option, :status, :is_8bit, :kconv_enc + attr_reader :list + + def initialize + @current = false + @option = false + @status = false + @is_8bit = false + @list = ['Shift_JIS', 'EUC-JP', 'UTF-8'] + if !$RUBY_M17N + @kconv_enc = nil + @kconv_list = {'Shift_JIS' => '--sjis-input', + 'EUC-JP' => '--euc-input', + 'UTF-8' => '--utf8-input'} + end + end + + def set_process_encoding(enc) + if @status == 'fixed' + raise 'dupulicate definition' + end + if enc == 'guess' + @option = 'guess' + @status = 'guess' + else + @current = enc + @option = enc + @status = 'fixed' + if !$RUBY_M17N + @kconv_enc = @kconv_list[enc] + end + end + return enc + end +end +enc = TeXEncoding.new + +Opts = {} + +OptionParser.new do |opt| + opt.on('-e', '--euc-jp', + 'set pTeX internal encoding to EUC-JP') {|v| + enc.set_process_encoding('EUC-JP') + } + opt.on('-s', '--shift_jis', + 'set pTeX internal encoding to Shift_JIS') {|v| + enc.set_process_encoding('Shift_JIS') + } + opt.on('-u', '--utf-8', + 'set upTeX internal encoding to UTF-8') {|v| + enc.set_process_encoding('UTF-8') + } + opt.on('-g', '--guess', + 'guess pTeX/upTeX internal encoding') {|v| + enc.set_process_encoding('guess') + } + enc_alias = Hash.new + enc.list.each { |e| + enc_alias[e] = e + enc_alias[e[0]] = e + enc_alias[e.downcase] = e + } + opt.on('--enc=ENC', enc_alias, + 'set pTeX/upTeX internal encoding to ENC') {|v| + enc.set_process_encoding(v) + } + opt.on('-o', '--out', + 'treat OUT files') {|v| + Opts[:mode] = 'out' + require "fileutils" + } + opt.on('-O', '--overwrite', + 'overwrite output files') {|v| + Opts[:mode] = 'overwrite' + require "fileutils" + } + opt.banner += " file0.ps [file1.ps ...]\n" \ + + opt.banner.sub('Usage:',' ') + " < in_file.ps > out_file.ps\n" \ + + opt.banner.sub('Usage:',' ') + ' -o file0.out [file1.out ...]' + + opt.parse! +end + +# default encoding +if enc.status == false + enc.set_process_encoding('UTF-8') +end +if Opts[:mode] == 'out' + OpenP, CloseP, OpenPEsc, ClosePEsc = '{', '}', '\{', '\}' + FileSfx = 'out' +else + OpenP, CloseP, OpenPEsc, ClosePEsc = '(', ')', '\(', '\)' + FileSfx = 'ps' +end + +def try_guess_encoding(line, enc) + return 'US-ASCII' if line.ascii_only? + + enc.is_8bit = true + valid_enc = false + count = 0 + enc.list.each { |e| + if line.dup.force_encoding(e).valid_encoding? + count += 1 + valid_enc = e + end + } + if count == 1 + enc.set_process_encoding(valid_enc) + return valid_enc + elsif count > 1 + return false # ambiguous + else + raise 'Cannot guess encoding!' + end +end + + +def check_parentheses_balance(line, enc) + depth = 0 + count = 0 + tmp_prev = '' + tmp_rest = line + + if enc.status == 'guess' + if tmp_enc = try_guess_encoding(line, enc) + # succeeded in guess or ascii only + tmp_rest = line.force_encoding(tmp_enc) + else + # ambiguous + raise 'unexpected internal condition!' + end + else + tmp_enc = enc.current + tmp_rest = tmp_rest.force_encoding(tmp_enc) + unless tmp_rest.valid_encoding? + # illegal input + $stdout = STDERR + p 'parameters: ' + p ' status: ' + enc.status + p ' option: ' + enc.option + p ' current: ' + enc.current + p enc.is_8bit + if !$RUBY_M17N + p ' inspect: ' + tmp_rest.inspect + p ' rest : [' + tmp_rest + ']' + end + p ' [' + line + ']' + raise 'encoding is not consistent' + end + end + + while tmp_rest.length>0 do + if (tmp_rest =~ /\A(\\#{OpenPEsc}|\\#{ClosePEsc}|[^#{OpenP}#{CloseP}])*(#{OpenPEsc}|#{ClosePEsc})/o) # parenthis + if $2 == OpenP + depth += 1 + count += 1 + else + depth -= 1 + end + tmp_prev += $& + tmp_rest = $' + else + tmp_prev += tmp_rest + tmp_rest = '' + end + if depth<1 + break + end + end + return depth, count, tmp_prev, tmp_rest +end + +# PDFDocEncoding -> UTF-16BE +# Ref. "PDF Reference, Sixth Edition, version 1.7", 2006, Adobe Systems Incorporated +# http://www.adobe.com/devnet/pdf/pdf_reference_archive.html +# http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf +PDF2UNI = Array(0..255) +PDF2UNI[0o030..0o037] = 0x02d8, 0x02c7, 0x02c6, 0x02d9, 0x02dd, 0x02db, 0x02da, 0x02dc +PDF2UNI[0o200..0o207] = 0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x0192, 0x2044 +PDF2UNI[0o210..0o217] = 0x2039, 0x203a, 0x2212, 0x2030, 0x201e, 0x201c, 0x201d, 0x2018 +PDF2UNI[0o220..0o227] = 0x2019, 0x201a, 0x2122, 0xfb01, 0xfb02, 0x0141, 0x0152, 0x0160 +PDF2UNI[0o230..0o237] = 0x0178, 0x017d, 0x0131, 0x0142, 0x0153, 0x0161, 0x017e, 0xfffd +PDF2UNI[0o240 ] = 0x20ac +PDF2UNI[0o255 ] = 0xfffd + +def conv_string_to_utf16be(line, enc) + if line !~ /(#{OpenPEsc})(.*)(#{ClosePEsc})/mo + raise 'illegal input!' + end + pre, tmp, post = $`, $2, $' + + if tmp.ascii_only? && tmp !~ /\\0x[0-9A-F]{4}/i + return line + end + + conv = '' + conv.force_encoding('UTF-8') + tmp.force_encoding(enc.current) + + while tmp.length>0 do + case tmp + when /\A[^\\\n\r]+/ + conv += $&.to_utf8(enc) + when /\A\\([0-3][0-7][0-7])/ # PDFDocEncoding -> UTF-8 + conv += [PDF2UNI[$1.oct]].pack("U*") + when /\A\\0x(D[8-B][0-9A-F]{2})\\0x(D[C-F][0-9A-F]{2})/i # surrogate pair + conv += [$1.hex, $2.hex].pack("n*").utf16be_to_utf8 + when /\A\\0x([0-9A-F]{4})/i + conv += [$1.hex].pack("U*") + when /\A\\[nrtbf\\]/ + conv += eval(%!"#{$&}"!) + when /\A(\r\n|\r|\n)/ + conv += "\n" + when /\A\\([\r\n]{1,2})|\\/ + # ignore + else + raise 'unexpected input!' + end + tmp = $' + end + + buf = '' + conv16be = "\xFE\xFF" # BOM U+FEFF + conv16be.force_encoding('UTF-16BE') if $RUBY_M17N + conv16be += conv.utf8_to_utf16be # UTF-16BE with BOM + conv16be.each_byte {|byte| + buf += (Opts[:mode] == 'out' ? '\%03o' : '%02X') % byte + } + buf = Opts[:mode] == 'out' ? '{' + buf + '}' : '<' + buf + '>' + return pre + buf + post +end + + +def file_treatment(ifile, ofile, enc) + if $RUBY_M17N + ifile.set_encoding('ASCII-8BIT') + ofile.set_encoding('ASCII-8BIT') + end + + line = '' + while l = ifile.gets do + line.force_encoding('ASCII-8BIT') if $RUBY_M17N + line += l + reg = Opts[:mode] == 'out' ? %r!(\{)! : %r!(/Title|/Author|/Keywords|/Subject|/Creator|/Producer)(\s+\(|$)! + if (line !~ reg ) + ofile.print line + line = '' + next + end + + ofile.print $` + line = $& + $' + + if enc.status == 'guess' + if tmp_enc = try_guess_encoding(line, enc) + # succeeded in guess or ascii only + line.force_encoding(tmp_enc) + else + # ambiguous + next + end + end + + while line.length>0 do + depth, count, tmp_prev, tmp_rest \ + = check_parentheses_balance(line, enc) + if depth<0 + p depth, count, tmp_prev, tmp_rest + raise 'illegal input! (depth<0)' + elsif depth>0 + break + elsif count==0 + ofile.print line + line = '' + break + elsif count>0 + ofile.print conv_string_to_utf16be(tmp_prev, enc) + line = tmp_rest + else + p depth, count, tmp_prev, tmp_rest + raise 'illegal input! (count<0)' + end + end + + end + + if enc.status == 'guess' && enc.is_8bit + raise 'did not succeed in guess encoding!' + end +end + + +### main +if ARGV.size == 0 + ifile = STDIN + ofile = STDOUT + file_treatment(ifile, ofile, enc) +else + ARGV.each {|fin| + if (fin !~ /\.#{FileSfx}$/io) + raise 'input file does not seem ' + FileSfx.upcase + ' file' + end + fout = fin.gsub(/\.#{FileSfx}$/io, "-convbkmk#{$&}") + open(fin, 'rb') {|ifile| + open(fout, 'wb') {|ofile| + file_treatment(ifile, ofile, enc) + } + } + if (Opts[:mode] == 'out' || Opts[:mode] == 'overwrite') + FileUtils.mv(fout, fin) + end + } +end + |