# encoding: utf-8 # # Copyright (c) 2002-2007 Minero Aoki # 2008-2016 Minero Aoki, Kenshi Muto # # This program is free software. # You can distribute or modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # require 'review/builder' require 'review/htmlutils' require 'review/textutils' require 'nkf' module ReVIEW class IDGXMLBuilder < Builder include TextUtils include HTMLUtils [:ttbold, :hint, :maru, :keytop, :labelref, :ref, :pageref, :balloon].each {|e| Compiler.definline(e) } Compiler.defsingle(:dtp, 1) Compiler.defblock(:insn, 0..1) Compiler.defblock(:planning, 0..1) Compiler.defblock(:best, 0..1) Compiler.defblock(:security, 0..1) Compiler.defblock(:point, 0..1) Compiler.defblock(:shoot, 0..1) Compiler.defblock(:reference, 0) Compiler.defblock(:term, 0) Compiler.defblock(:link, 0..1) Compiler.defblock(:practice, 0) Compiler.defblock(:expert, 0) Compiler.defblock(:rawblock, 0) def pre_paragraph '

' end def post_paragraph '

' end def extname '.xml' end def builder_init(no_error = false) @no_error = no_error end private :builder_init def builder_init_file @warns = [] @errors = [] @section = 0 @subsection = 0 @subsubsection = 0 @subsubsubsection = 0 @sec_counter = SecCounter.new(5, @chapter) @column = 0 @noindent = nil @ol_num = nil @rootelement = "doc" @secttags = nil @tsize = nil @texblockequation = 0 @texinlineequation = 0 print %Q(\n) print %Q(<#{@rootelement} xmlns:aid="http://ns.adobe.com/AdobeInDesign/4.0/">) @secttags = true unless @book.config["structuredxml"].nil? end private :builder_init_file def puts(arg) if @book.config["nolf"].present? print arg else super end end def result s = "" unless @secttags.nil? s += "" if @subsubsubsection > 0 s += "" if @subsubsection > 0 s += "" if @subsection > 0 s += "" if @section > 0 s += "" if @chapter.number > 0 end messages() + @output.string + s + "\n" end def warn(msg) if @no_error @warns.push [@location.filename, @location.lineno, msg] puts "----WARNING: #{escape_html(msg)}----" else $stderr.puts "#{@location}: warning: #{msg}" end end def error(msg) if @no_error @errors.push [@location.filename, @location.lineno, msg] puts "----ERROR: #{escape_html(msg)}----" else $stderr.puts "#{@location}: error: #{msg}" end end def messages error_messages() + warning_messages() end def error_messages return '' if @errors.empty? "

Syntax Errors

\n" + "\n" end def warning_messages return '' if @warns.empty? "

Warnings

