# Copyright (c) 2008-2018 Minero Aoki, Kenshi Muto
# 2002-2007 Minero Aoki
#
# 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
%i[ttbold hint maru keytop labelref ref].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
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
@first_line_num = nil
@rootelement = 'doc'
@tsize = nil
@texblockequation = 0
@texinlineequation = 0
print %Q(\n)
print %Q(<#{@rootelement} xmlns:aid="http://ns.adobe.com/AdobeInDesign/4.0/">)
@secttags = @book.config['structuredxml']
end
private :builder_init_file
def puts(arg)
if @book.config['nolf'].present?
print arg
else
super
end
end
def result
s = ''
if @secttags
s += '' if @subsubsubsection > 0
s += '' if @subsubsection > 0
s += '' if @subsection > 0
s += '' if @section > 0
s += '' if @chapter.number > 0
end
@output.string + s + "#{@rootelement}>\n"
end
def headline(level, label, caption)
case level
when 1
if @secttags
print '' if @subsubsubsection > 0
print '' if @subsubsection > 0
print '' if @subsection > 0
print '' if @section > 0
end
print %Q() if @secttags
@section = 0
@subsection = 0
@subsubsection = 0
@subsubsubsection = 0
when 2
if @secttags
print '' if @subsubsubsection > 0
print '' if @subsubsection > 0
print '' if @subsection > 0
print '' if @section > 0
end
@section += 1
print %Q() if @secttags
@subsection = 0
@subsubsection = 0
@subsubsubsection = 0
when 3
if @secttags
print '' if @subsubsubsection > 0
print '' if @subsubsection > 0
print '' if @subsection > 0
end
@subsection += 1
print %Q() if @secttags
@subsubsection = 0
@subsubsubsection = 0
when 4
if @secttags
print '' if @subsubsubsection > 0
print '' if @subsubsection > 0
end
@subsubsection += 1
print %Q() if @secttags
@subsubsubsection = 0
when 5
print '' if @secttags && @subsubsubsection > 0
@subsubsubsection += 1
print %Q() if @secttags
else
raise "caption level too deep or unsupported: #{level}"
end
prefix, _anchor = headline_prefix(level)
label = label.nil? ? '' : %Q( id="#{label}")
toccaption = escape(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_begin(lines)
print %Q(
#{lines.join.chomp})
end
def ul_item_end
puts '
'
end
def choice_single_begin
puts %Q()
end
def choice_multi_begin
puts %Q()
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 ''
@ol_num ||= 1
end
def ol_item(lines, num)
puts %Q(
#{lines.join.chomp}
)
@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, chapter = @chapter)
num = chapter.column(id).number
"column-#{num}"
end
private :column_label
def inline_column_chap(chapter, id)
if @book.config['chapterlink']
%Q(#{I18n.t('column', compile_inline(chapter.column(id).caption))})
else
I18n.t('column', compile_inline(chapter.column(id).caption))
end
rescue KeyError
error "unknown column: #{id}"
end
def inline_list(id)
"#{super(id)}"
end
def list_header(id, caption, _lang)
puts ''
return true unless caption.present?
if get_chap.nil?
puts %Q(
)
end
end
def codelines_body(lines)
no = 1
lines.each do |line|
if @book.config['listinfo']
print %Q('
end
print detab(line)
print "\n"
print '' if @book.config['listinfo']
no += 1
end
end
def list_body(_id, lines, _lang)
print '
'
codelines_body(lines)
puts '
'
end
def emlist(lines, caption = nil, _lang = nil)
quotedlist lines, 'emlist', caption
end
def emlistnum(lines, caption = nil, _lang = nil)
lines2 = []
first_line_num = line_num
lines.each_with_index do |line, i|
lines2 << detab(%Q() + (i + first_line_num).to_s.rjust(2) + ': ' + line)
end
quotedlist lines2, 'emlistnum', caption
end
def listnum_body(lines, _lang)
print '
'
no = 1
first_line_num = line_num
lines.each_with_index do |line, i|
if @book.config['listinfo']
print %Q('
end
print detab(%Q() + (i + first_line_num).to_s.rjust(2) + ': ' + line)
print "\n"
print '' if @book.config['listinfo']
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)}
" if caption.present?
print '
'
no = 1
lines.each do |line|
if @book.config['listinfo']
print %Q('
end
print detab(line)
print "\n"
print '' if @book.config['listinfo']
no += 1
end
puts '
'
end
private :quotedlist
def quote(lines)
blocked_lines = split_paragraph(lines)
puts "#{blocked_lines.join}"
end
def inline_table(id)
"#{super(id)}"
end
def inline_img(id)
"#{super(id)}"
end
def inline_eq(id)
"#{super(id)}"
end
def inline_imgref(id)
chapter, id = extract_chapter_id(id)
if chapter.image(id).caption.blank?
inline_img(id)
elsif 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
def handle_metric(str)
k, v = str.split('=', 2)
%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 '
'
image_header id, caption
puts ''
warn "image not bound: #{id}"
end
def image_header(id, caption)
return true unless caption.present?
if get_chap.nil?
puts %Q(
'
puts ''
if id
puts ''
end
end
def table(lines, id = nil, caption = nil)
tablewidth = @book.config['tableopt'] ? @book.config['tableopt'].split(',')[0].to_f / @book.config['pt_to_mm_unit'].to_f : nil
col = 0
puts '
'
rows = []
sepidx = nil
lines.each_with_index do |line, idx|
if /\A[\=\-]{12}/ =~ line
sepidx ||= idx
next
end
if tablewidth
rows.push(line.gsub(/\t\.\t/, "\tDUMMYCELLSPLITTER\t").gsub(/\t\.\.\t/, "\t.\t").gsub(/\t\.\Z/, "\tDUMMYCELLSPLITTER").gsub(/\t\.\.\Z/, "\t.").gsub(/\A\./, ''))
else
rows.push(line.gsub(/\t\.\t/, "\t\t").gsub(/\t\.\.\t/, "\t.\t").gsub(/\t\.\Z/, "\t").gsub(/\t\.\.\Z/, "\t.").gsub(/\A\./, ''))
end
col2 = rows[rows.length - 1].split(/\t/).length
col = col2 if col2 > col
end
cellwidth = []
if tablewidth
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
(cellwidth.size..(col - 1)).each { |i| cellwidth[i] = cw }
end
end
end
begin
table_header id, caption if caption.present?
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(
#{cell.sub('DUMMYCELLSPLITTER', '')}
)
i += 1
end
end
end
end
trputs(tablewidth, rows, cellwidth, sepidx)
puts '
'
@tsize = nil
end
def trputs(tablewidth, rows, cellwidth, sepidx)
sepidx = 0 if sepidx.nil?
if tablewidth
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
else
lastline = rows.pop
rows.each { |row| puts "
#{row}
" }
puts %Q(
#{lastline}
) if lastline
end
end
def table_header(id, caption)
if id.nil?
puts %Q(
#{compile_inline(caption)}" if caption.present?
blocked_lines = split_paragraph(lines)
puts "#{blocked_lines.join}#{type}>"
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
captionblock('notice-t', lines, caption, 'notice-title')
else
captionblock('notice', lines, nil)
end
end
def point(lines, caption = nil)
if caption
captionblock('point-t', lines, caption, 'point-title')
else
captionblock('point', lines, nil)
end
end
def shoot(lines, caption = nil)
if caption
captionblock('shoot-t', lines, caption, 'shoot-title')
else
captionblock('shoot', lines, nil)
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.present?
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)}#{titleclosetag}>)
else
puts "<#{type}>"
end
no = 1
lines.each do |line|
if @book.config['listinfo']
print %Q('
end
print detab(line)
print "\n"
print '' if @book.config['listinfo']
no += 1
end
puts "#{type}>"
end
def insn(lines, caption = nil)
syntaxblock('insn', lines, caption)
end
def box(lines, caption = nil)
syntaxblock('box', lines, caption)
end
def indepimage(_lines, id, caption = nil, metric = nil)
metrics = parse_metric('idgxml', metric)
puts ''
begin
puts %Q()
rescue
warn %Q(image not bound: #{id})
end
puts %Q(
#{compile_inline(caption)}
) if caption.present?
puts ''
end
alias_method :numberlessimage, :indepimage
def label(id)
# FIXME
print ""
end
def dtp(str)
print %Q()
end
def hr
print ''
end
def bpo(lines)
puts %Q(#{lines.join("\n")})
end
def inline_dtp(str)
""
end
def inline_code(str)
%Q(#{escape(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)
if @book.config.check_version('2', exception: false)
# backward compatibility
chs = ['', '「', '」']
if @book.config['chapref']
chs2 = @book.config['chapref'].split(',')
if chs2.size != 3
error '--chapsplitter must have exactly 3 parameters with comma.'
else
chs = chs2
end
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
else
title = super
if @book.config['chapterlink']
%Q(#{title})
else
title
end
end
rescue KeyError
error "unknown chapter: #{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}"
end
def inline_title(id)
title = super
if @book.config['chapterlink']
%Q(#{title})
else
title
end
rescue KeyError
error "unknown chapter: #{id}"
end
def source_header(caption)
puts ''
end
def bibpaper(lines, id, caption)
bibpaper_header id, caption
bibpaper_bibpaper id, caption, lines unless lines.empty?
puts ''
end
def bibpaper_header(id, caption)
puts %Q()
puts "
" if caption.present?
end
def bibpaper_bibpaper(_id, _caption, lines)
print split_paragraph(lines).join
end
def inline_bib(id)
%Q([#{@chapter.bibpaper(id).number}])
rescue KeyError
error "unknown bib: #{id}"
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('hd_quote', [n, compile_inline(chap.headline(id).caption)])
end
end
I18n.t('hd_quote_without_number', compile_inline(chap.headline(id).caption))
rescue KeyError
error "unknown headline: #{id}"
end
def inline_recipe(id)
# FIXME
%Q([XXX]「#{escape(id)}」 p.XX)
end
def nofunc_text(str)
escape(str)
end
def image_ext
'eps'
end
end
end # module ReVIEW