# encoding: utf-8 # # Copyright (c) 2002-2007 Minero Aoki # 2008-2009 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. # For details of LGPL, see the file "COPYING". # require 'review/extentions' require 'review/exception' require 'review/book/image_finder' module ReVIEW module Book class Index def Index.parse(src, *args) items = [] seq = 1 src.grep(%r<^//#{item_type()}>) do |line| if id = line.slice(/\[(.*?)\]/, 1) items.push item_class().new(id, seq) seq += 1 if id == "" warn "warning: no ID of #{item_type()} in #{line}" end end end new(items, *args) end Item = Struct.new(:id, :number) def Index.item_class self::Item end include Enumerable def item_type self.class.item_type end def initialize(items) @items = items @index = {} items.each do |i| warn "warning: duplicate ID: #{i.id} (#{i})" unless @index[i.id].nil? @index[i.id] = i end @image_finder = nil end def [](id) @index.fetch(id) rescue raise KeyError, "not found key '#{id}' for #{self.class}" end def number(id) @index.fetch(id).number.to_s end def each(&block) @items.each(&block) end def has_key?(id) return @index.has_key?(id) end end class ChapterIndex < Index def item_type 'chapter' end def number(id) chapter = @index.fetch(id) chapter.format_number rescue # part "#{I18n.t("part", chapter.number)}" end def title(id) @index.fetch(id).title rescue # non-file part @index.fetch(id).name end def display_string(id) "#{number(id)}「#{title(id)}」" end end class ListIndex < Index def ListIndex.item_type '(list|listnum)' end end class TableIndex < Index def TableIndex.item_type 'table' end end class FootnoteIndex < Index Item = Struct.new(:id, :number, :content) def FootnoteIndex.parse(src) items = [] seq = 1 src.grep(%r<^//footnote>) do |line| if m = /\[(.*?)\]\[(.*)\]/.match(line) m1 = m[1].gsub(/\\(\])/){$1} m2 = m[2].gsub(/\\(\])/){$1} items.push Item.new(m1, seq, m2) end seq += 1 end new(items) end end class ImageIndex < Index class Item def initialize(id, number) @id = id @number = number @path = nil end attr_reader :id attr_reader :number attr_writer :index # internal use only def bound? path end def path @path ||= @index.find_path(id) end end def ImageIndex.item_type '(image|graph)' end attr_reader :image_finder def initialize(items, chapid, basedir, types, builder) super items items.each do |i| i.index = self end @chapid = chapid @basedir = basedir @types = types @image_finder = ReVIEW::Book::ImageFinder.new(basedir, chapid, builder, types) end def find_path(id) @image_finder.find_path(id) end end class IconIndex < ImageIndex def initialize(items, chapid, basedir, types, builder) @items = items @index = {} items.each do |i| ## warn "warning: duplicate ID: #{i.id} (#{i})" unless @index[i.id].nil? @index[i.id] = i end items.each do |i| i.index = self end @chapid = chapid @basedir = basedir @types = types @image_finder = ImageFinder.new(basedir, chapid, builder, types) end def IconIndex.parse(src, *args) items = [] seq = 1 src.grep(%r!@!) do |line| line.gsub(/@\{(.+?)\}/) do |m| items.push item_class().new($1, seq) seq += 1 end end new(items, *args) end end class FormatRef def initialize(locale, index) @locale = locale @index = index end def title(id) sprintf(@locale["#{@index.item_type}_caption_format".intern], @index.title(id)) end def number(id) sprintf(@locale["#{@index.item_type}_number_format".intern], @index.number(id)) end def method_missing(mid, *args, &block) super unless @index.respond_to?(mid) @index.__send__(mid, *args, &block) end end class BibpaperIndex < Index Item = Struct.new(:id, :number, :caption) def BibpaperIndex.parse(src) items = [] seq = 1 src.grep(%r<^//bibpaper>) do |line| if m = /\[(.*?)\]\[(.*)\]/.match(line) m1 = m[1].gsub(/\\(.)/){$1} m2 = m[2].gsub(/\\(.)/){$1} items.push Item.new(m1, seq, m2) end seq += 1 end new(items) end end class NumberlessImageIndex < ImageIndex class Item < ImageIndex::Item def initialize(id, number) @id = id @number = "" @path = nil end end def NumberlessImageIndex.item_type 'numberlessimage' end def number(id) "" end end class IndepImageIndex < ImageIndex class Item < ImageIndex::Item def initialize(id, number) @id = id @number = "" @path = nil end end def IndepImageIndex.item_type 'indepimage' end def number(id) "" end end class HeadlineIndex < Index HEADLINE_PATTERN = /\A(=+)(?:\[(.+?)\])?(?:\{(.+?)\})?(.*)/ Item = Struct.new(:id, :number, :caption) attr_reader :items def HeadlineIndex.parse(src, chap) items = [] indexs = [] headlines = [] inside_column = false src.each do |line| if m = HEADLINE_PATTERN.match(line) next if m[1].size > 10 # Ignore too deep index index = m[1].size - 2 # column if m[2] == 'column' inside_column = true next end if m[2] == '/column' inside_column = false next end if indexs.present? and indexs[-1] <= index inside_column = false end if inside_column next end if index >= 0 if indexs.size > (index + 1) indexs = indexs.take(index + 1) headlines = headlines.take(index + 1) end if indexs[index].nil? (0..index).each{|i| indexs[i] = 0 if indexs[i].nil?} end indexs[index] += 1 headlines[index] = m[3].present? ? m[3].strip : m[4].strip items.push Item.new(headlines.join("|"), indexs.dup, m[4].strip) end end end new(items, chap) end def initialize(items, chap) @items = items @chap = chap @index = {} items.each do |i| warn "warning: duplicate ID: #{i.id}" unless @index[i.id].nil? @index[i.id] = i end end def number(id) return ([@chap.number] + @index.fetch(id).number).join(".") end end class ColumnIndex < Index COLUMN_PATTERN = /\A(=+)\[column\](?:\{(.+?)\})?(.*)/ Item = Struct.new(:id, :number, :caption) def ColumnIndex.parse(src, *args) items = [] seq = 1 src.each do |line| if m = COLUMN_PATTERN.match(line) level = m[1] ## not use it yet id = m[2] caption = m[3].strip if !id || id == "" id = caption end items.push item_class().new(id, seq, caption) seq += 1 end end new(items) end end end end