\n" + "\n" end def headline(level, label, caption) case level when 1 unless @secttags.nil? print "" if @subsubsubsection > 0 print "" if @subsubsection > 0 print "" if @subsection > 0 print "" if @section > 0 end print %Q() unless @secttags.nil? @section = 0 @subsection = 0 @subsubsection = 0 @subsubsubsection = 0 when 2 unless @secttags.nil? print "" if @subsubsubsection > 0 print "" if @subsubsection > 0 print "" if @subsection > 0 print "" if @section > 0 end @section += 1 print %Q() unless @secttags.nil? @subsection = 0 @subsubsection = 0 @subsubsubsection = 0 when 3 unless @secttags.nil? print "" if @subsubsubsection > 0 print "" if @subsubsection > 0 print "" if @subsection > 0 end @subsection += 1 print %Q() unless @secttags.nil? @subsubsection = 0 @subsubsubsection = 0 when 4 unless @secttags.nil? print "" if @subsubsubsection > 0 print "" if @subsubsection > 0 end @subsubsection += 1 print %Q() unless @secttags.nil? @subsubsubsection = 0 when 5 unless @secttags.nil? print "" if @subsubsubsection > 0 end @subsubsubsection += 1 print %Q() unless @secttags.nil? else raise "caption level too deep or unsupported: #{level}" end prefix, anchor = headline_prefix(level) label = label.nil? ? "" : " id=\"#{label}\"" toccaption = escape_html(compile_inline(caption.gsub(/@\{.+?\}/, '')).gsub(/<[^>]+>/, '')) puts %Q(#{prefix}#{compile_inline(caption)}) end def ul_begin level = block_given? ? yield : "" level = nil if level == 1 puts "" end def ul_item(lines) puts %Q(
  • #{lines.join.chomp}
  • ) end def ul_item_begin(lines) print %Q(
  • #{lines.join.chomp}) end def ul_item_end puts "
  • " end def choice_single_begin puts "" end def choice_multi_begin puts "" end def choice_single_end puts "" end def choice_multi_end puts "" end def ul_end level = block_given? ? yield : "" level = nil if level == 1 puts "" end def ol_begin puts '
      ' if !@ol_num @ol_num = 1 end end def ol_item(lines, num) puts %Q(
    1. #{lines.join.chomp}
    2. ) @ol_num += 1 end def ol_end puts '
    ' @ol_num = nil end def olnum(num) @ol_num = num.to_i end def dl_begin puts '
    ' end def dt(line) puts "
    #{line}
    " end def dd(lines) puts "
    #{lines.join.chomp}
    " end def dl_end puts '
    ' end def paragraph(lines) if @noindent.nil? if lines[0] =~ /\A(\t+)/ puts %Q(

    #{lines.join('').sub(/\A\t+/, "")}

    ) else puts "

    #{lines.join}

    " end else puts %Q(

    #{lines.join}

    ) @noindent = nil end end def read(lines) puts %Q[#{split_paragraph(lines).join}] end alias_method :lead, :read def column_label(id) num = @chapter.column(id).number "column-#{num}" end private :column_label def inline_column_chap(chapter, id) if @book.config["chapterlink"] %Q(#{escape_html(chapter.column(id).caption)}) else escape_html(chapter.column(id).caption) end end def inline_list(id) chapter, id = extract_chapter_id(id) if get_chap(chapter).nil? "#{I18n.t("list")}#{I18n.t("format_number_without_chapter", [chapter.list(id).number])}" else "#{I18n.t("list")}#{I18n.t("format_number", [get_chap(chapter), chapter.list(id).number])}" end end def list_header(id, caption, lang) puts %Q[] if get_chap.nil? puts %Q[#{I18n.t("list")}#{I18n.t("format_number_without_chapter", [@chapter.list(id).number])}#{I18n.t("caption_prefix_idgxml")}#{compile_inline(caption)}] else puts %Q[#{I18n.t("list")}#{I18n.t("format_number", [get_chap, @chapter.list(id).number])}#{I18n.t("caption_prefix_idgxml")}#{compile_inline(caption)}] end end def codelines_body(lines) no = 1 lines.each do |line| unless @book.config["listinfo"].nil? print "" end print detab(line) print "\n" print "" unless @book.config["listinfo"].nil? no += 1 end end def list_body(id, lines, lang) print %Q(
    )
          codelines_body(lines)
          puts "
    " end def emlist(lines, caption = nil, lang = nil) quotedlist lines, 'emlist', caption end def emlistnum(lines, caption = nil, lang = nil) _lines = [] lines.each_with_index do |line, i| _lines << detab("" + (i + 1).to_s.rjust(2) + ": " + line) end quotedlist _lines, 'emlistnum', caption end def listnum_body(lines, lang) print %Q(
    )
          no = 1
          lines.each_with_index do |line, i|
            unless @book.config["listinfo"].nil?
              print ""
            end
            print detab("" + (i + 1).to_s.rjust(2) + ": " + line)
            print "\n"
            print "" unless @book.config["listinfo"].nil?
            no += 1
          end
          puts "
    " end def cmd(lines, caption = nil) quotedlist lines, 'cmd', caption end def quotedlist(lines, css_class, caption) print %Q[] puts "#{compile_inline(caption)}" unless caption.nil? print %Q[
    ]
          no = 1
          lines.each do |line|
            unless @book.config["listinfo"].nil?
              print ""
            end
            print detab(line)
            print "\n"
            print "" unless @book.config["listinfo"].nil?
            no += 1
          end
          puts '
    ' end private :quotedlist def quote(lines) blocked_lines = split_paragraph(lines) puts "#{blocked_lines.join("")}" end def inline_table(id) chapter, id = extract_chapter_id(id) if get_chap(chapter).nil? "#{I18n.t("table")}#{I18n.t("format_number_without_chapter", [chapter.table(id).number])}" else "#{I18n.t("table")}#{I18n.t("format_number", [get_chap(chapter), chapter.table(id).number])}" end end def inline_img(id) chapter, id = extract_chapter_id(id) if get_chap(chapter).nil? "#{I18n.t("image")}#{I18n.t("format_number_without_chapter", [chapter.image(id).number])}" else "#{I18n.t("image")}#{I18n.t("format_number", [get_chap(chapter), chapter.image(id).number])}" end end def inline_imgref(id) chapter, id = extract_chapter_id(id) if chapter.image(id).caption.blank? inline_img(id) else if get_chap(chapter).nil? "#{I18n.t("image")}#{I18n.t("format_number_without_chapter", [chapter.image(id).number])}#{I18n.t('image_quote', chapter.image(id).caption)}" else "#{I18n.t("image")}#{I18n.t("format_number", [get_chap(chapter), chapter.image(id).number])}#{I18n.t('image_quote', chapter.image(id).caption)}" end end end def handle_metric(str) k, v = str.split('=', 2) return %Q|#{k}=\"#{v.sub(/\A["']/, '').sub(/["']\Z/, '')}\"| end def result_metric(array) " #{array.join(' ')}" end def image_image(id, caption, metric=nil) metrics = parse_metric("idgxml", metric) puts "" puts %Q[] image_header id, caption puts "" end def image_dummy(id, caption, lines) puts "" print %Q[
    ]
          lines.each do |line|
            print detab(line)
            print "\n"
          end
          print %Q[
    ] image_header id, caption puts "" warn "no such image: #{id}" end def image_header(id, caption) if get_chap.nil? puts %Q[#{I18n.t("image")}#{I18n.t("format_number_without_chapter", [@chapter.image(id).number])}#{I18n.t("caption_prefix_idgxml")}#{compile_inline(caption)}] else puts %Q[#{I18n.t("image")}#{I18n.t("format_number", [get_chap, @chapter.image(id).number])}#{I18n.t("caption_prefix_idgxml")}#{compile_inline(caption)}] end end def texequation(lines) @texblockequation += 1 puts %Q[] puts '
    '
          puts "#{lines.join("\n")}"
          puts '
    ' puts '
    ' end def table(lines, id = nil, caption = nil) tablewidth = nil col = 0 if @book.config["tableopt"] tablewidth = @book.config["tableopt"].split(",")[0].to_f / @book.config["pt_to_mm_unit"].to_f end puts "" rows = [] sepidx = nil lines.each_with_index do |line, idx| if /\A[\=\-]{12}/ =~ line sepidx ||= idx next end if tablewidth.nil? rows.push(line.gsub(/\t\.\t/, "\t\t").gsub(/\t\.\.\t/, "\t.\t").gsub(/\t\.\Z/, "\t").gsub(/\t\.\.\Z/, "\t.").gsub(/\A\./, "")) else rows.push(line.gsub(/\t\.\t/, "\tDUMMYCELLSPLITTER\t").gsub(/\t\.\.\t/, "\t.\t").gsub(/\t\.\Z/, "\tDUMMYCELLSPLITTER").gsub(/\t\.\.\Z/, "\t.").gsub(/\A\./, "")) end _col = rows[rows.length - 1].split(/\t/).length col = _col if _col > col end cellwidth = [] unless tablewidth.nil? if @tsize.nil? col.times {|n| cellwidth[n] = tablewidth / col } else cellwidth = @tsize.split(/\s*,\s*/) totallength = 0 cellwidth.size.times do |n| cellwidth[n] = cellwidth[n].to_f / @book.config["pt_to_mm_unit"].to_f totallength += cellwidth[n] warn "total length exceeds limit for table: #{id}" if totallength > tablewidth end if cellwidth.size < col cw = (tablewidth - totallength) / (col - cellwidth.size) warn "auto cell sizing exceeds limit for table: #{id}" if cw <= 0 for i in cellwidth.size..(col - 1) cellwidth[i] = cw end end end end begin table_header id, caption unless caption.nil? rescue KeyError error "no such table: #{id}" end return if rows.empty? if tablewidth.nil? print "" else print %Q[] end if sepidx sepidx.times do |y| if tablewidth.nil? puts %Q[#{rows.shift}] else i = 0 rows.shift.split(/\t/).each_with_index do |cell, x| print %Q[] i += 1 end end end end trputs(tablewidth, rows, cellwidth, sepidx) puts "
    #{cell.sub("DUMMYCELLSPLITTER", "")}
    " @tsize = nil end def trputs(tablewidth, rows, cellwidth, sepidx) sepidx = 0 if sepidx.nil? if tablewidth.nil? lastline = rows.pop rows.each {|row| puts %Q[#{row}] } puts %Q[#{lastline}] unless lastline.nil? else rows.each_with_index do |row, y| i = 0 row.split(/\t/).each_with_index do |cell, x| print %Q[#{cell.sub("DUMMYCELLSPLITTER", "")}] i += 1 end end end end def table_header(id, caption) if get_chap.nil? puts %Q[#{I18n.t("table")}#{I18n.t("format_number_without_chapter", [@chapter.table(id).number])}#{I18n.t("caption_prefix_idgxml")}#{compile_inline(caption)}] else puts %Q[#{I18n.t("table")}#{I18n.t("format_number", [get_chap, @chapter.table(id).number])}#{I18n.t("caption_prefix_idgxml")}#{compile_inline(caption)}] end end def table_begin(ncols) end def tr(rows) puts %Q[#{rows.join("\t")}] end def th(str) %Q[#{str}] end def td(str) str end def table_end print "" end def imgtable(lines, id, caption = nil, metric = nil) if @chapter.image(id).bound? metrics = parse_metric("idgxml", metric) puts "" table_header id, caption puts %Q[] puts "
    " else warn "image not bound: #{id}" if @strict image_dummy id, caption, lines end end def comment(str) print %Q() end def footnote(id, str) # see inline_fn end def inline_fn(id) %Q[#{compile_inline(@chapter.footnote(id).content.strip)}] end def compile_ruby(base, ruby) %Q[#{escape_html(base.sub(/\A\s+/, "").sub(/\s+$/, ""))}#{escape_html(ruby.sub(/\A\s+/, "").sub(/\s+$/, ""))}] end def compile_kw(word, alt) '' + if alt then escape_html("#{word}(#{alt.strip})") else escape_html(word) end + '' + %Q[] + if alt alt.split(/\s*,\s*/).collect! {|e| %Q[] }.join else "" end end def compile_href(url, label) %Q[#{label.nil? ? escape_html(url) : escape_html(label)}] end def inline_sup(str) %Q[#{escape_html(str)}] end def inline_sub(str) %Q[#{escape_html(str)}] end def inline_raw(str) %Q[#{super(str).gsub("\\n", "\n")}] end def inline_hint(str) if @book.config["nolf"].nil? %Q[\n#{escape_html(str)}] else %Q[#{escape_html(str)}] end end def inline_maru(str) if str =~ /\A\d+\Z/ sprintf("&#x%x;", 9311 + str.to_i) elsif str =~ /\A[A-Z]\Z/ begin sprintf("&#x%x;", 9398 + str.codepoints.to_a[0] - 65) rescue NoMethodError sprintf("&#x%x;", 9398 + str[0] - 65) end elsif str =~ /\A[a-z]\Z/ begin sprintf("&#x%x;", 9392 + str.codepoints.to_a[0] - 65) rescue NoMethodError sprintf("&#x%x;", 9392 + str[0] - 65) end else raise "can't parse maru: #{str}" end end def inline_idx(str) %Q(#{escape_html(str)}) end def inline_hidx(str) %Q() end def inline_ami(str) %Q(#{escape_html(str)}) end def inline_i(str) %Q(#{escape_html(str)}) end def inline_b(str) %Q(#{escape_html(str)}) end def inline_tt(str) %Q(#{escape_html(str)}) end def inline_ttb(str) %Q(#{escape_html(str)}) end alias_method :inline_ttbold, :inline_ttb def inline_tti(str) %Q(#{escape_html(str)}) end def inline_u(str) %Q(#{escape_html(str)}) end def inline_icon(id) begin %Q[] rescue warn "no such icon image: #{id}" "" end end def inline_bou(str) %Q[#{escape_html(str)}] end def inline_keytop(str) %Q[#{escape_html(str)}] end def inline_labelref(idref) %Q[「#{I18n.t("label_marker")}#{escape_html(idref)}」] # FIXME:節名とタイトルも込みで要出力 end alias_method :inline_ref, :inline_labelref def inline_pageref(idref) %Q[●●] # ページ番号を参照 end def inline_balloon(str) %Q[#{escape_html(str).gsub(/@maru\[(\d+)\]/) {|m| inline_maru($1)}}] end def inline_uchar(str) %Q[&#x#{str};] end def inline_m(str) @texinlineequation += 1 %Q[
    #{escape_html(str)}
    ] end def noindent @noindent = true end def linebreak # FIXME:pが閉じちゃってるので一度戻らないといけないが、難しい…。 puts "
    " end def pagebreak puts "" end def nonum_begin(level, label, caption) puts %Q[#{compile_inline(caption)}] end def nonum_end(level) end def circle_begin(level, label, caption) puts %Q[•#{compile_inline(caption)}] end def circle_end(level) end def common_column_begin(type, caption) @column += 1 a_id = %Q[id="column-#{@column}"] print "<#{type}column #{a_id}>" puts %Q[#{compile_inline(caption)}] end def common_column_end(type) puts "" end def column_begin(level, label, caption) common_column_begin("", caption) end def column_end(level) common_column_end("") end def xcolumn_begin(level, label, caption) common_column_begin("x", caption) end def xcolumn_end(level) common_column_end("x") end def world_begin(level, label, caption) common_column_begin("world", caption) end def world_end(level) common_column_end("world") end def hood_begin(level, label, caption) common_column_begin("hood", caption) end def hood_end(level) common_column_end("hood") end def edition_begin(level, label, caption) common_column_begin("edition", caption) end def edition_end(level) common_column_end("edition") end def insideout_begin(level, label, caption) common_column_begin("insideout", caption) end def insideout_end(level) common_column_end("insideout") end def ref_begin(level, label, caption) if !label.nil? puts "" else puts "" end end def ref_end(level) puts "" end def sup_begin(level, label, caption) if !label.nil? puts "" else puts "" end end def sup_end(level) puts "" end def flushright(lines) puts split_paragraph(lines).join.gsub("

    ", "

    ") end def centering(lines) puts split_paragraph(lines).join.gsub("

    ", "

    ") end def captionblock(type, lines, caption, specialstyle = nil) print "<#{type}>" style = specialstyle.nil? ? "#{type}-title" : specialstyle puts "#{compile_inline(caption)}" unless caption.nil? blocked_lines = split_paragraph(lines) puts "#{blocked_lines.join}" end def note(lines, caption = nil) captionblock("note", lines, caption) end def memo(lines, caption = nil) captionblock("memo", lines, caption) end def tip(lines, caption = nil) captionblock("tip", lines, caption) end def info(lines, caption = nil) captionblock("info", lines, caption) end def planning(lines, caption = nil) captionblock("planning", lines, caption) end def best(lines, caption = nil) captionblock("best", lines, caption) end def important(lines, caption = nil) captionblock("important", lines, caption) end def security(lines, caption = nil) captionblock("security", lines, caption) end def caution(lines, caption = nil) captionblock("caution", lines, caption) end def warning(lines, caption = nil) captionblock("warning", lines, caption) end def term(lines) captionblock("term", lines, nil) end def link(lines, caption = nil) captionblock("link", lines, caption) end def notice(lines, caption = nil) if caption.nil? captionblock("notice", lines, nil) else captionblock("notice-t", lines, caption, "notice-title") end end def point(lines, caption = nil) if caption.nil? captionblock("point", lines, nil) else captionblock("point-t", lines, caption, "point-title") end end def shoot(lines, caption = nil) if caption.nil? captionblock("shoot", lines, nil) else captionblock("shoot-t", lines, caption, "shoot-title") end end def reference(lines) captionblock("reference", lines, nil) end def practice(lines) captionblock("practice", lines, nil) end def expert(lines) captionblock("expert", lines, nil) end def syntaxblock(type, lines, caption) if caption.nil? puts %Q[<#{type}>] else titleopentag = %Q[caption aid:pstyle="#{type}-title"] titleclosetag = "caption" if type == "insn" titleopentag = %Q[floattitle type="insn"] titleclosetag = "floattitle" end puts %Q[<#{type}><#{titleopentag}>#{compile_inline(caption)}] end no = 1 lines.each do |line| unless @book.config["listinfo"].nil? print %Q[] end print detab(line) print "\n" print "" unless @book.config["listinfo"].nil? no += 1 end puts "" end def insn(lines, caption = nil) syntaxblock("insn", lines, caption) end def box(lines, caption = nil) syntaxblock("box", lines, caption) end def indepimage(id, caption=nil, metric=nil) metrics = parse_metric("idgxml", metric) puts "" begin puts %Q[] rescue warn %Q[no such image: #{id}] end puts %Q[#{compile_inline(caption)}] if caption.present? puts "" end alias_method :numberlessimage, :indepimage def label(id) # FIXME print "


    " end def bpo(lines) puts %Q[#{lines.join("\n")}] end def inline_dtp(str) "" end def inline_code(str) %Q[#{escape_html(str)}] end def inline_br(str) "\n" end def rawblock(lines) no = 1 lines.each do |l| print l.gsub("<", "<").gsub(">", ">").gsub(""", "\"").gsub("&", "&") print "\n" unless lines.length == no no += 1 end end def text(str) str end def inline_chapref(id) chs = ["", "「", "」"] unless @book.config["chapref"].nil? _chs = @book.config["chapref"].split(",") if _chs.size != 3 error "--chapsplitter must have exactly 3 parameters with comma." else chs = _chs end else end s = "#{chs[0]}#{@book.chapter_index.number(id)}#{chs[1]}#{@book.chapter_index.title(id)}#{chs[2]}" if @book.config["chapterlink"] %Q(#{s}) else s end rescue KeyError error "unknown chapter: #{id}" nofunc_text("[UnknownChapter:#{id}]") end def inline_chap(id) if @book.config["chapterlink"] %Q(#{@book.chapter_index.number(id)}) else @book.chapter_index.number(id) end rescue KeyError error "unknown chapter: #{id}" nofunc_text("[UnknownChapter:#{id}]") end def inline_title(id) title = super if @book.config["chapterlink"] %Q(#{title}) else title end rescue KeyError error "unknown chapter: #{id}" nofunc_text("[UnknownChapter:#{id}]") end def source_header(caption) puts %Q[] puts %Q[#{compile_inline(caption)}] end def source_body(lines, lang) puts %Q[
    ]
          codelines_body(lines)
          puts %Q[
    ] end def bibpaper(lines, id, caption) bibpaper_header id, caption unless lines.empty? bibpaper_bibpaper id, caption, lines end puts %Q() end def bibpaper_header(id, caption) puts %Q() puts "[#{@chapter.bibpaper(id).number}] #{compile_inline(caption)}" end def bibpaper_bibpaper(id, caption, lines) print split_paragraph(lines).join("") end def inline_bib(id) %Q([#{@chapter.bibpaper(id).number}]) end def inline_hd_chap(chap, id) if chap.number n = chap.headline_index.number(id) if @book.config["secnolevel"] >= n.split('.').size return I18n.t("chapter_quote", "#{n} #{compile_inline(chap.headline(id).caption)}") end end I18n.t("chapter_quote", compile_inline(chap.headline(id).caption)) end def inline_recipe(id) # FIXME %Q([XXX]「#{escape_html(id)}」 p.XX) end def nofunc_text(str) escape_html(str) end def image_ext "eps" end end end # module ReVIEW