diff --git a/.github/workflows/ruby-win.yml b/.github/workflows/ruby-win.yml index c0009f519..01a2dfe84 100644 --- a/.github/workflows/ruby-win.yml +++ b/.github/workflows/ruby-win.yml @@ -23,17 +23,23 @@ jobs: gem install bundler --no-document bundle install --retry 3 bundle exec ruby test/run_test.rb --max-diff-target-string-size=10000 --verbose=v - - name: Test with epubcheck-ruby + - name: install Re:VIEW and epubcheck-ruby shell: bash run: | gem install -N epubcheck-ruby - ruby bin/review-init hello + rake install + - name: Test with epubcheck-ruby + shell: bash + run: | + review-init hello cd hello - ruby ../bin/review-epubmaker config.yml + ruby -e 'Dir.glob("*.re"){|path| File.write(path, File.read(path).gsub("=", "= foo")) }' + review-epubmaker config.yml epubcheck book.epub cd .. - ruby bin/review-init hello2 --epub-version 2 + review-init hello2 --epub-version 2 cd hello2 - ruby ../bin/review-epubmaker config.yml + ruby -e 'Dir.glob("*.re"){|path| File.write(path, File.read(path).gsub("=", "= foo")) }' + review-epubmaker config.yml epubcheck book.epub cd .. diff --git a/.rubocop.yml b/.rubocop.yml index 0380909e1..ca4173efb 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -3,13 +3,13 @@ AllCops: - lib/lineinput.rb - lib/uuid.rb - test/syntax-book/* + - tmp/* DisplayCopNames: true + NewCops: enable require: - rubocop-performance -inherit_from: .rubocop_todo.yml - #### Lint Lint/EmptyWhen: @@ -25,9 +25,6 @@ Lint/AssignmentInCondition: Lint/ParenthesesAsGroupedExpression: Enabled: true -#Lint/RescueWithoutErrorClass: -# Enabled: false - Lint/UnderscorePrefixedVariableName: Enabled: true @@ -43,6 +40,18 @@ Lint/UselessAssignment: Lint/ErbNewArguments: Enabled: false +Lint/DeprecatedOpenSSLConstant: + Enabled: true + +Lint/MixedRegexpCaptureTypes: + Enabled: true + +Lint/RaiseException: + Enabled: true + +Lint/StructNewOverride: + Enabled: true + #### Performance Performance/RegexpMatch: @@ -58,6 +67,39 @@ Performance/StringReplacement: Performance/RangeInclude: Enabled: true +Performance/DeletePrefix: + Enabled: false + +Performance/RedundantMerge: + Enabled: true + +Performance/AncestorsInclude: + Enabled: true + +Performance/BigDecimalWithNumericArgument: + Enabled: true + +Performance/RedundantSortBlock: + Enabled: true + +Performance/RedundantStringChars: + Enabled: true + +Performance/ReverseFirst: + Enabled: true + +Performance/SortReverse: + Enabled: true + +Performance/Squeeze: + Enabled: true + +Performance/StringInclude: + Enabled: false + +Performance/CollectionLiteralInLoop: + Enabled: false + #### Style Style/AccessModifierDeclarations: @@ -72,7 +114,7 @@ Style/Alias: Enabled: true Style/AndOr: - Enabled: false + Enabled: true Style/BarePercentLiterals: EnforcedStyle: percent_q @@ -81,9 +123,6 @@ Style/BarePercentLiterals: Style/BlockDelimiters: Enabled: true -Style/BracesAroundHashParameters: - Enabled: false - Style/CaseEquality: Enabled: false @@ -120,7 +159,7 @@ Style/EmptyElse: Enabled: true Style/EvalWithLocation: - Enabled: false + Enabled: true Style/FormatString: EnforcedStyle: sprintf @@ -141,13 +180,10 @@ Style/IfUnlessModifier: Style/MutableConstant: Enabled: false -#Style/MultipleComparison: -# Enabled: true - -Style/RedundantInterpolation: +Style/MultipleComparison: Enabled: true -Performance/RedundantMerge: +Style/RedundantInterpolation: Enabled: true Style/ZeroLengthPredicate: @@ -216,6 +252,9 @@ Style/MethodCallWithArgsParentheses: - 'source' - 'assert' - 'assert_equal' + - 'assert_raise' + - 'assert_raises' + - 'assert_nothing_raised' - 'sh' - 'mv' - 'rm_rf' @@ -310,8 +349,50 @@ Style/RedundantPercentQ: Style/WhileUntilModifier: Enabled: false -#Style/YodaCondition: -# Enabled: true +Style/YodaCondition: + Enabled: true + +Style/ExponentialNotation: + Enabled: true + +Style/HashEachMethods: + Enabled: true + +Style/HashTransformKeys: + Enabled: true + +Style/HashTransformValues: + Enabled: true + +Style/RedundantFetchBlock: + Enabled: true + +Style/RedundantRegexpCharacterClass: + Enabled: true + +Style/RedundantRegexpEscape: + Enabled: true + +Style/SlicingWithRange: + Enabled: false + +Style/AccessorGrouping: + Enabled: false + +Style/CaseLikeIf: + Enabled: false + +Style/StringConcatenation: + Enabled: false + +Style/OptionalBooleanParameter: + Enabled: false + +Style/SoleNestedConditional: + Enabled: false + +Style/CombinableLoops: + Enabled: false ### Layout @@ -422,6 +503,17 @@ Layout/TrailingWhitespace: Enabled: true AllowInHeredoc: true +Layout/EmptyLinesAroundAttributeAccessor: + Enabled: true + +Layout/SpaceAroundMethodCallOperator: + Enabled: true + +Layout/LineLength: + Max: 256 + Exclude: + - "test/**/*" + #### Metrics Metrics/BlockNesting: @@ -441,11 +533,6 @@ Metrics/ClassLength: Exclude: - 'test/*.rb' -Metrics/LineLength: - Max: 256 - Exclude: - - "test/**/*" - ### should be < 50 Metrics/MethodLength: Max: 100 @@ -480,8 +567,8 @@ Naming/AccessorMethodName: Enabled: true # When defining the == operator, name its argument other. -#Style/BinaryOperatorParameterName: -# Enabled: true +Naming/BinaryOperatorParameterName: + Enabled: true Naming/FileName: Enabled: false @@ -510,3 +597,8 @@ Naming/VariableNumber: Security/YAMLLoad: Enabled: true + +#### Gemspec + +Gemspec/RequiredRubyVersion: + Enabled: false diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml deleted file mode 100644 index 74db4a1e7..000000000 --- a/.rubocop_todo.yml +++ /dev/null @@ -1,7 +0,0 @@ -# This configuration was generated by -# `rubocop --auto-gen-config` -# on 2017-02-18 14:39:22 +0900 using RuboCop version 0.47.1. -# The point is for the user to remove these configuration records -# one by one as the offenses are removed from the code base. -# Note that changes in the inspected code, or installation of new -# versions of RuboCop, may require this file to be generated again. diff --git a/NEWS.ja.md b/NEWS.ja.md index c5fac736f..e388a0989 100644 --- a/NEWS.ja.md +++ b/NEWS.ja.md @@ -1,3 +1,32 @@ +# Version 4.2.0 +## 新機能 +* 図・表・リスト・式のキャプションの位置を内容の上側・下側どちらにするかを指定する `caption_position` パラメータを追加しました。`caption_position` の下位パラメータとして `image`・`table`・`list`・`equation` のパラメータがあり、値として `top` (上側) または `bottom` (下側) を指定します。デフォルトは `image` のみ `bottom`、ほかは `top` です ([#1320]) + +## 非互換の変更 +* review-vol を再構成しました。部の処理や見出し内のインライン命令の処理を正しました。表示形式をわかりやすい形に変更しました。部を指定したときに部のボリュームではなく、部ファイル単体のボリュームを返すようにしました。`-P`, `--directory` オプションは廃止しました ([#1485]) +* review-index を再構成しました。オプション名を大幅に変更しています。行数・文字数は `-d` オプションを指定したときのみ表示するようにしました。また、ファイルの行数・文字数ではなく、PLAINTEXTBuilder を利用して、変換結果に近い行数・文字数を返すようにしました (review-vol よりも正確です)。特定の章は `-y` オプションで複数指定できるようにしました ([#1485]) + +## バグ修正 +* 重複する `@non_parsed_commands` 宣言を削除しました ([#1499]) +* WebMaker、TextMaker で数式画像が作成されない問題を修正しました ([#1501]) + +## 機能強化 +* imgmath での数式画像の作成処理を最適化し、高速化しました ([#1488]) +* デフォルト以外の固有の YAML 設定を PDFMaker に引き渡したいときのために、`layouts/config-local.tex.erb` ファイルが存在すればそれを評価・読み込みするようにしました ([#1505]) + +## その他 +* GitHub Actions を eregon/use-ruby-action から ruby/setup-ruby に切り替えました ([#1490]) +* テストの際、samples フォルダ内にあるビルド成果物を無視するようにしました ([#1504]) + +[#1320]: https://github.com/kmuto/review/issues/1320 +[#1485]: https://github.com/kmuto/review/issues/1485 +[#1488]: https://github.com/kmuto/review/issues/1488 +[#1490]: https://github.com/kmuto/review/pull/1490 +[#1499]: https://github.com/kmuto/review/issues/1499 +[#1501]: https://github.com/kmuto/review/pull/1501 +[#1504]: https://github.com/kmuto/review/pull/1504 +[#1505]: https://github.com/kmuto/review/issues/1505 + # Version 4.1.0 ## 新機能 * 表のセル区切りの文字を `table_row_separator` パラメータで変更できるようにしました。指定可能な値は tabs (1個以上のタブ、デフォルト)、singletab (1文字のタブ文字区切り)、spaces (1文字以上のスペースまたはタブ文字の区切り)、 verticalbar ("0個以上の空白 | 0個以上の空白" の区切り) です ([#1420]) diff --git a/NEWS.md b/NEWS.md index 3908cd3dc..c14b57f49 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,32 @@ +# Version 4.2.0 +## New Features +* introduce `caption_position` parameter to specify a caption position of image, table, list, and equation. `caption_position` has child parameters `image`, `table`, `list`, and `equation` and the value is `top` or `bottom` ([#1320]) + +## Breaking Changes +* review-vol is rewritten. Improved processing of parts and inline instructions in headings. Changed display format. When a part is specified, the volume of the part file itself is returned instead of the volume of the part. The `-P` and `--directory` options have been removed ([#1485]) +* review-index is rewritten. Most option names have been changed. The number of lines and characters are now displayed only when `-d` option is specified. review-index uses PLAINTEXTBuilder to return accurate line and character counts. `-y` option is provided to specify a target chapter ([#1485]) + +## Bug Fixes +* remove duplicated `@non_parsed_commands` declaration ([#1499]) +* mathematical images not being created in WebMaker and TextMaker has been fixed ([#1501]) + +## Enhancements +* improve a performance of building math figures on imgmath ([#1488]) +* for those times when you want to hand over non-default YAML parameters to PDFMaker, you can write your own `layouts/config-local.tex.erb` file ([#1505]) + +## Others +* GitHub Actions: use `ruby/setup-ruby` instead of `eregon/use-ruby-action` ([#1490]) +* skip artifacts in the sample folder during testing ([#1504]) + +[#1320]: https://github.com/kmuto/review/issues/1320 +[#1485]: https://github.com/kmuto/review/issues/1485 +[#1488]: https://github.com/kmuto/review/issues/1488 +[#1490]: https://github.com/kmuto/review/pull/1490 +[#1499]: https://github.com/kmuto/review/issues/1499 +[#1501]: https://github.com/kmuto/review/pull/1501 +[#1504]: https://github.com/kmuto/review/pull/1504 +[#1505]: https://github.com/kmuto/review/issues/1505 + # Version 4.1.0 ## New Features * add `table_row_separator` to specify a separator that separates table rows. Accceptable value: tabs (means `\t+`, default), `singletab` (means `\t`), spaces (means `\s+`), verticalbar (means `\s*\|\s*`) ([#1420]) diff --git a/bin/review-catalog-converter b/bin/review-catalog-converter index 10dfa1ccd..e96189558 100755 --- a/bin/review-catalog-converter +++ b/bin/review-catalog-converter @@ -109,7 +109,7 @@ def parse_postdef(str, to_appendix = false) end def parse_parts(parts_str, chaps_str) - if parts_str.blank? or chaps_str.blank? + if parts_str.blank? || chaps_str.blank? return "CHAPS:\n\n" end diff --git a/bin/review-check b/bin/review-check index 49c2daa44..65b20b501 100755 --- a/bin/review-check +++ b/bin/review-check @@ -31,9 +31,7 @@ end def main @config = ReVIEW::Configure.values - @book = ReVIEW::Book::Base.load - @book.config = @config - + @book = ReVIEW::Book::Base.new(config: @config) @logger = ReVIEW.logger modes = nil @@ -87,7 +85,7 @@ def check_text(files) s = para.join m = re.match(s) next if m.nil? || m[0] == @review_utils_word_ok - next if neg and neg =~ s + next if neg && neg =~ s str, offset = find_line(para, re) out = sprintf("%s:%d: %s\n", path, lineno + offset, str) print out diff --git a/bin/review-checkdep b/bin/review-checkdep index 88997a1e1..6b654e873 100755 --- a/bin/review-checkdep +++ b/bin/review-checkdep @@ -30,7 +30,7 @@ def main when /\A\#@provide\((.*)\)/ provide($1) else - line.scan(/@\{(.*?)[,\}]/) { provide $1 } + line.scan(/@\{(.*?)[,}]/) { provide($1) } end end end diff --git a/bin/review-compile b/bin/review-compile index 0ae76bea6..e02c469c6 100755 --- a/bin/review-compile +++ b/bin/review-compile @@ -79,32 +79,30 @@ def _main error('no input') if ARGV.empty? @basedir = File.dirname(ARGV[0]) - book = ReVIEW::Book::Base.load(@basedir) - book.config = @config # needs only at the first time + book = ReVIEW::Book::Base.new(@basedir, config: @config) ARGV.each do |item| error("file not found: #{item}") unless File.exist?(File.join(book.config['contentdir'], item)) chap_name = File.basename(item, '.*') chap = book.chapter(chap_name) - compiler = ReVIEW::Compiler.new(load_strategy_class(@target, @check_only)) + compiler = ReVIEW::Compiler.new(load_builder_class(@target, @check_only)) result = compiler.compile(chap) if @output_filename - write(@output_filename, result) + File.write(@output_filename, result) else puts result unless @check_only end end when :dir - book = @basedir ? ReVIEW::Book.load(@basedir) : ReVIEW::Book::Base.load - book.config = @config - compiler = ReVIEW::Compiler.new(load_strategy_class(@target, @check_only)) + book = @basedir ? ReVIEW::Book::Base.new(@basedir, config: @config) : ReVIEW::Book::Base.new(config: @config) + compiler = ReVIEW::Compiler.new(load_builder_class(@target, @check_only)) book.chapters.each do |chap| str = compiler.compile(chap) - write("#{chap.name}#{compiler.strategy.extname}", str) unless @check_only + File.write("#{chap.name}#{compiler.builder.extname}", str) unless @check_only end # PART book.parts_in_file.each do |part| str = compiler.compile(part) - write("#{part.name}#{compiler.strategy.extname}", str) unless @check_only + File.write("#{part.name}#{compiler.builder.extname}", str) unless @check_only end else raise "must not happen: #{@mode}" @@ -178,13 +176,9 @@ def warn(msg) @logger.warn msg end -def load_strategy_class(target, strict) +def load_builder_class(target, strict) require "review/#{target}builder" ReVIEW.const_get("#{target.upcase}Builder").new(strict) end -def write(path, str) - File.open(path, 'w') { |f| f.puts str } -end - main diff --git a/bin/review-validate b/bin/review-validate index 6117430d5..b6dabb89e 100755 --- a/bin/review-validate +++ b/bin/review-validate @@ -53,7 +53,7 @@ ARGF.each do |line| end elsif block == 'table' next if line.start_with?('#@') - if line !~ /\A\-\-\-\-\-/ + if line !~ /\A-----/ # table colcount = line.split("\t").size if maxcolcount == 0 diff --git a/doc/config.yml.sample b/doc/config.yml.sample index 3c54ac0ff..d69531f5c 100644 --- a/doc/config.yml.sample +++ b/doc/config.yml.sample @@ -92,6 +92,9 @@ debug: null # ISBN。省略した場合はurnidが入る # isbn: null # +# @, @, @, @<hd>命令をハイパーリンクにする(nullでハイパーリンクにしない) +# chapterlink: true + # HTMLファイルの拡張子(省略した場合はhtml) # htmlext: html # @@ -188,6 +191,14 @@ toc: true # 省略した場合はnull (挿入しない)。別途unicode-eaw gemファイルが必要 # join_lines_by_lang: null +# 図・表・コードリスト・数式のキャプション位置。 +# 値はtop(上)またはbottom(下)でデフォルトは以下のとおり +# caption_position: +# image: bottom +# table: top +# list: top +# equation: top + # review-toc向けのヒント情報 # (文字幅を考慮した行数計測には、別途unicode-eaw gemファイルが必要) # ページあたりの行数文字数を用紙サイズで指定する(A5 or B5) diff --git a/doc/format.ja.md b/doc/format.ja.md index 7eff7a5ed..0beb575b4 100644 --- a/doc/format.ja.md +++ b/doc/format.ja.md @@ -2,7 +2,7 @@ Re:VIEW フォーマットの文法について解説します。Re:VIEW フォーマットはアスキー社(現カドカワ)の EWB を基本としながら、一部に RD や各種 Wiki の文法を取り入れて簡素化しています。 -このドキュメントは、Re:VIEW 4.2 に基づいています。 +このドキュメントは、Re:VIEW 5.0 に基づいています。 ## 段落 @@ -502,6 +502,19 @@ complexmatrixという識別子に基づく画像ファイルが貼り込まれ 内容には、空行で区切って複数の段落を記述可能です。 +Re:VIEW 5.0 以降では、囲み記事に箇条書きや図表・リストを含めることもできます。 + +``` +//note{ + +箇条書きを含むノートです。 + + 1. 箇条書き1 + 2. 箇条書き2 + +//} +``` + ## 脚注 脚注は「`//footnote`」を使って記述します。 @@ -588,7 +601,7 @@ LaTeX の式を挿入するには、`//texequation{ 〜 //}` を使います。 「式1.1」のように連番を付けたいときには、識別子とキャプションを指定します。 ``` -//texequationl[emc][質量とエネルギーの等価性]{ +//texequation[emc][質量とエネルギーの等価性]{ \sum_{i=1}^nf_n(x) //} ``` diff --git a/doc/format.md b/doc/format.md index add39650d..d707a5f60 100644 --- a/doc/format.md +++ b/doc/format.md @@ -4,7 +4,7 @@ The document is a brief guide for Re:VIEW markup syntax. Re:VIEW is based on EWB of ASCII (now KADOKAWA), influenced RD and other Wiki system's syntax. -This document explains about the format of Re:VIEW 4.2. +This document explains about the format of Re:VIEW 5.0. ## Paragraph @@ -529,6 +529,19 @@ Some block commands are used for short column. The content is like paragraph; separated by empty lines. +From Re:VIEW 5.0, it is also possible to include itemize, figures and tables in short columns. + +``` +//note{ + +With ordered itemize. + + 1. item1 + 2. item2 + +//} +``` + ## Footnotes You can use `//footnote` to write footnotes. @@ -621,7 +634,7 @@ Usage: If you'd like to assign a number like 'Equation 1.1`, specify the identifier and caption. ``` -//texequationl[emc][The Equivalence of Mass and Energy]{ +//texequation[emc][The Equivalence of Mass and Energy]{ \sum_{i=1}^nf_n(x) //} ``` diff --git a/lib/epubmaker/content.rb b/lib/epubmaker/content.rb index de362c5a2..791cccf99 100644 --- a/lib/epubmaker/content.rb +++ b/lib/epubmaker/content.rb @@ -73,7 +73,7 @@ def ==(other) # Complement other parameters by using file parameter. def complement if @id.nil? - @id = @file.gsub(%r{[\\/\. ]}, '-') + @id = @file.gsub(%r{[\\/. ]}, '-') end if @id =~ /\A[^a-z]/i @id = "rv-#{@id}" diff --git a/lib/epubmaker/epubcommon.rb b/lib/epubmaker/epubcommon.rb index 3f986d1b0..5ee2348c9 100644 --- a/lib/epubmaker/epubcommon.rb +++ b/lib/epubmaker/epubcommon.rb @@ -10,7 +10,11 @@ require 'review/i18n' require 'review/template' -require 'cgi' +begin + require 'cgi/escape' +rescue LoadError + require 'cgi/util' +end module EPUBMaker # EPUBCommon is the common class for EPUB producer. @@ -22,6 +26,10 @@ def initialize(producer) @body_ext = nil end + def h(str) + CGI.escapeHTML(str) + end + # Return mimetype content. def mimetype 'application/epub+zip' @@ -59,10 +67,10 @@ def ncx_isbn def ncx_doctitle <<EOT <docTitle> - <text>#{CGI.escapeHTML(@producer.config['title'])}</text> + <text>#{h(@producer.config['title'])}</text> </docTitle> <docAuthor> - <text>#{@producer.config['aut'].nil? ? '' : CGI.escapeHTML(join_with_separator(@producer.config['aut'], ReVIEW::I18n.t('names_splitter')))}</text> + <text>#{@producer.config['aut'].nil? ? '' : h(join_with_separator(@producer.config['aut'], ReVIEW::I18n.t('names_splitter')))}</text> </docAuthor> EOT end @@ -72,7 +80,7 @@ def ncx_navmap(indentarray) <navMap> <navPoint id="top" playOrder="1"> <navLabel> - <text>#{CGI.escapeHTML(@producer.config['title'])}</text> + <text>#{h(@producer.config['title'])}</text> </navLabel> <content src="#{@producer.config['cover']}"/> </navPoint> @@ -84,7 +92,7 @@ def ncx_navmap(indentarray) s << <<EOT <navPoint id="toc" playOrder="#{nav_count}"> <navLabel> - <text>#{CGI.escapeHTML(@producer.res.v('toctitle'))}</text> + <text>#{h(@producer.res.v('toctitle'))}</text> </navLabel> <content src="#{@producer.config['bookname']}-toc.#{@producer.config['htmlext']}"/> </navPoint> @@ -100,7 +108,7 @@ def ncx_navmap(indentarray) s << <<EOT <navPoint id="nav-#{nav_count}" playOrder="#{nav_count}"> <navLabel> - <text>#{indent[level]}#{CGI.escapeHTML(item.title)}</text> + <text>#{indent[level]}#{h(item.title)}</text> </navLabel> <content src="#{item.file}"/> </navPoint> @@ -131,21 +139,21 @@ def cover(type = nil) raise "coverimage #{@producer.config['coverimage']} not found. Abort." unless file @body = <<-EOT <div id="cover-image" class="cover-image"> - <img src="#{file}" alt="#{CGI.escapeHTML(@producer.config.name_of('title'))}" class="max"/> + <img src="#{file}" alt="#{h(@producer.config.name_of('title'))}" class="max"/> </div> EOT else @body = <<-EOT -<h1 class="cover-title">#{CGI.escapeHTML(@producer.config.name_of('title'))}</h1> +<h1 class="cover-title">#{h(@producer.config.name_of('title'))}</h1> EOT if @producer.config['subtitle'] @body << <<-EOT -<h2 class="cover-subtitle">#{CGI.escapeHTML(@producer.config.name_of('subtitle'))}</h2> +<h2 class="cover-subtitle">#{h(@producer.config.name_of('subtitle'))}</h2> EOT end end - @title = CGI.escapeHTML(@producer.config.name_of('title')) + @title = h(@producer.config.name_of('title')) @language = @producer.config['language'] @stylesheets = @producer.config['stylesheet'] tmplfile = if @producer.config['htmlversion'].to_i == 5 @@ -161,7 +169,7 @@ def cover(type = nil) # NOTE: this method is not used yet. # see lib/review/epubmaker.rb#build_titlepage def titlepage - @title = CGI.escapeHTML(@producer.config.name_of('title')) + @title = h(@producer.config.name_of('title')) @body = <<EOT <h1 class="tp-title">#{@title}</h1> @@ -169,7 +177,7 @@ def titlepage if @producer.config['subtitle'] @body << <<EOT - <h2 class="tp-subtitle">#{CGI.escapeHTML(@producer.config.name_of('subtitle'))}</h2> + <h2 class="tp-subtitle">#{h(@producer.config.name_of('subtitle'))}</h2> EOT end @@ -179,7 +187,7 @@ def titlepage <br /> <br /> </p> - <h2 class="tp-author">#{CGI.escapeHTML(join_with_separator(@producer.config.names_of('aut'), ReVIEW::I18n.t('names_splitter')))}</h2> + <h2 class="tp-author">#{h(join_with_separator(@producer.config.names_of('aut'), ReVIEW::I18n.t('names_splitter')))}</h2> EOT end @@ -192,7 +200,7 @@ def titlepage <br /> <br /> </p> - <h3 class="tp-publisher">#{CGI.escapeHTML(join_with_separator(publisher, ReVIEW::I18n.t('names_splitter')))}</h3> + <h3 class="tp-publisher">#{h(join_with_separator(publisher, ReVIEW::I18n.t('names_splitter')))}</h3> EOT end @@ -209,18 +217,18 @@ def titlepage # Return colophon content. def colophon - @title = CGI.escapeHTML(@producer.res.v('colophontitle')) + @title = h(@producer.res.v('colophontitle')) @body = <<EOT <div class="colophon"> EOT if @producer.config['subtitle'].nil? @body << <<EOT - <p class="title">#{CGI.escapeHTML(@producer.config.name_of('title'))}</p> + <p class="title">#{h(@producer.config.name_of('title'))}</p> EOT else @body << <<EOT - <p class="title">#{CGI.escapeHTML(@producer.config.name_of('title'))}<br /><span class="subtitle">#{CGI.escapeHTML(@producer.config.name_of('subtitle'))}</span></p> + <p class="title">#{h(@producer.config.name_of('title'))}<br /><span class="subtitle">#{h(@producer.config.name_of('subtitle'))}</span></p> EOT end @@ -229,7 +237,7 @@ def colophon @body << %Q( <table class="colophon">\n) @body << @producer.config['colophon_order'].map do |role| if @producer.config[role] - %Q( <tr><th>#{CGI.escapeHTML(@producer.res.v(role))}</th><td>#{CGI.escapeHTML(join_with_separator(@producer.config.names_of(role), ReVIEW::I18n.t('names_splitter')))}</td></tr>\n) + %Q( <tr><th>#{h(@producer.res.v(role))}</th><td>#{h(join_with_separator(@producer.config.names_of(role), ReVIEW::I18n.t('names_splitter')))}</td></tr>\n) else '' end @@ -238,7 +246,7 @@ def colophon @body << %Q( <tr><th>ISBN</th><td>#{@producer.isbn_hyphen}</td></tr>\n) if @producer.isbn_hyphen @body << %Q( </table>\n) if @producer.config['rights'] && !@producer.config['rights'].empty? - @body << %Q( <p class="copyright">#{join_with_separator(@producer.config.names_of('rights').map { |m| CGI.escapeHTML(m) }, '<br />')}</p>\n) + @body << %Q( <p class="copyright">#{join_with_separator(@producer.config.names_of('rights').map { |m| h(m) }, '<br />')}</p>\n) end @body << %Q( </div>\n) @@ -261,11 +269,11 @@ def colophon_history items.each_with_index do |item, rev| editstr = edit == 0 ? ReVIEW::I18n.t('first_edition') : ReVIEW::I18n.t('nth_edition', (edit + 1).to_s) revstr = ReVIEW::I18n.t('nth_impression', (rev + 1).to_s) - if item =~ /\A\d+\-\d+\-\d+\Z/ + if item =~ /\A\d+-\d+-\d+\Z/ buf << %Q( <p>#{ReVIEW::I18n.t('published_by1', [date_to_s(item), editstr + revstr])}</p>\n) - elsif item =~ /\A(\d+\-\d+\-\d+)[\s ](.+)/ + elsif item =~ /\A(\d+-\d+-\d+)[\s ](.+)/ # custom date with string - item.match(/\A(\d+\-\d+\-\d+)[\s ](.+)/) do |m| + item.match(/\A(\d+-\d+-\d+)[\s ](.+)/) do |m| buf << %Q( <p>#{ReVIEW::I18n.t('published_by3', [date_to_s(m[1]), m[2]])}</p>\n) end else @@ -289,9 +297,9 @@ def date_to_s(date) # Return own toc content. def mytoc - @title = CGI.escapeHTML(@producer.res.v('toctitle')) + @title = h(@producer.res.v('toctitle')) - @body = %Q( <h1 class="toc-title">#{CGI.escapeHTML(@producer.res.v('toctitle'))}</h1>\n) + @body = %Q( <h1 class="toc-title">#{h(@producer.res.v('toctitle'))}</h1>\n) if @producer.config['epubmaker']['flattoc'].nil? @body << hierarchy_ncx('ul') else @@ -385,7 +393,7 @@ def flat_ncx(type, indent = nil) @producer.contents.each do |item| next if !item.notoc.nil? || item.level.nil? || item.file.nil? || item.title.nil? || item.level > @producer.config['toclevel'].to_i is = indent == true ? ' ' * item.level : '' - s << %Q(<li><a href="#{item.file}">#{is}#{CGI.escapeHTML(item.title)}</a></li>\n) + s << %Q(<li><a href="#{item.file}">#{is}#{h(item.title)}</a></li>\n) end s << %Q(</#{type}>\n) diff --git a/lib/epubmaker/epubv2.rb b/lib/epubmaker/epubv2.rb index d7a998ac5..d5d09db5c 100644 --- a/lib/epubmaker/epubv2.rb +++ b/lib/epubmaker/epubv2.rb @@ -9,14 +9,13 @@ # require 'epubmaker/epubcommon' -require 'cgi' require 'epubmaker/zip_exporter' module EPUBMaker # EPUBv2 is EPUB version 2 producer. class EPUBv2 < EPUBCommon # Construct object with parameter hash +config+ and message resource hash +res+. - def initialize(producer) + def initialize(producer) # rubocop:disable Lint/UselessMethodDefinition super end @@ -37,9 +36,9 @@ def opf_metainfo %w[title language date type format source description relation coverage subject rights].each do |item| next unless @producer.config[item] if @producer.config[item].is_a?(Array) - s << @producer.config.names_of(item).map { |i| %Q( <dc:#{item}>#{CGI.escapeHTML(i)}</dc:#{item}>\n) }.join + s << @producer.config.names_of(item).map { |i| %Q( <dc:#{item}>#{h(i)}</dc:#{item}>\n) }.join else - s << %Q( <dc:#{item}>#{CGI.escapeHTML(@producer.config.name_of(item).to_s)}</dc:#{item}>\n) + s << %Q( <dc:#{item}>#{h(@producer.config.name_of(item).to_s)}</dc:#{item}>\n) end end @@ -54,7 +53,7 @@ def opf_metainfo %w[aut a-adp a-ann a-arr a-art a-asn a-aqt a-aft a-aui a-ant a-bkp a-clb a-cmm a-dsr a-edt a-ill a-lyr a-mdc a-mus a-nrt a-oth a-pht a-prt a-red a-rev a-spn a-ths a-trc a-trl].each do |role| next unless @producer.config[role] @producer.config.names_of(role).each do |v| - s << %Q( <dc:creator opf:role="#{role.sub('a-', '')}">#{CGI.escapeHTML(v)}</dc:creator>\n) + s << %Q( <dc:creator opf:role="#{role.sub('a-', '')}">#{h(v)}</dc:creator>\n) end end @@ -62,7 +61,7 @@ def opf_metainfo %w[adp ann arr art asn aqt aft aui ant bkp clb cmm dsr edt ill lyr mdc mus nrt oth pht prt red rev spn ths trc trl].each do |role| next unless @producer.config[role] @producer.config.names_of(role).each do |v| - s << %Q( <dc:contributor opf:role="#{role}">#{CGI.escapeHTML(v)}</dc:contributor>\n) + s << %Q( <dc:contributor opf:role="#{role}">#{h(v)}</dc:contributor>\n) if role == 'prt' s << %Q( <dc:publisher>#{v}</dc:publisher>\n) end diff --git a/lib/epubmaker/epubv3.rb b/lib/epubmaker/epubv3.rb index c6115741d..b744e9c8a 100644 --- a/lib/epubmaker/epubv3.rb +++ b/lib/epubmaker/epubv3.rb @@ -40,6 +40,7 @@ def opf ReVIEW::Template.load(tmplfile).result(binding) end + # rubocop:disable Metrics/PerceivedComplexity def opf_metainfo s = '' %w[title language date type format source description relation coverage subject rights].each do |item| @@ -47,23 +48,23 @@ def opf_metainfo if @producer.config[item].is_a?(Array) @producer.config[item].each_with_index do |v, i| if v.is_a?(Hash) - s << %Q( <dc:#{item} id="#{item}-#{i}">#{CGI.escapeHTML(v['name'])}</dc:#{item}>\n) + s << %Q( <dc:#{item} id="#{item}-#{i}">#{h(v['name'])}</dc:#{item}>\n) v.each_pair do |name, val| next if name == 'name' - s << %Q( <meta refines="##{item}-#{i}" property="#{name}">#{CGI.escapeHTML(val)}</meta>\n) + s << %Q( <meta refines="##{item}-#{i}" property="#{name}">#{h(val)}</meta>\n) end else - s << %Q( <dc:#{item} id="#{item}-#{i}">#{CGI.escapeHTML(v.to_s)}</dc:#{item}>\n) + s << %Q( <dc:#{item} id="#{item}-#{i}">#{h(v.to_s)}</dc:#{item}>\n) end end elsif @producer.config[item].is_a?(Hash) - s << %Q( <dc:#{item} id="#{item}">#{CGI.escapeHTML(@producer.config[item]['name'])}</dc:#{item}>\n) + s << %Q( <dc:#{item} id="#{item}">#{h(@producer.config[item]['name'])}</dc:#{item}>\n) @producer.config[item].each_pair do |name, val| next if name == 'name' - s << %Q( <meta refines="##{item}" property="#{name}">#{CGI.escapeHTML(val)}</meta>\n) + s << %Q( <meta refines="##{item}" property="#{name}">#{h(val)}</meta>\n) end else - s << %Q( <dc:#{item} id="#{item}">#{CGI.escapeHTML(@producer.config[item].to_s)}</dc:#{item}>\n) + s << %Q( <dc:#{item} id="#{item}">#{h(@producer.config[item].to_s)}</dc:#{item}>\n) end end @@ -81,14 +82,14 @@ def opf_metainfo next unless @producer.config[role] @producer.config[role].each_with_index do |v, i| if v.is_a?(Hash) - s << %Q( <dc:creator id="#{role}-#{i}">#{CGI.escapeHTML(v['name'])}</dc:creator>\n) + s << %Q( <dc:creator id="#{role}-#{i}">#{h(v['name'])}</dc:creator>\n) s << %Q( <meta refines="##{role}-#{i}" property="role" scheme="marc:relators">#{role.sub('a-', '')}</meta>\n) v.each_pair do |name, val| next if name == 'name' - s << %Q( <meta refines="##{role.sub('a-', '')}-#{i}" property="#{name}">#{CGI.escapeHTML(val)}</meta>\n) + s << %Q( <meta refines="##{role.sub('a-', '')}-#{i}" property="#{name}">#{h(val)}</meta>\n) end else - s << %Q( <dc:creator id="#{role}-#{i}">#{CGI.escapeHTML(v)}</dc:creator>\n) + s << %Q( <dc:creator id="#{role}-#{i}">#{h(v)}</dc:creator>\n) s << %Q( <meta refines="##{role}-#{i}" property="role" scheme="marc:relators">#{role.sub('a-', '')}</meta>\n) end end @@ -99,27 +100,27 @@ def opf_metainfo next unless @producer.config[role] @producer.config[role].each_with_index do |v, i| if v.is_a?(Hash) - s << %Q( <dc:contributor id="#{role}-#{i}">#{CGI.escapeHTML(v['name'])}</dc:contributor>\n) + s << %Q( <dc:contributor id="#{role}-#{i}">#{h(v['name'])}</dc:contributor>\n) s << %Q( <meta refines="##{role}-#{i}" property="role" scheme="marc:relators">#{role}</meta>\n) v.each_pair do |name, val| next if name == 'name' - s << %Q( <meta refines="##{role}-#{i}" property="#{name}">#{CGI.escapeHTML(val)}</meta>\n) + s << %Q( <meta refines="##{role}-#{i}" property="#{name}">#{h(val)}</meta>\n) end else - s << %Q( <dc:contributor id="#{role}-#{i}">#{CGI.escapeHTML(v)}</dc:contributor>\n) + s << %Q( <dc:contributor id="#{role}-#{i}">#{h(v)}</dc:contributor>\n) s << %Q( <meta refines="##{role}-#{i}" property="role" scheme="marc:relators">#{role}</meta>\n) end if %w[prt pbl].include?(role) if v.is_a?(Hash) - s << %Q( <dc:publisher id="pub-#{role}-#{i}">#{CGI.escapeHTML(v['name'])}</dc:publisher>\n) + s << %Q( <dc:publisher id="pub-#{role}-#{i}">#{h(v['name'])}</dc:publisher>\n) s << %Q( <meta refines="#pub-#{role}-#{i}" property="role" scheme="marc:relators">#{role}</meta>\n) v.each_pair do |name, val| next if name == 'name' - s << %Q( <meta refines="#pub-#{role}-#{i}" property="#{name}">#{CGI.escapeHTML(val)}</meta>\n) + s << %Q( <meta refines="#pub-#{role}-#{i}" property="#{name}">#{h(val)}</meta>\n) end else - s << %Q( <dc:publisher id="pub-#{role}-#{i}">#{CGI.escapeHTML(v)}</dc:publisher>\n) + s << %Q( <dc:publisher id="pub-#{role}-#{i}">#{h(v)}</dc:publisher>\n) s << %Q( <meta refines="#pub-#{role}-#{i}" property="role" scheme="marc:relators">prt</meta>\n) end end @@ -129,12 +130,13 @@ def opf_metainfo ## add custom <meta> element if @producer.config['opf_meta'].present? @producer.config['opf_meta'].each do |k, v| - s << %Q( <meta property="#{k}">#{CGI.escapeHTML(v)}</meta>\n) + s << %Q( <meta property="#{k}">#{h(v)}</meta>\n) end end s end + # rubocop:enable Metrics/PerceivedComplexity def opf_manifest s = '' @@ -206,11 +208,11 @@ def ncx(indentarray) @body = <<EOT <nav xmlns:epub="http://www.idpf.org/2007/ops" epub:type="toc" id="toc"> - <h1 class="toc-title">#{CGI.escapeHTML(@producer.res.v('toctitle'))}</h1> + <h1 class="toc-title">#{h(@producer.res.v('toctitle'))}</h1> #{ncx_main} </nav> EOT - @title = CGI.escapeHTML(@producer.res.v('toctitle')) + @title = h(@producer.res.v('toctitle')) @language = @producer.config['language'] @stylesheets = @producer.config['stylesheet'] tmplfile = File.expand_path('./html/layout-html5.html.erb', ReVIEW::Template::TEMPLATE_DIR) diff --git a/lib/review/book.rb b/lib/review/book.rb index 5e1218987..3e3df80d8 100644 --- a/lib/review/book.rb +++ b/lib/review/book.rb @@ -18,8 +18,8 @@ module ReVIEW module Book - def self.load(dir) - Base.load(dir) + def self.load(_dir) + raise NotImplementedError, 'ReVIEW::Book.load is obsoleted. Please use ReVIEW::Book::Base.new.' end end end diff --git a/lib/review/book/base.rb b/lib/review/book/base.rb index d8437c2dd..3d96ff0fd 100644 --- a/lib/review/book/base.rb +++ b/lib/review/book/base.rb @@ -9,26 +9,34 @@ # require 'review/configure' require 'review/catalog' +require 'review/book/bib' module ReVIEW module Book class Base attr_accessor :config attr_writer :parts - attr_writer :catalog + attr_accessor :catalog attr_reader :basedir + attr_accessor :bibpaper_index - def self.load(dir = '.') - new(dir) + def self.load(basedir = '.', config: nil) + new(basedir, config: config) end - def initialize(basedir = '.') + def initialize(basedir = '.', config: nil) @basedir = basedir @logger = ReVIEW.logger @parts = nil @chapter_index = nil - @config = ReVIEW::Configure.values + @config = config || ReVIEW::Configure.values @catalog = nil + @bibpaper_index = nil + catalog_path = filename_join(@basedir, @config['catalogfile']) + if catalog_path && File.file?(catalog_path) + parse_catalog_file(catalog_path) + end + @warn_old_files = {} # XXX for checking CHAPS, PREDEF, POSTDEF @basedir_seen = {} update_rubyenv @@ -44,6 +52,14 @@ def update_rubyenv end end + def execute_indexer + return unless @catalog + + parts.each do |part| + part.chapters.each(&:execute_indexer) + end + end + def bib_file config['bib_file'] end @@ -94,6 +110,30 @@ def htmlversion end end + def create_chapter_index + chapter_index = ChapterIndex.new + each_chapter do |chap| + chapter_index.add_item(Index::Item.new(chap.id, chap.number, chap)) + end + parts.each do |prt| + if prt.id.present? + chapter_index.add_item(Index::Item.new(prt.id, prt.number, prt)) + end + end + chapter_index + end + + def generate_indexes + if bib_exist? + bib = ReVIEW::Book::Bib.new(file_content: bib_content, book: self) + bib.generate_indexes(use_bib: true) + @bibpaper_index = bib.bibpaper_index + end + self.each_chapter(&:generate_indexes) + self.parts.map(&:generate_indexes) + @chapter_index = create_chapter_index + end + def parts @parts ||= read_parts end @@ -101,7 +141,7 @@ def parts def parts_in_file # TODO: should be `parts.find_all{|part| part.present? and part.file?}` ? parts.find_all do |part| - part if part.present? and part.file? + part if part.present? && part.file? end end @@ -136,15 +176,7 @@ def each_chapter_r(&block) def chapter_index return @chapter_index if @chapter_index - @chapter_index = ChapterIndex.new - each_chapter do |chap| - @chapter_index.add_item(Index::Item.new(chap.id, chap.number, chap)) - end - parts.each do |prt| - if prt.id.present? - @chapter_index.add_item(Index::Item.new(prt.id, prt.number, prt)) - end - end + @chapter_index = create_chapter_index @chapter_index end @@ -183,17 +215,15 @@ def load_config(filename) @config.merge!(new_conf) end - def catalog - return @catalog if @catalog.present? - - catalogfile_path = filename_join(@basedir, config['catalogfile']) - if File.file?(catalogfile_path) - @catalog = File.open(catalogfile_path, 'rt:BOM|utf-8') { |f| Catalog.new(f) } + def parse_catalog_file(path) + unless File.file?(path) + raise FileNotFound, "catalog.yml is not found #{path}" end - if @catalog + + File.open(path, 'rt:BOM|utf-8') do |f| + @catalog = Catalog.new(f) @catalog.validate!(@config, basedir) end - @catalog end def read_chaps @@ -252,6 +282,10 @@ def bib_exist? File.exist?(File.join(contentdir, bib_file)) end + def bib_content + File.read(File.join(contentdir, bib_file)) + end + def prefaces if catalog return Part.mkpart_from_namelist(self, catalog.predef) @@ -333,8 +367,8 @@ def parse_chapters end end - chap = read_chaps.map(&:strip).join("\n").split(/\n{2,}/). - map do |part_chunk| + # rubocop:disable Style/RedundantAssignment + chap = read_chaps.map(&:strip).join("\n").split(/\n{2,}/).map do |part_chunk| chaps = part_chunk.split.map { |chapid| Chapter.new(self, num += 1, chapid, File.join(contentdir, chapid)) } if part_exist? && read_part.size > part Part.new(self, part += 1, chaps, read_part[part - 1]) @@ -342,6 +376,8 @@ def parse_chapters Part.new(self, nil, chaps) end end + # rubocop:enable Style/RedundantAssignment + chap end diff --git a/lib/review/book/bib.rb b/lib/review/book/bib.rb new file mode 100644 index 000000000..fb1f66307 --- /dev/null +++ b/lib/review/book/bib.rb @@ -0,0 +1,21 @@ +# +# Copyright (c) 2009-2020 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 the GNU LGPL, see the file "COPYING". +# +require 'review/book/book_unit' +require 'review/lineinput' +require 'review/preprocessor' + +module ReVIEW + module Book + class Bib < BookUnit + def number + nil + end + end + end +end diff --git a/lib/review/book/book_unit.rb b/lib/review/book/book_unit.rb new file mode 100644 index 000000000..944658a36 --- /dev/null +++ b/lib/review/book/book_unit.rb @@ -0,0 +1,155 @@ +# Copyright (c) 2009-2017 Minero Aoki, Kenshi Muto +# 2002-2008 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. +# For details of the GNU LGPL, see the file "COPYING". +# +require 'review/textutils' +require 'review/index_builder' + +module ReVIEW + module Book + class BookUnit + include TextUtils + attr_reader :book + attr_reader :path + attr_reader :lines + attr_accessor :content + + attr_reader :list_index, :table_index, :equation_index, :footnote_index, + :numberless_image_index, :image_index, :icon_index, :indepimage_index, + :headline_index, :column_index + + def initialize(file_content: nil, book: nil) + if book + @book = book + end + if file_content + @content = file_content + end + if @content + @lines = @content.lines + end + end + + def execute_indexer(force: false) + if @index_builder && !force + return @index_builder + end + + @index_builder = ReVIEW::IndexBuilder.new + compiler = ReVIEW::Compiler.new(@index_builder) + compiler.compile(self) + @index_builder + end + + def generate_indexes(use_bib: false) + return unless content + + @lines = content.lines + + @indexes = execute_indexer + + @list_index = @indexes.list_index + @table_index = @indexes.table_index + @equation_index = @indexes.equation_index + @footnote_index = @indexes.footnote_index + @headline_index = @indexes.headline_index + @column_index = @indexes.column_index + if use_bib + @book.bibpaper_index = @indexes.bibpaper_index + end + end + + def dirname + @path && File.dirname(@path) + end + + def basename + @path && File.basename(@path) + end + + def name + @name && File.basename(@name, '.*') + end + + alias_method :id, :name + + def title + return @title if @title + + @title = '' + return @title unless content + content.each_line do |line| + if line =~ /\A=+/ + @title = line.sub(/\A=+(\[.+?\])?(\{.+?\})?/, '').strip + break + end + end + @title + end + + def size + content.size + end + + def volume + @volume ||= Volume.count_file(path) + end + + def list(id) + list_index[id] + end + + def table(id) + table_index[id] + end + + def equation(id) + equation_index[id] + end + + def footnote(id) + footnote_index[id] + end + + def image(id) + return image_index[id] if image_index.key?(id) + return icon_index[id] if icon_index.key?(id) + return numberless_image_index[id] if numberless_image_index.key?(id) + indepimage_index[id] + end + + def bibpaper(id) + bibpaper_index[id] + end + + def bibpaper_index + raise FileNotFound, "no such bib file: #{@book.bib_file}" unless @book.bib_exist? + @book.bibpaper_index + end + + def headline(caption) + headline_index[caption] + end + + def column(id) + column_index[id] + end + + def next_chapter + book.next_chapter(self) + end + + def prev_chapter + book.prev_chapter(self) + end + + def image_bound?(item_id) + image(item_id).path + end + end + end +end diff --git a/lib/review/book/chapter.rb b/lib/review/book/chapter.rb index 014d752b4..ec52c596d 100644 --- a/lib/review/book/chapter.rb +++ b/lib/review/book/chapter.rb @@ -7,15 +7,13 @@ # the GNU LGPL, Lesser General Public License version 2.1. # For details of the GNU LGPL, see the file "COPYING". # -require 'review/book/compilable' +require 'review/book/book_unit' require 'review/lineinput' require 'review/preprocessor' module ReVIEW module Book - class Chapter - include Compilable - + class Chapter < BookUnit attr_reader :number, :book def self.mkchap(book, name, number = nil) @@ -53,35 +51,41 @@ def initialize(book, number, name, path, io = nil) @content = File.read(@path, mode: 'rt:BOM|utf-8') @number = nil if %w[nonum nodisp notoc].include?(find_first_header_option) end - @list_index = nil - @table_index = nil - @equation_index = nil - @footnote_index = nil - @image_index = nil - @icon_index = nil - @numberless_image_index = nil - @indepimage_index = nil - @headline_index = nil - @column_index = nil - @volume = nil + + super() + end + + def generate_indexes + super + + return unless content + + @numberless_image_index = @indexes.numberless_image_index + @image_index = @indexes.image_index + @icon_index = @indexes.icon_index + @indepimage_index = @indexes.indepimage_index end def find_first_header_option f = LineInput.new(StringIO.new(@content)) - while f.next? - case f.peek - when /\A=+[\[\s\{]/ - m = /\A(=+)(?:\[(.+?)\])?(?:\{(.+?)\})?(.*)/.match(f.gets) - return m[2] # tag - when %r{/\A//[a-z]+/} - line = f.gets - if line.rstrip[-1, 1] == '{' - f.until_match(%r{\A//\}}) + begin + while f.next? + case f.peek + when /\A=+[\[\s{]/ + m = /\A(=+)(?:\[(.+?)\])?(?:\{(.+?)\})?(.*)/.match(f.gets) + return m[2] # tag + when %r{/\A//[a-z]+/} + line = f.gets + if line.rstrip[-1, 1] == '{' + f.until_match(%r{\A//\}}) + end end + f.gets end - f.gets + nil + rescue ArgumentError => e + raise ReVIEW::CompileError, "#{@name}: #{e}" end - nil end def inspect diff --git a/lib/review/book/compilable.rb b/lib/review/book/compilable.rb deleted file mode 100644 index 16b4ed5b8..000000000 --- a/lib/review/book/compilable.rb +++ /dev/null @@ -1,174 +0,0 @@ -# Copyright (c) 2009-2017 Minero Aoki, Kenshi Muto -# 2002-2008 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. -# For details of the GNU LGPL, see the file "COPYING". -# -require 'review/textutils' - -module ReVIEW - module Book - module Compilable - include TextUtils - attr_reader :book - attr_reader :path - attr_accessor :content - - def dirname - return nil unless @path - File.dirname(@path) - end - - def basename - return nil unless @path - File.basename(@path) - end - - def name - return nil unless @name - File.basename(@name, '.*') - end - - alias_method :id, :name - - def title - return @title if @title - - @title = '' - return @title unless content - content.each_line do |line| - if line =~ /\A=+/ - @title = line.sub(/\A=+(\[.+?\])?(\{.+?\})?/, '').strip - break - end - end - @title - end - - def size - content.size - end - - def volume - @volume ||= Volume.count_file(path) - end - - def lines - # FIXME: we cannot duplicate Enumerator on ruby 1.9 HEAD - (@lines ||= content.lines.to_a).dup - end - - def list(id) - list_index[id] - end - - def list_index - @list_index ||= ListIndex.parse(lines) - @list_index - end - - def table(id) - table_index[id] - end - - def table_index - @table_index ||= TableIndex.parse(lines) - @table_index - end - - def equation(id) - equation_index[id] - end - - def equation_index - @equation_index ||= EquationIndex.parse(lines) - @equation_index - end - - def footnote(id) - footnote_index[id] - end - - def footnote_index - @footnote_index ||= FootnoteIndex.parse(lines) - @footnote_index - end - - def image(id) - return image_index[id] if image_index.key?(id) - return icon_index[id] if icon_index.key?(id) - return numberless_image_index[id] if numberless_image_index.key?(id) - indepimage_index[id] - end - - def numberless_image_index - @numberless_image_index ||= - NumberlessImageIndex.parse(lines, id, - @book.imagedir, - @book.image_types, @book.config['builder']) - end - - def image_index - @image_index ||= ImageIndex.parse(lines, id, - @book.imagedir, - @book.image_types, @book.config['builder']) - @image_index - end - - def icon_index - @icon_index ||= IconIndex.parse(lines, id, - @book.imagedir, - @book.image_types, @book.config['builder']) - @icon_index - end - - def indepimage_index - @indepimage_index ||= - IndepImageIndex.parse(lines, id, - @book.imagedir, - @book.image_types, @book.config['builder']) - end - - def bibpaper(id) - bibpaper_index[id] - end - - def bibpaper_index - raise FileNotFound, "no such bib file: #{@book.bib_file}" unless @book.bib_exist? - @bibpaper_index ||= BibpaperIndex.parse(@book.read_bib.lines.to_a) - @bibpaper_index - end - - def headline(caption) - headline_index[caption] - end - - def headline_index - @headline_index ||= HeadlineIndex.parse(lines, self) - end - - def column(id) - column_index[id] - end - - def column_index - @column_index ||= ColumnIndex.parse(lines) - end - - def next_chapter - book.next_chapter(self) - end - - def prev_chapter - book.prev_chapter(self) - end - - def image_bound?(item_id) - item = self.image(item_id) - item.path - end - end - end -end diff --git a/lib/review/book/index.rb b/lib/review/book/index.rb index 7ba3c50f1..37c66671e 100644 --- a/lib/review/book/index.rb +++ b/lib/review/book/index.rb @@ -17,21 +17,6 @@ module ReVIEW module Book class Index - def self.parse(src, *args) - index = self.new(*args) - seq = 1 - src.grep(%r{\A//#{item_type}}) do |line| - if id = line.slice(/\[(.*?)\]/, 1) - index.add_item(ReVIEW::Book::Index::Item.new(id, seq)) - seq += 1 - if id.empty? - ReVIEW.logger.warn "warning: no ID of #{item_type} in #{line}" - end - end - end - index - end - include Enumerable def item_type @@ -44,9 +29,13 @@ def initialize @image_finder = nil end + def size + @index.size + end + def add_item(item) - if @index[item.id] - @logger.warn "warning: duplicate ID: #{item.id} (#{item})" + if @index[item.id] && self.class != ReVIEW::Book::IconIndex + @logger.warn "warning: duplicate ID: #{item.id} (#{item.inspect})" end @index[item.id] = item if item.class != ReVIEW::Book::Chapter @@ -57,13 +46,13 @@ def add_item(item) def [](id) @index.fetch(id) rescue - if @index.keys.map { |i| i.split('|').last }.flatten. # unfold all ids - each_with_object(Hash.new(0)) { |i, h| h[i] += 1 }. # number of occurrences + index_keys = @index.keys.map { |i| i.split('|').last }.flatten # unfold all ids + if index_keys.each_with_object(Hash.new(0)) { |i, h| h[i] += 1 }. # number of occurrences select { |k, v| k == id && v > 1 }.present? # detect duplicated raise KeyError, "key '#{id}' is ambiguous for #{self.class}" end - @index.values.each do |item| + @index.each_value do |item| if item.id.split('|').include?(id) return item end @@ -134,55 +123,24 @@ def self.item_type end class FootnoteIndex < Index - def self.parse(src) - index = self.new - seq = 1 - src.grep(%r{\A//footnote}) do |line| - if m = /\[(.*?)\]\[(.*)\]/.match(line) - m1 = m[1].gsub(/\\(\])/) { $1 } - m2 = m[2].gsub(/\\(\])/) { $1 } - index.add_item(Item.new(m1, seq, m2)) - end - seq += 1 - end - index - end end class ImageIndex < Index - def self.parse(src, *args) - index = self.new(*args) - seq = 1 - src.grep(%r{\A//#{item_type}}) do |line| - # ex. ["//image", "id", "", "caption"] - elements = line.split(/\[(.*?)\]/) - if elements[1].present? - if line.start_with?('//imgtable') - index.add_item(ReVIEW::Book::Index::Item.new(elements[1], 0, elements[3])) - else ## %r<\A//(image|graph)> - index.add_item(ReVIEW::Book::Index::Item.new(elements[1], seq, elements[3])) - seq += 1 - end - if elements[1] == '' - ReVIEW.logger.warn "warning: no ID of #{item_type} in #{line}" - end - end - end - index - end - def self.item_type '(image|graph|imgtable)' end attr_reader :image_finder - def initialize(chapid, basedir, types, builder) + def initialize(chapter) super() - @chapid = chapid - @basedir = basedir - @types = types - @logger = ReVIEW.logger + @chapter = chapter + book = @chapter.book + + chapid = chapter.id + basedir = book.imagedir + builder = book.config['builder'] + types = book.image_types @image_finder = ReVIEW::Book::ImageFinder.new(basedir, chapid, builder, types) end @@ -193,43 +151,9 @@ def find_path(id) end class IconIndex < ImageIndex - def initialize(chapid, basedir, types, builder) - @index = {} - @chapid = chapid - @basedir = basedir - @types = types - @logger = ReVIEW.logger - - @image_finder = ImageFinder.new(basedir, chapid, builder, types) - end - - def self.parse(src, *args) - index = self.new(*args) - seq = 1 - src.grep(/@<icon>/) do |line| - line.gsub(/@<icon>\{(.+?)\}/) do - index.add_item(ReVIEW::Book::Index::Item.new($1, seq)) - seq += 1 - end - end - index - end end class BibpaperIndex < Index - def self.parse(src) - index = self.new - seq = 1 - src.grep(%r{\A//bibpaper}) do |line| - if m = /\[(.*?)\]\[(.*)\]/.match(line) - m1 = m[1].gsub(/\\(.)/) { $1 } - m2 = m[2].gsub(/\\(.)/) { $1 } - index.add_item(Item.new(m1, seq, m2)) - end - seq += 1 - end - index - end end class NumberlessImageIndex < ImageIndex @@ -255,77 +179,9 @@ def number(_id) class HeadlineIndex < Index HEADLINE_PATTERN = /\A(=+)(?:\[(.+?)\])?(?:\{(.+?)\})?(.*)/ - def self.parse(src, chap) - headline_index = self.new(chap) - indexs = [] - headlines = [] - inside_column = false - inside_block = nil - column_level = -1 - src.each do |line| - if line =~ %r{\A//[a-z]+.*\{\Z} - inside_block = true - next - elsif line.start_with?('//}') - inside_block = nil - next - elsif inside_block - next - end - - m = HEADLINE_PATTERN.match(line) - if m.nil? || m[1].size > 10 # Ignore too deep index - next - end - - index = m[1].size - 2 - - # column - if m[2] == 'column' - inside_column = true - column_level = index - next - elsif m[2] == '/column' - inside_column = false - next - end - if indexs.blank? || index <= column_level - inside_column = false - end - next if inside_column - next if m[4].strip.empty? # no title - - next unless index >= 0 - if indexs.size > (index + 1) - unless %w[nonum notoc nodisp].include?(m[2]) - indexs = indexs.take(index + 1) - end - headlines = headlines.take(index + 1) - end - if indexs[index].nil? - (0..index).each do |i| - indexs[i] ||= 0 - end - end - - if %w[nonum notoc nodisp].include?(m[2]) - headlines[index] = m[3].present? ? m[3].strip : m[4].strip - item_id = headlines.join('|') - headline_index.add_item(Item.new(item_id, nil, m[4].strip)) - else - indexs[index] += 1 - headlines[index] = m[3].present? ? m[3].strip : m[4].strip - item_id = headlines.join('|') - headline_index.add_item(Item.new(item_id, indexs.dup, m[4].strip)) - end - end - headline_index - end - - def initialize(chap) - @chap = chap - @index = {} - @logger = ReVIEW.logger + def initialize(chapter) + super() + @chapter = chapter end def number(id) @@ -333,34 +189,16 @@ def number(id) # when notoc return '' end - n = @chap.number + n = @chapter.number # XXX: remove magic number (move to lib/review/book/chapter.rb) - if @chap.on_appendix? && @chap.number > 0 && @chap.number < 28 - n = @chap.format_number(false) + if @chapter.on_appendix? && @chapter.number > 0 && @chapter.number < 28 + n = @chapter.format_number(false) end ([n] + self[id].number).join('.') end end class ColumnIndex < Index - COLUMN_PATTERN = /\A(=+)\[column\](?:\{(.+?)\})?(.*)/ - - def self.parse(src, *_args) - index = self.new - seq = 1 - src.each do |line| - m = COLUMN_PATTERN.match(line) - next unless m - _level = m[1] ## not use it yet - id = m[2] - caption = m[3].strip - id = caption if id.nil? || id.empty? - - index.add_item(ReVIEW::Book::Index::Item.new(id, seq, caption)) - seq += 1 - end - index - end end end end diff --git a/lib/review/book/index/item.rb b/lib/review/book/index/item.rb index f2edece56..d9458e68c 100644 --- a/lib/review/book/index/item.rb +++ b/lib/review/book/index/item.rb @@ -32,7 +32,16 @@ def initialize(id, number, caption = nil) alias_method :content, :caption def path - @path ||= @index.find_path(id) + if @path + return @path + end + + if @id =~ /\s/ + raise ReVIEW::SyntaxError, "invalid ID character for path: `#{@id}`" + end + @path = @index.find_path(@id) + + @path end end end diff --git a/lib/review/book/part.rb b/lib/review/book/part.rb index e540452cd..6ea4a7879 100644 --- a/lib/review/book/part.rb +++ b/lib/review/book/part.rb @@ -1,4 +1,4 @@ -# Copyright (c) 2009-2019 Minero Aoki, Kenshi Muto +# Copyright (c) 2009-2020 Minero Aoki, Kenshi Muto, Masayoshi Takahashi # 2002-2008 Minero Aoki # # This program is free software. @@ -6,13 +6,11 @@ # the GNU LGPL, Lesser General Public License version 2.1. # For details of the GNU LGPL, see the file "COPYING". # -require 'review/book/compilable' +require 'review/book/book_unit' module ReVIEW module Book - class Part - include Compilable - + class Part < BookUnit def self.mkpart_from_namelistfile(book, path) chaps = [] File.read(path, mode: 'rt:BOM|utf-8').split.each_with_index do |name, number| @@ -30,7 +28,7 @@ def self.mkpart_from_namelist(book, names) end def self.mkpart(chaps) - chaps.empty? ? nil : Part.new(self, nil, chaps) + chaps.empty? ? nil : Part.new(chaps[0].book, nil, chaps) end # if Part is dummy, `number` is nil. @@ -38,15 +36,16 @@ def self.mkpart(chaps) def initialize(book, number, chapters, name = '', io = nil) @book = book @number = number - @chapters = chapters @name = name + @chapters = chapters @path = name - @content = '' if io @content = io.read elsif @path.present? && File.exist?(File.join(@book.config['contentdir'], @path)) @content = File.read(File.join(@book.config['contentdir'], @path), mode: 'rt:BOM|utf-8') - @name = File.basename(@name, '.re') + @name = File.basename(name, '.re') + else + @content = '' end if file? @title = nil @@ -54,6 +53,19 @@ def initialize(book, number, chapters, name = '', io = nil) @title = name end @volume = nil + + super() + end + + def generate_indexes + super + + return unless content + + @numberless_image_index = @indexes.numberless_image_index + @image_index = @indexes.image_index + @icon_index = @indexes.icon_index + @indepimage_index = @indexes.indepimage_index end attr_reader :number diff --git a/lib/review/book/volume.rb b/lib/review/book/volume.rb index d5a02a8f0..8a6d03040 100644 --- a/lib/review/book/volume.rb +++ b/lib/review/book/volume.rb @@ -22,7 +22,7 @@ def self.count_file(path) end def self.sum(vols) - vols.inject(new) { |sum, i| sum + i } + vols.inject(new) { |sum, i| sum + i } # rubocop:disable Performance/Sum end def self.dummy diff --git a/lib/review/builder.rb b/lib/review/builder.rb index d9888006b..4b4e36c5f 100644 --- a/lib/review/builder.rb +++ b/lib/review/builder.rb @@ -11,7 +11,6 @@ require 'review/compiler' require 'review/sec_counter' require 'stringio' -require 'cgi' require 'fileutils' require 'tempfile' require 'csv' @@ -20,7 +19,7 @@ module ReVIEW class Builder include TextUtils - CAPTION_TITLES = %w[note memo tip info warning important caution notice].freeze + CAPTION_TITLES = Compiler.minicolumn_names def pre_paragraph nil @@ -32,20 +31,15 @@ def post_paragraph attr_accessor :doc_status, :previous_list_type - def initialize(strict = false, *args) + def initialize(strict = false, *_args) @strict = strict @output = nil @logger = ReVIEW.logger @doc_status = {} @dictionary = {} @previous_list_type = nil - builder_init(*args) end - def builder_init(*args) - end - private :builder_init - def bind(compiler, chapter, location) @compiler = compiler @chapter = chapter @@ -54,6 +48,10 @@ def bind(compiler, chapter, location) if @chapter.present? @book = @chapter.book end + @chapter.generate_indexes + if @book + @book.generate_indexes + end @tabwidth = nil @tsize = nil if @book && @book.config @@ -85,6 +83,7 @@ def bind(compiler, chapter, location) def builder_init_file @sec_counter = SecCounter.new(5, @chapter) + @doc_status = {} end private :builder_init_file @@ -154,25 +153,28 @@ def line_num def list(lines, id, caption, lang = nil) begin - list_header(id, caption, lang) + list_header(id, caption, lang) if caption_top?('list') + list_body(id, lines, lang) + list_header(id, caption, lang) unless caption_top?('list') rescue KeyError error "no such list: #{id}" end - list_body(id, lines, lang) end def listnum(lines, id, caption, lang = nil) begin - list_header(id, caption, lang) + list_header(id, caption, lang) if caption_top?('list') + listnum_body(lines, lang) + list_header(id, caption, lang) unless caption_top?('list') rescue KeyError error "no such list: #{id}" end - listnum_body(lines, lang) end def source(lines, caption = nil, lang = nil) - source_header(caption) + source_header(caption) if caption_top?('list') source_body(lines, lang) + source_header(caption) unless caption_top?('list') end def image(lines, id, caption, metric = nil) @@ -187,15 +189,18 @@ def image(lines, id, caption, metric = nil) def table(lines, id = nil, caption = nil) sepidx, rows = parse_table_rows(lines) begin - if caption.present? + if caption_top?('table') && caption.present? + table_header(id, caption) + end + table_begin(rows.first.size) + table_rows(sepidx, rows) + table_end + if !caption_top?('table') && caption.present? table_header(id, caption) end rescue KeyError error "no such table: #{id}" end - table_begin(rows.first.size) - table_rows(sepidx, rows) - table_end end def table_row_separator_regexp @@ -217,7 +222,7 @@ def parse_table_rows(lines) sepidx = nil rows = [] lines.each_with_index do |line, idx| - if /\A[\=\-]{12}/ =~ line || /\A[\=\{\-\}]{12}/ =~ line + if /\A[=\-]{12}/ =~ line || /\A[={\-}]{12}/ =~ line sepidx ||= idx next end @@ -246,7 +251,7 @@ def table_rows(sepidx, rows) def adjust_n_cols(rows) rows.each do |cols| - while cols.last and cols.last.strip.empty? + while cols.last && cols.last.strip.empty? cols.pop end end @@ -550,9 +555,36 @@ def captionblock(_type, _lines, _caption, _specialstyle = nil) CAPTION_TITLES.each do |name| class_eval %Q( def #{name}(lines, caption = nil) + check_nested_minicolumn captionblock("#{name}", lines, caption) end - ) + + def #{name}_begin(caption = nil) + check_nested_minicolumn + @doc_status[:minicolumn] = '#{name}' + if caption + puts compile_inline(caption) + end + end + + def #{name}_end + @doc_status[:minicolumn] = nil + end + ), __FILE__, __LINE__ - 17 + end + + def check_nested_minicolumn + if @doc_status[:minicolumn] + error "#{@location}: nested mini-column is not allowed" + end + end + + def in_minicolumn? + @doc_status[:minicolumn] + end + + def minicolumn_block_name?(name) + CAPTION_TITLES.include?(name) end def graph(lines, id, command, caption = '') @@ -709,5 +741,12 @@ def endchild(_comment = nil) puts "\x01→/#{@children.pop}←\x01" end end + + def caption_top?(type) + unless %w[top bottom].include?(@book.config['caption_position'][type]) + warn("invalid caption_position/#{type} parameter. 'top' is assumed") + end + @book.config['caption_position'][type] != 'bottom' + end end end # module ReVIEW diff --git a/lib/review/catalog.rb b/lib/review/catalog.rb index 3f94d816b..cde56a0b8 100644 --- a/lib/review/catalog.rb +++ b/lib/review/catalog.rb @@ -41,7 +41,7 @@ def parts def replace_part(old_name, new_name) @yaml['CHAPS'].map! do |e| - if e.is_a?(Hash) and (e.keys.first == old_name) + if e.is_a?(Hash) && (e.keys.first == old_name) e = { new_name => e.values.first } end e @@ -89,7 +89,7 @@ def validate!(config, basedir) filenames.concat(postdef) end filenames.each do |filename| - refile = File.join(basedir, config['contentdir'], filename) + refile = File.expand_path(File.join(config['contentdir'], filename), basedir) unless File.exist?(refile) raise FileNotFound, "file not found in catalog.yml: #{refile}" end diff --git a/lib/review/compiler.rb b/lib/review/compiler.rb index fcf9b9e6f..65f71995d 100644 --- a/lib/review/compiler.rb +++ b/lib/review/compiler.rb @@ -14,8 +14,8 @@ module ReVIEW class Compiler - def initialize(strategy) - @strategy = strategy + def initialize(builder) + @builder = builder ## commands which do not parse block lines in compiler @non_parsed_commands = %i[embed texequation graph] @@ -24,10 +24,16 @@ def initialize(strategy) @command_name_stack = [] end - attr_reader :strategy, :previous_list_type + attr_reader :previous_list_type + attr_reader :builder + + def strategy + error 'Compiler#strategy is obsoleted. Use Compiler#builder.' + @builder + end def non_escaped_commands - if @strategy.highlight? + if @builder.highlight? %i[list emlist listnum emlistnum cmd] else [] @@ -37,7 +43,7 @@ def non_escaped_commands def compile(chap) @chapter = chap do_compile - @strategy.result + @builder.result end class SyntaxElement @@ -68,12 +74,16 @@ def min_argc end end + def minicolumn? + @type == :minicolumn + end + def block_required? - @type == :block + @type == :block or @type == :minicolumn end def block_allowed? - @type == :block or @type == :optional + @type == :block or @type == :optional or @type == :minicolumn end end @@ -83,6 +93,10 @@ def self.defblock(name, argc, optional = false, &block) defsyntax(name, (optional ? :optional : :block), argc, &block) end + def self.defminicolumn(name, argc, _optional = false, &block) + defsyntax(name, :minicolumn, argc, &block) + end + def self.defsingle(name, argc, &block) defsyntax(name, :line, argc, &block) end @@ -95,6 +109,16 @@ def self.definline(name) INLINE[name] = InlineSyntaxElement.new(name) end + def self.minicolumn_names + buf = [] + SYNTAX.each do |name, syntax| + if syntax.minicolumn? + buf << name.to_s + end + end + buf + end + def syntax_defined?(name) SYNTAX.key?(name.to_sym) end @@ -143,18 +167,19 @@ def inline_defined?(name) defblock :bpo, 0 defblock :flushright, 0 defblock :centering, 0 - defblock :note, 0..1 - defblock :memo, 0..1 - defblock :info, 0..1 - defblock :important, 0..1 - defblock :caution, 0..1 - defblock :notice, 0..1 - defblock :warning, 0..1 - defblock :tip, 0..1 defblock :box, 0..1 defblock :comment, 0..1, true defblock :embed, 0..1 + defminicolumn :note, 0..1 + defminicolumn :memo, 0..1 + defminicolumn :tip, 0..1 + defminicolumn :info, 0..1 + defminicolumn :warning, 0..1 + defminicolumn :important, 0..1 + defminicolumn :caution, 0..1 + defminicolumn :notice, 0..1 + defsingle :footnote, 2 defsingle :noindent, 0 defsingle :blankline, 0 @@ -233,45 +258,62 @@ def inline_defined?(name) def do_compile f = LineInput.new(StringIO.new(@chapter.content)) - @strategy.bind(self, @chapter, Location.new(@chapter.basename, f)) + @builder.bind(self, @chapter, Location.new(@chapter.basename, f)) + + ## in minicolumn, such as note/info/alert... + @minicolumn_name = nil tagged_section_init while f.next? case f.peek when /\A\#@/ f.gets # Nothing to do - when /\A=+[\[\s\{]/ + when /\A=+[\[\s{]/ compile_headline(f.gets) - @strategy.previous_list_type = nil + @builder.previous_list_type = nil when /\A\s+\*/ compile_ulist(f) - @strategy.previous_list_type = 'ul' + @builder.previous_list_type = 'ul' when /\A\s+\d+\./ compile_olist(f) - @strategy.previous_list_type = 'ol' + @builder.previous_list_type = 'ol' when /\A\s+:\s/ compile_dlist(f) - @strategy.previous_list_type = 'dl' + @builder.previous_list_type = 'dl' when /\A\s*:\s/ warn 'Definition list starting with `:` is deprecated. It should start with ` : `.' compile_dlist(f) - @strategy.previous_list_type = 'dl' + @builder.previous_list_type = 'dl' when %r{\A//\}} - f.gets - error 'block end seen but not opened' + if in_minicolumn? + _line = f.gets + compile_minicolumn_end + else + f.gets + error 'block end seen but not opened' + end when %r{\A//[a-z]+} - # @command_name_stack.push(name) ## <- move into read_command() to use name - name, args, lines = read_command(f) - syntax = syntax_descriptor(name) - unless syntax - error "unknown command: //#{name}" - compile_unknown_command(args, lines) + line = f.peek + matched = line =~ %r|\A//([a-z]+)(:?\[.*\])?{\s*$| + if matched && minicolumn_block_name?($1) + line = f.gets + name = $1 + args = parse_args(line.sub(%r{\A//[a-z]+}, '').rstrip.chomp('{'), name) + compile_minicolumn_begin(name, *args) + else + # @command_name_stack.push(name) ## <- move into read_command() to use name + name, args, lines = read_command(f) + syntax = syntax_descriptor(name) + unless syntax + error "unknown command: //#{name}" + compile_unknown_command(args, lines) + @command_name_stack.pop + next + end + compile_command(syntax, args, lines) @command_name_stack.pop - next end - compile_command(syntax, args, lines) - @command_name_stack.pop - @strategy.previous_list_type = nil + @builder.previous_list_type = nil when %r{\A//} line = f.gets warn "`//' seen but is not valid command: #{line.strip.inspect}" @@ -279,19 +321,46 @@ def do_compile warn 'skipping block...' read_block(f, false) end - @strategy.previous_list_type = nil + @builder.previous_list_type = nil else if f.peek.strip.empty? f.gets next end compile_paragraph(f) - @strategy.previous_list_type = nil + @builder.previous_list_type = nil end end close_all_tagged_section end + def compile_minicolumn_begin(name, caption = nil) + mid = "#{name}_begin" + unless @builder.respond_to?(mid) + error "strategy does not support minicolumn: #{name}" + end + + if @minicolumn_name + error "minicolumn cannot be nested: #{name}" + return + end + @minicolumn_name = name + + @builder.__send__(mid, caption) + end + + def compile_minicolumn_end + unless @minicolumn_name + error "minicolumn is not used: #{name}" + return + end + name = @minicolumn_name + + mid = "#{name}_end" + @builder.__send__(mid) + @minicolumn_name = nil + end + def compile_headline(line) @headline_indexs ||= [@chapter.number.to_i - 1] m = /\A(=+)(?:\[(.+?)\])?(?:\{(.+?)\})?(.*)/.match(line) @@ -327,18 +396,18 @@ def compile_headline(line) end @headline_indexs[index] += 1 close_current_tagged_section(level) - @strategy.headline(level, label, caption) + @builder.headline(level, label, caption) end end def close_current_tagged_section(level) - while @tagged_section.last and @tagged_section.last[1] >= level + while @tagged_section.last && (@tagged_section.last[1] >= level) close_tagged_section(* @tagged_section.pop) end end def headline(level, label, caption) - @strategy.headline(level, label, caption) + @builder.headline(level, label, caption) end def tagged_section_init @@ -347,21 +416,21 @@ def tagged_section_init def open_tagged_section(tag, level, label, caption) mid = "#{tag}_begin" - unless @strategy.respond_to?(mid) - error "strategy does not support tagged section: #{tag}" + unless @builder.respond_to?(mid) + error "builder does not support tagged section: #{tag}" headline(level, label, caption) return end @tagged_section.push([tag, level]) - @strategy.__send__(mid, level, label, caption) + @builder.__send__(mid, level, label, caption) end def close_tagged_section(tag, level) mid = "#{tag}_end" - if @strategy.respond_to?(mid) - @strategy.__send__(mid, level) + if @builder.respond_to?(mid) + @builder.__send__(mid, level) else - error "strategy does not support block op: #{mid}" + error "builder does not support block op: #{mid}" end end @@ -384,38 +453,38 @@ def compile_ulist(f) line =~ /\A\s+(\*+)/ current_level = $1.size if level == current_level - @strategy.ul_item_end + @builder.ul_item_end # body - @strategy.ul_item_begin(buf) + @builder.ul_item_begin(buf) elsif level < current_level # down level_diff = current_level - level if level_diff != 1 error 'too many *.' end level = current_level - @strategy.ul_begin { level } - @strategy.ul_item_begin(buf) + @builder.ul_begin { level } + @builder.ul_item_begin(buf) elsif level > current_level # up level_diff = level - current_level level = current_level (1..level_diff).to_a.reverse_each do |i| - @strategy.ul_item_end - @strategy.ul_end { level + i } + @builder.ul_item_end + @builder.ul_end { level + i } end - @strategy.ul_item_end + @builder.ul_item_end # body - @strategy.ul_item_begin(buf) + @builder.ul_item_begin(buf) end end (1..level).to_a.reverse_each do |i| - @strategy.ul_item_end - @strategy.ul_end { i } + @builder.ul_item_end + @builder.ul_end { i } end end def compile_olist(f) - @strategy.ol_begin + @builder.ol_begin f.while_match(/\A\s+\d+\.|\A\#@/) do |line| next if line =~ /\A\#@/ @@ -424,24 +493,24 @@ def compile_olist(f) f.while_match(/\A\s+(?!\d+\.)\S/) do |cont| buf.push(text(cont.strip)) end - @strategy.ol_item(buf, num) + @builder.ol_item(buf, num) end - @strategy.ol_end + @builder.ol_end end def compile_dlist(f) - @strategy.dl_begin + @builder.dl_begin while /\A\s*:/ =~ f.peek # defer compile_inline to handle footnotes - @strategy.doc_status[:dt] = true - @strategy.dt(text(f.gets.sub(/\A\s*:/, '').strip)) - @strategy.doc_status[:dt] = nil + @builder.doc_status[:dt] = true + @builder.dt(text(f.gets.sub(/\A\s*:/, '').strip)) + @builder.doc_status[:dt] = nil desc = f.break(/\A(\S|\s*:|\s+\d+\.\s|\s+\*\s)/).map { |line| text(line.strip) } - @strategy.dd(desc) + @builder.dd(desc) f.skip_blank_lines f.skip_comment_lines end - @strategy.dl_end + @builder.dl_end end def compile_paragraph(f) @@ -450,7 +519,7 @@ def compile_paragraph(f) break if line.strip.empty? buf.push(text(line.sub(/^(\t+)\s*/) { |m| '<!ESCAPETAB!>' * m.size }.strip.gsub('<!ESCAPETAB!>', "\t"))) end - @strategy.paragraph(buf) + @builder.paragraph(buf) end def read_command(f) @@ -459,9 +528,9 @@ def read_command(f) ignore_inline = @non_parsed_commands.include?(name) @command_name_stack.push(name) args = parse_args(line.sub(%r{\A//[a-z]+}, '').rstrip.chomp('{'), name) - @strategy.doc_status[name] = true + @builder.doc_status[name] = true lines = block_open?(line) ? read_block(f, ignore_inline) : nil - @strategy.doc_status[name] = nil + @builder.doc_status[name] = nil [name, args, lines] end @@ -506,8 +575,8 @@ def parse_args(str, _name = nil) end def compile_command(syntax, args, lines) - unless @strategy.respond_to?(syntax.name) - error "strategy does not support command: //#{syntax.name}" + unless @builder.respond_to?(syntax.name) + error "builder does not support command: //#{syntax.name}" compile_unknown_command(args, lines) return end @@ -528,11 +597,11 @@ def compile_command(syntax, args, lines) end def compile_unknown_command(args, lines) - @strategy.unknown_command(args, lines) + @builder.unknown_command(args, lines) end def compile_block(syntax, args, lines) - @strategy.__send__(syntax.name, (lines || default_block(syntax)), *args) + @builder.__send__(syntax.name, (lines || default_block(syntax)), *args) end def default_block(syntax) @@ -543,7 +612,7 @@ def default_block(syntax) end def compile_single(syntax, args) - @strategy.__send__(syntax.name, *args) + @builder.__send__(syntax.name, *args) end def replace_fence(str) @@ -569,7 +638,7 @@ def in_non_escaped_command? def text(str, block_mode = false) return '' if str.empty? - words = replace_fence(str).split(/(@<\w+>\{(?:[^\}\\]|\\.)*?\})/, -1) + words = replace_fence(str).split(/(@<\w+>\{(?:[^}\\]|\\.)*?\})/, -1) words.each do |w| if w.scan(/@<\w+>/).size > 1 && !/\A@<raw>/.match(w) error "`@<xxx>' seen but is not valid inline op: #{w}" @@ -580,7 +649,7 @@ def text(str, block_mode = false) if in_non_escaped_command? && block_mode result << revert_replace_fence(words.shift) else - result << @strategy.nofunc_text(revert_replace_fence(words.shift)) + result << @builder.nofunc_text(revert_replace_fence(words.shift)) end break if words.empty? result << compile_inline(revert_replace_fence(words.shift.gsub(/\\\}/, '}').gsub(/\\\\/, '\\'))) @@ -589,28 +658,36 @@ def text(str, block_mode = false) rescue => e error e.message end - public :text # called from strategy + public :text # called from builder def compile_inline(str) op, arg = /\A@<(\w+)>\{(.*?)\}\z/.match(str).captures unless inline_defined?(op) raise CompileError, "no such inline op: #{op}" end - unless @strategy.respond_to?("inline_#{op}") - raise "strategy does not support inline op: @<#{op}>" + unless @builder.respond_to?("inline_#{op}") + raise "builder does not support inline op: @<#{op}>" end - @strategy.__send__("inline_#{op}", arg) + @builder.__send__("inline_#{op}", arg) rescue => e error e.message - @strategy.nofunc_text(str) + @builder.nofunc_text(str) + end + + def in_minicolumn? + @builder.in_minicolumn? + end + + def minicolumn_block_name?(name) + @builder.minicolumn_block_name?(name) end def warn(msg) - @strategy.warn msg + @builder.warn msg end def error(msg) - @strategy.error msg + @builder.error msg end end end # module ReVIEW diff --git a/lib/review/configure.rb b/lib/review/configure.rb index 4428bfff7..20e675338 100644 --- a/lib/review/configure.rb +++ b/lib/review/configure.rb @@ -18,7 +18,7 @@ def self.values 'bookname' => 'book', # it defines epub file name also 'booktitle' => 'Re:VIEW Sample Book', 'title' => nil, - 'aut' => ['anonymous'], # author + 'aut' => nil, # author 'prt' => nil, # printer(publisher) 'asn' => nil, # associated name 'ant' => nil, # bibliographic antecedent @@ -64,6 +64,7 @@ def self.values 'bib_file' => 'bib.re', 'words_file' => nil, 'colophon_order' => %w[aut csl trl dsr ill cov edt pbl contact prt], + 'chapterlink' => true, 'externallink' => true, 'join_lines_by_lang' => nil, # experimental. default should be nil 'table_row_separator' => 'tabs', @@ -106,12 +107,38 @@ def self.values 'lineheight' => 10 * 1.2, 'pdfcrop_pixelize_cmd' => 'pdftocairo -%t -r 90 -f %p -l %p -singlefile %i %O', 'dvipng_cmd' => 'dvipng -T tight -z 9 -p %p -l %p -o %o %i' + }, + 'caption_position' => { + 'list' => 'top', + 'image' => 'bottom', + 'table' => 'top', + 'equation' => 'top' } ] conf.maker = nil conf end + def self.create(maker: nil, yamlfile: nil, config: nil) + conf = self.values + conf.maker = maker + + if yamlfile + begin + loader = ReVIEW::YAMLLoader.new + conf.deep_merge!(loader.load_file(yamlfile)) + rescue => e + error "yaml error #{e.message}" + end + end + # YAML configs will be overridden by command line options. + if config + conf.deep_merge!(config) + end + + conf + end + def [](key) maker = self.maker if maker && self.key?(maker) && self.fetch(maker) && self.fetch(maker).key?(key) diff --git a/lib/review/converter.rb b/lib/review/converter.rb index 11cc6e209..38c0bb0fa 100644 --- a/lib/review/converter.rb +++ b/lib/review/converter.rb @@ -17,7 +17,7 @@ def convert(file, output_path) chap_name = File.basename(file, '.*') chap = @book.chapter(chap_name) result = @compiler.compile(chap) - File.open(output_path, 'w') { |f| f.puts result } + File.write(output_path, result) end end end diff --git a/lib/review/epub2html.rb b/lib/review/epub2html.rb index ed0670193..0689573f8 100644 --- a/lib/review/epub2html.rb +++ b/lib/review/epub2html.rb @@ -8,10 +8,15 @@ require 'zip' require 'rexml/document' -require 'cgi' require 'optparse' require 'review/version' +begin + require 'cgi/escape' +rescue + require 'cgi/util' +end + module ReVIEW class Epub2Html def self.execute(*args) diff --git a/lib/review/epubmaker.rb b/lib/review/epubmaker.rb index 428690388..c45fa803c 100644 --- a/lib/review/epubmaker.rb +++ b/lib/review/epubmaker.rb @@ -18,7 +18,6 @@ require 'review/htmltoc' require 'review/htmlbuilder' -require 'review/yamlloader' require 'rexml/document' require 'rexml/streamlistener' require 'epubmaker' @@ -52,18 +51,9 @@ def log(msg) end def load_yaml(yamlfile) - loader = ReVIEW::YAMLLoader.new - @config = ReVIEW::Configure.values - begin - @config.deep_merge!(loader.load_file(yamlfile)) - rescue => e - error "yaml error #{e.message}" - end - @producer = Producer.new(@config) @producer.load(yamlfile) @config = @producer.config - @config.maker = 'epubmaker' end def self.execute(*args) @@ -94,13 +84,13 @@ def parse_opts(args) end def execute(*args) - @config = ReVIEW::Configure.values - @config.maker = 'epubmaker' cmd_config, yamlfile, exportfile = parse_opts(args) error "#{yamlfile} not found." unless File.exist?(yamlfile) + @config = ReVIEW::Configure.create(maker: 'epubmaker', + yamlfile: yamlfile, + config: cmd_config) load_yaml(yamlfile) - @config.deep_merge!(cmd_config) update_log_level log("Loaded yaml file (#{yamlfile}).") @@ -252,7 +242,7 @@ def copy_images(resdir, destdir, allow_exts = nil) if @config['epubmaker']['verify_target_images'].present? @config['epubmaker']['force_include_images'].each do |file| unless File.exist?(file) - if file !~ /\Ahttp[s]?:/ + if file !~ /\Ahttps?:/ warn "#{file} is not found, skip." end next @@ -307,8 +297,7 @@ def build_body(basetmpdir, yamlfile) basedir = File.dirname(yamlfile) base_path = Pathname.new(basedir) - book = ReVIEW::Book.load(basedir) - book.config = @config + book = ReVIEW::Book::Base.new(basedir, config: @config) @converter = ReVIEW::Converter.new(book, ReVIEW::HTMLBuilder.new) @compile_errors = nil @@ -340,9 +329,9 @@ def build_part(part, basetmpdir, htmlfile) File.open(File.join(basetmpdir, htmlfile), 'w') do |f| @body = '' @body << %Q(<div class="part">\n) - @body << %Q(<h1 class="part-number">#{CGI.escapeHTML(ReVIEW::I18n.t('part', part.number))}</h1>\n) + @body << %Q(<h1 class="part-number">#{h(ReVIEW::I18n.t('part', part.number))}</h1>\n) if part.name.strip.present? - @body << %Q(<h2 class="part-title">#{CGI.escapeHTML(part.name.strip)}</h2>\n) + @body << %Q(<h2 class="part-title">#{h(part.name.strip)}</h2>\n) end @body << %Q(</div>\n) @@ -563,19 +552,19 @@ def copy_frontmatter(basetmpdir) def build_titlepage(basetmpdir, htmlfile) # TODO: should be created via epubcommon - @title = CGI.escapeHTML(@config.name_of('booktitle')) + @title = h(@config.name_of('booktitle')) File.open(File.join(basetmpdir, htmlfile), 'w') do |f| @body = '' @body << %Q(<div class="titlepage">\n) - @body << %Q(<h1 class="tp-title">#{CGI.escapeHTML(@config.name_of('booktitle'))}</h1>\n) + @body << %Q(<h1 class="tp-title">#{h(@config.name_of('booktitle'))}</h1>\n) if @config['subtitle'] - @body << %Q(<h2 class="tp-subtitle">#{CGI.escapeHTML(@config.name_of('subtitle'))}</h2>\n) + @body << %Q(<h2 class="tp-subtitle">#{h(@config.name_of('subtitle'))}</h2>\n) end if @config['aut'] - @body << %Q(<h2 class="tp-author">#{CGI.escapeHTML(@config.names_of('aut').join(ReVIEW::I18n.t('names_splitter')))}</h2>\n) + @body << %Q(<h2 class="tp-author">#{h(@config.names_of('aut').join(ReVIEW::I18n.t('names_splitter')))}</h2>\n) end if @config['pbl'] - @body << %Q(<h3 class="tp-publisher">#{CGI.escapeHTML(@config.names_of('pbl').join(ReVIEW::I18n.t('names_splitter')))}</h3>\n) + @body << %Q(<h3 class="tp-publisher">#{h(@config.names_of('pbl').join(ReVIEW::I18n.t('names_splitter')))}</h3>\n) end @body << '</div>' diff --git a/lib/review/htmlbuilder.rb b/lib/review/htmlbuilder.rb index fe3eb0c76..e68532234 100644 --- a/lib/review/htmlbuilder.rb +++ b/lib/review/htmlbuilder.rb @@ -41,10 +41,6 @@ def extname ".#{@book.config['htmlext']}" end - def builder_init - end - private :builder_init - def builder_init_file @noindent = nil @ol_num = nil @@ -242,6 +238,7 @@ def sup_end(_level) end def captionblock(type, lines, caption) + check_nested_minicolumn puts %Q(<div class="#{type}">) if caption.present? puts %Q(<p class="caption">#{compile_inline(caption)}</p>) @@ -300,15 +297,25 @@ def shoot(lines, caption = nil) end def box(lines, caption = nil) - puts %Q(<div class="syntax">) + captionstr = nil if caption.present? - puts %Q(<p class="caption">#{compile_inline(caption)}</p>) + captionstr = %Q(<p class="caption">#{compile_inline(caption)}</p>) + end + puts %Q(<div class="syntax">) + + if caption_top?('list') && caption.present? + puts captionstr end + print %Q(<pre class="syntax">) lines.each do |line| puts detab(line) end puts '</pre>' + + if !caption_top?('list') && caption.present? + puts captionstr + end puts '</div>' end @@ -316,6 +323,24 @@ def note(lines, caption = nil) captionblock('note', lines, caption) end + CAPTION_TITLES.each do |name| + class_eval %Q( + def #{name}_begin(caption = nil) + check_nested_minicolumn + @doc_status[:minicolumn] = '#{name}' + puts %Q(<div class="#{name}">) + if caption.present? + puts %Q(<p class="caption">\#{compile_inline(caption)}</p>) + end + end + + def #{name}_end + puts '</div>' + @doc_status[:minicolumn] = nil + end + ), __FILE__, __LINE__ - 14 + end + def ul_begin puts '<ul>' end @@ -458,7 +483,7 @@ def listnum_body(lines, lang) def emlist(lines, caption = nil, lang = nil) puts %Q(<div class="emlist-code">) - if caption.present? + if caption_top?('list') && caption.present? puts %Q(<p class="caption">#{compile_inline(caption)}</p>) end class_names = ['emlist'] @@ -469,12 +494,15 @@ def emlist(lines, caption = nil, lang = nil) lexer = lang puts highlight(body: body, lexer: lexer, format: 'html') puts '</pre>' + if !caption_top?('list') && caption.present? + puts %Q(<p class="caption">#{compile_inline(caption)}</p>) + end puts '</div>' end def emlistnum(lines, caption = nil, lang = nil) puts %Q(<div class="emlistnum-code">) - if caption.present? + if caption_top?('list') && caption.present? puts %Q(<p class="caption">#{compile_inline(caption)}</p>) end @@ -496,19 +524,29 @@ def emlistnum(lines, caption = nil, lang = nil) puts '</pre>' end + if !caption_top?('list') && caption.present? + puts %Q(<p class="caption">#{compile_inline(caption)}</p>) + end + puts '</div>' end def cmd(lines, caption = nil) puts %Q(<div class="cmd-code">) - if caption.present? + if caption_top?('list') && caption.present? puts %Q(<p class="caption">#{compile_inline(caption)}</p>) end + print %Q(<pre class="cmd">) body = lines.inject('') { |i, j| i + detab(j) + "\n" } lexer = 'shell-session' puts highlight(body: body, lexer: lexer, format: 'html') puts '</pre>' + + if !caption_top?('list') && caption.present? + puts %Q(<p class="caption">#{compile_inline(caption)}</p>) + end + puts '</div>' end @@ -544,12 +582,13 @@ def talk(lines) def texequation(lines, id = nil, caption = '') if id puts %Q(<div id="#{normalize_id(id)}" class="caption-equation">) - texequation_header(id, caption) + texequation_header(id, caption) if caption_top?('equation') end texequation_body(lines) if id + texequation_header(id, caption) unless caption_top?('equation') puts '</div>' end end @@ -617,20 +656,22 @@ def result_metric(array) def image_image(id, caption, metric) metrics = parse_metric('html', metric) puts %Q(<div id="#{normalize_id(id)}" class="image">) + image_header(id, caption) if caption_top?('image') puts %Q(<img src="#{@chapter.image(id).path.sub(%r{\A\./}, '')}" alt="#{escape(compile_inline(caption))}"#{metrics} />) - image_header(id, caption) + image_header(id, caption) unless caption_top?('image') puts '</div>' end def image_dummy(id, caption, lines) warn "image not bound: #{id}" puts %Q(<div id="#{normalize_id(id)}" class="image">) + image_header(id, caption) if caption_top?('image') puts %Q(<pre class="dummyimage">) lines.each do |line| puts detab(line) end puts '</pre>' - image_header(id, caption) + image_header(id, caption) unless caption_top?('image') puts '</div>' end @@ -693,15 +734,19 @@ def imgtable(lines, id, caption = nil, metric = nil) puts %Q(<div id="#{normalize_id(id)}" class="imgtable image">) begin - if caption.present? + if caption_top?('table') && caption.present? + table_header(id, caption) + end + + imgtable_image(id, caption, metric) + + if !caption_top?('table') && caption.present? table_header(id, caption) end rescue KeyError error "no such table: #{id}" end - imgtable_image(id, caption, metric) - puts '</div>' end @@ -738,7 +783,19 @@ def footnote(id, str) def indepimage(lines, id, caption = '', metric = nil) metrics = parse_metric('html', metric) caption = '' unless caption.present? + caption_str = nil + if caption.present? + caption_str = <<-EOS +<p class="caption"> +#{I18n.t('numberless_image')}#{I18n.t('caption_prefix')}#{compile_inline(caption)} +</p> +EOS + end + puts %Q(<div id="#{normalize_id(id)}" class="image">) + if caption_top?('image') && caption.present? + puts caption_str + end begin puts %Q(<img src="#{@chapter.image(id).path.sub(%r{\A\./}, '')}" alt="#{escape(compile_inline(caption))}"#{metrics} />) rescue @@ -752,10 +809,8 @@ def indepimage(lines, id, caption = '', metric = nil) end end - if caption.present? - puts %Q(<p class="caption">) - puts %Q(#{I18n.t('numberless_image')}#{I18n.t('caption_prefix')}#{compile_inline(caption)}) - puts '</p>' + if !caption_top?('image') && caption.present? + puts caption_str end puts '</div>' end @@ -992,7 +1047,7 @@ def column_label(id, chapter = @chapter) def inline_column_chap(chapter, id) if @book.config['chapterlink'] - %Q(<a href="\##{column_label(id, chapter)}" class="columnref">#{I18n.t('column', compile_inline(chapter.column(id).caption))}</a>) + %Q(<a href="#{chapter.id}#{extname}##{column_label(id, chapter)}" class="columnref">#{I18n.t('column', compile_inline(chapter.column(id).caption))}</a>) else I18n.t('column', compile_inline(chapter.column(id).caption)) end @@ -1154,7 +1209,7 @@ def inline_balloon(str) %Q(<span class="balloon">#{escape_html(str)}</span>) end - def inline_raw(str) + def inline_raw(str) # rubocop:disable Lint/UselessMethodDefinition super(str) end diff --git a/lib/review/htmlutils.rb b/lib/review/htmlutils.rb index bd76d5673..a6de3771d 100644 --- a/lib/review/htmlutils.rb +++ b/lib/review/htmlutils.rb @@ -7,19 +7,16 @@ # the GNU LGPL, Lesser General Public License version 2.1. # -require 'cgi/util' +begin + require 'cgi/escape' +rescue + require 'cgi/util' +end + module ReVIEW module HTMLUtils - ESC = { - '&' => '&', - '<' => '<', - '>' => '>', - '"' => '"' - } # .freeze - def escape(str) - t = ESC - str.gsub(/[&"<>]/) { |c| t[c] } + CGI.escapeHTML(str) end alias_method :escape_html, :escape # for backward compatibility diff --git a/lib/review/i18n.rb b/lib/review/i18n.rb index 4c524b238..01ba5d24b 100644 --- a/lib/review/i18n.rb +++ b/lib/review/i18n.rb @@ -125,7 +125,7 @@ def t(str, args = nil) frmt.gsub!('%%', '##') unless args.is_a?(Array) - args = args.nil? && frmt !~ /\%/ ? [] : [args] + args = args.nil? && frmt !~ /%/ ? [] : [args] end percents = frmt.scan(/%[A-Za-z]{1,3}/) diff --git a/lib/review/idgxmlbuilder.rb b/lib/review/idgxmlbuilder.rb index 18b38e129..8f45b5a27 100644 --- a/lib/review/idgxmlbuilder.rb +++ b/lib/review/idgxmlbuilder.rb @@ -46,10 +46,6 @@ def extname '.xml' end - def builder_init - end - private :builder_init - def builder_init_file @warns = [] @errors = [] @@ -365,7 +361,9 @@ def cmd(lines, caption = nil) def quotedlist(lines, css_class, caption) print %Q(<list type='#{css_class}'>) - puts "<caption aid:pstyle='#{css_class}-title'>#{compile_inline(caption)}</caption>" if caption.present? + if caption_top?('list') && caption.present? + puts "<caption aid:pstyle='#{css_class}-title'>#{compile_inline(caption)}</caption>" + end print '<pre>' no = 1 lines.each do |line| @@ -380,7 +378,11 @@ def quotedlist(lines, css_class, caption) print '</listinfo>' if @book.config['listinfo'] no += 1 end - puts '</pre></list>' + puts '</pre>' + if !caption_top?('list') && caption.present? + puts "<caption aid:pstyle='#{css_class}-title'>#{compile_inline(caption)}</caption>" + end + puts '</list>' end private :quotedlist @@ -424,20 +426,22 @@ def result_metric(array) def image_image(id, caption, metric = nil) metrics = parse_metric('idgxml', metric) puts '<img>' + image_header(id, caption) if caption_top?('image') puts %Q(<Image href="file://#{@chapter.image(id).path.sub(%r{\A./}, '')}"#{metrics} />) - image_header(id, caption) + image_header(id, caption) unless caption_top?('image') puts '</img>' end def image_dummy(id, caption, lines) puts '<img>' + image_header(id, caption) if caption_top?('image') print %Q(<pre aid:pstyle="dummyimage">) lines.each do |line| print detab(line) print "\n" end print '</pre>' - image_header(id, caption) + image_header(id, caption) unless caption_top?('image') puts '</img>' warn "image not bound: #{id}" end @@ -453,13 +457,15 @@ def image_header(id, caption) def texequation(lines, id = nil, caption = '') @texblockequation += 1 + caption_str = nil if id puts '<equationblock>' if get_chap.nil? - puts %Q(<caption>#{I18n.t('equation')}#{I18n.t('format_number_without_chapter', [@chapter.equation(id).number])}#{I18n.t('caption_prefix_idgxml')}#{compile_inline(caption)}</caption>) + caption_str = %Q(<caption>#{I18n.t('equation')}#{I18n.t('format_number_without_chapter', [@chapter.equation(id).number])}#{I18n.t('caption_prefix_idgxml')}#{compile_inline(caption)}</caption>) else - puts %Q(<caption>#{I18n.t('equation')}#{I18n.t('format_number', [get_chap, @chapter.equation(id).number])}#{I18n.t('caption_prefix_idgxml')}#{compile_inline(caption)}</caption>) + caption_str = %Q(<caption>#{I18n.t('equation')}#{I18n.t('format_number', [get_chap, @chapter.equation(id).number])}#{I18n.t('caption_prefix_idgxml')}#{compile_inline(caption)}</caption>) end + puts caption_str if caption_top?('equation') end puts %Q(<replace idref="texblock-#{@texblockequation}">) @@ -469,6 +475,7 @@ def texequation(lines, id = nil, caption = '') puts '</replace>' if id + puts caption_str unless caption_top?('equation') puts '</equationblock>' end end @@ -484,19 +491,26 @@ def table(lines, id = nil, caption = nil) puts '<table>' begin - table_header(id, caption) if caption.present? + if caption_top?('table') && caption.present? + table_header(id, caption) + end + + if @tablewidth.nil? + print '<tbody>' + else + print %Q(<tbody xmlns:aid5="http://ns.adobe.com/AdobeInDesign/5.0/" aid:table="table" aid:trows="#{rows.length}" aid:tcols="#{@col}">) + end + @table_id = id + table_rows(sepidx, rows) + puts '</tbody>' + + if !caption_top?('table') && caption.present? + table_header(id, caption) + end rescue KeyError error "no such table: #{id}" end - - if @tablewidth.nil? - print '<tbody>' - else - print %Q(<tbody xmlns:aid5="http://ns.adobe.com/AdobeInDesign/5.0/" aid:table="table" aid:trows="#{rows.length}" aid:tcols="#{@col}">) - end - @table_id = id - table_rows(sepidx, rows) - puts '</tbody></table>' + puts '</table>' @tsize = nil end @@ -504,7 +518,7 @@ def parse_table_rows(lines) sepidx = nil rows = [] lines.each_with_index do |line, idx| - if /\A[\=\-]{12}/ =~ line + if /\A[=\-]{12}/ =~ line sepidx ||= idx next end @@ -610,8 +624,13 @@ def imgtable(lines, id, caption = nil, metric = nil) if @chapter.image_bound?(id) metrics = parse_metric('idgxml', metric) puts '<table>' - table_header(id, caption) if caption.present? + if caption_top?('table') && caption.present? + table_header(id, caption) + end puts %Q(<imgtable><Image href="file://#{@chapter.image(id).path.sub(%r{\A./}, '')}"#{metrics} /></imgtable>) + if !caption_top?('table') && caption.present? + table_header(id, caption) + end puts '</table>' else warn "image not bound: #{id}" if @strict @@ -933,18 +952,22 @@ def captionblock(type, lines, caption, specialstyle = nil) end def note(lines, caption = nil) + check_nested_minicolumn captionblock('note', lines, caption) end def memo(lines, caption = nil) + check_nested_minicolumn captionblock('memo', lines, caption) end def tip(lines, caption = nil) + check_nested_minicolumn captionblock('tip', lines, caption) end def info(lines, caption = nil) + check_nested_minicolumn captionblock('info', lines, caption) end @@ -957,6 +980,7 @@ def best(lines, caption = nil) end def important(lines, caption = nil) + check_nested_minicolumn captionblock('important', lines, caption) end @@ -965,10 +989,12 @@ def security(lines, caption = nil) end def caution(lines, caption = nil) + check_nested_minicolumn captionblock('caution', lines, caption) end def warning(lines, caption = nil) + check_nested_minicolumn captionblock('warning', lines, caption) end @@ -981,6 +1007,7 @@ def link(lines, caption = nil) end def notice(lines, caption = nil) + check_nested_minicolumn if caption captionblock('notice-t', lines, caption, 'notice-title') else @@ -1016,7 +1043,35 @@ def expert(lines) captionblock('expert', lines, nil) end + CAPTION_TITLES.each do |name| + class_eval %Q( + def #{name}_begin(caption = nil) + check_nested_minicolumn + if '#{name}' == 'notice' && caption.present? + @doc_status[:minicolumn] = '#{name}-t' + print "<#{name}-t>" + else + @doc_status[:minicolumn] = '#{name}' + print "<#{name}>" + end + if caption.present? + puts %Q(<title aid:pstyle='#{name}-title'>\#{compile_inline(caption)}) + end + end + + def #{name}_end + if '#{name}' == 'notice' && @doc_status[:minicolumn] == 'notice-t' + print "" + else + print "" + end + @doc_status[:minicolumn] = nil + end + ), __FILE__, __LINE__ - 23 + end + def syntaxblock(type, lines, caption) + captionstr = nil if caption.present? titleopentag = %Q(caption aid:pstyle="#{type}-title") titleclosetag = 'caption' @@ -1024,9 +1079,13 @@ def syntaxblock(type, lines, caption) titleopentag = %Q(floattitle type="insn") titleclosetag = 'floattitle' end - puts %Q(<#{type}><#{titleopentag}>#{compile_inline(caption)}) + captionstr = %Q(<#{titleopentag}>#{compile_inline(caption)}) + end + print "<#{type}>" + if caption_top?('list') + puts captionstr else - puts "<#{type}>" + puts '' end no = 1 @@ -1042,6 +1101,9 @@ def syntaxblock(type, lines, caption) print '' if @book.config['listinfo'] no += 1 end + unless caption_top?('list') + print captionstr + end puts "" end @@ -1056,12 +1118,17 @@ def box(lines, caption = nil) def indepimage(_lines, id, caption = nil, metric = nil) metrics = parse_metric('idgxml', metric) puts '' + if caption_top?('image') + puts %Q(#{compile_inline(caption)}) if caption.present? + end begin puts %Q() rescue warn %Q(image not bound: #{id}) end - puts %Q(#{compile_inline(caption)}) if caption.present? + unless caption_top?('image') + puts %Q(#{compile_inline(caption)}) if caption.present? + end puts '' end @@ -1162,8 +1229,13 @@ def inline_title(id) def source(lines, caption = nil, lang = nil) puts '' - source_header(caption) + if caption_top?('list') + source_header(caption) + end source_body(lines, lang) + unless caption_top?('list') + source_header(caption) + end puts '' end diff --git a/lib/review/idgxmlmaker.rb b/lib/review/idgxmlmaker.rb index 4e7de2c5a..d449f4311 100644 --- a/lib/review/idgxmlmaker.rb +++ b/lib/review/idgxmlmaker.rb @@ -15,9 +15,12 @@ require 'review/yamlloader' require 'review/idgxmlbuilder' require 'review/version' +require 'review/makerhelper' module ReVIEW class IDGXMLMaker + include MakerHelper + attr_accessor :config, :basedir def initialize @@ -74,19 +77,12 @@ def remove_old_files(path) end def execute(*args) - @config = ReVIEW::Configure.values - @config.maker = 'idgxmlmaker' cmd_config, yamlfile = parse_opts(args) error "#{yamlfile} not found." unless File.exist?(yamlfile) - begin - loader = ReVIEW::YAMLLoader.new - @config.deep_merge!(loader.load_file(yamlfile)) - rescue => e - error "yaml error #{e.message}" - end - # YAML configs will be overridden by command line options. - @config.deep_merge!(cmd_config) + @config = ReVIEW::Configure.create(maker: 'idgxmlmaker', + yamlfile: yamlfile, + config: cmd_config) I18n.setup(@config['language']) begin generate_idgxml_files(yamlfile) @@ -102,8 +98,7 @@ def generate_idgxml_files(yamlfile) remove_old_files(@path) Dir.mkdir(@path) - @book = ReVIEW::Book.load(@basedir) - @book.config = @config + @book = ReVIEW::Book::Base.new(@basedir, config: @config) if @table @book.config['tableopt'] = @table end @@ -155,9 +150,9 @@ def build_part(part, basetmpdir, xmlfile) end f.puts '' f.print '' - f.print CGI.escapeHTML(title) + f.print h(title) f.print '' end apply_filter(File.join(basetmpdir, xmlfile)) diff --git a/lib/review/index_builder.rb b/lib/review/index_builder.rb new file mode 100644 index 000000000..2ac9e687b --- /dev/null +++ b/lib/review/index_builder.rb @@ -0,0 +1,624 @@ +# Copyright (c) 2008-2020 Minero Aoki, Kenshi Muto, Masayoshi Takahashi, +# KADO Masanori +# +# 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/book/index' +require 'review/exception' +require 'review/builder' +require 'review/sec_counter' + +module ReVIEW + class IndexBuilder < Builder + attr_reader :list_index, :table_index, :equation_index, :footnote_index, + :numberless_image_index, :image_index, :icon_index, :indepimage_index, + :headline_index, :column_index, :bibpaper_index + + def initialize(strict = false, *args) + super + end + + def pre_paragraph + '' + end + + def post_paragraph + '' + end + + def bind(compiler, chapter, location) + @compiler = compiler + @chapter = chapter + @location = location + @output = StringIO.new + if @chapter.present? + @book = @chapter.book + end + builder_init_file + end + + def builder_init_file + @sec_counter = SecCounter.new(5, @chapter) + + @headline_stack = [] + + @list_index = ReVIEW::Book::ListIndex.new + @table_index = ReVIEW::Book::TableIndex.new + @equation_index = ReVIEW::Book::EquationIndex.new + @footnote_index = ReVIEW::Book::FootnoteIndex.new + @headline_index = ReVIEW::Book::HeadlineIndex.new(@chapter) + @column_index = ReVIEW::Book::ColumnIndex.new + @chapter_index = ReVIEW::Book::ChapterIndex.new + @bibpaper_index = ReVIEW::Book::BibpaperIndex.new + + if @book + @image_index = ReVIEW::Book::ImageIndex.new(@chapter) + @icon_index = ReVIEW::Book::IconIndex.new(@chapter) + @numberless_image_index = ReVIEW::Book::NumberlessImageIndex.new(@chapter) + @indepimage_index = ReVIEW::Book::IndepImageIndex.new(@chapter) + end + end + private :builder_init_file + + def result + nil + end + + def target_name + 'index' + end + + def headline(level, label, caption) + @sec_counter.inc(level) + return if level < 2 + + cursor = level - 2 + + if label + @headline_stack[cursor] = label + else + @headline_stack[cursor] = caption + end + if @headline_stack.size > cursor + 1 + @headline_stack = @headline_stack.take(cursor + 1) + end + + item_id = @headline_stack.join('|') + + item = ReVIEW::Book::Index::Item.new(item_id, @sec_counter.number_list, caption) + @headline_index.add_item(item) + end + + def nonum_begin(level, label, caption) + return if level < 2 + + cursor = level - 2 + + if label + @headline_stack[cursor] = label + else + @headline_stack[cursor] = caption + end + if @headline_stack.size > cursor + 1 + @headline_stack = @headline_stack.take(cursor + 1) + end + + item_id = @headline_stack.join('|') + + item = ReVIEW::Book::Index::Item.new(item_id, nil, caption) + @headline_index.add_item(item) + end + + def nonum_end(_level) + end + + def notoc_begin(level, label, caption) + return if level < 2 + + cursor = level - 2 + + if label + @headline_stack[cursor] = label + else + @headline_stack[cursor] = caption + end + if @headline_stack.size > cursor + 1 + @headline_stack = @headline_stack.take(cursor + 1) + end + + item_id = @headline_stack.join('|') + + item = ReVIEW::Book::Index::Item.new(item_id, nil, caption) + @headline_index.add_item(item) + end + + def notoc_end(_level) + end + + def nodisp_begin(level, label, caption) + return if level < 2 + + cursor = level - 2 + + if label + @headline_stack[cursor] = label + else + @headline_stack[cursor] = caption + end + if @headline_stack.size > cursor + 1 + @headline_stack = @headline_stack.take(cursor + 1) + end + + item_id = @headline_stack.join('|') + + item = ReVIEW::Book::Index::Item.new(item_id, nil, caption) + @headline_index.add_item(item) + end + + def nodisp_end(_level) + end + + def column_begin(_level, label, caption) + item_id = label || caption + item = ReVIEW::Book::Index::Item.new(item_id, @column_index.size + 1, caption) + @column_index.add_item(item) + end + + def column_end(_level) + end + + def xcolumn_begin(level, label, caption) + end + + def xcolumn_end(_level) + end + + def sup_begin(level, label, caption) + end + + def sup_end(_level) + end + + def ul_begin + end + + def ul_item_begin(lines) + end + + def ul_item_end + end + + def ul_end + end + + def ol_begin + end + + def ol_item(lines, _num) + end + + def ol_end + end + + def dl_begin + end + + def dt(line) + end + + def dd(lines) + end + + def dl_end + end + + def paragraph(lines) + end + + def parasep + '' + end + + def nofunc_text(_str) + '' + end + + def read(_lines) + end + + alias_method :lead, :read + + def list(_lines, id, _caption, _lang = nil) + item = ReVIEW::Book::Index::Item.new(id, @list_index.size + 1) + @list_index.add_item(item) + end + + def source(_lines, _caption = nil, _lang = nil) + end + + def listnum(_lines, id, _caption, _lang = nil) + item = ReVIEW::Book::Index::Item.new(id, @list_index.size + 1) + @list_index.add_item(item) + end + + def emlist(lines, caption = nil, lang = nil) + end + + def emlistnum(lines, caption = nil, lang = nil) + end + + def cmd(lines, caption = nil) + end + + def quote(lines) + end + + def image(_lines, id, caption, _metric = nil) + item = ReVIEW::Book::Index::Item.new(id, @image_index.size + 1, caption) + @image_index.add_item(item) + end + + def table(_lines, id = nil, caption = nil) + if id + item = ReVIEW::Book::Index::Item.new(id, @table_index.size + 1, caption) + @table_index.add_item(item) + end + end + + def emtable(_lines, _caption = nil) + # item = ReVIEW::Book::TableIndex::Item.new(id, @table_index.size + 1) + # @table_index << item + end + + def comment(lines, comment = nil) + end + + def imgtable(_lines, id, _caption = nil, _metric = nil) + item = ReVIEW::Book::Index::Item.new(id, @table_index.size + 1) + @table_index.add_item(item) + + ## to find image path + item = ReVIEW::Book::Index::Item.new(id, @indepimage_index.size + 1) + @indepimage_index.add_item(item) + end + + def footnote(id, str) + item = ReVIEW::Book::Index::Item.new(id, @footnote_index.size + 1, str) + @footnote_index.add_item(item) + end + + def indepimage(_lines, id, _caption = '', _metric = nil) + item = ReVIEW::Book::Index::Item.new(id, @indepimage_index.size + 1) + @indepimage_index.add_item(item) + end + + def numberlessimage(_lines, id, _caption = '', _metric = nil) + item = ReVIEW::Book::Index::Item.new(id, @indepimage_index.size + 1) + @indepimage_index.add_item(item) + end + + def hr + end + + def label(_id) + end + + def blankline + end + + def flushright(_lines) + end + + def centering(lines) + end + + def olnum(_num) + end + + def pagebreak + end + + def bpo(_lines) + end + + def noindent + end + + def compile_inline(s) + @compiler.text(s) + end + + def inline_chapref(_id) + '' + end + + def inline_chap(_id) + '' + end + + def inline_title(_id) + '' + end + + def inline_list(_id) + '' + end + + def inline_img(_id) + '' + end + + def inline_imgref(_id) + '' + end + + def inline_table(_id) + '' + end + + def inline_eq(_id) + '' + end + + def inline_fn(_id) + '' + end + + def inline_i(_str) + '' + end + + def inline_b(_str) + '' + end + + def inline_ami(_str) + '' + end + + def inline_bou(str) + str + end + + def inline_tti(_str) + '' + end + + def inline_ttb(_str) + '' + end + + def inline_dtp(_str) + '' + end + + def inline_code(_str) + '' + end + + def inline_idx(_str) + '' + end + + def inline_hidx(_str) + '' + end + + def inline_br(_str) + '' + end + + def inline_m(_str) + '' + end + + def firstlinenum(_num) + '' + end + + def inline_ruby(_arg) + '' + end + + def inline_kw(_arg) + '' + end + + def inline_href(_arg) + '' + end + + def inline_hr(_arg) + '' + end + + def text(_str) + '' + end + + def bibpaper(_lines, id, caption) + item = ReVIEW::Book::Index::Item.new(id, @bibpaper_index.size + 1, caption) + @bibpaper_index.add_item(item) + end + + def inline_hd(_id) + '' + end + + def inline_bib(_id) + '' + end + + def inline_column(_id) + '' + end + + def inline_column_chap(_chapter, _id) + '' + end + + def inline_pageref(_id) + '' + end + + def inline_tcy(_arg) + '' + end + + def inline_balloon(_arg) + '' + end + + def inline_w(_s) + '' + end + + def inline_wb(_s) + '' + end + + def inline_abbr(_str) + '' + end + + def inline_acronym(_str) + '' + end + + def inline_cite(_str) + '' + end + + def inline_dfn(_str) + '' + end + + def inline_em(_str) + '' + end + + def inline_kbd(_str) + '' + end + + def inline_samp(_str) + '' + end + + def inline_strong(_str) + '' + end + + def inline_var(_str) + '' + end + + def inline_big(_str) + '' + end + + def inline_small(_str) + '' + end + + def inline_sub(_str) + '' + end + + def inline_sup(_str) + '' + end + + def inline_tt(_str) + '' + end + + def inline_del(_str) + '' + end + + def inline_ins(_str) + '' + end + + def inline_u(_str) + '' + end + + def inline_recipe(_str) + '' + end + + def inline_icon(id) + item = ReVIEW::Book::Index::Item.new(id, @icon_index.size + 1) + @icon_index.add_item(item) + '' + end + + def inline_uchar(_str) + '' + end + + def raw(_str) + '' + end + + def embed(_lines, _arg = nil) + '' + end + + def warn(msg) + @logger.warn "#{@location}: #{msg}" + end + + def error(msg = '(no message)') + if msg =~ /builder does not support command/ + # ignore + return + end + super + end + + def texequation(_lines, id = nil, _caption = '') + if id + item = ReVIEW::Book::Index::Item.new(id, @equation_index.size + 1) + @equation_index.add_item(item) + end + end + + def get_chap(_chapter = nil) + '' + end + + def extract_chapter_id(_chap_ref) + '' + end + + def captionblock(_type, _lines, _caption, _specialstyle = nil) + '' + end + + def tsize(_str) + '' + end + + def inline_raw(_args) + '' + end + + def inline_embed(_args) + '' + end + + def highlight? + false + end + + def unknown_command(*_args) + # ignore + end + end +end # module ReVIEW diff --git a/lib/review/init.rb b/lib/review/init.rb index 48c5bae0e..92c0915a1 100644 --- a/lib/review/init.rb +++ b/lib/review/init.rb @@ -114,9 +114,7 @@ def parse_options(args) exit 1 end - initdir = File.expand_path(args[0]) - - initdir + File.expand_path(args[0]) end def generate_dir(dir) @@ -135,8 +133,7 @@ def generate_sample(dir) end def generate_catalog_file(dir) - File.open(File.join(dir, 'catalog.yml'), 'w') do |file| - file.write <<-EOS + File.write(File.join(dir, 'catalog.yml'), <<-EOS) PREDEF: CHAPS: @@ -147,7 +144,6 @@ def generate_catalog_file(dir) POSTDEF: EOS - end end def generate_images_dir(dir) @@ -223,13 +219,11 @@ def generate_texmacro(dir) def generate_rakefile(dir) FileUtils.mkdir_p(File.join(dir, 'lib/tasks')) - File.open(File.join(dir, 'Rakefile'), 'w') do |file| - file.write <<-EOS + File.write(File.join(dir, 'Rakefile'), <<-EOS) Dir.glob('lib/tasks/*.rake').sort.each do |file| load(file) end EOS - end FileUtils.cp(File.join(@review_dir, 'samples/sample-book/src/lib/tasks/review.rake'), File.join(dir, 'lib/tasks/review.rake')) @@ -240,14 +234,12 @@ def generate_locale(dir) end def generate_gemfile(dir) - File.open(File.join(dir, 'Gemfile'), 'w') do |file| - file.write <<-EOS + File.write(File.join(dir, 'Gemfile'), <<-EOS) source 'https://rubygems.org' gem 'rake' gem 'review', '#{ReVIEW::VERSION}' EOS - end end def generate_doc(dir) @@ -342,7 +334,7 @@ def start_webui # validation if @web_result @web_result.each do |s| - if s !~ /\A[a-z0-9=_,\.-]*\Z/i + if s !~ /\A[a-z0-9=_,.-]*\Z/i @web_result = nil break end diff --git a/lib/review/latexbuilder.rb b/lib/review/latexbuilder.rb index 896b8e4d9..5a65fe159 100644 --- a/lib/review/latexbuilder.rb +++ b/lib/review/latexbuilder.rb @@ -226,7 +226,53 @@ def column_end(_level) @doc_status[:column] = nil end + def common_block_begin(type, caption = nil) + check_nested_minicolumn + if @book.config.check_version('2', exception: false) + type = 'minicolumn' + end + + @doc_status[:minicolumn] = type + print "\\begin{review#{type}}" + + @doc_status[:caption] = true + if @book.config.check_version('2', exception: false) + puts + if caption.present? + puts "\\reviewminicolumntitle{#{compile_inline(caption)}}" + end + else + if caption.present? + print "[#{compile_inline(caption)}]" + end + puts + end + @doc_status[:caption] = nil + end + + def common_block_end(type) + if @book.config.check_version('2', exception: false) + type = 'minicolumn' + end + + puts "\\end{review#{type}}" + @doc_status[:minicolumn] = nil + end + + CAPTION_TITLES.each do |name| + class_eval %Q( + def #{name}_begin(caption = nil) + common_block_begin('#{name}', caption) + end + + def #{name}_end + common_block_end('#{name}') + end + ), __FILE__, __LINE__ - 8 + end + def captionblock(type, lines, caption) + check_nested_minicolumn if @book.config.check_version('2', exception: false) type = 'minicolumn' end @@ -417,18 +463,19 @@ def cmd(lines, caption = nil, lang = nil) def common_code_block(id, lines, command, caption, _lang) @doc_status[:caption] = true + captionstr = nil unless @book.config.check_version('2', exception: false) puts '\\begin{reviewlistblock}' end if caption.present? if command =~ /emlist/ || command =~ /cmd/ || command =~ /source/ - puts macro(command + 'caption', compile_inline(caption)) + captionstr = macro(command + 'caption', compile_inline(caption)) else begin if get_chap.nil? - puts macro('reviewlistcaption', "#{I18n.t('list')}#{I18n.t('format_number_header_without_chapter', [@chapter.list(id).number])}#{I18n.t('caption_prefix')}#{compile_inline(caption)}") + captionstr = macro('reviewlistcaption', "#{I18n.t('list')}#{I18n.t('format_number_header_without_chapter', [@chapter.list(id).number])}#{I18n.t('caption_prefix')}#{compile_inline(caption)}") else - puts macro('reviewlistcaption', "#{I18n.t('list')}#{I18n.t('format_number_header', [get_chap, @chapter.list(id).number])}#{I18n.t('caption_prefix')}#{compile_inline(caption)}") + captionstr = macro('reviewlistcaption', "#{I18n.t('list')}#{I18n.t('format_number_header', [get_chap, @chapter.list(id).number])}#{I18n.t('caption_prefix')}#{compile_inline(caption)}") end rescue KeyError error "no such list: #{id}" @@ -436,6 +483,11 @@ def common_code_block(id, lines, command, caption, _lang) end end @doc_status[:caption] = nil + + if caption_top?('list') && captionstr + puts captionstr + end + body = '' lines.each_with_index do |line, idx| body.concat(yield(line, idx)) @@ -443,6 +495,11 @@ def common_code_block(id, lines, command, caption, _lang) puts macro('begin', command) print body puts macro('end', command) + + if !caption_top?('list') && captionstr + puts captionstr + end + unless @book.config.check_version('2', exception: false) puts '\\end{reviewlistblock}' end @@ -515,10 +572,24 @@ def result_metric(array) end def image_image(id, caption, metric) + captionstr = nil + @doc_status[:caption] = true + if @book.config.check_version('2', exception: false) + captionstr = macro('caption', compile_inline(caption)) + "\n" if caption.present? + else + captionstr = macro('reviewimagecaption', compile_inline(caption)) + "\n" if caption.present? + end + captionstr << macro('label', image_label(id)) + @doc_status[:caption] = nil + metrics = parse_metric('latex', metric) # image is always bound here puts "\\begin{reviewimage}%%#{id}" + if caption_top?('image') && captionstr + puts captionstr + end + command = 'reviewincludegraphics' if @book.config.check_version('2', exception: false) command = 'includegraphics' @@ -529,15 +600,11 @@ def image_image(id, caption, metric) else puts "\\#{command}[width=\\maxwidth]{#{@chapter.image(id).path}}" end - @doc_status[:caption] = true - if @book.config.check_version('2', exception: false) - puts macro('caption', compile_inline(caption)) if caption.present? - else - puts macro('reviewimagecaption', compile_inline(caption)) if caption.present? + if !caption_top?('image') && captionstr + puts captionstr end - @doc_status[:caption] = nil - puts macro('label', image_label(id)) + puts '\end{reviewimage}' end @@ -592,7 +659,8 @@ def bib_label(id) end private :bib_label - def column_label(id, chapter = @chapter) + def column_label(id, chapter = nil) + chapter ||= @chapter filename = chapter.id num = chapter.column(id).number "column:#{filename}:#{num}" @@ -602,9 +670,21 @@ def column_label(id, chapter = @chapter) def indepimage(lines, id, caption = nil, metric = nil) metrics = parse_metric('latex', metric) + captionstr = nil + if caption.present? + @doc_status[:caption] = true + captionstr = macro('reviewindepimagecaption', + %Q(#{I18n.t('numberless_image')}#{I18n.t('caption_prefix')}#{compile_inline(caption)})) + @doc_status[:caption] = nil + end + if @chapter.image(id).path puts "\\begin{reviewimage}%%#{id}" + if caption_top?('image') && captionstr + puts captionstr + end + command = 'reviewincludegraphics' if @book.config.check_version('2', exception: false) command = 'includegraphics' @@ -618,18 +698,15 @@ def indepimage(lines, id, caption = nil, metric = nil) else warn "image not bound: #{id}" puts '\begin{reviewdummyimage}' - puts "--[[path = #{id} (#{existence(id)})]]--" + puts "--[[path = #{escape(id)} (#{existence(id)})]]--" lines.each do |line| puts detab(line.rstrip) end end - @doc_status[:caption] = true - if caption.present? - puts macro('reviewindepimagecaption', - %Q(#{I18n.t('numberless_image')}#{I18n.t('caption_prefix')}#{compile_inline(caption)})) + if !caption_top?('image') && captionstr + puts captionstr end - @doc_status[:caption] = nil if @chapter.image(id).path puts '\end{reviewimage}' @@ -651,7 +728,9 @@ def table(lines, id = nil, caption = nil) sepidx, rows = parse_table_rows(lines) begin - table_header(id, caption) if caption.present? + if caption_top?('table') && caption.present? + table_header(id, caption) + end rescue KeyError error "no such table: #{id}" end @@ -659,6 +738,9 @@ def table(lines, id = nil, caption = nil) table_rows(sepidx, rows) table_end if caption.present? + unless caption_top?('table') + table_header(id, caption) + end puts '\end{table}' end blank @@ -821,12 +903,16 @@ def imgtable(lines, id, caption = nil, metric = nil) return end + captionstr = nil begin if caption.present? puts "\\begin{table}[h]%%#{id}" @doc_status[:caption] = true - puts macro('reviewimgtablecaption', compile_inline(caption)) + captionstr = macro('reviewimgtablecaption', compile_inline(caption)) @doc_status[:caption] = nil + if caption_top?('table') + puts captionstr + end end puts macro('label', table_label(id)) rescue ReVIEW::KeyError @@ -835,6 +921,9 @@ def imgtable(lines, id, caption = nil, metric = nil) imgtable_image(id, caption, metric) if caption.present? + unless caption_top?('table') + puts captionstr + end puts '\end{table}' end blank @@ -874,22 +963,31 @@ def flushright(lines) def texequation(lines, id = nil, caption = '') blank + captionstr = nil if id puts macro('begin', 'reviewequationblock') if get_chap.nil? - puts macro('reviewequationcaption', "#{I18n.t('equation')}#{I18n.t('format_number_header_without_chapter', [@chapter.equation(id).number])}#{I18n.t('caption_prefix')}#{compile_inline(caption)}") + captionstr = macro('reviewequationcaption', "#{I18n.t('equation')}#{I18n.t('format_number_header_without_chapter', [@chapter.equation(id).number])}#{I18n.t('caption_prefix')}#{compile_inline(caption)}") else - puts macro('reviewequationcaption', "#{I18n.t('equation')}#{I18n.t('format_number_header', [get_chap, @chapter.equation(id).number])}#{I18n.t('caption_prefix')}#{compile_inline(caption)}") + captionstr = macro('reviewequationcaption', "#{I18n.t('equation')}#{I18n.t('format_number_header', [get_chap, @chapter.equation(id).number])}#{I18n.t('caption_prefix')}#{compile_inline(caption)}") end end + if caption_top?('equation') && captionstr + puts captionstr + end + puts macro('begin', 'equation*') lines.each do |line| puts line end puts macro('end', 'equation*') + if !caption_top?('equation') && captionstr + puts captionstr + end + if id puts macro('end', 'reviewequationblock') end @@ -1180,7 +1278,7 @@ def inline_column_chap(chapter, id) error "unknown column: #{id}" end - def inline_raw(str) + def inline_raw(str) # rubocop:disable Lint/UselessMethodDefinition super(str) end diff --git a/lib/review/logger.rb b/lib/review/logger.rb index 88186e340..8e1e53a31 100644 --- a/lib/review/logger.rb +++ b/lib/review/logger.rb @@ -2,14 +2,14 @@ module ReVIEW class Logger < ::Logger - def initialize(io = STDERR, progname: '--') + def initialize(io = $stderr, progname: '--') super(io, progname: progname) self.formatter = ->(severity, _datetime, name, msg) { "#{severity} #{name}: #{msg}\n" } end end def self.logger - @logger ||= ReVIEW::Logger.new(STDERR, progname: File.basename($PROGRAM_NAME, '.*')) + @logger ||= ReVIEW::Logger.new($stderr, progname: File.basename($PROGRAM_NAME, '.*')) end def self.logger=(logger) diff --git a/lib/review/makerhelper.rb b/lib/review/makerhelper.rb index 6be95054e..796589527 100644 --- a/lib/review/makerhelper.rb +++ b/lib/review/makerhelper.rb @@ -10,6 +10,12 @@ require 'yaml' require 'shellwords' +begin + require 'cgi/escape' +rescue + require 'cgi/util' +end + module ReVIEW module MakerHelper # Return review/bin directory @@ -18,6 +24,11 @@ def bindir end module_function :bindir + def h(str) + CGI.escapeHTML(str) + end + module_function :h + # Copy image files under from_dir to to_dir recursively # ==== Args # from_dir :: path to the directory which has image files to be copied diff --git a/lib/review/markdownbuilder.rb b/lib/review/markdownbuilder.rb index c56395ff5..4df8a6ca9 100644 --- a/lib/review/markdownbuilder.rb +++ b/lib/review/markdownbuilder.rb @@ -155,10 +155,28 @@ def captionblock(type, lines, caption, _specialstyle = nil) puts %Q(
) puts %Q(

#{compile_inline(caption)}

) if caption.present? blocked_lines = split_paragraph(lines) - puts blocked_lines.join("\n") + puts blocked_lines.join("\n\n") puts '
' end + CAPTION_TITLES.each do |name| + class_eval %Q( + def #{name}_begin(caption = nil) + check_nested_minicolumn + @doc_status[:minicolumn] = '#{name}' + puts %Q(
) + if caption.present? + puts %Q(

\#{compile_inline(caption)}

) + end + end + + def #{name}_end + puts '
' + @doc_status[:minicolumn] = nil + end + ), __FILE__, __LINE__ - 14 + end + def hr puts '----' end diff --git a/lib/review/pdfmaker.rb b/lib/review/pdfmaker.rb index 664101935..fcf2a2aa7 100644 --- a/lib/review/pdfmaker.rb +++ b/lib/review/pdfmaker.rb @@ -122,20 +122,13 @@ def parse_opts(args) end def execute(*args) - @config = ReVIEW::Configure.values - @config.maker = 'pdfmaker' cmd_config, yamlfile = parse_opts(args) error "#{yamlfile} not found." unless File.exist?(yamlfile) - begin - loader = ReVIEW::YAMLLoader.new - @config.deep_merge!(loader.load_file(yamlfile)) - rescue => e - error "yaml error #{e.message}" - end + @config = ReVIEW::Configure.create(maker: 'pdfmaker', + yamlfile: yamlfile, + config: cmd_config) - # YAML configs will be overridden by command line options. - @config.deep_merge!(cmd_config) I18n.setup(@config['language']) @basedir = File.absolute_path(File.dirname(yamlfile)) @@ -272,8 +265,7 @@ def generate_pdf begin @compile_errors = nil - book = ReVIEW::Book.load(@basedir) - book.config = @config + book = ReVIEW::Book::Base.new(@basedir, config: @config) @converter = ReVIEW::Converter.new(book, ReVIEW::LATEXBuilder.new) @input_files = make_input_files(book) @@ -311,23 +303,12 @@ def output_chaps(filename) end end - # PDFMaker#copy_images should copy image files _AND_ execute extractbb (or ebb). + # PDFMaker#copy_images should copy image files # def copy_images(from, to) return unless File.exist?(from) Dir.mkdir(to) ReVIEW::MakerHelper.copy_images_to_dir(from, to) - Dir.chdir(to) do - images = Dir.glob('**/*').find_all { |f| File.file?(f) and f =~ /\.(jpg|jpeg|png|pdf|ai|eps|tif)\z/i } - break if images.empty? - if @config['pdfmaker']['bbox'] - system_with_info('extractbb', '-B', @config['pdfmaker']['bbox'], *images) - system_or_raise('ebb', '-B', @config['pdfmaker']['bbox'], *images) unless system('extractbb', '-B', @config['pdfmaker']['bbox'], '-m', *images) - else - system_with_info('extractbb', *images) - system_or_raise('ebb', *images) unless system('extractbb', '-m', *images) - end - end end def make_custom_page(file) @@ -385,11 +366,11 @@ def make_history_list items.each_with_index do |item, rev| editstr = edit == 0 ? ReVIEW::I18n.t('first_edition') : ReVIEW::I18n.t('nth_edition', (edit + 1).to_s) revstr = ReVIEW::I18n.t('nth_impression', (rev + 1).to_s) - if item =~ /\A\d+\-\d+\-\d+\Z/ + if item =~ /\A\d+-\d+-\d+\Z/ buf << ReVIEW::I18n.t('published_by1', [date_to_s(item), editstr + revstr]) - elsif item =~ /\A(\d+\-\d+\-\d+)[\s ](.+)/ + elsif item =~ /\A(\d+-\d+-\d+)[\s ](.+)/ # custom date with string - item.match(/\A(\d+\-\d+\-\d+)[\s ](.+)/) { |m| buf << ReVIEW::I18n.t('published_by3', [date_to_s(m[1]), m[2]]) } + item.match(/\A(\d+-\d+-\d+)[\s ](.+)/) { |m| buf << ReVIEW::I18n.t('published_by3', [date_to_s(m[1]), m[2]]) } else # free format buf << item @@ -455,15 +436,15 @@ def erb_config end @locale_latex = {} - part_tuple = I18n.get('part').split(/\%[A-Za-z]{1,3}/, 2) - chapter_tuple = I18n.get('chapter').split(/\%[A-Za-z]{1,3}/, 2) - appendix_tuple = I18n.get('appendix').split(/\%[A-Za-z]{1,3}/, 2) - @locale_latex['prepartname'] = part_tuple[0] - @locale_latex['postpartname'] = part_tuple[1] - @locale_latex['prechaptername'] = chapter_tuple[0] - @locale_latex['postchaptername'] = chapter_tuple[1] - @locale_latex['preappendixname'] = appendix_tuple[0] - @locale_latex['postappendixname'] = appendix_tuple[1] + part_tuple = I18n.get('part').split(/%[A-Za-z]{1,3}/, 2) + chapter_tuple = I18n.get('chapter').split(/%[A-Za-z]{1,3}/, 2) + appendix_tuple = I18n.get('appendix').split(/%[A-Za-z]{1,3}/, 2) + @locale_latex['prepartname'] = part_tuple[0].to_s + @locale_latex['postpartname'] = part_tuple[1].to_s + @locale_latex['prechaptername'] = chapter_tuple[0].to_s + @locale_latex['postchaptername'] = chapter_tuple[1].to_s + @locale_latex['preappendixname'] = appendix_tuple[0].to_s + @locale_latex['postappendixname'] = appendix_tuple[1].to_s end def erb_content(file) diff --git a/lib/review/plaintextbuilder.rb b/lib/review/plaintextbuilder.rb index ecd3a0d3b..78a142180 100644 --- a/lib/review/plaintextbuilder.rb +++ b/lib/review/plaintextbuilder.rb @@ -159,13 +159,19 @@ def read(lines) def list(lines, id, caption, lang = nil) blank begin - list_header(id, caption, lang) + if caption_top?('list') + list_header(id, caption, lang) + blank + end + list_body(id, lines, lang) + unless caption_top?('list') + blank + list_header(id, caption, lang) + end rescue KeyError error "no such list: #{id}" end blank - list_body(id, lines, lang) - blank end def list_header(id, caption, _lang) @@ -184,8 +190,13 @@ def list_body(_id, lines, _lang) def base_block(_type, lines, caption = nil) blank - puts compile_inline(caption) if caption.present? + if caption_top?('list') && caption.present? + puts compile_inline(caption) + end puts lines.join("\n") + if !caption_top?('list') && caption.present? + puts compile_inline(caption) + end blank end @@ -202,23 +213,34 @@ def emlist(lines, caption = nil, _lang = nil) def emlistnum(lines, caption = nil, _lang = nil) blank - puts compile_inline(caption) if caption.present? + if caption_top?('list') + puts compile_inline(caption) if caption.present? + end lines.each_with_index do |line, i| puts((i + 1).to_s.rjust(2) + ": #{line}") end + unless caption_top?('list') + puts compile_inline(caption) if caption.present? + end blank end def listnum(lines, id, caption, lang = nil) blank begin - list_header(id, caption, lang) + if caption_top?('list') + list_header(id, caption, lang) + blank + end + listnum_body(lines, lang) + unless caption_top?('list') + blank + list_header(id, caption, lang) + end rescue KeyError error "no such list: #{id}" end blank - listnum_body(lines, lang) - blank end def listnum_body(lines, _lang) @@ -247,8 +269,9 @@ def image(_lines, id, caption, _metric = nil) def texequation(lines, id = nil, caption = '') blank - texequation_header(id, caption) + texequation_header(id, caption) if caption_top?('equation') puts lines.join("\n") + texequation_header(id, caption) unless caption_top?('equation') blank end @@ -270,6 +293,10 @@ def table(lines, id = nil, caption = nil, noblank = nil) end def table_header(id, caption) + unless caption_top?('table') + blank + end + if id.nil? puts compile_inline(caption) elsif get_chap @@ -277,7 +304,10 @@ def table_header(id, caption) else puts "#{I18n.t('table')}#{I18n.t('format_number_without_chapter', [@chapter.table(id).number])}#{I18n.t('caption_prefix_idgxml')}#{compile_inline(caption)}" end - blank + + if caption_top?('table') + blank + end end def table_begin(_ncols) @@ -490,18 +520,22 @@ def centering(lines) end def note(lines, caption = nil) + check_nested_minicolumn base_parablock('note', lines, caption) end def memo(lines, caption = nil) + check_nested_minicolumn base_parablock('memo', lines, caption) end def tip(lines, caption = nil) + check_nested_minicolumn base_parablock('tip', lines, caption) end def info(lines, caption = nil) + check_nested_minicolumn base_parablock('info', lines, caption) end @@ -510,10 +544,12 @@ def planning(lines, caption = nil) end def best(lines, caption = nil) + check_nested_minicolumn base_parablock('best', lines, caption) end def important(lines, caption = nil) + check_nested_minicolumn base_parablock('important', lines, caption) end @@ -522,6 +558,7 @@ def security(lines, caption = nil) end def caution(lines, caption = nil) + check_nested_minicolumn base_parablock('caution', lines, caption) end @@ -534,6 +571,7 @@ def link(lines, caption = nil) end def notice(lines, caption = nil) + check_nested_minicolumn base_parablock('notice', lines, caption) end @@ -562,11 +600,30 @@ def insn(lines, caption = nil) end def warning(lines, caption = nil) + check_nested_minicolumn base_parablock('warning', lines, caption) end alias_method :box, :insn + CAPTION_TITLES.each do |name| + class_eval %Q( + def #{name}_begin(caption = nil) + check_nested_minicolumn + @doc_status[:minicolumn] = '#{name}' + blank + if caption.present? + puts compile_inline(caption) + end + end + + def #{name}_end + blank + @doc_status[:minicolumn] = nil + end + ), __FILE__, __LINE__ - 14 + end + def indepimage(_lines, _id, caption = nil, _metric = nil) blank puts "図 #{compile_inline(caption)}" if caption.present? @@ -602,7 +659,7 @@ def text(str) str end - def inline_chap(id) + def inline_chap(id) # rubocop:disable Lint/UselessMethodDefinition # "「第#{super}章 #{inline_title(id)}」" # "第#{super}章" super diff --git a/lib/review/preprocessor.rb b/lib/review/preprocessor.rb index 9f05699cf..c2a76e610 100644 --- a/lib/review/preprocessor.rb +++ b/lib/review/preprocessor.rb @@ -195,7 +195,7 @@ def parse_directive(line, argc, *optdecl) op = m[1] args = m[2].split(/,\s*/) opts = parse_optargs(m[3]) - return if argc == 0 and args.empty? + return if (argc == 0) && args.empty? if argc == -1 # Any number of arguments are allowed. elsif args.size != argc @@ -284,7 +284,7 @@ def get_output(cmd, use_stderr) err = stderr.readlines end end - if err and !err.empty? + if err && !err.empty? $stderr.puts '[unexpected stderr message]' err.each { |line| $stderr.print line } error 'get_output: got unexpected output' @@ -355,14 +355,14 @@ def git?(fname) def parse_git_blob(g_obj) IO.popen('git show ' + g_obj.sub(/\Agit\|/, ''), 'r') do |f| - init_errorutils f + init_errorutils(f) return _parse_file(f) end end def parse_file(fname) File.open(fname, 'rt:BOM|utf-8') do |f| - init_errorutils f + init_errorutils(f) return _parse_file(f) end end @@ -426,7 +426,7 @@ def _parse_file(f) curr.each_value { |list| list.push(Line.new(nil, line)) } else - next if yacchack and line.strip == ';' + next if yacchack && (line.strip == ';') line = canonical(line) curr.each_value { |list| list.push(Line.new(lineno, line)) } lineno += 1 diff --git a/lib/review/rstbuilder.rb b/lib/review/rstbuilder.rb index f095955a5..8a432c1d2 100644 --- a/lib/review/rstbuilder.rb +++ b/lib/review/rstbuilder.rb @@ -97,7 +97,7 @@ def headline(level, label, caption) end p = '=' case level - when 1 then + when 1 unless label puts ".. _#{@chapter.name}:" blank @@ -373,8 +373,9 @@ def compile_ruby(base, ruby) def compile_kw(word, alt) if alt - then " **#{word}(#{alt.strip})** " - else " **#{word}** " + " **#{word}(#{alt.strip})** " + else + " **#{word}** " end end @@ -582,6 +583,24 @@ def centering(lines) base_parablock('centering', lines, nil) end + CAPTION_TITLES.each do |name| + class_eval %Q( + def #{name}_begin(caption = nil) + check_nested_minicolumn + @doc_status[:minicolumn] = '#{name}' + puts ".. #{name}::" + blank + puts " " + compile_inline(caption).to_s unless caption.nil? + print " " + end + + def #{name}_end + blank + @doc_status[:minicolumn] = nil + end + ), __FILE__, __LINE__ - 14 + end + def note(lines, caption = nil) base_parablock('note', lines, caption) end @@ -705,7 +724,7 @@ def text(str) str end - def inline_chap(id) + def inline_chap(id) # rubocop:disable Lint/UselessMethodDefinition super end diff --git a/lib/review/sec_counter.rb b/lib/review/sec_counter.rb index aab02bc69..831fb5a64 100644 --- a/lib/review/sec_counter.rb +++ b/lib/review/sec_counter.rb @@ -37,6 +37,19 @@ def anchor(level) str end + def number_list + buf = @counter.dup + while buf.present? + if buf.last == 0 + buf.pop + else + return buf + end + end + + buf + end + def prefix(level, secnolevel) return nil if @chapter.number.blank? diff --git a/lib/review/textmaker.rb b/lib/review/textmaker.rb index 3a712fb0e..50dc4513d 100644 --- a/lib/review/textmaker.rb +++ b/lib/review/textmaker.rb @@ -75,20 +75,13 @@ def remove_old_files(path) end def execute(*args) - @config = ReVIEW::Configure.values - @config.maker = 'textmaker' cmd_config, yamlfile = parse_opts(args) error "#{yamlfile} not found." unless File.exist?(yamlfile) - begin - loader = ReVIEW::YAMLLoader.new - @config.deep_merge!(loader.load_file(yamlfile)) - rescue => e - error "yaml error #{e.message}" - end + @config = ReVIEW::Configure.create(maker: 'textmaker', + yamlfile: yamlfile, + config: cmd_config) - # YAML configs will be overridden by command line options. - @config.deep_merge!(cmd_config) I18n.setup(@config['language']) begin generate_text_files(yamlfile) @@ -109,8 +102,7 @@ def generate_text_files(yamlfile) remove_old_files(@path) Dir.mkdir(@path) - @book = ReVIEW::Book.load(@basedir) - @book.config = @config + @book = ReVIEW::Book::Base.new(@basedir, config: @config) build_body(@path, yamlfile) end diff --git a/lib/review/tocprinter.rb b/lib/review/tocprinter.rb index ec1e6960a..267fcf136 100644 --- a/lib/review/tocprinter.rb +++ b/lib/review/tocprinter.rb @@ -50,7 +50,7 @@ def initialize @logger = ReVIEW.logger @config = ReVIEW::Configure.values @yamlfile = 'config.yml' - @book = ReVIEW::Book::Base.load + @book = ReVIEW::Book::Base.new('.', config: @config) @upper = 4 @indent = true @buildonly = nil @@ -59,7 +59,6 @@ def initialize def execute(*args) parse_options(args) - @book.config = ReVIEW::Configure.values unless File.readable?(@yamlfile) @logger.error("No such fiile or can't open #{@yamlfile}.") exit 1 @@ -108,7 +107,7 @@ def build_result_array result_array.push({ part: 'end' }) end end - rescue ReVIEW::FileNotFound => e + rescue ReVIEW::FileNotFound, ReVIEW::CompileError => e @logger.error e exit 1 end @@ -166,15 +165,12 @@ def calc_linesize(l) def parse_contents(name, upper, content) headline_array = [] - counter = {} + counter = { lines: 0, chars: 0, list_lines: 0, text_lines: 0 } listmode = nil content.split("\n").each do |l| if l.start_with?("\x01STARTLIST\x01") listmode = true - if counter.empty? - counter = { lines: 0, chars: 0, list_lines: 0, text_lines: 0 } - end next elsif l.start_with?("\x01ENDLIST\x01") listmode = nil @@ -184,7 +180,9 @@ def parse_contents(name, upper, content) level = $1.to_i l = $' if level <= upper - headline_array.push(counter) + if counter[:chars] > 0 + headline_array.push(counter) + end headline = l counter = { level: level, diff --git a/lib/review/topbuilder.rb b/lib/review/topbuilder.rb index d60bf059b..67942a305 100644 --- a/lib/review/topbuilder.rb +++ b/lib/review/topbuilder.rb @@ -1,4 +1,4 @@ -# Copyright (c) 2008-2019 Minero Aoki, Kenshi Muto +# Copyright (c) 2008-2020 Minero Aoki, Kenshi Muto # 2002-2006 Minero Aoki # # This program is free software. @@ -95,12 +95,18 @@ def list(lines, id, caption, lang = nil) blank puts "◆→開始:#{@titles['list']}←◆" begin - list_header(id, caption, lang) + if caption_top?('list') + list_header(id, caption, lang) + blank + end + list_body(id, lines, lang) + unless caption_top?('list') + blank + list_header(id, caption, lang) + end rescue KeyError error "no such list: #{id}" end - blank - list_body(id, lines, lang) puts "◆→終了:#{@titles['list']}←◆" blank end @@ -122,8 +128,13 @@ def list_body(_id, lines, _lang) def base_block(type, lines, caption = nil) blank puts "◆→開始:#{@titles[type]}←◆" - puts "■#{compile_inline(caption)}" if caption.present? + if caption_top?('list') && caption.present? + puts "■#{compile_inline(caption)}" + end puts lines.join("\n") + if !caption_top?('list') && caption.present? + puts "■#{compile_inline(caption)}" + end puts "◆→終了:#{@titles[type]}←◆" blank end @@ -140,10 +151,15 @@ def base_parablock(type, lines, caption = nil) def emlistnum(lines, caption = nil, _lang = nil) blank puts "◆→開始:#{@titles['emlist']}←◆" - puts "■#{compile_inline(caption)}" if caption.present? + if caption_top?('list') && caption.present? + puts "■#{compile_inline(caption)}" + end lines.each_with_index do |line, i| puts((i + 1).to_s.rjust(2) + ": #{line}") end + if !caption_top?('list') && caption.present? + puts "■#{compile_inline(caption)}" + end puts "◆→終了:#{@titles['emlist']}←◆" blank end @@ -152,12 +168,18 @@ def listnum(lines, id, caption, lang = nil) blank puts "◆→開始:#{@titles['list']}←◆" begin - list_header(id, caption, lang) + if caption_top?('list') && caption.present? + list_header(id, caption, lang) + blank + end + listnum_body(lines, lang) + if !caption_top?('list') && caption.present? + blank + list_header(id, caption, lang) + end rescue KeyError error "no such list: #{id}" end - blank - listnum_body(lines, lang) puts "◆→終了:#{@titles['list']}←◆" blank end @@ -173,8 +195,10 @@ def image(lines, id, caption, metric = nil) metrics = " #{metrics}" if metrics.present? blank puts "◆→開始:#{@titles['image']}←◆" - image_header(id, caption) - blank + if caption_top?('image') + image_header(id, caption) + blank + end if @chapter.image_bound?(id) puts "◆→#{@chapter.image(id).path}#{metrics}←◆" else @@ -183,6 +207,10 @@ def image(lines, id, caption, metric = nil) puts line end end + unless caption_top?('image') + blank + image_header(id, caption) + end puts "◆→終了:#{@titles['image']}←◆" blank end @@ -198,7 +226,7 @@ def image_header(id, caption) def texequation(lines, id = nil, caption = '') blank puts "◆→開始:#{@titles['texequation']}←◆" - texequation_header(id, caption) + texequation_header(id, caption) if caption_top?('equation') if @book.config['imgmath'] fontsize = @book.config['imgmath_options']['fontsize'].to_f @@ -214,6 +242,7 @@ def texequation(lines, id = nil, caption = '') puts lines.join("\n") end + texequation_header(id, caption) unless caption_top?('equation') puts "◆→終了:#{@titles['texequation']}←◆" blank end @@ -415,17 +444,48 @@ def common_column_end(type) blank end + def common_block_begin(type, _level, _label, caption = nil) + blank + puts "◆→開始:#{@titles[type]}←◆" + puts '■' + compile_inline(caption) if caption.present? + end + + def common_block_end(type, _level) + puts "◆→終了:#{@titles[type]}←◆" + blank + end + + CAPTION_TITLES.each do |name| + class_eval %Q( + def #{name}_begin(caption = nil) + check_nested_minicolumn + @doc_status[:minicolumn] = '#{name}' + common_block_begin('#{name}', nil, nil, caption) + end + + def #{name}_end + common_block_end('#{name}', nil) + @doc_status[:minicolumn] = nil + end + ), __FILE__, __LINE__ - 11 + end + def indepimage(_lines, id, caption = nil, metric = nil) metrics = parse_metric('top', metric) metrics = " #{metrics}" if metrics.present? blank + if caption_top?('image') && caption.present? + puts "図 #{compile_inline(caption)}" + end begin puts "◆→画像 #{@chapter.image(id).path.sub(%r{\A\./}, '')}#{metrics}←◆" rescue warn "image not bound: #{id}" puts "◆→画像 #{id}←◆" end - puts "図 #{compile_inline(caption)}" if caption.present? + if !caption_top?('image') && caption.present? + puts "図 #{compile_inline(caption)}" + end blank end diff --git a/lib/review/update.rb b/lib/review/update.rb index e89c643b9..2f55dabed 100644 --- a/lib/review/update.rb +++ b/lib/review/update.rb @@ -239,7 +239,7 @@ def check_own_files(dir) def update_version @config_ymls.each do |yml| config = YAML.load_file(yml) - if config['review_version'].to_f == TARGET_VERSION.to_f + if config['review_version'].to_f.round(1) == TARGET_VERSION.to_f.round(1) next end @@ -513,11 +513,11 @@ def update_tex_stys(template, dir) def update_tex_command @tex_ymls.each do |yml| config = YAML.load_file(yml) - if !config['texcommand'] || config['texcommand'] !~ /\s+\-/ + if !config['texcommand'] || config['texcommand'] !~ /\s+-/ next end # option should be moved to texoptions - cmd, opts = config['texcommand'].split(/\s+\-/, 2) + cmd, opts = config['texcommand'].split(/\s+-/, 2) opts = "-#{opts}" unless confirm("%s: 'texcommand' has options ('%s'). Move it to 'texoptions'?", [File.basename(yml), opts]) @@ -537,12 +537,12 @@ def update_tex_command def update_dvi_command @tex_ymls.each do |yml| config = YAML.load_file(yml) - if !config['dvicommand'] || config['dvicommand'] !~ /\s+\-/ + if !config['dvicommand'] || config['dvicommand'] !~ /\s+-/ next end # option should be moved to dvioptions - cmd, opts = config['dvicommand'].split(/\s+\-/, 2) + cmd, opts = config['dvicommand'].split(/\s+-/, 2) opts = "-#{opts}" unless confirm("%s: 'dvicommand' has options ('%s'). Move it to 'dvioptions'?", [File.basename(yml), opts]) diff --git a/lib/review/version.rb b/lib/review/version.rb index f7289f32a..f4f318b69 100644 --- a/lib/review/version.rb +++ b/lib/review/version.rb @@ -1,3 +1,3 @@ module ReVIEW - VERSION = '4.1.0'.freeze + VERSION = '4.2.0'.freeze end diff --git a/lib/review/volumeprinter.rb b/lib/review/volumeprinter.rb index 55fa0972e..1987916dc 100644 --- a/lib/review/volumeprinter.rb +++ b/lib/review/volumeprinter.rb @@ -28,8 +28,7 @@ def initialize def execute(*args) parse_options(args) - @book = ReVIEW::Book::Base.load - @book.config = @config + @book = ReVIEW::Book::Base.new('.', config: @config) unless File.readable?(@yamlfile) @logger.error("No such fiile or can't open #{@yamlfile}.") exit 1 @@ -46,7 +45,7 @@ def execute(*args) print_chapter_volume(chap) end end - rescue ReVIEW::FileNotFound => e + rescue ReVIEW::FileNotFound, ReVIEW::CompileError => e @logger.error e exit 1 end diff --git a/lib/review/webmaker.rb b/lib/review/webmaker.rb index 2b22d5139..20733cced 100644 --- a/lib/review/webmaker.rb +++ b/lib/review/webmaker.rb @@ -19,7 +19,6 @@ require 'review/template' require 'review/tocprinter' require 'review/version' -require 'erb' require 'review/makerhelper' module ReVIEW @@ -80,19 +79,13 @@ def remove_old_files(path) end def execute(*args) - @config = ReVIEW::Configure.values - @config.maker = 'webmaker' cmd_config, yamlfile = parse_opts(args) error "#{yamlfile} not found." unless File.exist?(yamlfile) - begin - loader = ReVIEW::YAMLLoader.new - @config.deep_merge!(loader.load_file(yamlfile)) - rescue => e - error "yaml error #{e.message}" - end - # YAML configs will be overridden by command line options. - @config.deep_merge!(cmd_config) + @config = ReVIEW::Configure.create(maker: 'webmaker', + yamlfile: yamlfile, + config: cmd_config) + @config['htmlext'] = 'html' I18n.setup(@config['language']) begin @@ -109,8 +102,7 @@ def generate_html_files(yamlfile) remove_old_files(@path) Dir.mkdir(@path) - @book = ReVIEW::Book.load(@basedir) - @book.config = @config + @book = ReVIEW::Book::Base.new(@basedir, config: @config) copy_stylesheet(@path) copy_frontmatter(@path) @@ -129,12 +121,6 @@ def generate_html_files(yamlfile) copy_resources(@config['fontdir'], "#{@path}/fonts", @config['font_ext']) end - def clean_mathdir - if @config['imgmath'] && File.exist?("#{@config['imagedir']}/_review_math") - FileUtils.rm_rf("#{@config['imagedir']}/_review_math") - end - end - def build_body(basetmpdir, _yamlfile) base_path = Pathname.new(@basedir) builder = ReVIEW::HTMLBuilder.new @@ -156,6 +142,7 @@ def build_body(basetmpdir, _yamlfile) end def build_part(part, basetmpdir, htmlfile) + @title = h("#{ReVIEW::I18n.t('part', part.number)} #{part.name.strip}") File.open("#{basetmpdir}/#{htmlfile}", 'w') do |f| @body = '' @body << %Q(
\n) @@ -259,6 +246,7 @@ def copy_frontmatter(basetmpdir) end def build_indexpage(basetmpdir) + @title = h('index') File.open("#{basetmpdir}/index.html", 'w') do |f| if @config['coverimage'] file = File.join(@config['imagedir'], @config['coverimage']) @@ -281,10 +269,11 @@ def build_indexpage(basetmpdir) end def build_titlepage(basetmpdir, htmlfile) + @title = h('titlepage') File.open("#{basetmpdir}/#{htmlfile}", 'w') do |f| @body = '' @body << %Q(
) - @body << %Q(

#{CGI.escapeHTML(@config.name_of('booktitle'))}

) + @body << %Q(

#{h(@config.name_of('booktitle'))}

) if @config['aut'] @body << %Q(

#{join_with_separator(@config.names_of('aut'), ReVIEW::I18n.t('names_splitter'))}

) end diff --git a/lib/review/webtocprinter.rb b/lib/review/webtocprinter.rb index 145f17a68..ea3968849 100644 --- a/lib/review/webtocprinter.rb +++ b/lib/review/webtocprinter.rb @@ -24,6 +24,10 @@ def print_result(result_array) path = '' result_array.each do |result| + unless result[:headline] + result[:headline] = '-' + end + if result[:name] path = "#{result[:name]}.#{@book.config['htmlext']}" next diff --git a/review.gemspec b/review.gemspec index 41806a72b..cfe5b8e7c 100644 --- a/review.gemspec +++ b/review.gemspec @@ -12,7 +12,7 @@ Gem::Specification.new do |gem| gem.summary = 'Re:VIEW: a easy-to-use digital publishing system' gem.description = 'Re:VIEW is a digital publishing system for books and ebooks. It supports InDesign, EPUB and LaTeX.' gem.required_rubygems_version = Gem::Requirement.new('>= 0') if gem.respond_to?(:required_rubygems_version=) - gem.date = '2020-02-29' + gem.date = '2020-06-29' gem.files = `git ls-files`.split("\n") gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") @@ -25,7 +25,7 @@ Gem::Specification.new do |gem| gem.add_dependency('rubyzip') gem.add_development_dependency('pygments.rb') gem.add_development_dependency('rake') - gem.add_development_dependency('rubocop', '~> 0.77.0') + gem.add_development_dependency('rubocop', '~> 0.91.0') gem.add_development_dependency('rubocop-performance') gem.add_development_dependency('simplecov') gem.add_development_dependency('test-unit') diff --git a/samples/syntax-book/Gemfile b/samples/syntax-book/Gemfile index 999999ebb..f488ff05e 100644 --- a/samples/syntax-book/Gemfile +++ b/samples/syntax-book/Gemfile @@ -1,4 +1,4 @@ source 'https://rubygems.org' gem 'rake' -gem 'review', '2.3.0' +gem 'review', '4.2.0' diff --git a/templates/latex/config.erb b/templates/latex/config.erb index d148a6757..1f61e7447 100644 --- a/templates/latex/config.erb +++ b/templates/latex/config.erb @@ -1,50 +1,51 @@ +<%- initialize_metachars(@config['texcommand']) -%> \makeatletter \def\review@reviewversion{<%= ReVIEW::VERSION %>} \def\review@texcompiler{<%= @texcompiler %>} \def\review@documentclass{<%= @documentclass %>} <%- %w(booktitle subtitle).each do |item| -%> -<%- if @config[item] -%>\def\review@<%= item %>name{<%= escape_latex(@config.name_of(item)) %>} +<%- if @config[item] -%>\def\review@<%= item %>name{<%= escape(@config.name_of(item)) %>} <%- end -%> <%- end -%> -<%- %w(aut adp ann arr art asn aqt aft aui ant bkp clb cmm csl dsr edt ill lyr mdc mus nrt oth pht pbl prt red rev spn ths trc trl).each do |item| %><%- if @config[item] -%>\def\review@<%= item %>names{<%= escape_latex(@config.names_of(item).join(I18n.t('names_splitter'))) %>} +<%- %w(aut adp ann arr art asn aqt aft aui ant bkp clb cmm csl dsr edt ill lyr mdc mus nrt oth pht pbl prt red rev spn ths trc trl).each do |item| %><%- if @config[item] -%>\def\review@<%= item %>names{<%= escape(@config.names_of(item).join(I18n.t('names_splitter'))) %>} <%- end -%> <%- end -%> \def\review@titlepageauthors{<%= @authors %>} -\def\review@date{<%= escape_latex(@config['date'].to_s) %>} +\def\review@date{<%= escape(@config['date'].to_s) %>} -<%- %w(bookname language urnid isbn).each do |item| -%><%- if @config[item] -%>\def\review@<%= item %>{<%= escape_latex(@config[item]) %>} +<%- %w(bookname language urnid isbn).each do |item| -%><%- if @config[item] -%>\def\review@<%= item %>{<%= escape(@config[item]) %>} <%- end -%> <%- end -%> <%- %w(rights description subject type format source relation coverage).each do |item| -%> <%- if @config[item] -%> -<%- a = [@config[item]].flatten -%>\def\review@<%= item %>{<%= a.map{|s| escape_latex(s)}.join('\\' + '\\') %>} +<%- a = [@config[item]].flatten -%>\def\review@<%= item %>{<%= a.map{|s| escape(s)}.join('\\' + '\\') %>} <%- end -%> <%- end -%> <%- if @config['highlight'] && @config['highlight']['latex'] -%>\def\review@highlightlatex{<%= @config['highlight']['latex'] %>} <%- end -%> -\def\review@intn@list{<%= escape_latex(I18n.t('list')) %>} -\def\review@intn@columnhead{<%= escape_latex(I18n.t('column_head')) %>} -\def\review@intn@image{<%= escape_latex(I18n.t('image')) %>} -\def\review@intn@table{<%= escape_latex(I18n.t('table')) %>} -\def\review@intn@equation{<%= escape_latex(I18n.t('equation')) %>} -\def\review@intn@columnname{<%= escape_latex(I18n.t('columnname')) %>} -\def\review@intn@memohead{<%= escape_latex(I18n.t('memo_head')) %>} -\def\review@intn@edition{<%= escape_latex(I18n.t('edition')) %>} -\def\review@intn@publishedby{<%= escape_latex(I18n.t('published_by', @config.names_of('pbl').join(I18n.t('names_splitter'))))%>} -\def\review@intn@captionprefix{<%= escape_latex(I18n.t('caption_prefix')) %>} -\def\review@toctitle{<%= escape_latex(@config['toctitle'].present? ? @config['toctitle'] : I18n.t('toctitle')) %>} -\def\review@prepartname{<%= escape_latex(@locale_latex['prepartname']) %>} -\def\review@postpartname{<%= escape_latex(@locale_latex['postpartname']) %>} -\def\review@prechaptername{<%= escape_latex(@locale_latex['prechaptername']) %>} -\def\review@postchaptername{<%= escape_latex(@locale_latex['postchaptername']) %>} -\def\review@figurename{<%= escape_latex(I18n.t('image')) %>} -\def\review@tablename{<%= escape_latex(I18n.t('table')) %>} -\def\review@appendixname{<%= escape_latex(@locale_latex['preappendixname']) %>} +\def\review@intn@list{<%= escape(I18n.t('list')) %>} +\def\review@intn@columnhead{<%= escape(I18n.t('column_head')) %>} +\def\review@intn@image{<%= escape(I18n.t('image')) %>} +\def\review@intn@table{<%= escape(I18n.t('table')) %>} +\def\review@intn@equation{<%= escape(I18n.t('equation')) %>} +\def\review@intn@columnname{<%= escape(I18n.t('columnname')) %>} +\def\review@intn@memohead{<%= escape(I18n.t('memo_head')) %>} +\def\review@intn@edition{<%= escape(I18n.t('edition')) %>} +\def\review@intn@publishedby{<%= escape(I18n.t('published_by', @config.names_of('pbl').join(I18n.t('names_splitter'))))%>} +\def\review@intn@captionprefix{<%= escape(I18n.t('caption_prefix')) %>} +\def\review@toctitle{<%= escape(@config['toctitle'].present? ? @config['toctitle'] : I18n.t('toctitle')) %>} +\def\review@prepartname{<%= escape(@locale_latex['prepartname']) %>} +\def\review@postpartname{<%= escape(@locale_latex['postpartname']) %>} +\def\review@prechaptername{<%= escape(@locale_latex['prechaptername']) %>} +\def\review@postchaptername{<%= escape(@locale_latex['postchaptername']) %>} +\def\review@figurename{<%= escape(I18n.t('image')) %>} +\def\review@tablename{<%= escape(I18n.t('table')) %>} +\def\review@appendixname{<%= escape(@locale_latex['preappendixname']) %>} <%- if @config['toc'] -%> \def\review@toc{true} @@ -100,6 +101,9 @@ <%- if @config['use_part'] -%> \def\reviewusepart{true} <%- end -%> +<%- if @config['pdfmaker']['bbox'] -%> +\def\review@bbox{<%= @config['pdfmaker']['bbox'] %>} +<%- end -%> \def\reviewbackcompatibilityhook{ \ifdefined\reviewimagecaption\else% for 3.0.0 compatibility diff --git a/templates/latex/review-jlreq/README.md b/templates/latex/review-jlreq/README.md index f9181d52a..8f37e64b1 100644 --- a/templates/latex/review-jlreq/README.md +++ b/templates/latex/review-jlreq/README.md @@ -38,7 +38,9 @@ texdocumentclass: ["review-jlreq", "クラスオプションたち(省略可 `media` の値によって表紙(config.yml の coverimage に指定した画像)の配置の有無は自動で切り替わりますが、`cover=true` とすれば必ず表紙を入れるようになります。 -なお、config.yml の coverimage で指定する画像ファイルは、原寸を想定しています。 +### 表紙画像のサイズの仕上がり紙面合わせ `cover_fit_page=` + +上記の coverimage で指定する画像ファイルは、原寸を想定しているため、サイズが異なる場合にははみ出たり、小さすぎたりすることになります。できるだけ原寸で用意することを推奨しますが、`cover_fit_page=true` とすれば表紙画像を紙面の仕上がりサイズに合わせて拡縮します。 ### 特定の用紙サイズ `paper=<用紙サイズ>` diff --git a/templates/latex/review-jlreq/review-base.sty b/templates/latex/review-jlreq/review-base.sty index 0e7c4c359..32fedc77c 100644 --- a/templates/latex/review-jlreq/review-base.sty +++ b/templates/latex/review-jlreq/review-base.sty @@ -1,22 +1,30 @@ -\ProvidesClass{review-base}[2020/01/02] +\ProvidesClass{review-base}[2020/08/16] % jlreq用基本設定 \def\recls@tmp{luatex}\ifx\recls@tmp\recls@driver \hypersetup{ pdftitle={\review@booktitlename}, - pdfauthor={\review@autnames}, + pdfauthor={\ifdefined\review@autnames\review@autnames\fi}, pdfcreator={Re:VIEW \review@reviewversion, with LaTeX} } \else - \newcommand*\PDFDocumentInformation[1]{% - \AtBeginShipoutFirst{\special{pdf:docinfo <<#1>>}}} - \@onlypreamble\PDFDocumentInformation - \PDFDocumentInformation{ - /Title (\review@booktitlename) - /Author (\review@autnames) - % /Subject () - % /Keywords (,,) - /Creator (Re:VIEW \review@reviewversion, with LaTeX) - } + \def\recls@tmp{ebook}\ifx\recls@cameraready\recls@tmp + \hypersetup{ + pdftitle={\review@booktitlename}, + pdfauthor={\ifdefined\review@autnames\review@autnames\fi}, + pdfcreator={Re:VIEW \review@reviewversion, with LaTeX} + } + \else + \newcommand*\PDFDocumentInformation[1]{% + \AtBeginShipoutFirst{\special{pdf:docinfo <<#1>>}}} + \@onlypreamble\PDFDocumentInformation + \PDFDocumentInformation{ + /Title (\review@booktitlename) + \ifdefined\review@autnames /Author (\review@autnames)\fi + % /Subject () + % /Keywords (,,) + /Creator (Re:VIEW \review@reviewversion, with LaTeX) + } + \fi \fi \RequirePackage{pxrubrica} @@ -26,6 +34,11 @@ \long\def\review@ifempty#1{\expandafter\ifx\expandafter\relax\detokenize{#1}\relax\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi} +%% pass bbox setting to extractbb of graphicx +\ifdefined\review@bbox + \def\Gin@pagebox{\review@bbox} +\fi + % コードリスト装飾のデフォルト \newenvironment{reviewemlist}{% \begin{tcolorbox}[skin=enhanced jigsaw,breakable,colback=black!10,colframe=black!10,boxrule=0mm,arc=0mm]\ifdefined\reviewlistxkanjiskip\setxkanjiskip{\reviewlistxkanjiskip}\fi\begin{alltt}}% @@ -122,9 +135,9 @@ \newcommand{\reviewtableref}[2]{\review@intn@table #1} \newcommand{\reviewlistref}[1]{\review@intn@list #1} \newcommand{\reviewequationref}[1]{\review@intn@equation #1} -\newcommand{\reviewbibref}[2]{#1} -\newcommand{\reviewcolumnref}[2]{#1} -\newcommand{\reviewsecref}[2]{#1} +\newcommand{\reviewbibref}[2]{\hyperref[#2]{#1}} +\newcommand{\reviewcolumnref}[2]{#1}% XXX:ハイパーリンクにはreviewcolumn側の調整が必要 +\newcommand{\reviewsecref}[2]{\hyperref[#2]{#1}} \renewcommand{\contentsname}{\review@toctitle} @@ -325,6 +338,9 @@ \if@reclscover \ifdefined\review@coverimage + \ifrecls@coverfitpage + \def\review@coverimageoption{width=\paperwidth,height=\paperheight} + \fi \def\reviewcoverpagecont{% \expandafter\includefullpagegraphics\expandafter[\review@coverimageoption]{\review@coverimage} \cleardoublepage diff --git a/templates/latex/review-jlreq/review-jlreq.cls b/templates/latex/review-jlreq/review-jlreq.cls index f4d88f93f..409d969e9 100644 --- a/templates/latex/review-jlreq/review-jlreq.cls +++ b/templates/latex/review-jlreq/review-jlreq.cls @@ -1,5 +1,5 @@ %#!ptex2pdf -l -u -ot '-synctex=1' test-rejlreqbk -% Copyright (c) 2018-2019 Kenshi Muto. +% Copyright (c) 2018-2020 Kenshi Muto. % % Permission is hereby granted, free of charge, to any person obtaining a copy % of this software and associated documentation files (the "Software"), to deal @@ -20,8 +20,7 @@ % THE SOFTWARE. \NeedsTeXFormat{LaTeX2e} -\ProvidesClass{review-jlreq}[2019/11/11 Re:VIEW pLaTeX class modified for jlreq. -cls] +\ProvidesClass{review-jlreq}[2020/08/16 Re:VIEW 5.0 upLaTeX/LuaLaTeX class modified for jlreq.cls] %% hook at end of reviewmacro \let\@endofreviewmacrohook\@empty @@ -65,11 +64,7 @@ cls] \recls@error{Not define such hiddenfolio: #1}}\relax %% set hiddenfolio preset \expandafter\let\expandafter\@makehiddenfolio\csname @makehiddenfolio@#1\endcsname - %% redefine to output \@makehiddenfolio for every page - %% TODO: would better to use \jlreqtrimmarkssetup - \@bannertoken{\hskip-5mm\smash{\hiddenfolio@font\@makehiddenfolio}}% - \def\jlreq@trimmarks@banner@even@yoko@top@left{\hskip-5mm\smash{\hiddenfolio@font\@makehiddenfolio}}% - \AddEverypageHook{\maketombowbox}% + \@makehiddenfolio \fi} \def\hiddenfolio@font{\reset@font @@ -77,29 +72,16 @@ cls] %% hiddenfolio=default \@namedef{@makehiddenfolio@default}{% - \ifodd\c@page - \llap{\thepage\hspace{\dimexpr\recls@tombobleed}}% - \else - \rlap{\hspace{\dimexpr\paperwidth+\recls@tombobleed}\thepage}% - \fi} + \jlreqtrimmarkssetup{banner = { top-gutter = { \hiddenfolio@font\selectfont \thepage} } } } %% hiddenfolio=marusho-ink \@namedef{@makehiddenfolio@marusho-ink}{% - \gdef\recls@tombobleed{5mm}% + \gdef\recls@tombobleed{5mm}% XXX: won't work. should set via bleed_margin \@nameuse{@makehiddenfolio@nikko-pc}} %% hiddenfolio=nikko-pc \@namedef{@makehiddenfolio@nikko-pc}{% - \def\recls@hiddfolio{% - \edef\recls@tmp{\thepage}% - \lower\dimexpr4pt+\recls@tombobleed+.5\paperheight+5\p@\hbox{% - \vbox{\expandafter\@tfor\expandafter\recls@x\expandafter:\expandafter=\recls@tmp\do{% - \hbox to 1\zw{\hss\recls@x\hss}}}}}% - \ifodd\c@page - \rlap{\recls@hiddfolio}% - \else - \llap{\recls@hiddfolio\hspace{-\paperwidth}}% - \fi} + \jlreqtrimmarkssetup{banner = { center-gutter = { in = {\hiddenfolio@font\selectfont \thepage} } } } } %% hiddenfolio=shippo \@namedef{@makehiddenfolio@shippo}{% @@ -110,12 +92,14 @@ cls] \newif\if@pdfhyperlink \@pdfhyperlinkfalse \newif\if@pdftombo \@pdftombofalse \newif\if@reclscover \@reclscovertrue +\newif\ifrecls@coverfitpage \recls@coverfitpagefalse \newif\ifrecls@serialpage \recls@serialpagefalse \DeclareOptionX{cameraready}[print]{\gdef\recls@cameraready{#1}} \DeclareOptionX{media}[print]{\gdef\recls@cameraready{#1}} \DeclareOptionX{tombopaper}[a4]{\gdef\recls@tombopaper{#1}} \DeclareOptionX{bleed_margin}[3mm]{\gdef\recls@tombobleed{#1}} \DeclareOptionX{cover}[\@empty]{\gdef\recls@forcecover{#1}} +\DeclareOptionX{cover_fit_page}[false]{\csname recls@coverfitpage#1\endcsname} \DeclareOptionX{startpage}[1]{\gdef\recls@startpage{\numexpr #1\relax}} \DeclareOptionX{serial_pagination}[false]{\csname recls@serialpage#1\endcsname} diff --git a/templates/latex/review-jsbook/README.md b/templates/latex/review-jsbook/README.md index 42e7796d5..6f271da9d 100644 --- a/templates/latex/review-jsbook/README.md +++ b/templates/latex/review-jsbook/README.md @@ -40,19 +40,21 @@ texdocumentclass: ["review-jsbook", "クラスオプションたち(省略可 `media` の値によって表紙(config.yml の coverimage に指定した画像)の配置の有無は自動で切り替わりますが、`cover=true` とすれば必ず表紙を入れるようになります。 -なお、config.yml の coverimage で指定する画像ファイルは、原寸を想定しています。 +### 表紙画像のサイズの仕上がり紙面合わせ `cover_fit_page=` + +上記の coverimage で指定する画像ファイルは、原寸を想定しているため、サイズが異なる場合にははみ出たり、小さすぎたりすることになります。できるだけ原寸で用意することを推奨しますが、`cover_fit_page=true` とすれば表紙画像を紙面の仕上がりサイズに合わせて拡縮します。 ### 特定の用紙サイズ `paper=<用紙サイズ>` 利用可能な特定の用紙サイズを指定できます。 - * `a3` + * `a3` * `a4` [デフォルト] * `a5` - * `a6` - * `b4`:JIS B4 + * `a6` + * `b4`:JIS B4 * `b5`:JIS B5 - * `b6`:JIS B6 + * `b6`:JIS B6 * `a4var`:210mm x 283mm * `b5var`:182mm x 230mm * `letter` diff --git a/templates/latex/review-jsbook/review-base.sty b/templates/latex/review-jsbook/review-base.sty index 57110f529..fb41ce572 100644 --- a/templates/latex/review-jsbook/review-base.sty +++ b/templates/latex/review-jsbook/review-base.sty @@ -1,4 +1,4 @@ -\ProvidesClass{review-base}[2020/01/02] +\ProvidesClass{review-base}[2020/08/16] \RequirePackage{ifthen} \@ifundefined{Hy@Info}{% for jsbook.cls \RequirePackage[dvipdfmx,bookmarks=true,bookmarksnumbered=true]{hyperref} @@ -48,6 +48,11 @@ \long\def\review@ifempty#1{\expandafter\ifx\expandafter\relax\detokenize{#1}\relax\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi} +%% pass bbox setting to extractbb of graphicx +\ifdefined\review@bbox + \def\Gin@pagebox{\review@bbox} +\fi + \newenvironment{shadedb}{% \def\FrameCommand{\fboxsep=\FrameSep \colorbox{shadecolorb}}% \MakeFramed {\FrameRestore}}% @@ -55,18 +60,6 @@ \newcommand{\parasep}{\vspace*{3zh}} -\newcommand*\PDFDocumentInformation[1]{% - \AtBeginShipoutFirst{\special{pdf:docinfo <<#1>>}}} -\@onlypreamble\PDFDocumentInformation - -\PDFDocumentInformation{ - /Title (\review@booktitlename) - /Author (\review@autnames) - % /Subject () - % /Keywords (,,) - /Creator (Re:VIEW \review@reviewversion, with LaTeX) -} - \RequirePackage{pxrubrica} \@ifpackagelater{pxrubrica}{2017/04/20}{% \rubysetup{J}}{% @@ -205,9 +198,9 @@ \newcommand{\reviewtableref}[2]{\review@intn@table #1} \newcommand{\reviewlistref}[1]{\review@intn@list #1} \newcommand{\reviewequationref}[1]{\review@intn@equation #1} -\newcommand{\reviewbibref}[2]{#1} -\newcommand{\reviewcolumnref}[2]{#1} -\newcommand{\reviewsecref}[2]{#1} +\newcommand{\reviewbibref}[2]{\hyperref[#2]{#1}} +\newcommand{\reviewcolumnref}[2]{#1}% XXX:ハイパーリンクにはreviewcolumn側の調整が必要 +\newcommand{\reviewsecref}[2]{\hyperref[#2]{#1}} \newenvironment{reviewpart}{% \setcounter{section}{0}% @@ -373,6 +366,28 @@ \renewcommand{\appendixname}{\reviewappendixname} \fi +% PDF meta information +\def\recls@tmp{ebook}\ifx\recls@cameraready\recls@tmp +\hypersetup{ + pdftitle={\review@booktitlename}, + pdfauthor={\ifdefined\review@autnames\review@autnames\fi}, + pdfcreator={Re:VIEW \review@reviewversion, with LaTeX} + } +\else +\newcommand*\PDFDocumentInformation[1]{% + \AtBeginShipoutFirst{\special{pdf:docinfo <<#1>>}}} +\@onlypreamble\PDFDocumentInformation + +% for non hyperref. escaped character will be displayed funny... +\PDFDocumentInformation{ + /Title (\review@booktitlename) + \ifdefined\review@autnames /Author (\review@autnames)\fi + % /Subject () + % /Keywords (,,) + /Creator (Re:VIEW \review@reviewversion, with LaTeX) +} +\fi + %% maxwidth is the original width if it is less than linewidth %% otherwise use linewidth (to make sure the graphics do not exceed the margin) \def\maxwidth{% @@ -417,6 +432,9 @@ \if@reclscover \ifdefined\review@coverimage + \ifrecls@coverfitpage + \def\review@coverimageoption{width=\paperwidth,height=\paperheight} + \fi \def\reviewcoverpagecont{% \expandafter\includefullpagegraphics\expandafter[\review@coverimageoption]{\review@coverimage} \cleardoublepage diff --git a/templates/latex/review-jsbook/review-jsbook.cls b/templates/latex/review-jsbook/review-jsbook.cls index 9ce2f47bc..d25699960 100644 --- a/templates/latex/review-jsbook/review-jsbook.cls +++ b/templates/latex/review-jsbook/review-jsbook.cls @@ -1,5 +1,5 @@ %#!ptex2pdf -l -u -ot '-synctex=1' test-rejsbk -% Copyright (c) 2018-2019 Munehiro Yamamoto, Kenshi Muto. +% Copyright (c) 2018-2020 Munehiro Yamamoto, Kenshi Muto. % % Permission is hereby granted, free of charge, to any person obtaining a copy % of this software and associated documentation files (the "Software"), to deal @@ -21,7 +21,7 @@ \NeedsTeXFormat{pLaTeX2e} \ProvidesClass{review-jsbook} - [2019/09/22 v4.0 Re:VIEW pLaTeX class modified for jsbook.cls] + [2020/08/16 v5.0 Re:VIEW pLaTeX class modified for jsbook.cls] \def\recls@error{\ClassError{review-jsbook}} \def\recls@warning{\ClassWarning{review-jsbook}} @@ -226,8 +226,10 @@ %% 表紙・開始番号・通しノンブル \newif\if@reclscover \@reclscovertrue +\newif\ifrecls@coverfitpage \recls@coverfitpagefalse \newif\ifrecls@serialpage \recls@serialpagefalse \DeclareOptionX{cover}[\@empty]{\gdef\recls@forcecover{#1}} +\DeclareOptionX{cover_fit_page}[false]{\csname recls@coverfitpage#1\endcsname} \DeclareOptionX{startpage}[1]{\gdef\recls@startpage{\numexpr#1}} \DeclareOptionX{serial_pagination}[false]{\csname recls@serialpage#1\endcsname} diff --git a/templates/web/html/layout-html5.html.erb b/templates/web/html/layout-html5.html.erb index 510adda1f..06befa316 100644 --- a/templates/web/html/layout-html5.html.erb +++ b/templates/web/html/layout-html5.html.erb @@ -11,7 +11,7 @@ <% if @next.present? %>" /><% end %> <% if @prev.present? %>" /><% end %> - <%=h @title %> | <%=h @book.config.name_of("booktitle")%> + <%= @title %> | <%=h @book.config.name_of("booktitle")%> >
diff --git a/test/assets/test_template.tex b/test/assets/test_template.tex index 92b119592..ade4cb037 100644 --- a/test/assets/test_template.tex +++ b/test/assets/test_template.tex @@ -1,6 +1,6 @@ \documentclass[dvipdfmx]{review-jsbook} \makeatletter -\def\review@reviewversion{3.2.0} +\def\review@reviewversion{4.2.0} \def\review@texcompiler{uplatex} \def\review@documentclass{review-jsbook} @@ -9,7 +9,7 @@ \def\review@autnames{anonymous} \def\review@titlepageauthors{anonymous 著} -\def\review@date{2011{-}01{-}01} +\def\review@date{2020{-}07{-}11} \def\review@bookname{sample} \def\review@language{ja} @@ -38,7 +38,7 @@ \def\review@usecovernombre{true} \def\review@titlepage{true} -\def\review@pubhistories{2011年1月1日 発行} +\def\review@pubhistories{2020年7月11日 発行} \def\review@colophonnames{著 者 & anonymous \\ } diff --git a/test/assets/test_template_backmatter.tex b/test/assets/test_template_backmatter.tex index ab816b3c6..f6c687f9b 100644 --- a/test/assets/test_template_backmatter.tex +++ b/test/assets/test_template_backmatter.tex @@ -1,6 +1,6 @@ \documentclass[dvipdfmx]{review-jsbook} \makeatletter -\def\review@reviewversion{3.2.0} +\def\review@reviewversion{4.2.0} \def\review@texcompiler{uplatex} \def\review@documentclass{review-jsbook} @@ -9,7 +9,7 @@ \def\review@autnames{anonymous} \def\review@titlepageauthors{anonymous 著} -\def\review@date{2011{-}01{-}01} +\def\review@date{2020{-}07{-}11} \def\review@bookname{sample} \def\review@language{ja} @@ -49,7 +49,7 @@ } \null} -\def\review@pubhistories{2011年1月1日 発行} +\def\review@pubhistories{2020年7月11日 発行} \def\review@colophonnames{著 者 & anonymous \\ } diff --git a/test/book_test_helper.rb b/test/book_test_helper.rb index 1be747f3a..8d8329fcd 100644 --- a/test/book_test_helper.rb +++ b/test/book_test_helper.rb @@ -13,13 +13,19 @@ def mktmpbookdir(files = {}) Dir.mktmpdir do |tmpdir| Dir.chdir(tmpdir) do dir = '.' - files.each_pair do |basename, content| - path = File.join(dir, basename) + files.each_pair do |filename, content| + path = File.join(dir, filename) + FileUtils.mkdir_p(File.dirname(path)) File.open(path, 'w') { |o| o.print content } - created_files[basename] = path + created_files[filename] = path end - book = Book::Base.load(dir) - book.config = ReVIEW::Configure.values + conf_path = File.expand_path('config.yml', dir) + if File.exist?(conf_path) + config = ReVIEW::Configure.create(yamlfile: conf_path) + else + config = ReVIEW::Configure.values + end + book = Book::Base.new(dir, config: config) yield(dir, book, created_files) end end diff --git a/test/test_book.rb b/test/test_book.rb index f995ea54d..176e359d5 100644 --- a/test/test_book.rb +++ b/test/test_book.rb @@ -23,8 +23,8 @@ def test_update_rubyenv test_const = "ReVIEW__BOOK__TEST__#{num}" begin Dir.mktmpdir do |dir| - File.open(File.join(dir, 'review-ext.rb'), 'w') { |o| o.puts "#{test_const} = #{num}" } - Book::Base.load(dir) + File.write(File.join(dir, 'review-ext.rb'), "#{test_const} = #{num}") + Book::Base.new(dir) assert_equal num, (Object.class_eval { const_get(test_const) }) end ensure @@ -159,12 +159,12 @@ def test_set_config end def test_parse_chapters - mktmpbookdir 'CHAPS' => '' do |_dir, book, _files| + mktmpbookdir('CHAPS' => '') do |_dir, book, _files| parts = book.instance_eval { parse_chapters } assert_equal 0, parts.size end - mktmpbookdir 'CHAPS' => "chapter1.re\nchapter2\n" do |dir, book, _files| + mktmpbookdir('CHAPS' => "chapter1.re\nchapter2\n") do |dir, book, _files| parts = book.instance_eval { parse_chapters } assert_equal 1, parts.size @@ -178,7 +178,7 @@ def test_parse_chapters assert_equal expect, chaps end - mktmpbookdir 'CHAPS' => < < '' do |_dir, book, _files| + mktmpbookdir('PREDEF' => '') do |_dir, book, _files| assert_equal nil, book.prefaces # XXX: OK? end - mktmpbookdir 'PREDEF' => 'chapter1', - 'chapter1.re' => '' do |_dir, book, files| - assert_kind_of Book::Part, book.prefaces + mktmpbookdir('PREDEF' => 'chapter1', + 'chapter1.re' => '') do |_dir, book, files| + assert_kind_of(Book::Part, book.prefaces) assert_equal '', book.prefaces.name assert_equal 1, book.prefaces.chapters.size assert_equal 'chapter1', book.prefaces.chapters.first.name assert_equal files['chapter1.re'], book.prefaces.chapters.first.path end - mktmpbookdir 'PREDEF' => "chapter1\n\nchapter2", - 'chapter1.re' => '', 'chapter2.re' => '' do |_dir, book, files| - assert_kind_of Book::Part, book.prefaces + mktmpbookdir('PREDEF' => "chapter1\n\nchapter2", + 'chapter1.re' => '', 'chapter2.re' => '') do |_dir, book, files| + assert_kind_of(Book::Part, book.prefaces) assert_equal '', book.prefaces.name assert_equal 2, book.prefaces.chapters.size assert_equal 'chapter1', book.prefaces.chapters.first.name @@ -304,29 +304,29 @@ def test_prefaces assert_equal files['chapter2.re'], book.prefaces.chapters.last.path end - mktmpbookdir 'PREDEF' => 'chapter1 chapter2', - 'chapter1.re' => '', 'chapter2.re' => '' do |_dir, book, _files| - assert_kind_of Book::Part, book.prefaces + mktmpbookdir('PREDEF' => 'chapter1 chapter2', + 'chapter1.re' => '', 'chapter2.re' => '') do |_dir, book, _files| + assert_kind_of(Book::Part, book.prefaces) assert_equal '', book.prefaces.name assert_equal 2, book.prefaces.chapters.size # XXX: OK? end - mktmpbookdir 'PREDEF' => 'not_exist' do |_dir, book, _files| + mktmpbookdir('PREDEF' => 'not_exist') do |_dir, book, _files| assert_raises FileNotFound do assert_equal nil, book.prefaces end end - mktmpbookdir 'PREDEF' => 'chapter1.re', - 'chapter1.re' => '' do |_dir, book, _files| - assert_kind_of Book::Part, book.prefaces + mktmpbookdir('PREDEF' => 'chapter1.re', + 'chapter1.re' => '') do |_dir, book, _files| + assert_kind_of(Book::Part, book.prefaces) assert_equal '', book.prefaces.name assert_equal 1, book.prefaces.chapters.size end - mktmpbookdir 'PREDEF' => 'chapter1.txt', - 'chapter1.txt' => '' do |_dir, book, _files| - assert_kind_of Book::Part, book.prefaces + mktmpbookdir('PREDEF' => 'chapter1.txt', + 'chapter1.txt' => '') do |_dir, book, _files| + assert_kind_of(Book::Part, book.prefaces) assert_equal '', book.prefaces.name assert_equal 1, book.prefaces.chapters.size end @@ -337,13 +337,13 @@ def test_appendix assert_equal nil, book.appendix end - mktmpbookdir 'POSTDEF' => '' do |_dir, book, _files| + mktmpbookdir('POSTDEF' => '') do |_dir, book, _files| assert_equal nil, book.appendix end - mktmpbookdir 'POSTDEF' => 'chapter1', - 'chapter1.re' => '' do |_dir, book, files| - assert_kind_of Book::Part, book.appendix + mktmpbookdir('POSTDEF' => 'chapter1', + 'chapter1.re' => '') do |_dir, book, files| + assert_kind_of(Book::Part, book.appendix) assert_equal '', book.appendix.name assert_equal 1, book.appendix.chapters.size assert_equal 'chapter1', book.appendix.chapters.first.name @@ -351,9 +351,9 @@ def test_appendix assert_equal 1, book.appendix.chapters.first.number end - mktmpbookdir 'POSTDEF' => "chapter1\n\nchapter2", - 'chapter1.re' => '', 'chapter2.re' => '' do |_dir, book, files| - assert_kind_of Book::Part, book.appendix + mktmpbookdir('POSTDEF' => "chapter1\n\nchapter2", + 'chapter1.re' => '', 'chapter2.re' => '') do |_dir, book, files| + assert_kind_of(Book::Part, book.appendix) assert_equal '', book.appendix.name assert_equal 2, book.appendix.chapters.size assert_equal 'chapter1', book.appendix.chapters.first.name @@ -364,32 +364,32 @@ def test_appendix assert_equal 2, book.appendix.chapters.last.number end - mktmpbookdir 'POSTDEF' => 'chapter1 chapter2', - 'chapter1.re' => '', 'chapter2.re' => '' do |_dir, book, _files| - assert_kind_of Book::Part, book.appendix + mktmpbookdir('POSTDEF' => 'chapter1 chapter2', + 'chapter1.re' => '', 'chapter2.re' => '') do |_dir, book, _files| + assert_kind_of(Book::Part, book.appendix) assert_equal '', book.appendix.name assert_equal 2, book.appendix.chapters.size # XXX: OK? assert_equal 1, book.appendix.chapters.first.number assert_equal 2, book.appendix.chapters.last.number end - mktmpbookdir 'POSTDEF' => 'not_exist' do |_dir, book, _files| + mktmpbookdir('POSTDEF' => 'not_exist') do |_dir, book, _files| assert_raises FileNotFound do assert_equal nil, book.appendix end end - mktmpbookdir 'catalog.yml' => "APPENDIX:\n - p01.re", - 'p01.re' => '= appendix' do |_dir, book, _files| + mktmpbookdir('catalog.yml' => "APPENDIX:\n - p01.re", + 'p01.re' => '= appendix') do |_dir, book, _files| assert_equal 'appendix', book.appendix.chapters.first.title assert_equal 1, book.appendix.chapters.first.number end end def test_postscripts - mktmpbookdir 'catalog.yml' => "POSTDEF:\n - b01.re", - 'b01.re' => '= back' do |_dir, book, _files| - assert_kind_of Book::Part, book.postscripts + mktmpbookdir('catalog.yml' => "POSTDEF:\n - b01.re", + 'b01.re' => '= back') do |_dir, book, _files| + assert_kind_of(Book::Part, book.postscripts) assert_equal 1, book.postscripts.chapters.size assert_equal 'back', book.postscripts.chapters.first.title assert_equal nil, book.postscripts.chapters.first.number @@ -407,7 +407,7 @@ def test_parts assert tmp.empty? end - mktmpbookdir 'CHAPS' => "ch1\nch2\n\nch3", 'PART' => "foo\nbar\n" do |_dir, book, _files| + mktmpbookdir('CHAPS' => "ch1\nch2\n\nch3", 'PART' => "foo\nbar\n") do |_dir, book, _files| parts = book.parts assert_equal 2, parts.size assert !book.part(0) @@ -432,10 +432,10 @@ def test_parts_in_file assert tmp.empty? end - mktmpbookdir 'CHAPS' => "ch1.re\nch2.re\n\nch3.re\n", + mktmpbookdir('CHAPS' => "ch1.re\nch2.re\n\nch3.re\n", 'PART' => "foo\nbar\n", 'ch1.re' => "= ch1\n\n", 'ch2.re' => "= ch2\n\n", - 'ch3.re' => "= ch3\n\n" do |_dir, book, _files| + 'ch3.re' => "= ch3\n\n") do |_dir, book, _files| parts = book.parts_in_file assert_equal 0, parts.size assert !book.part(0) @@ -448,11 +448,11 @@ def test_parts_in_file assert_equal [1, 2], tmp end - mktmpbookdir 'CHAPS' => "ch1.re\nch2.re\n\nch3.re\n", + mktmpbookdir('CHAPS' => "ch1.re\nch2.re\n\nch3.re\n", 'PART' => "foo.re\nbar.re\n", 'foo.re' => "= part1\n\n", 'bar.re' => "= part2\n\n", 'ch1.re' => "= ch1\n\n", 'ch2.re' => "= ch2\n\n", - 'ch3.re' => "= ch3\n\n" do |_dir, book, _files| + 'ch3.re' => "= ch3\n\n") do |_dir, book, _files| parts = book.parts_in_file assert_equal 2, parts.size assert !book.part(0) @@ -467,7 +467,7 @@ def test_parts_in_file end def test_chapters - mktmpbookdir 'CHAPS' => "ch1\nch2\n\nch3" do |_dir, book, _files| + mktmpbookdir('CHAPS' => "ch1\nch2\n\nch3") do |_dir, book, _files| chapters = book.chapters assert_equal 3, chapters.size @@ -486,7 +486,7 @@ def test_chapters end end - mktmpbookdir 'CHAPS' => "ch1.txt\nch2.txt\n\nch3.txt" do |_dir, book, _files| + mktmpbookdir('CHAPS' => "ch1.txt\nch2.txt\n\nch3.txt") do |_dir, book, _files| chapters = book.chapters assert_equal 3, chapters.size @@ -507,7 +507,7 @@ def test_chapters end def test_next_chapter - mktmpbookdir 'CHAPS' => "ch1\nch2" do |_dir, book, _files| + mktmpbookdir('CHAPS' => "ch1\nch2") do |_dir, book, _files| chapter = book.chapter('ch1') assert_equal book.chapter('ch2'), book.next_chapter(chapter) @@ -517,7 +517,7 @@ def test_next_chapter end def test_prev_chapter - mktmpbookdir 'CHAPS' => "ch1\nch2" do |_dir, book, _files| + mktmpbookdir('CHAPS' => "ch1\nch2") do |_dir, book, _files| chapter = book.chapter('ch2') assert_equal book.chapter('ch1'), book.prev_chapter(chapter) @@ -534,14 +534,14 @@ def test_volume assert_equal 0, book.volume.lines end - mktmpbookdir 'CHAPS' => 'chapter1.re', 'chapter1.re' => '12345' do |_dir, book, _files| + mktmpbookdir('CHAPS' => 'chapter1.re', 'chapter1.re' => '12345') do |_dir, book, _files| assert book.volume assert book.volume.bytes > 0 assert book.volume.chars > 0 assert book.volume.lines > 0 end - mktmpbookdir 'preface.re' => '12345' do |dir, _book, _files| + mktmpbookdir('preface.re' => '12345') do |dir, _book, _files| Dir.chdir(dir) do book2 = Book::Base.new('.') assert book2.volume @@ -560,12 +560,9 @@ def test_basedir end def test_contentdir - mktmpbookdir('config.yml' => "contentdir: content\n", 'catalog.yml' => "CHAPS:\n - ch01.re\n") do |dir, _book, _files| - Dir.mkdir('content') - File.open('content/ch01.re', 'w') { |f| f.puts "foo\n" } - book = Book::Base.new(dir) - config_file = File.join(dir, 'config.yml') - book.load_config(config_file) + mktmpbookdir('config.yml' => "contentdir: content\n", + 'catalog.yml' => "CHAPS:\n - ch01.re\n", + 'content/ch01.re' => "foo\n") do |_dir, book, _files| assert_equal "foo\n", book.chapters[0].content end end @@ -578,19 +575,13 @@ def test_page_metric end def test_page_metric_config - mktmpbookdir('config.yml' => "bookname: book\npage_metric: B5\n") do |dir, _book, _files| - book = Book::Base.new(dir) - config_file = File.join(dir, 'config.yml') - book.load_config(config_file) + mktmpbookdir('config.yml' => "bookname: book\npage_metric: B5\n") do |_dir, book, _files| assert_equal ReVIEW::Book::PageMetric::B5, book.page_metric end end def test_page_metric_config_array - mktmpbookdir('config.yml' => "bookname: book\npage_metric: [50, 40, 36, 40, 1]\n") do |dir, _book, _files| - book = Book::Base.new(dir) - config_file = File.join(dir, 'config.yml') - book.load_config(config_file) + mktmpbookdir('config.yml' => "bookname: book\npage_metric: [50, 40, 36, 40, 1]\n") do |_dir, book, _files| assert_equal ReVIEW::Book::PageMetric::B5, book.page_metric end end diff --git a/test/test_book_chapter.rb b/test/test_book_chapter.rb index 4e2845682..52e19cd9b 100644 --- a/test/test_book_chapter.rb +++ b/test/test_book_chapter.rb @@ -2,6 +2,10 @@ class ChapterTest < Test::Unit::TestCase include BookTestHelper + def setup + I18n.setup + end + def test_initialize ch = Book::Chapter.new(:book, :number, :name, '/foo/bar', :io) assert_equal :book, ch.book @@ -66,6 +70,7 @@ def test_lines book = Book::Base.new ch = Book::Chapter.new(book, nil, nil, tf.path) + ch.generate_indexes assert_equal lines, ch.lines lines = ["1\n", "2\n", '3'] @@ -78,6 +83,7 @@ def test_lines tf2.close ch = Book::Chapter.new(book, nil, nil, tf1.path, tf2.path) + ch.generate_indexes assert_equal lines, ch.lines # XXX: OK? end @@ -103,19 +109,19 @@ def test_volume end def test_on_chaps? - mktmpbookdir 'CHAPS' => "chapter1.re\nchapter2.re", - 'chapter1.re' => '12345', 'preface.re' => 'abcde' do |dir, book, files| + mktmpbookdir('CHAPS' => "chapter1.re\nchapter2.re", + 'chapter1.re' => '12345', 'preface.re' => 'abcde') do |dir, book, files| ch1 = Book::Chapter.new(book, 1, 'chapter1', files['chapter1.re']) pre = Book::Chapter.new(book, nil, 'preface', files['preface.re']) assert ch1.on_chaps? assert !pre.on_chaps? - ch2_path = File.join(dir, 'chapter2.er') + ch2_path = File.join(dir, 'chapter2.re') File.open(ch2_path, 'w') {} ch2 = Book::Chapter.new(book, 2, 'chapter2', ch2_path) - ch3_path = File.join(dir, 'chapter3.er') + ch3_path = File.join(dir, 'chapter3.re') File.open(ch3_path, 'w') {} ch3 = Book::Chapter.new(book, 3, 'chapter3', ch3_path) @@ -124,50 +130,70 @@ def test_on_chaps? end end + def test_invalid_encoding + mktmpbookdir('CHAPS' => 'chapter1.re', + 'chapter1.re' => "= 日本語UTF-8\n") do |_dir, book, files| + assert Book::Chapter.new(book, 1, 'chapter1', files['chapter1.re']) + end + + # UTF-16LE UTF-16BE UTF-32LE UTF-32BE cause error on Windows + %w[CP932 SHIFT_JIS EUC-JP].each do |enc| + mktmpbookdir('CHAPS' => 'chapter1.re', + 'chapter1.re' => "= 日本語UTF-8\n".encode(enc)) do |_dir, book, files| + e = assert_raises(ReVIEW::CompileError) { Book::Chapter.new(book, 1, 'chapter1', files['chapter1.re']) } + assert_equal 'chapter1: invalid byte sequence in UTF-8', e.message + end + end + end + def test_list_index do_test_index(< EOT - File.open(path, 'w') { |f| f.write(html) } + File.write(path, html) assert_equal ['mathml'], epubmaker.detect_properties(path) end end @@ -656,7 +656,7 @@ def test_detect_mathml_ns EOT - File.open(path, 'w') { |f| f.write(html) } + File.write(path, html) assert_equal ['mathml'], epubmaker.detect_properties(path) end end diff --git a/test/test_epubmaker.rb b/test/test_epubmaker.rb index fe9702d14..a34eab850 100644 --- a/test/test_epubmaker.rb +++ b/test/test_epubmaker.rb @@ -1,5 +1,6 @@ require 'test_helper' require 'epubmaker' +require 'review/epubmaker' class EPUBMakerTest < Test::Unit::TestCase include EPUBMaker diff --git a/test/test_helper.rb b/test/test_helper.rb index 881dc7021..cf4a0fff9 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -16,7 +16,7 @@ def prepare_samplebook(srcdir, bookdir, latextemplatedir, configfile) samplebook_dir = File.expand_path("../samples/#{bookdir}/", File.dirname(__FILE__)) files = Dir.glob(File.join(samplebook_dir, '*')) # ignore temporary built files - files.delete_if { |file| file =~ /.*\-(pdf|epub|text)/ || file == 'webroot' } + files.delete_if { |file| file =~ /.*-(pdf|epub|text)/ || file == 'webroot' } FileUtils.cp_r(files, srcdir) if latextemplatedir # copy from review-jsbook or review-jlreq @@ -39,11 +39,13 @@ def compile_block(text) def compile_block_default(text) @chapter.content = text + @chapter.execute_indexer(force: true) @compiler.compile(@chapter) end def compile_block_html(text) @chapter.content = text + @chapter.execute_indexer(force: true) matched = @compiler.compile(@chapter).match(Regexp.new(%Q(\n(.+)), Regexp::MULTILINE)) if matched && matched.size > 1 matched[1] @@ -54,5 +56,6 @@ def compile_block_html(text) def compile_block_idgxml(text) @chapter.content = text + @chapter.execute_indexer(force: true) @compiler.compile(@chapter).gsub(Regexp.new(%Q(.*), Regexp::MULTILINE), '').gsub("\n", '') end diff --git a/test/test_htmlbuilder.rb b/test/test_htmlbuilder.rb index 236b0d31f..af112eaef 100644 --- a/test/test_htmlbuilder.rb +++ b/test/test_htmlbuilder.rb @@ -61,7 +61,7 @@ def test_headline_postdef_roman Dir.mktmpdir do |dir| Dir.chdir(dir) do file = File.join(dir, 'locale.yml') - File.open(file, 'w') { |f| f.write "locale: ja\nappendix: 付録%pR" } + File.write(file, "locale: ja\nappendix: 付録%pR") I18n.setup('ja') @chapter.instance_eval do def on_appendix? @@ -82,7 +82,7 @@ def test_headline_postdef_alpha Dir.mktmpdir do |dir| Dir.chdir(dir) do file = File.join(dir, 'locale.yml') - File.open(file, 'w') { |f| f.write "locale: ja\nappendix: 付録%pA" } + File.write(file, "locale: ja\nappendix: 付録%pA") I18n.setup('ja') @chapter.instance_eval do def on_appendix? @@ -250,6 +250,15 @@ def @chapter.headline_index idx end + @config['secnolevel'] = 2 + actual = compile_inline('test @{chap1|test} test2') + assert_equal 'test 「te_st」 test2', actual + + @config['secnolevel'] = 3 + actual = compile_inline('test @{chap1|test} test2') + assert_equal 'test 「1.1.1 te_st」 test2', actual + + @config['chapterlink'] = nil @config['secnolevel'] = 2 actual = compile_inline('test @{chap1|test} test2') assert_equal 'test 「te_st」 test2', actual @@ -263,7 +272,7 @@ def test_inline_hd_chap_postdef_roman Dir.mktmpdir do |dir| Dir.chdir(dir) do file = File.join(dir, 'locale.yml') - File.open(file, 'w') { |f| f.write "locale: ja\nappendix: 付録%pR" } + File.write(file, "locale: ja\nappendix: 付録%pR") I18n.setup('ja') @chapter.instance_eval do def on_appendix? @@ -278,6 +287,10 @@ def @chapter.headline_index idx end + actual = compile_inline('test @{test} test2') + assert_equal 'test 「I.1 te_st」 test2', actual + + @config['chapterlink'] = nil actual = compile_inline('test @{test} test2') assert_equal 'test 「I.1 te_st」 test2', actual end @@ -288,7 +301,7 @@ def test_inline_hd_chap_postdef_alpha Dir.mktmpdir do |dir| Dir.chdir(dir) do file = File.join(dir, 'locale.yml') - File.open(file, 'w') { |f| f.write "locale: ja\nappendix: 付録%pA" } + File.write(file, "locale: ja\nappendix: 付録%pA") I18n.setup('ja') @chapter.instance_eval do def on_appendix? @@ -303,6 +316,10 @@ def @chapter.headline_index idx end + actual = compile_inline('test @{test} test2') + assert_equal 'test 「A.1 te_st」 test2', actual + + @config['chapterlink'] = nil actual = compile_inline('test @{test} test2') assert_equal 'test 「A.1 te_st」 test2', actual end @@ -354,6 +371,11 @@ def @chapter.image(_id) item end + actual = compile_block("@{sampleimg}\n") + expected = %Q(

図1.1

\n) + assert_equal expected, actual + + @config['chapterlink'] = nil actual = compile_block("@{sampleimg}\n") expected = %Q(

図1.1

\n) assert_equal expected, actual @@ -366,6 +388,11 @@ def @chapter.image(_id) item end + actual = compile_block("@{sampleimg}\n") + expected = %Q(

図1.1「sample photo」

\n) + assert_equal expected, actual + + @config['chapterlink'] = nil actual = compile_block("@{sampleimg}\n") expected = %Q(

図1.1「sample photo」

\n) assert_equal expected, actual @@ -378,6 +405,11 @@ def @chapter.image(_id) item end + actual = compile_block("@{sampleimg}\n") + expected = %Q(

図1.1

\n) + assert_equal expected, actual + + @config['chapterlink'] = nil actual = compile_block("@{sampleimg}\n") expected = %Q(

図1.1

\n) assert_equal expected, actual @@ -389,20 +421,24 @@ def test_inline_imgref3 file1 = File.join(dir, 'images', 'img1.png') filet1 = File.join(dir, 'images', 'tbl1.png') file2 = File.join(dir, 'images', 'img2.png') + file3 = File.join(dir, 'images', 'icon3.png') re1 = File.join(dir, 'sample1.re') cat = File.join(dir, 'catalog.yml') FileUtils.mkdir_p(File.join(dir, 'images')) - File.open(file1, 'w') { |f| f.write '' } - File.open(filet1, 'w') { |f| f.write '' } - File.open(file2, 'w') { |f| f.write '' } - File.open(cat, 'w') { |f| f.write "CHAPS:\n - sample1.re\n" } - File.open(re1, 'w') { |f| f.write <{tbl1}. img2 is @{img2}. +icon3 is @{icon3}. + //image[img1][image 1]{ //} @@ -417,8 +453,37 @@ def test_inline_imgref3 expected = <<-EOS

第1章 test

+

tbl1 is 表1.1.

+

img2 is 図1.2.

+

icon3 is [icon3].

+
+image 1 +

+図1.1: image 1 +

+
+
+

表1.1: table 1

+table 1 +
+
+image 2 +

+図1.2: image 2 +

+
+EOS + + assert_equal expected, actual + + @config['chapterlink'] = nil + actual = compile_block(content) + + expected = <<-EOS +

第1章 test

tbl1 is 表1.1.

img2 is 図1.2.

+

icon3 is [icon3].

image 1

@@ -546,6 +611,18 @@ def @chapter.image(_id) 図1.1: sample photo

+EOS + assert_equal expected, actual + + @config['caption_position']['image'] = 'top' + actual = compile_block("//image[sampleimg][sample photo]{\n//}\n") + expected = <<-EOS +
+

+図1.1: sample photo +

+sample photo +
EOS assert_equal expected, actual end @@ -588,25 +665,32 @@ def @chapter.image(_id) assert_equal expected, actual end - def test_image_with_tricky_id + def test_image_with_tricky_id_kana def @chapter.image(_id) - item = Book::Index::Item.new('123 あ_;', 1) - item.instance_eval { @path = './images/chap1-123 あ_;.png' } + item = Book::Index::Item.new('123あいう', 1) + item.instance_eval { @path = './images/123あいう.png' } item end - - actual = compile_block("//image[123 あ_;][sample photo]{\n//}\n") + @chapter.instance_eval { @name = 'ch01' } + actual = compile_block("//image[123あいう][sample photo]{\n//}\nimg: @{123あいう}\n") expected = <<-EOS -
-sample photo +
+sample photo

図1.1: sample photo

+

img: 図1.1

EOS assert_equal expected, actual end + def test_image_with_tricky_id_space + assert_raise(ReVIEW::SyntaxError) do + _result = compile_block("//image[123 abc][sample photo]{\n//}\n") + end + end + def test_indepimage def @chapter.image(_id) item = Book::Index::Item.new('sampleimg', 1) @@ -622,6 +706,18 @@ def @chapter.image(_id) 図: sample photo

+EOS + assert_equal expected, actual + + @config['caption_position']['image'] = 'top' + actual = compile_block("//indepimage[sampleimg][sample photo]\n") + expected = <<-EOS +
+

+図: sample photo +

+sample photo +
EOS assert_equal expected, actual end @@ -779,11 +875,10 @@ def test_dlist_beforeulol end def test_dt_inline - fn = Book::FootnoteIndex.parse(['//footnote[bar][bar]']) - @chapter.instance_eval { @footnote_index = fn } - actual = compile_block(" : foo@{bar}[]<>&@$\\alpha[]$\n") + actual = compile_block("//footnote[bar][bar]\n\n : foo@{bar}[]<>&@$\\alpha[]$\n") expected = <<-EOS +

[*1] bar

foo*1[]<>&\\alpha[]
@@ -806,6 +901,20 @@ def @chapter.list(_id) test2
+EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//list[samplelist][this is @{test}<&>_]{\ntest1\ntest1.5\n\ntest@{2}\n//}\n") + expected = <<-EOS +
+
test1
+test1.5
+
+test2
+
+

リスト1.1: this is test<&>_

+
EOS assert_equal expected, actual end @@ -814,22 +923,28 @@ def test_inline_list def @chapter.list(_id) Book::Index::Item.new('samplelist', 1) end - actual = compile_block("@{sampletest}\n") + actual = compile_block("@{samplelist}\n") + assert_equal %Q(

リスト1.1

\n), actual + + @config['chapterlink'] = nil + actual = compile_block("@{samplelist}\n") assert_equal %Q(

リスト1.1

\n), actual end def test_inline_list_href - book = ReVIEW::Book::Base.load + book = ReVIEW::Book::Base.new book.config['chapterlink'] = true book.catalog = ReVIEW::Catalog.new('CHAPS' => %w[ch1.re ch2.re]) - io1 = StringIO.new("//list[sampletest]{\nfoo\n//}\n") + io1 = StringIO.new("//list[sampletest][a]{\nfoo\n//}\n") io2 = StringIO.new("= BAR\n") chap1 = ReVIEW::Book::Chapter.new(book, 1, 'ch1', 'ch1.re', io1) chap2 = ReVIEW::Book::Chapter.new(book, 2, 'ch2', 'ch2.re', io2) - book.parts = [ReVIEW::Book::Part.new(self, nil, [chap1, chap2])] + book.parts = [ReVIEW::Book::Part.new(book, nil, [chap1, chap2])] builder = ReVIEW::HTMLBuilder.new comp = ReVIEW::Compiler.new(builder) builder.bind(comp, chap2, nil) + + chap1.generate_indexes actual = builder.inline_list('ch1|sampletest') assert_equal %Q(リスト1.1), actual end @@ -1023,6 +1138,29 @@ def foo(a1, a2=:test) 4: end
+EOS + + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block(<<-EOS) +//listnum[samplelist][this is @{test}<&>_][ruby]{ +def foo(a1, a2=:test) + (1..3).times{|i| a.include?(:foo)} + return true +end +//} +EOS + + expected = <<-EOS +
+
 1: def foo(a1, a2=:test)
+ 2:   (1..3).times{|i| a.include?(:foo)}
+ 3:   return true
+ 4: end
+
+

リスト1.1: this is test<&>_

+
EOS assert_equal expected, actual @@ -1220,6 +1358,20 @@ def test_source buz
+EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//source[foo/bar/test.rb]{\nfoo\nbar\n\nbuz\n//}\n") + expected = <<-EOS +
+
foo
+bar
+
+buz
+
+

foo/bar/test.rb

+
EOS assert_equal expected, actual end @@ -1257,6 +1409,18 @@ def test_box bar +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//box[FOO]{\nfoo\nbar\n//}\n") + expected = <<-EOS +
+
foo
+bar
+
+

FOO

+
EOS assert_equal expected, actual end @@ -1301,6 +1465,18 @@ def test_emlist_caption lineB +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//emlist[cap1]{\nlineA\nlineB\n//}\n") + expected = <<-EOS +
+
lineA
+lineB
+
+

cap1

+
EOS assert_equal expected, actual end @@ -1341,6 +1517,18 @@ def test_emlistnum_lang 2: lineB +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//emlistnum[cap][text]{\nlineA\nlineB\n//}\n") + expected = <<-EOS +
+
 1: lineA
+ 2: lineB
+
+

cap

+
EOS assert_equal expected, actual end @@ -1413,6 +1601,18 @@ def test_cmd_caption lineB +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//cmd[cap1]{\nlineA\nlineB\n//}\n") + expected = <<-EOS +
+
lineA
+lineB
+
+

cap1

+
EOS assert_equal expected, actual end @@ -1648,6 +1848,20 @@ def test_column_ref

inside column

+

next level

+

this is コラム「test」.

+EOS + + assert_equal expected, column_helper(review) + + @config['chapterlink'] = nil + expected = <<-EOS +
+ +

test

+

inside column

+
+

next level

this is コラム「test」.

EOS @@ -1663,6 +1877,11 @@ def @chapter.column_index idx end + actual = compile_inline('test @{chap1|column} test2') + expected = 'test コラム「column_cap」 test2' + assert_equal expected, actual + + @config['chapterlink'] = nil actual = compile_inline('test @{chap1|column} test2') expected = 'test コラム「column_cap」 test2' assert_equal expected, actual @@ -1960,8 +2179,6 @@ def test_embed2b end def test_footnote - fn = Book::FootnoteIndex.parse(['//footnote[foo][bar\\a\\$buz]']) - @chapter.instance_eval { @footnote_index = fn } actual = compile_block("//footnote[foo][bar\\a\\$buz]\n") expected = <<-'EOS'

[*1] bar\a\$buz

@@ -1986,8 +2203,6 @@ def test_footnote end def test_footnote_with_tricky_id - fn = Book::FootnoteIndex.parse(['//footnote[123 あ_;][bar\\a\\$buz]']) - @chapter.instance_eval { @footnote_index = fn } actual = compile_block("//footnote[123 あ_;][bar\\a\\$buz]\n") expected = <<-'EOS'

[*1] bar\a\$buz

@@ -1996,48 +2211,61 @@ def test_footnote_with_tricky_id end def test_inline_fn - book = ReVIEW::Book::Base.load - book.catalog = ReVIEW::Catalog.new('CHAPS' => %w[ch1.re]) - io1 = StringIO.new("//footnote[foo][bar]\n") - chap1 = ReVIEW::Book::Chapter.new(book, 1, 'ch1', 'ch1.re', io1) - book.parts = [ReVIEW::Book::Part.new(self, nil, [chap1])] - builder = ReVIEW::HTMLBuilder.new - comp = ReVIEW::Compiler.new(builder) - builder.bind(comp, chap1, nil) - fn = builder.inline_fn('foo') - assert_equal '*1', fn + fn = compile_block("//footnote[foo][bar]\n\n@{foo}\n") + expected = <<-EOS +

[*1] bar

+

*1

+EOS + assert_equal expected, fn I18n.set('html_footnote_refmark', '+%s') - fn = builder.inline_fn('foo') - assert_equal '+1', fn + fn = compile_block("//footnote[foo][bar]\n\n@{foo}\n") + expected = <<-EOS +

[*1] bar

+

+1

+EOS + assert_equal expected, fn end def test_inline_hd - book = ReVIEW::Book::Base.load + book = ReVIEW::Book::Base.new book.catalog = ReVIEW::Catalog.new('CHAPS' => %w[ch1.re ch2.re]) io1 = StringIO.new("= test1\n\nfoo\n\n== test1-1\n\nbar\n\n== test1-2\n\nbar\n\n") io2 = StringIO.new("= test2\n\nfoo\n\n== test2-1\n\nbar\n\n== test2-2\n\nbar\n\n") chap1 = ReVIEW::Book::Chapter.new(book, 1, 'ch1', 'ch1.re', io1) chap2 = ReVIEW::Book::Chapter.new(book, 2, 'ch2', 'ch2.re', io2) - book.parts = [ReVIEW::Book::Part.new(self, nil, [chap1, chap2])] + book.parts = [ReVIEW::Book::Part.new(book, nil, [chap1, chap2])] builder = ReVIEW::HTMLBuilder.new comp = ReVIEW::Compiler.new(builder) builder.bind(comp, chap2, nil) + + chap1.generate_indexes + chap2.generate_indexes + hd = builder.inline_hd('ch1|test1-1') + assert_equal '「1.1 test1-1」', hd + + builder.instance_eval { @book.config['chapterlink'] = nil } hd = builder.inline_hd('ch1|test1-1') assert_equal '「1.1 test1-1」', hd end def test_inline_hd_for_part - book = ReVIEW::Book::Base.load + book = ReVIEW::Book::Base.new book.catalog = ReVIEW::Catalog.new('CHAPS' => %w[ch1.re ch2.re]) io1 = StringIO.new("= test1\n\nfoo\n\n== test1-1\n\nbar\n\n== test1-2\n\nbar\n\n") io2 = StringIO.new("= test2\n\nfoo\n\n== test2-1\n\nbar\n\n== test2-2\n\nbar\n\n") io_p1 = StringIO.new("= part1\n\nfoo\n\n== part1-1\n\nbar\n\n== part1-2\n\nbar\n\n") chap1 = ReVIEW::Book::Chapter.new(book, 1, 'ch1', 'ch1.re', io1) chap2 = ReVIEW::Book::Chapter.new(book, 2, 'ch2', 'ch2.re', io2) - book.parts = [ReVIEW::Book::Part.new(self, 1, [chap1, chap2], 'part1.re', io_p1)] + book.parts = [ReVIEW::Book::Part.new(book, 1, [chap1, chap2], 'part1.re', io_p1)] builder = ReVIEW::HTMLBuilder.new comp = ReVIEW::Compiler.new(builder) builder.bind(comp, chap2, nil) + book.generate_indexes + + hd = builder.inline_hd('part1|part1-1') + assert_equal '「1.1 part1-1」', hd + + builder.instance_eval { @book.config['chapterlink'] = nil } hd = builder.inline_hd('part1|part1-1') assert_equal '「1.1 part1-1」', hd end @@ -2048,7 +2276,12 @@ def test_inline_hd_with_block location = Location.new(nil, nil) @builder.bind(@compiler, chap1, location) hd = @builder.inline_hd('foo') + assert_equal '「1.1 foo」', hd + + @config['chapterlink'] = nil + hd = @builder.inline_hd('foo') assert_equal '「1.1 foo」', hd + hd = @builder.inline_hd('bar') assert_equal '「1.2 bar」', hd end @@ -2074,15 +2307,28 @@ def test_table cccddd<>& +EOS + assert_equal expected, actual + + @config['caption_position']['table'] = 'bottom' + actual = compile_block("//table[foo][FOO]{\naaa\tbbb\n------------\nccc\tddd<>&\n//}\n") + expected = <<-EOS +
+ + + +
aaabbb
cccddd<>&
+

表1.1: FOO

+
EOS assert_equal expected, actual end def test_empty_table - e = assert_raises(ReVIEW::ApplicationError) { compile_block "//table{\n//}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("//table{\n//}\n") } assert_equal ':2: error: no rows in the table', e.message - e = assert_raises(ReVIEW::ApplicationError) { compile_block "//table{\n------------\n//}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("//table{\n------------\n//}\n") } assert_equal ':3: error: no rows in the table', e.message end @@ -2091,6 +2337,10 @@ def @chapter.table(_id) Book::Index::Item.new('sampletable', 1) end actual = compile_block("@{sampletest}\n") + assert_equal %Q(

表1.1

\n), actual + + @config['chapterlink'] = nil + actual = compile_block("@
{sampletest}\n") assert_equal %Q(

表1.1

\n), actual end @@ -2110,6 +2360,25 @@ def test_emtable
cccddd<>&
+EOS + assert_equal expected, actual + + @config['caption_position']['table'] = 'bottom' + actual = compile_block("//emtable[foo]{\naaa\tbbb\n------------\nccc\tddd<>&\n//}\n//emtable{\naaa\tbbb\n------------\nccc\tddd<>&\n//}\n") + expected = <<-EOS +
+ + + +
aaabbb
cccddd<>&
+

foo

+
+
+ + + +
aaabbb
cccddd<>&
+
EOS assert_equal expected, actual end @@ -2127,6 +2396,16 @@ def @chapter.image(_id)

表1.1: test for imgtable

test for imgtable +EOS + assert_equal expected, actual + + @config['caption_position']['table'] = 'bottom' + actual = compile_block("//imgtable[sampleimg][test for imgtable]{\n//}\n") + expected = <<-EOS +
+test for imgtable +

表1.1: test for imgtable

+
EOS assert_equal expected, actual end @@ -2287,6 +2566,163 @@ def test_major_blocks assert_equal expected, actual end + def test_minicolumn_blocks + %w[note memo tip info warning important caution notice].each do |type| + src = <<-EOS +//#{type}[#{type}1]{ + +//} + +//#{type}[#{type}2]{ +//} +EOS + + expected = <<-EOS +
+

#{type}1

+
+
+

#{type}2

+
+EOS + assert_equal expected, compile_block(src) + + src = <<-EOS +//#{type}[#{type}2]{ + +//} + +//#{type}[#{type}3]{ + +//} + +//#{type}[#{type}4]{ + +//} + +//#{type}[#{type}5]{ + +//} + +//#{type}[#{type}6]{ + +//} +EOS + + expected = <<-EOS +
+

#{type}2

+
+
+

#{type}3

+
+
+

#{type}4

+
+
+

#{type}5

+
+
+

#{type}6

+
+EOS + assert_equal expected, compile_block(src) + + src = <<-EOS +//#{type}{ + + * A + + 1. B + +//} + +//#{type}[OMITEND1]{ + +//emlist{ +LIST +//} + +//} + +//#{type}[OMITEND2]{ +//} +EOS + + expected = <<-EOS +
+
    +
  • A
  • +
+
    +
  1. B
  2. +
+
+
+

OMITEND1

+
+
LIST
+
+
+
+
+

OMITEND2

+
+EOS + assert_equal expected, compile_block(src) + end + end + + def test_minicolumn_blocks_nest_error1 + %w[note memo tip info warning important caution notice].each do |type| + @builder.doc_status.clear + src = <<-EOS +//#{type}{ + +//#{type}{ +//} + +//} +EOS + e = assert_raises(ReVIEW::ApplicationError) { compile_block(src) } + assert_match(/minicolumn cannot be nested:/, e.message) + end + end + + def test_minicolumn_blocks_nest_error2 + %w[note memo tip info warning important caution notice].each do |type| + @builder.doc_status.clear + src = <<-EOS +//#{type}{ + +//#{type}{ + +//} + +//} +EOS + e = assert_raises(ReVIEW::ApplicationError) { compile_block(src) } + assert_match(/minicolumn cannot be nested:/, e.message) + end + end + + def test_minicolumn_blocks_nest_error3 + %w[memo tip info warning important caution notice].each do |type| + @builder.doc_status.clear + src = <<-EOS +//#{type}{ + +//note{ + +//} + +//} +EOS + e = assert_raises(ReVIEW::ApplicationError) { compile_block(src) } + assert_match(/minicolumn cannot be nested:/, e.message) + end + end + def test_comment actual = compile_block('//comment[コメント]') assert_equal '', actual @@ -2334,18 +2770,18 @@ def test_inline_w end def test_inline_unknown - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@{n}\n") } assert_equal ':1: error: unknown image: n', e.message - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@{n}\n") } assert_equal ':1: error: unknown footnote: n', e.message - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@{n}\n") } assert_equal ':1: error: unknown headline: n', e.message %w[list table column].each do |name| - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@<#{name}>{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@<#{name}>{n}\n") } assert_equal ":1: error: unknown #{name}: n", e.message end %w[chap chapref title].each do |name| - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@<#{name}>{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@<#{name}>{n}\n") } assert_equal ':1: error: key not found: "n"', e.message end end @@ -2375,6 +2811,20 @@ def test_texequation_with_caption //} EOS expected = <<-EOS +

式1.1

+
+

式1.1: The Equivalence of Mass and Energy

+
+
e=mc^2
+
+
+
+EOS + actual = compile_block(src) + assert_equal expected, actual + + @config['chapterlink'] = nil + expected = <<-EOS

式1.1

式1.1: The Equivalence of Mass and Energy

@@ -2383,6 +2833,20 @@ def test_texequation_with_caption
+EOS + actual = compile_block(src) + assert_equal expected, actual + + @config['caption_position']['equation'] = 'bottom' + expected = <<-EOS +

式1.1

+
+
+
e=mc^2
+
+
+

式1.1: The Equivalence of Mass and Energy

+
EOS actual = compile_block(src) assert_equal expected, actual diff --git a/test/test_htmlutils.rb b/test/test_htmlutils.rb index 881b56f2a..4215c1d35 100644 --- a/test/test_htmlutils.rb +++ b/test/test_htmlutils.rb @@ -17,18 +17,6 @@ def test_unescape_html assert_equal '&', unescape('&amp;') end - def test_escape_html_ex - keys = ESC.keys - ESC['.'] = 'X' - ESC.each_pair do |ch, ref| - if keys.include?(ch) - assert_equal ref, escape(ch) - else - assert_equal ch, escape(ch) - end - end - end - def test_strip_html assert_equal 'thisistest.', strip_html('

thisistest

.') end diff --git a/test/test_i18n.rb b/test/test_i18n.rb index 4ea00846e..90c35d080 100644 --- a/test/test_i18n.rb +++ b/test/test_i18n.rb @@ -9,7 +9,7 @@ def test_load_locale_yml Dir.mktmpdir do |dir| Dir.chdir(dir) do file = File.join(dir, 'locale.yml') - File.open(file, 'w') { |f| f.write %Q(locale: ja\nfoo: "bar"\n) } + File.write(file, %Q(locale: ja\nfoo: "bar"\n)) I18n.setup assert_equal 'bar', I18n.t('foo') end @@ -20,7 +20,7 @@ def test_load_locale_yaml Dir.mktmpdir do |dir| Dir.chdir(dir) do file = File.join(dir, 'locale.yaml') - File.open(file, 'w') { |f| f.write %Q(locale: ja\nfoo: "bar"\n) } + File.write(file, %Q(locale: ja\nfoo: "bar"\n)) assert_raise ReVIEW::ConfigError do I18n.setup end @@ -32,7 +32,7 @@ def test_load_foo_yaml Dir.mktmpdir do |dir| Dir.chdir(dir) do file = File.join(dir, 'foo.yml') - File.open(file, 'w') { |f| f.write %Q(locale: ja\nfoo: "bar"\n) } + File.write(file, %Q(locale: ja\nfoo: "bar"\n)) I18n.setup('ja', 'foo.yml') assert_equal 'bar', I18n.t('foo') end @@ -43,7 +43,7 @@ def test_update_foo_yaml Dir.mktmpdir do |dir| Dir.chdir(dir) do file = File.join(dir, 'foo.yml') - File.open(file, 'w') { |f| f.write %Q(locale: ja\nfoo: "bar"\n) } + File.write(file, %Q(locale: ja\nfoo: "bar"\n)) i18n = ReVIEW::I18n.new('ja') i18n.update_localefile(File.join(Dir.pwd, 'foo.yml')) assert_equal 'bar', i18n.t('foo') @@ -55,7 +55,7 @@ def test_update_foo_yaml_i18nclass Dir.mktmpdir do |dir| Dir.chdir(dir) do file = File.join(dir, 'foo.yml') - File.open(file, 'w') { |f| f.write %Q(locale: ja\nfoo: "bar"\n) } + File.write(file, %Q(locale: ja\nfoo: "bar"\n)) I18n.setup('ja', 'foo.yml') assert_equal 'bar', I18n.t('foo') end @@ -66,7 +66,7 @@ def test_load_locale_yml_i18n Dir.mktmpdir do |dir| Dir.chdir(dir) do file = File.join(dir, 'locale.yml') - File.open(file, 'w') { |f| f.write %Q(ja:\n foo: "bar"\nen:\n foo: "buz"\n) } + File.write(file, %Q(ja:\n foo: "bar"\nen:\n foo: "buz"\n)) I18n.setup assert_equal 'bar', I18n.t('foo') assert_equal '図', I18n.t('image') @@ -81,7 +81,7 @@ def test_load_locale_invalid_yml Dir.mktmpdir do |dir| Dir.chdir(dir) do file = File.join(dir, 'locale.yml') - File.open(file, 'w') { |f| f.write %Q(local: ja\nfoo: "bar"\n) } + File.write(file, %Q(local: ja\nfoo: "bar"\n)) assert_raises(ReVIEW::KeyError) do I18n.setup end @@ -93,59 +93,59 @@ def test_custom_format Dir.mktmpdir do |dir| Dir.chdir(dir) do file = File.join(dir, 'locale.yml') - File.open(file, 'w') { |f| f.write "locale: ja\nchapter: 第%pa章" } + File.write(file, "locale: ja\nchapter: 第%pa章") I18n.setup('ja') assert_equal '第a章', I18n.t('chapter', 1) - File.open(file, 'w') { |f| f.write "locale: ja\nchapter: 第%pA章" } + File.write(file, "locale: ja\nchapter: 第%pA章") I18n.setup('ja') assert_equal '第B章', I18n.t('chapter', 2) - File.open(file, 'w') { |f| f.write "locale: ja\nchapter: 第%pAW章" } + File.write(file, "locale: ja\nchapter: 第%pAW章") I18n.setup('ja') assert_equal '第B章', I18n.t('chapter', 2) - File.open(file, 'w') { |f| f.write "locale: ja\nchapter: 第%paW章" } + File.write(file, "locale: ja\nchapter: 第%paW章") I18n.setup('ja') assert_equal '第b章', I18n.t('chapter', 2) - File.open(file, 'w') { |f| f.write "locale: ja\nchapter: 第%pR章" } + File.write(file, "locale: ja\nchapter: 第%pR章") I18n.setup('ja') assert_equal '第I章', I18n.t('chapter', 1) - File.open(file, 'w') { |f| f.write "locale: ja\nchapter: 第%pr章" } + File.write(file, "locale: ja\nchapter: 第%pr章") I18n.setup('ja') assert_equal '第ii章', I18n.t('chapter', 2) - File.open(file, 'w') { |f| f.write "locale: ja\nchapter: 第%pRW章" } + File.write(file, "locale: ja\nchapter: 第%pRW章") I18n.setup('ja') assert_equal '第Ⅻ章', I18n.t('chapter', 12) - File.open(file, 'w') { |f| f.write "locale: ja\nchapter: 第%pJ章" } + File.write(file, "locale: ja\nchapter: 第%pJ章") I18n.setup('ja') assert_equal '第二十七章', I18n.t('chapter', 27) - File.open(file, 'w') { |f| f.write "locale: ja\nchapter: 第%pdW章" } + File.write(file, "locale: ja\nchapter: 第%pdW章") I18n.setup('ja') assert_equal '第1章', I18n.t('chapter', 1) - File.open(file, 'w') { |f| f.write "locale: ja\nchapter: 第%pdW章" } + File.write(file, "locale: ja\nchapter: 第%pdW章") I18n.setup('ja') assert_equal '第27章', I18n.t('chapter', 27) - File.open(file, 'w') { |f| f.write "locale: ja\nchapter: 第%pDW章" } + File.write(file, "locale: ja\nchapter: 第%pDW章") I18n.setup('ja') assert_equal '第1章', I18n.t('chapter', 1) - File.open(file, 'w') { |f| f.write "locale: ja\nchapter: 第%pDW章" } + File.write(file, "locale: ja\nchapter: 第%pDW章") I18n.setup('ja') assert_equal '第27章', I18n.t('chapter', 27) - File.open(file, 'w') { |f| f.write "locale: ja\npart: Part %pRW" } + File.write(file, "locale: ja\npart: Part %pRW") I18n.setup('ja') assert_equal 'Part 0', I18n.t('part', 0) - File.open(file, 'w') { |f| f.write "locale: ja\npart: 第%pJ部" } + File.write(file, "locale: ja\npart: 第%pJ部") I18n.setup('ja') assert_equal '第一部', I18n.t('part', 1) end @@ -157,23 +157,23 @@ def test_custom_format_numbers Dir.chdir(dir) do file = File.join(dir, 'locale.yml') - File.open(file, 'w') { |f| f.write %Q(locale: ja\nformat_number_header: "%s-%pA:") } + File.write(file, %Q(locale: ja\nformat_number_header: "%s-%pA:")) I18n.setup('ja') assert_equal '1-B:', I18n.t('format_number_header', [1, 2]) - File.open(file, 'w') { |f| f.write %Q(locale: ja\nformat_number_header: "%s.%pa:") } + File.write(file, %Q(locale: ja\nformat_number_header: "%s.%pa:")) I18n.setup('ja') assert_equal '2.c:', I18n.t('format_number_header', [2, 3]) - File.open(file, 'w') { |f| f.write %Q(locale: ja\nformat_number_header: "%pA,%pAW:") } + File.write(file, %Q(locale: ja\nformat_number_header: "%pA,%pAW:")) I18n.setup('ja') assert_equal 'C,D:', I18n.t('format_number_header', [3, 4]) - File.open(file, 'w') { |f| f.write %Q(locale: ja\nformat_number_header: "%pJ・%pJ:") } + File.write(file, %Q(locale: ja\nformat_number_header: "%pJ・%pJ:")) I18n.setup('ja') assert_equal '十二・二十六:', I18n.t('format_number_header', [12, 26]) - File.open(file, 'w') { |f| f.write %Q(locale: ja\nformat_number_header: "%pdW―%pdW:") } + File.write(file, %Q(locale: ja\nformat_number_header: "%pdW―%pdW:")) I18n.setup('ja') assert_equal '3―12:', I18n.t('format_number_header', [3, 12]) end @@ -185,11 +185,11 @@ def test_format_with_mismatched_number_of_arguments Dir.chdir(dir) do file = File.join(dir, 'locale.yml') - File.open(file, 'w') { |f| f.write %Q(locale: ja\nformat_number_header: "%2$d") } + File.write(file, %Q(locale: ja\nformat_number_header: "%2$d")) I18n.setup('ja') assert_equal '10', I18n.t('format_number_header', [1, 10]) - File.open(file, 'w') { |f| f.write %Q(locale: ja\nformat_number_header: "%2$d-%1$d") } + File.write(file, %Q(locale: ja\nformat_number_header: "%2$d-%1$d")) I18n.setup('ja') # ERROR: returns raw format assert_equal '%2$d-%1$d', I18n.t('format_number_header', [1]) @@ -238,11 +238,11 @@ def test_htmlbuilder def _setup_htmlbuilder I18n.setup('en') @builder = HTMLBuilder.new - @config = ReVIEW::Configure[ - 'secnolevel' => 2, # for IDGXMLBuilder, HTMLBuilder - 'stylesheet' => nil, # for HTMLBuilder - 'ext' => '.re' - ] + @config = ReVIEW::Configure.values.merge( + { 'secnolevel' => 2, # for IDGXMLBuilder, HTMLBuilder + 'stylesheet' => nil, # for HTMLBuilder + 'ext' => '.re' } + ) @book = Book::Base.new('.') @book.config = @config @compiler = ReVIEW::Compiler.new(@builder) diff --git a/test/test_idgxmlbuilder.rb b/test/test_idgxmlbuilder.rb index 724fced9e..c6153eb9f 100644 --- a/test/test_idgxmlbuilder.rb +++ b/test/test_idgxmlbuilder.rb @@ -100,6 +100,27 @@ def test_inline_in_table_without_header_and_cellwidth assert_equal %Q(1\t23\t4<>&
), actual end + def test_table + actual = compile_block("//table{\naaa\tbbb\n------------\nccc\tddd<>&\n//}\n") + expected = <<-EOS.chomp +
aaabbbcccddd<>&
+EOS + assert_equal expected, actual + + actual = compile_block("//table[foo][FOO]{\naaa\tbbb\n------------\nccc\tddd<>&\n//}\n") + expected = <<-EOS.chomp +
表1.1 FOO
aaabbbcccddd<>&
+EOS + assert_equal expected, actual + + @config['caption_position']['table'] = 'bottom' + actual = compile_block("//table[foo][FOO]{\naaa\tbbb\n------------\nccc\tddd<>&\n//}\n") + expected = <<-EOS.chomp +
aaabbbcccddd<>&
表1.1 FOO
+EOS + assert_equal expected, actual + end + def test_customize_cellwidth actual = compile_block("//tsize[2,3,5]\n//table{\nA\tB\tC\n//}\n") assert_equal %Q(
ABC
), actual @@ -134,16 +155,20 @@ def test_customize_mmtopt end def test_empty_table - e = assert_raises(ReVIEW::ApplicationError) { compile_block "//table{\n//}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("//table{\n//}\n") } assert_equal ':2: error: no rows in the table', e.message - e = assert_raises(ReVIEW::ApplicationError) { compile_block "//table{\n------------\n//}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("//table{\n------------\n//}\n") } assert_equal ':3: error: no rows in the table', e.message end def test_emtable actual = compile_block("//emtable[foo]{\nA\n//}\n//emtable{\nA\n//}") assert_equal %Q(
foo
A
A
), actual + + @config['caption_position']['table'] = 'bottom' + actual = compile_block("//emtable[foo]{\nA\n//}\n//emtable{\nA\n//}") + assert_equal %Q(
A
foo
A
), actual end def test_table_row_separator @@ -227,9 +252,7 @@ def test_dlist_beforeulol end def test_dt_inline - fn = Book::FootnoteIndex.parse(['//footnote[bar][bar]']) - @chapter.instance_eval { @footnote_index = fn } - actual = compile_block(" : foo@{bar}[]<>&@$\\alpha[]$\n") + actual = compile_block("//footnote[bar][bar]\n\n : foo@{bar}[]<>&@$\\alpha[]$\n") expected = <<-EOS.chomp
foobar[]<>&
\\alpha[]
@@ -299,6 +322,145 @@ def test_major_blocks assert_equal expected, actual end + def test_minicolumn_blocks + %w[note memo tip info warning important caution notice].each do |type| + src = <<-EOS +//#{type}[#{type}1]{ + +//} + +//#{type}[#{type}2]{ +//} +EOS + + if type == 'notice' # exception pattern + expected = <<-EOS.chomp +<#{type}-t>#{type}1<#{type}-t>#{type}2 +EOS + else + expected = <<-EOS.chomp +<#{type}>#{type}1<#{type}>#{type}2 +EOS + end + assert_equal expected, compile_block(src) + + src = <<-EOS +//#{type}[#{type}2]{ + +//} + +//#{type}[#{type}3]{ + +//} + +//#{type}[#{type}4]{ + +//} + +//#{type}[#{type}5]{ + +//} + +//#{type}[#{type}6]{ + +//} +EOS + + if type == 'notice' # exception pattern + expected = <<-EOS.chomp +<#{type}-t>#{type}2<#{type}-t>#{type}3<#{type}-t>#{type}4<#{type}-t>#{type}5<#{type}-t>#{type}6 +EOS + else + expected = <<-EOS.chomp +<#{type}>#{type}2<#{type}>#{type}3<#{type}>#{type}4<#{type}>#{type}5<#{type}>#{type}6 +EOS + end + assert_equal expected, compile_block(src) + + src = <<-EOS +//#{type}{ + + * A + + 1. B + +//} + +//#{type}[OMITEND1]{ + +//emlist{ +LIST +//} + +//} +//#{type}[OMITEND2]{ +//} +EOS + + if type == 'notice' # exception pattern + expected = <<-EOS.chomp +<#{type}>
  • A
  1. B
<#{type}-t>OMITEND1
LIST
+
<#{type}-t>OMITEND2 +EOS + else + expected = <<-EOS.chomp +<#{type}>
  • A
  1. B
<#{type}>OMITEND1
LIST
+
<#{type}>OMITEND2 +EOS + end + assert_equal expected, compile_block(src) + end + end + + def test_minicolumn_blocks_nest_error1 + %w[note memo tip info warning important caution notice].each do |type| + @builder.doc_status.clear + src = <<-EOS +//#{type}{ + +//#{type}{ +//} + +//} +EOS + e = assert_raises(ReVIEW::ApplicationError) { compile_block(src) } + assert_match(/minicolumn cannot be nested:/, e.message) + end + end + + def test_minicolumn_blocks_nest_error2 + %w[note memo tip info warning important caution notice].each do |type| + @builder.doc_status.clear + src = <<-EOS +//#{type}{ + +//#{type}{ + +//} + +//} +EOS + e = assert_raises(ReVIEW::ApplicationError) { compile_block(src) } + assert_match(/minicolumn cannot be nested:/, e.message) + end + end + + def test_minicolumn_blocks_nest_error3 + %w[memo tip info warning important caution notice].each do |type| + @builder.doc_status.clear + src = <<-EOS +//#{type}{ + +//note{ +//} + +//} +EOS + e = assert_raises(ReVIEW::ApplicationError) { compile_block(src) } + assert_match(/minicolumn cannot be nested:/, e.message) + end + end + def test_term actual = compile_block("//term{\ntest1\ntest1.5\n\ntest@{2}\n//}\n") assert_equal '

test1test1.5

test2

', actual @@ -329,11 +491,19 @@ def test_point_without_caption def test_emlist actual = compile_block("//emlist[this is @{test}<&>_]{\ntest1\ntest1.5\n\ntest@{2}\n//}\n") assert_equal %Q(this is test<&>_
test1\ntest1.5\n\ntest2\n
), actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//emlist[this is @{test}<&>_]{\ntest1\ntest1.5\n\ntest@{2}\n//}\n") + assert_equal %Q(
test1\ntest1.5\n\ntest2\n
this is test<&>_
), actual end def test_emlistnum actual = compile_block("//emlistnum[this is @{test}<&>_]{\ntest1\ntest1.5\n\ntest@{2}\n//}\n") assert_equal %Q(this is test<&>_
 1: test1\n 2: test1.5\n 3: \n 4: test2\n
), actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//emlistnum[this is @{test}<&>_]{\ntest1\ntest1.5\n\ntest@{2}\n//}\n") + assert_equal %Q(
 1: test1\n 2: test1.5\n 3: \n 4: test2\n
this is test<&>_
), actual end def test_emlist_listinfo @@ -385,6 +555,17 @@ def @chapter.list(_id) test2 +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//list[samplelist][this is @{test}<&>_]{\ntest1\ntest1.5\n\ntest@{2}\n//}\n") + expected = <<-EOS.chomp +
test1
+test1.5
+
+test2
+
リスト1.1 this is test<&>_
EOS assert_equal expected, actual end @@ -400,6 +581,17 @@ def @chapter.list(_id) 3: 4: test2 +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//listnum[samplelist][this is @{test}<&>_]{\ntest1\ntest1.5\n\ntest@{2}\n//}\n") + expected = <<-EOS.chomp +
 1: test1
+ 2: test1.5
+ 3: 
+ 4: test2
+
リスト1.1 this is test<&>_
EOS assert_equal expected, actual end @@ -415,6 +607,17 @@ def @chapter.list(_id) 102: 103: test2 +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//firstlinenum[100]\n//listnum[samplelist][this is @{test}<&>_]{\ntest1\ntest1.5\n\ntest@{2}\n//}\n") + expected = <<-EOS.chomp +
100: test1
+101: test1.5
+102: 
+103: test2
+
リスト1.1 this is test<&>_
EOS assert_equal expected, actual end @@ -431,6 +634,17 @@ def @chapter.list(_id) test2 +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//list[samplelist][this is @{test}<&>_]{\ntest1\ntest1.5\n\ntest@{2}\n//}\n") + expected = <<-EOS.chomp +
test1
+test1.5
+
+test2
+
リスト1.1 this is test<&>_
EOS assert_equal expected, actual end @@ -449,6 +663,15 @@ def test_cmd cap1
lineA
 lineB
 
+EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//cmd[cap1]{\nlineA\nlineB\n//}\n") + expected = <<-EOS.chomp +
lineA
+lineB
+
cap1
EOS assert_equal expected, actual end @@ -461,6 +684,17 @@ def test_source buz +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//source[foo/bar/test.rb]{\nfoo\nbar\n\nbuz\n//}\n") + expected = <<-EOS.chomp +
foo
+bar
+
+buz
+
foo/bar/test.rb EOS assert_equal expected, actual end @@ -498,6 +732,17 @@ def test_insn test2 +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//insn[this is @{test}<&>_]{\ntest1\ntest1.5\n\ntest@{2}\n//}\n") + expected = <<-EOS.chomp +test1 +test1.5 + +test2 +this is test<&>_ EOS assert_equal expected, actual end @@ -511,6 +756,17 @@ def test_box test2 +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//box[this is @{test}<&>_]{\ntest1\ntest1.5\n\ntest@{2}\n//}\n") + expected = <<-EOS.chomp +test1 +test1.5 + +test2 +this is test<&>_ EOS assert_equal expected, actual end @@ -524,6 +780,17 @@ def test_box_non_listinfo test2 +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//box[this is @{test}<&>_]{\ntest1\ntest1.5\n\ntest@{2}\n//}\n") + expected = <<-EOS.chomp +test1 +test1.5 + +test2 +this is test<&>_ EOS assert_equal expected, actual end @@ -569,6 +836,10 @@ def @chapter.image(_id) actual = compile_block("//image[sampleimg][sample photo]{\n//}\n") assert_equal %Q(図1.1 sample photo), actual + + @config['caption_position']['image'] = 'top' + actual = compile_block("//image[sampleimg][sample photo]{\n//}\n") + assert_equal %Q(図1.1 sample photo), actual end def test_image_with_metric @@ -602,6 +873,10 @@ def @chapter.image(_id) actual = compile_block("//indepimage[sampleimg][sample photo]\n") assert_equal %Q(sample photo), actual + + @config['caption_position']['image'] = 'top' + actual = compile_block("//indepimage[sampleimg][sample photo]\n") + assert_equal %Q(sample photo), actual end def test_indepimage_without_caption @@ -709,10 +984,16 @@ def test_column_ref this is @{foo}. EOS expected = <<-EOS.chomp -test

inside column

next level

this is コラム「test」.

+test

inside column

next level

this is コラム「test」.

EOS assert_equal expected, column_helper(review) + + @config['chapterlink'] = nil + expected = <<-EOS.chomp +test

inside column

next level

this is コラム「test」.

+EOS + assert_equal expected, column_helper(review) end def test_column_in_aother_chapter_ref @@ -723,6 +1004,11 @@ def @chapter.column_index idx end + actual = compile_inline('test @{chap1|column} test2') + expected = 'test コラム「column_cap」 test2' + assert_equal expected, actual + + @config['chapterlink'] = nil actual = compile_inline('test @{chap1|column} test2') expected = 'test コラム「column_cap」 test2' assert_equal expected, actual @@ -832,18 +1118,18 @@ def test_ol end def test_inline_unknown - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@{n}\n") } assert_equal ':1: error: unknown image: n', e.message - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@{n}\n") } assert_equal ':1: error: unknown footnote: n', e.message - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@{n}\n") } assert_equal ':1: error: unknown headline: n', e.message %w[list table column].each do |name| - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@<#{name}>{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@<#{name}>{n}\n") } assert_equal ":1: error: unknown #{name}: n", e.message end %w[chap chapref title].each do |name| - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@<#{name}>{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@<#{name}>{n}\n") } assert_equal ':1: error: key not found: "n"', e.message end end @@ -972,6 +1258,11 @@ def test_texequation_with_caption expected = %Q(

式1.1

式1.1 The Equivalence of Mass and Energy
e=mc^2
) actual = compile_block(src) assert_equal expected, actual + + @config['caption_position']['equation'] = 'bottom' + expected = %Q(

式1.1

e=mc^2
式1.1 The Equivalence of Mass and Energy
) + actual = compile_block(src) + assert_equal expected, actual end def test_nest_error_close1 diff --git a/test/test_idgxmlmaker_cmd.rb b/test/test_idgxmlmaker_cmd.rb index 5a6e60d03..5160342e7 100644 --- a/test/test_idgxmlmaker_cmd.rb +++ b/test/test_idgxmlmaker_cmd.rb @@ -29,7 +29,7 @@ def common_buildidgxml(bookdir, configfile, targetfile, option) ruby_cmd = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name']) + RbConfig::CONFIG['EXEEXT'] Dir.chdir(@tmpdir1) do _o, e, s = Open3.capture3("#{ruby_cmd} -S #{REVIEW_IDGXMLMAKER} #{option} #{configfile}") - STDERR.puts e unless e.empty? + assert_equal '', e assert s.success? end assert File.exist?(File.join(@tmpdir1, targetfile)) diff --git a/test/test_index.rb b/test/test_index.rb index c14416787..4f0922ead 100644 --- a/test/test_index.rb +++ b/test/test_index.rb @@ -1,11 +1,28 @@ require 'test_helper' +require 'review/compiler' require 'review/book' require 'review/book/index' +require 'review/topbuilder' +require 'review/i18n' class IndexTest < Test::Unit::TestCase include ReVIEW + + def setup + @builder = TOPBuilder.new + @config = ReVIEW::Configure.create(config: { 'secnolevel' => 2, 'language' => 'ja' }) + @book = Book::Base.new(config: @config) + @compiler = ReVIEW::Compiler.new(@builder) + @chapter = Book::Chapter.new(@book, 1, '-', nil, StringIO.new) + location = Location.new(nil, nil) + @builder.bind(@compiler, @chapter, location) + + I18n.setup(@config['language']) + end + def test_footnote_index - fn = Book::FootnoteIndex.parse(['//footnote[foo][bar]']) + compile_block("//footnote[foo][bar]\n") + fn = @chapter.footnote_index items = fn.to_a item = items[0] assert_equal 'foo', item.id @@ -13,7 +30,8 @@ def test_footnote_index end def test_footnote_index_with_escape - fn = Book::FootnoteIndex.parse(['//footnote[foo][bar[\]buz]']) + compile_block('//footnote[foo][bar[\]buz]' + "\n") + fn = @chapter.footnote_index items = fn.to_a item = items[0] assert_equal 'foo', item.id @@ -21,7 +39,8 @@ def test_footnote_index_with_escape end def test_footnote_index_with_escape2 - fn = Book::FootnoteIndex.parse(['//footnote[foo][bar\\a\\$buz]']) + compile_block('//footnote[foo][bar\\a\\$buz]' + "\n") + fn = @chapter.footnote_index items = fn.to_a item = items[0] assert_equal 'foo', item.id @@ -29,7 +48,8 @@ def test_footnote_index_with_escape2 end def test_footnote_index_key? - fn = Book::FootnoteIndex.parse(['//footnote[foo][bar]']) + compile_block('//footnote[foo][bar]' + "\n") + fn = @chapter.footnote_index assert_equal true, fn.key?('foo') ## for compatibility @@ -38,7 +58,7 @@ def test_footnote_index_key? # rubocop:enable Style/PreferredHashMethods end - def test_headeline_index + def test_headline_index src = <<-EOB = chap1 == sec1-1 @@ -52,14 +72,13 @@ def test_headeline_index == sec1-3 ==== sec1-3-0-1 EOB - book = Book::Base.load - chap = Book::Chapter.new(book, 1, '-', nil) # dummy - index = Book::HeadlineIndex.parse(src.lines.to_a, chap) + compile_block(src) + index = @chapter.headline_index assert_equal [2, 2], index['sec1-2|sec1-2-2'].number assert_equal '1.2.2', index.number('sec1-2|sec1-2-2') end - def test_headeline_index2 + def test_headline_index2 src = <<-EOB = chap1 == sec1-1 @@ -69,14 +88,13 @@ def test_headeline_index2 == sec1-3 === sec1-3-1 EOB - book = Book::Base.load - chap = Book::Chapter.new(book, 1, '-', nil) # dummy - index = Book::HeadlineIndex.parse(src.lines, chap) + compile_block(src) + index = @chapter.headline_index assert_equal [3, 1], index['sec1-3|sec1-3-1'].number assert_equal '1.3.1', index.number('sec1-3|sec1-3-1') end - def test_headeline_index3 + def test_headline_index3 src = <<-EOB = chap1 == sec1-1 @@ -87,9 +105,8 @@ def test_headeline_index3 == sec1-3 === sec1-3-1 EOB - book = Book::Base.load - chap = Book::Chapter.new(book, 1, '-', nil) # dummy - index = Book::HeadlineIndex.parse(src.lines.to_a, chap) + compile_block(src) + index = @chapter.headline_index assert_equal [2, 2], index['sec1-2|sec1-2-2'].number assert_equal '1.2.2', index.number('sec1-2|sec1-2-2') @@ -97,7 +114,7 @@ def test_headeline_index3 assert_equal '1.3.1', index.number('sec1-3|sec1-3-1') end - def test_headeline_index4 + def test_headline_index4 src = <<-EOB = chap1 ====[column] c1 @@ -106,14 +123,13 @@ def test_headeline_index4 === sec1-2-1 === sec1-2-2 EOB - book = Book::Base.load - chap = Book::Chapter.new(book, 1, '-', nil) # dummy - index = Book::HeadlineIndex.parse(src.lines.to_a, chap) + compile_block(src) + index = @chapter.headline_index assert_equal [2, 2], index['sec1-2|sec1-2-2'].number assert_equal '1.2.2', index.number('sec1-2|sec1-2-2') end - def test_headeline_index5 + def test_headline_index5 src = <<-EOB = chap1 ====[column] c1 @@ -122,14 +138,13 @@ def test_headeline_index5 === sec1-2-1 === sec1-2-2 EOB - book = Book::Base.load - chap = Book::Chapter.new(book, 1, '-', nil) # dummy - index = Book::HeadlineIndex.parse(src.lines.to_a, chap) + compile_block(src) + index = @chapter.headline_index assert_equal [2, 2], index['sec1-2-2'].number assert_equal '1.2.2', index.number('sec1-2-2') end - def test_headeline_index6 + def test_headline_index6 src = <<-EOB = chap1 == sec1 @@ -137,34 +152,33 @@ def test_headeline_index6 == sec2 EOB - book = Book::Base.load - chap = Book::Chapter.new(book, 1, '-', nil) # dummy - index = Book::HeadlineIndex.parse(src.lines.to_a, chap) + compile_block(src) + index = @chapter.headline_index assert_equal [1, 1], index['target'].number assert_equal '1.1.1', index.number('target') end - def test_headeline_index7 + def test_headline_index7 src = <<-EOB = chap1 == sec1 === target ^-- dummy target + == sec2 === target ^-- real target but it cannot be detected, because there is another one. EOB - book = Book::Base.load - chap = Book::Chapter.new(book, 1, '-', nil) # dummy - index = Book::HeadlineIndex.parse(src.lines.to_a, chap) + compile_block(src) + index = @chapter.headline_index assert_raise ReVIEW::KeyError do assert_equal [1, 1], index['target'].number end end - def test_headeline_index8 + def test_headline_index8 src = <<-EOB = chap1 == sec1 @@ -172,13 +186,13 @@ def test_headeline_index8 ==== sec1-1-1 EOB - book = Book::Base.load - chap = Book::Chapter.new(book, 1, '-', nil) - index = Book::HeadlineIndex.parse(src.lines.to_a, chap) + compile_block(src) + index = @chapter.headline_index + assert_equal '1.1.1', index.number('sec1-1') end - def test_headeline_index9 + def test_headline_index9 src = <<-EOB = chap1 == sec1 @@ -188,13 +202,12 @@ def test_headeline_index9 ==== sec1-1-1 === sec1-2 EOB - book = Book::Base.load - chap = Book::Chapter.new(book, 1, '-', nil) - index = Book::HeadlineIndex.parse(src.lines.to_a, chap) + compile_block(src) + index = @chapter.headline_index assert_equal [1, 1, 1], index['sec1-1-1'].number end - def test_headeline_index10 + def test_headline_index10 src = <<-EOB = chap1 == sec1 @@ -203,13 +216,12 @@ def test_headeline_index10 ==== sec1-1-1 === sec1-2 EOB - book = Book::Base.load - chap = Book::Chapter.new(book, 1, '-', nil) - index = Book::HeadlineIndex.parse(src.lines.to_a, chap) + compile_block(src) + index = @chapter.headline_index assert_equal [1, 1, 1], index['sec1-1-1'].number end - def test_headeline_index11 + def test_headline_index11 src = <<-EOB = chap1 ==[nodisp] sec01 @@ -220,9 +232,8 @@ def test_headeline_index11 ==[nonum] sec03 == sec04 EOB - book = Book::Base.load - chap = Book::Chapter.new(book, 1, '-', nil) - index = Book::HeadlineIndex.parse(src.lines.to_a, chap) + compile_block(src) + index = @chapter.headline_index assert_equal nil, index['sec01'].number assert_equal nil, index['sec02'].number assert_equal [1], index['sec1'].number @@ -232,7 +243,7 @@ def test_headeline_index11 assert_equal [2], index['sec04'].number end - def test_headeline_index12 + def test_headline_index12 src = <<-EOB = chap1 == A @@ -240,9 +251,8 @@ def test_headeline_index12 ==[nonum] B === B2 EOB - book = Book::Base.load - chap = Book::Chapter.new(book, 1, '-', nil) - index = Book::HeadlineIndex.parse(src.lines.to_a, chap) + compile_block(src) + index = @chapter.headline_index assert_equal [1], index['A'].number assert_equal [1, 1], index['A2'].number assert_equal nil, index['B'].number diff --git a/test/test_latexbuilder.rb b/test/test_latexbuilder.rb index 56c4c5f0b..b349066b7 100644 --- a/test/test_latexbuilder.rb +++ b/test/test_latexbuilder.rb @@ -255,6 +255,10 @@ def @chapter.headline_index @config['secnolevel'] = 3 actual = compile_inline('test @{chap1|test} test2') + assert_equal 'test \reviewsecref{「1.1.1 te\\textunderscore{}st」}{sec:1-1-1} test2', actual + + @config['chapterlink'] = nil + actual = compile_inline('test @{chap1|test} test2') assert_equal 'test 「1.1.1 te\\textunderscore{}st」 test2', actual end @@ -389,9 +393,7 @@ def test_dlist_beforeulol end def test_dt_inline - fn = Book::FootnoteIndex.parse(['//footnote[bar][bar]']) - @chapter.instance_eval { @footnote_index = fn } - actual = compile_block(" : foo@{bar}[]<>&@$\\alpha[]$\n") + actual = compile_block("//footnote[bar][bar]\n\n : foo@{bar}[]<>&@$\\alpha[]$\n") expected = <<-EOS @@ -432,6 +434,22 @@ def test_cmd_caption buz \\end{reviewcmd} \\end{reviewlistblock} +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//cmd[cap1]{\nfoo\nbar\n\nbuz\n//}\n") + expected = <<-EOS + +\\begin{reviewlistblock} +\\begin{reviewcmd} +foo +bar + +buz +\\end{reviewcmd} +\\reviewcmdcaption{cap1} +\\end{reviewlistblock} EOS assert_equal expected, actual end @@ -507,6 +525,22 @@ def test_emlist_caption buz \\end{reviewemlist} \\end{reviewlistblock} +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//emlist[cap1]{\nfoo\nbar\n\nbuz\n//}\n") + expected = <<-EOS + +\\begin{reviewlistblock} +\\begin{reviewemlist} +foo +bar + +buz +\\end{reviewemlist} +\\reviewemlistcaption{cap1} +\\end{reviewlistblock} EOS assert_equal expected, actual end @@ -573,6 +607,22 @@ def test_emlistnum_caption 4: buz \\end{reviewemlist} \\end{reviewlistblock} +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//emlistnum[cap1]{\nfoo\nbar\n\nbuz\n//}\n") + expected = <<-EOS + +\\begin{reviewlistblock} +\\begin{reviewemlist} + 1: foo + 2: bar + 3: + 4: buz +\\end{reviewemlist} +\\reviewemlistcaption{cap1} +\\end{reviewlistblock} EOS assert_equal expected, actual end @@ -589,6 +639,21 @@ def test_list buz \\end{reviewlist} \\end{reviewlistblock} +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//list[id1][cap1]{\nfoo\nbar\n\nbuz\n//}\n") + expected = <<-EOS +\\begin{reviewlistblock} +\\begin{reviewlist} +foo +bar + +buz +\\end{reviewlist} +\\reviewlistcaption{リスト1.1: cap1} +\\end{reviewlistblock} EOS assert_equal expected, actual end @@ -601,6 +666,16 @@ def test_list_lst \\begin{reviewlistlst}[caption={cap1},language={sql}] SELECT COUNT(*) FROM tests WHERE tests.no > 10 AND test.name LIKE 'ABC%' \\end{reviewlistlst} +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + # XXX: caption_position won't work with highlight + actual = compile_block("//list[id1][cap1][sql]{\nSELECT COUNT(*) FROM tests WHERE tests.no > 10 AND test.name LIKE 'ABC%'\n//}\n") + expected = <<-EOS +\\begin{reviewlistlst}[caption={cap1},language={sql}] +SELECT COUNT(*) FROM tests WHERE tests.no > 10 AND test.name LIKE 'ABC%' +\\end{reviewlistlst} EOS assert_equal expected, actual end @@ -633,6 +708,24 @@ def test_listnum 7: end \\end{reviewlist} \\end{reviewlistblock} +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//listnum[test1][ruby]{\nclass Foo\n def foo\n bar\n\n buz\n end\nend\n//}\n") + expected = <<-EOS +\\begin{reviewlistblock} +\\begin{reviewlist} + 1: class Foo + 2: def foo + 3: bar + 4: + 5: buz + 6: end + 7: end +\\end{reviewlist} +\\reviewlistcaption{リスト1.1: ruby} +\\end{reviewlistblock} EOS assert_equal expected, actual end @@ -670,6 +763,22 @@ def foo end end \\end{reviewlistnumlst} +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + # XXX: caption_position won't work with highlight + actual = compile_block("//listnum[test1][ruby]{\nclass Foo\n def foo\n bar\n\n buz\n end\nend\n//}\n") + expected = <<-EOS +\\begin{reviewlistnumlst}[caption={ruby},language={}] +class Foo + def foo + bar + + buz + end +end +\\end{reviewlistnumlst} EOS assert_equal expected, actual end @@ -704,6 +813,21 @@ def test_source buz \\end{reviewsource} \\end{reviewlistblock} +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//source[foo/bar/test.rb]{\nfoo\nbar\n\nbuz\n//}\n") + expected = <<-EOS +\\begin{reviewlistblock} +\\begin{reviewsource} +foo +bar + +buz +\\end{reviewsource} +\\reviewsourcecaption{foo/bar/test.rb} +\\end{reviewlistblock} EOS assert_equal expected, actual end @@ -732,6 +856,19 @@ def test_source_lst foo bar +buz +\\end{reviewsourcelst} +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + # XXX: caption_position won't work with highlight + actual = compile_block("//source[foo/bar/test.rb]{\nfoo\nbar\n\nbuz\n//}\n") + expected = <<-EOS +\\begin{reviewsourcelst}[title={foo/bar/test.rb},language={}] +foo +bar + buz \\end{reviewsourcelst} EOS @@ -767,9 +904,11 @@ def test_memo actual = compile_block("//memo[this is @{test}<&>_]{\ntest1\n\ntest@{2}\n//}\n") expected = <<-EOS \\begin{reviewmemo}[this is \\reviewbold{test}\\textless{}\\&\\textgreater{}\\textunderscore{}] + test1 test\\reviewit{2} + \\end{reviewmemo} EOS assert_equal expected, actual @@ -888,6 +1027,18 @@ def @chapter.image(_id) actual = compile_block("//image[sampleimg][sample photo][]{\n//}\n") assert_equal expected, actual + + @book.config['pdfmaker']['use_original_image_size'] = nil + @config['caption_position']['image'] = 'top' + actual = compile_block("//image[sampleimg][sample photo]{\n//}\n") + expected = <<-EOS +\\begin{reviewimage}%%sampleimg +\\reviewimagecaption{sample photo} +\\label{image:chap1:sampleimg} +\\reviewincludegraphics[width=\\maxwidth]{./images/chap1-sampleimg.png} +\\end{reviewimage} +EOS + assert_equal expected, actual end def test_image_with_metric @@ -1008,6 +1159,17 @@ def @chapter.image(_id) actual = compile_block("//indepimage[sampleimg][sample photo][]\n") assert_equal expected, actual + + @book.config['pdfmaker']['use_original_image_size'] = nil + @config['caption_position']['image'] = 'top' + actual = compile_block("//indepimage[sampleimg][sample photo]\n") + expected = <<-EOS +\\begin{reviewimage}%%sampleimg +\\reviewindepimagecaption{図: sample photo} +\\reviewincludegraphics[width=\\maxwidth]{./images/chap1-sampleimg.png} +\\end{reviewimage} +EOS + assert_equal expected, actual end def test_indepimage_without_caption @@ -1112,6 +1274,31 @@ def @chapter.image(_id) assert_equal expected, actual end + def test_indepimage_nofile + def @chapter.image(_id) + item = Book::Index::Item.new('sample_img#&', 1) + item.instance_eval do + def path + nil + end + end + item + end + + io = StringIO.new + @builder.instance_eval{ @logger = ReVIEW::Logger.new(io) } + + actual = compile_block("//indepimage[sample_img#&][sample photo]\n") + expected = <<-EOS +\\begin{reviewdummyimage} +--[[path = sample\\textunderscore{}img\\#\\& (not exist)]]-- +\\reviewindepimagecaption{図: sample photo} +\\end{reviewdummyimage} +EOS + assert_equal expected, actual + assert_match(/WARN --: :1: image not bound: sample_img#&/, io.string) + end + def test_table actual = compile_block("//table{\naaa\tbbb\n------------\nccc\tddd<>&\n//}\n") expected = <<-EOS @@ -1134,15 +1321,30 @@ def test_table ccc & ddd\\textless{}\\textgreater{}\\& \\\\ \\hline \\end{reviewtable} \\end{table} +EOS + assert_equal expected, actual + + @config['caption_position']['table'] = 'bottom' + actual = compile_block("//table[foo][FOO]{\naaa\tbbb\n------------\nccc\tddd<>&\n//}\n") + expected = <<-EOS +\\begin{table}%%foo +\\begin{reviewtable}{|l|l|} +\\hline +\\reviewth{aaa} & \\reviewth{bbb} \\\\ \\hline +ccc & ddd\\textless{}\\textgreater{}\\& \\\\ \\hline +\\end{reviewtable} +\\reviewtablecaption{FOO} +\\label{table:chap1:foo} +\\end{table} EOS assert_equal expected, actual end def test_empty_table - e = assert_raises(ReVIEW::ApplicationError) { compile_block "//table{\n//}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("//table{\n//}\n") } assert_equal ':2: error: no rows in the table', e.message - e = assert_raises(ReVIEW::ApplicationError) { compile_block "//table{\n------------\n//}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("//table{\n------------\n//}\n") } assert_equal ':3: error: no rows in the table', e.message end @@ -1240,6 +1442,26 @@ def test_emtable \\end{reviewtable} \\end{table} +\\begin{reviewtable}{|l|l|} +\\hline +\\reviewth{aaa} & \\reviewth{bbb} \\\\ \\hline +ccc & ddd\\textless{}\\textgreater{}\\& \\\\ \\hline +\\end{reviewtable} +EOS + assert_equal expected, actual + + @config['caption_position']['table'] = 'bottom' + actual = compile_block("//emtable[foo]{\naaa\tbbb\n------------\nccc\tddd<>&\n//}\n//emtable{\naaa\tbbb\n------------\nccc\tddd<>&\n//}\n") + expected = <<-EOS +\\begin{table}%% +\\begin{reviewtable}{|l|l|} +\\hline +\\reviewth{aaa} & \\reviewth{bbb} \\\\ \\hline +ccc & ddd\\textless{}\\textgreater{}\\& \\\\ \\hline +\\end{reviewtable} +\\reviewtablecaption*{foo} +\\end{table} + \\begin{reviewtable}{|l|l|} \\hline \\reviewth{aaa} & \\reviewth{bbb} \\\\ \\hline @@ -1285,6 +1507,21 @@ def @chapter.image(_id) actual = compile_block("//imgtable[sampleimg][test for imgtable][]{\n//}\n") assert_equal expected, actual + + @book.config['pdfmaker']['use_original_image_size'] = nil + @config['caption_position']['table'] = 'bottom' + actual = compile_block("//imgtable[sampleimg][test for imgtable]{\n//}\n") + + expected = <<-EOS +\\begin{table}[h]%%sampleimg +\\label{table:chap1:sampleimg} +\\begin{reviewimage}%%sampleimg +\\reviewincludegraphics[width=\\maxwidth]{./images/chap1-sampleimg.png} +\\end{reviewimage} +\\reviewimgtablecaption{test for imgtable} +\\end{table} +EOS + assert_equal expected, actual end def test_imgtable_with_metrics @@ -1664,12 +1901,16 @@ def test_major_blocks actual = compile_block("//note{\nA\n\nB\n//}\n//note[caption]{\nA\n//}") expected = <<-EOS \\begin{reviewnote} + A B + \\end{reviewnote} \\begin{reviewnote}[caption] + A + \\end{reviewnote} EOS assert_equal expected, actual @@ -1677,12 +1918,16 @@ def test_major_blocks actual = compile_block("//memo{\nA\n\nB\n//}\n//memo[caption]{\nA\n//}") expected = <<-EOS \\begin{reviewmemo} + A B + \\end{reviewmemo} \\begin{reviewmemo}[caption] + A + \\end{reviewmemo} EOS assert_equal expected, actual @@ -1690,12 +1935,16 @@ def test_major_blocks actual = compile_block("//info{\nA\n\nB\n//}\n//info[caption]{\nA\n//}") expected = <<-EOS \\begin{reviewinfo} + A B + \\end{reviewinfo} \\begin{reviewinfo}[caption] + A + \\end{reviewinfo} EOS assert_equal expected, actual @@ -1703,12 +1952,16 @@ def test_major_blocks actual = compile_block("//important{\nA\n\nB\n//}\n//important[caption]{\nA\n//}") expected = <<-EOS \\begin{reviewimportant} + A B + \\end{reviewimportant} \\begin{reviewimportant}[caption] + A + \\end{reviewimportant} EOS assert_equal expected, actual @@ -1716,12 +1969,16 @@ def test_major_blocks actual = compile_block("//caution{\nA\n\nB\n//}\n//caution[caption]{\nA\n//}") expected = <<-EOS \\begin{reviewcaution} + A B + \\end{reviewcaution} \\begin{reviewcaution}[caption] + A + \\end{reviewcaution} EOS assert_equal expected, actual @@ -1729,12 +1986,16 @@ def test_major_blocks actual = compile_block("//notice{\nA\n\nB\n//}\n//notice[caption]{\nA\n//}") expected = <<-EOS \\begin{reviewnotice} + A B + \\end{reviewnotice} \\begin{reviewnotice}[caption] + A + \\end{reviewnotice} EOS assert_equal expected, actual @@ -1742,12 +2003,16 @@ def test_major_blocks actual = compile_block("//warning{\nA\n\nB\n//}\n//warning[caption]{\nA\n//}") expected = <<-EOS \\begin{reviewwarning} + A B + \\end{reviewwarning} \\begin{reviewwarning}[caption] + A + \\end{reviewwarning} EOS assert_equal expected, actual @@ -1755,17 +2020,173 @@ def test_major_blocks actual = compile_block("//tip{\nA\n\nB\n//}\n//tip[caption]{\nA\n//}") expected = <<-EOS \\begin{reviewtip} + A B + \\end{reviewtip} \\begin{reviewtip}[caption] + A + \\end{reviewtip} EOS assert_equal expected, actual end + def test_minicolumn_blocks + %w[note memo tip info warning important caution notice].each do |type| + src = <<-EOS +//#{type}[#{type}1]{ + +//} + +//#{type}[#{type}2]{ +//} +EOS + + expected = <<-EOS +\\begin{review#{type}}[#{type}1] +\\end{review#{type}} +\\begin{review#{type}}[#{type}2] +\\end{review#{type}} +EOS + assert_equal expected, compile_block(src) + + src = <<-EOS +//#{type}[#{type}2]{ + +//} + +//#{type}[#{type}3]{ + +//} + +//#{type}[#{type}4]{ + +//} + +//#{type}[#{type}5]{ + +//} + +//#{type}[#{type}6]{ + +//} +EOS + + expected = <<-EOS +\\begin{review#{type}}[#{type}2] +\\end{review#{type}} +\\begin{review#{type}}[#{type}3] +\\end{review#{type}} +\\begin{review#{type}}[#{type}4] +\\end{review#{type}} +\\begin{review#{type}}[#{type}5] +\\end{review#{type}} +\\begin{review#{type}}[#{type}6] +\\end{review#{type}} +EOS + assert_equal expected, compile_block(src) + + src = <<-EOS +//#{type}{ + + * A + + 1. B + +//} + +//#{type}[OMITEND1]{ + +//emlist{ +LIST +//} + +//} +//#{type}[OMITEND2]{ +//} +EOS + + expected = <<-EOS +\\begin{review#{type}} + +\\begin{itemize} +\\item A +\\end{itemize} + +\\begin{enumerate} +\\item B +\\end{enumerate} + +\\end{review#{type}} +\\begin{review#{type}}[OMITEND1] + +\\begin{reviewlistblock} +\\begin{reviewemlist} +LIST +\\end{reviewemlist} +\\end{reviewlistblock} + +\\end{review#{type}} +\\begin{review#{type}}[OMITEND2] +\\end{review#{type}} +EOS + assert_equal expected, compile_block(src) + end + end + + def test_minicolumn_blocks_nest_error1 + %w[note memo tip info warning important caution notice].each do |type| + @builder.doc_status.clear + src = <<-EOS +//#{type}{ + +//#{type}{ +//} + +//} +EOS + e = assert_raises(ReVIEW::ApplicationError) { compile_block(src) } + assert_match(/minicolumn cannot be nested:/, e.message) + end + end + + def test_minicolumn_blocks_nest_error2 + %w[note memo tip info warning important caution notice].each do |type| + @builder.doc_status.clear + src = <<-EOS +//#{type}{ + +//#{type}{ + +//} + +//} +EOS + e = assert_raises(ReVIEW::ApplicationError) { compile_block(src) } + assert_match(/minicolumn cannot be nested:/, e.message) + end + end + + def test_minicolumn_blocks_nest_error3 + %w[memo tip info warning important caution notice].each do |type| + @builder.doc_status.clear + src = <<-EOS +//#{type}{ + +//note{ +//} + +//} +EOS + e = assert_raises(ReVIEW::ApplicationError) { compile_block(src) } + assert_match(/minicolumn cannot be nested:/, e.message) + end + end + def test_inline_raw0 assert_equal 'normal', compile_inline('@{normal}') end @@ -1903,18 +2324,18 @@ def test_inline_w end def test_inline_unknown - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@{n}\n") } assert_equal ':1: error: unknown image: n', e.message - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@{n}\n") } assert_equal ':1: error: unknown footnote: n', e.message - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@{n}\n") } assert_equal ':1: error: unknown headline: n', e.message %w[list table column].each do |name| - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@<#{name}>{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@<#{name}>{n}\n") } assert_equal ":1: error: unknown #{name}: n", e.message end %w[chap chapref title].each do |name| - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@<#{name}>{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@<#{name}>{n}\n") } assert_equal ':1: error: key not found: "n"', e.message end end @@ -2039,6 +2460,21 @@ def test_texequation_with_caption e=mc^2 \\end{equation*} \\end{reviewequationblock} +EOS + actual = compile_block(src) + assert_equal expected, actual + + @config['caption_position']['equation'] = 'bottom' + expected = <<-EOS + +\\reviewequationref{1.1} + +\\begin{reviewequationblock} +\\begin{equation*} +e=mc^2 +\\end{equation*} +\\reviewequationcaption{式1.1: The Equivalence of Mass \\reviewit{and} Energy} +\\end{reviewequationblock} EOS actual = compile_block(src) assert_equal expected, actual diff --git a/test/test_latexbuilder_v2.rb b/test/test_latexbuilder_v2.rb index f0126fd8e..a71dccf7a 100644 --- a/test/test_latexbuilder_v2.rb +++ b/test/test_latexbuilder_v2.rb @@ -229,6 +229,10 @@ def @chapter.headline_index @config['secnolevel'] = 3 actual = compile_inline('test @{chap1|test} test2') + assert_equal 'test \reviewsecref{「1.1.1 te\\textunderscore{}st」}{sec:1-1-1} test2', actual + + @config['chapterlink'] = nil + actual = compile_inline('test @{chap1|test} test2') assert_equal 'test 「1.1.1 te\\textunderscore{}st」 test2', actual end @@ -662,9 +666,11 @@ def test_memo expected = <<-EOS \\begin{reviewminicolumn} \\reviewminicolumntitle{this is \\textbf{test}\\textless{}\\&\\textgreater{}\\textunderscore{}} + test1 test\\textit{2} + \\end{reviewminicolumn} EOS assert_equal expected, actual @@ -1285,13 +1291,17 @@ def test_major_blocks actual = compile_block("//note{\nA\n\nB\n//}\n//note[caption]{\nA\n//}") expected = <<-EOS \\begin{reviewminicolumn} + A B + \\end{reviewminicolumn} \\begin{reviewminicolumn} \\reviewminicolumntitle{caption} + A + \\end{reviewminicolumn} EOS assert_equal expected, actual @@ -1299,13 +1309,17 @@ def test_major_blocks actual = compile_block("//memo{\nA\n\nB\n//}\n//memo[caption]{\nA\n//}") expected = <<-EOS \\begin{reviewminicolumn} + A B + \\end{reviewminicolumn} \\begin{reviewminicolumn} \\reviewminicolumntitle{caption} + A + \\end{reviewminicolumn} EOS assert_equal expected, actual @@ -1313,13 +1327,17 @@ def test_major_blocks actual = compile_block("//info{\nA\n\nB\n//}\n//info[caption]{\nA\n//}") expected = <<-EOS \\begin{reviewminicolumn} + A B + \\end{reviewminicolumn} \\begin{reviewminicolumn} \\reviewminicolumntitle{caption} + A + \\end{reviewminicolumn} EOS assert_equal expected, actual @@ -1327,13 +1345,17 @@ def test_major_blocks actual = compile_block("//important{\nA\n\nB\n//}\n//important[caption]{\nA\n//}") expected = <<-EOS \\begin{reviewminicolumn} + A B + \\end{reviewminicolumn} \\begin{reviewminicolumn} \\reviewminicolumntitle{caption} + A + \\end{reviewminicolumn} EOS assert_equal expected, actual @@ -1341,13 +1363,17 @@ def test_major_blocks actual = compile_block("//caution{\nA\n\nB\n//}\n//caution[caption]{\nA\n//}") expected = <<-EOS \\begin{reviewminicolumn} + A B + \\end{reviewminicolumn} \\begin{reviewminicolumn} \\reviewminicolumntitle{caption} + A + \\end{reviewminicolumn} EOS assert_equal expected, actual @@ -1355,13 +1381,17 @@ def test_major_blocks actual = compile_block("//notice{\nA\n\nB\n//}\n//notice[caption]{\nA\n//}") expected = <<-EOS \\begin{reviewminicolumn} + A B + \\end{reviewminicolumn} \\begin{reviewminicolumn} \\reviewminicolumntitle{caption} + A + \\end{reviewminicolumn} EOS assert_equal expected, actual @@ -1369,13 +1399,17 @@ def test_major_blocks actual = compile_block("//warning{\nA\n\nB\n//}\n//warning[caption]{\nA\n//}") expected = <<-EOS \\begin{reviewminicolumn} + A B + \\end{reviewminicolumn} \\begin{reviewminicolumn} \\reviewminicolumntitle{caption} + A + \\end{reviewminicolumn} EOS assert_equal expected, actual @@ -1383,13 +1417,17 @@ def test_major_blocks actual = compile_block("//tip{\nA\n\nB\n//}\n//tip[caption]{\nA\n//}") expected = <<-EOS \\begin{reviewminicolumn} + A B + \\end{reviewminicolumn} \\begin{reviewminicolumn} \\reviewminicolumntitle{caption} + A + \\end{reviewminicolumn} EOS assert_equal expected, actual @@ -1506,18 +1544,18 @@ def test_inline_fence end def test_inline_unknown - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@{n}\n") } assert_equal ':1: error: unknown image: n', e.message - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@{n}\n") } assert_equal ':1: error: unknown footnote: n', e.message - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@{n}\n") } assert_equal ':1: error: unknown headline: n', e.message %w[list table column].each do |name| - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@<#{name}>{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@<#{name}>{n}\n") } assert_equal ":1: error: unknown #{name}: n", e.message end %w[chap chapref title].each do |name| - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@<#{name}>{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@<#{name}>{n}\n") } assert_equal ':1: error: key not found: "n"', e.message end end diff --git a/test/test_logger.rb b/test/test_logger.rb index f05dad31c..d041dbe7d 100644 --- a/test/test_logger.rb +++ b/test/test_logger.rb @@ -6,26 +6,26 @@ def setup end def test_logging - old_stderr = STDERR.dup + old_stderr = $stderr.dup IO.pipe do |r, w| - STDERR.reopen(w) + $stderr.reopen(w) @logger = ReVIEW::Logger.new @logger.warn('test') msg = r.readline - STDERR.reopen(old_stderr) + $stderr.reopen(old_stderr) assert_equal "WARN --: test\n", msg end end def test_logging_with_progname - old_stderr = STDERR.dup + old_stderr = $stderr.dup IO.pipe do |r, w| - STDERR.reopen(w) - @logger = ReVIEW::Logger.new(STDERR, progname: 'review-dummy-cmd') + $stderr.reopen(w) + @logger = ReVIEW::Logger.new($stderr, progname: 'review-dummy-cmd') @logger.warn('test') msg = r.readline - STDERR.reopen(old_stderr) + $stderr.reopen(old_stderr) assert_equal "WARN review-dummy-cmd: test\n", msg end diff --git a/test/test_makerhelper.rb b/test/test_makerhelper.rb index d92f1e3de..1220e49fe 100644 --- a/test/test_makerhelper.rb +++ b/test/test_makerhelper.rb @@ -32,18 +32,6 @@ def test_copy_images_to_dir assert image_files.include?(File.join(@tmpdir1, 'subdir/foo.png')) end - def test_copy_images_to_dir_convert - if /mswin|mingw|cygwin/ !~ RUBY_PLATFORM && (`convert -version` rescue nil) && (`gs --version` rescue nil) - FileUtils.cp(File.join(assets_dir, 'black.eps'), File.join(@tmpdir1, 'foo.eps')) - - image_files = MakerHelper.copy_images_to_dir(@tmpdir1, @tmpdir2, - convert: { eps: :png }) - - assert File.exist?(File.join(@tmpdir2, 'foo.eps.png')), 'EPS to PNG conversion failed' - assert image_files.include?(File.join(@tmpdir1, 'foo.eps.png')) - end - end - def test_copy_images_to_dir_with_exts types = %w[png gif jpg jpeg svg pdf eps] types4epub = %w[png gif jpg jpeg svg] diff --git a/test/test_markdownbuilder.rb b/test/test_markdownbuilder.rb index 61b0747a1..6305e8b68 100644 --- a/test/test_markdownbuilder.rb +++ b/test/test_markdownbuilder.rb @@ -16,6 +16,7 @@ def setup @chapter = Book::Chapter.new(@book, 1, '-', nil, StringIO.new) location = Location.new(nil, nil) @builder.bind(@compiler, @chapter, location) + I18n.setup(@config['language']) end def test_quote @@ -47,7 +48,9 @@ def test_memo

this is **test**<&>_

test1 + test*2* +
EOS assert_equal expected, actual diff --git a/test/test_pdfmaker.rb b/test/test_pdfmaker.rb index 72d7767a2..a319a9cff 100644 --- a/test/test_pdfmaker.rb +++ b/test/test_pdfmaker.rb @@ -10,9 +10,10 @@ def setup @config.merge!( 'bookname' => 'sample', 'title' => 'Sample Book', - 'review_version' => 3, + 'aut' => 'anonymous', + 'review_version' => 4, 'urnid' => 'http://example.jp/', - 'date' => '2011-01-01', + 'date' => '2020-07-11', 'language' => 'ja', 'texcommand' => 'uplatex' ) @@ -75,7 +76,7 @@ def test_make_custom_page Dir.mktmpdir do |dir| coverfile = 'cover.html' content = 'test' - File.open(File.join(dir, 'cover.tex'), 'w') { |f| f.write(content) } + File.write(File.join(dir, 'cover.tex'), content) page = @maker.make_custom_page(File.join(dir, coverfile)) assert_equal(content, page) end @@ -144,7 +145,7 @@ def test_template_content @maker.erb_config tmpl = @maker.template_content expect = File.read(File.join(assets_dir, 'test_template.tex')) - expect.gsub!(/\\def\\review@reviewversion{[^\}]+}/, "\\def\\review@reviewversion{#{ReVIEW::VERSION}}") + expect.gsub!(/\\def\\review@reviewversion{[^}]+}/, "\\def\\review@reviewversion{#{ReVIEW::VERSION}}") assert_equal(expect, tmpl) end end @@ -159,7 +160,7 @@ def test_template_content_with_localconfig @maker.erb_config tmpl = @maker.template_content expect = File.read(File.join(assets_dir, 'test_template.tex')) - expect.gsub!(/\\def\\review@reviewversion{[^\}]+}/, "\\def\\review@reviewversion{#{ReVIEW::VERSION}}") + expect.gsub!(/\\def\\review@reviewversion{[^}]+}/, "\\def\\review@reviewversion{#{ReVIEW::VERSION}}") expect.sub!("\\makeatother\n", '\&' + "%% BEGIN: config-local.tex.erb\n\\def\\customvalue{\\#\\textunderscore{}TEST\\textunderscore{}}\n%% END: config-local.tex.erb\n") assert_equal(expect, tmpl) end @@ -175,13 +176,13 @@ def test_gettemplate_with_backmatter Dir.mktmpdir do |dir| Dir.chdir(dir) do profile = "\\thispagestyle{empty}\\chapter*{Profile}\nsome profile\n" - File.open(File.join(dir, 'profile.tex'), 'w') { |f| f.write(profile) } + File.write(File.join(dir, 'profile.tex'), profile) advfile = "\\thispagestyle{empty}\\chapter*{Ad}\nsome ad content\n" - File.open(File.join(dir, 'advfile.tex'), 'w') { |f| f.write(advfile) } + File.write(File.join(dir, 'advfile.tex'), advfile) backcover = "\\clearpage\n\\thispagestyle{empty}\\AddToShipoutPictureBG{%\n\\AtPageLowerLeft{\\includegraphics[width=\\paperwidth,height=\\paperheight]{images/backcover.png}}\n}\n\\null" - File.open(File.join(dir, 'backcover.tex'), 'w') { |f| f.write(backcover) } + File.write(File.join(dir, 'backcover.tex'), backcover) expect = File.read(File.join(assets_dir, 'test_template_backmatter.tex')) - expect.gsub!(/\\def\\review@reviewversion{[^\}]+}/, "\\def\\review@reviewversion{#{ReVIEW::VERSION}}") + expect.gsub!(/\\def\\review@reviewversion{[^}]+}/, "\\def\\review@reviewversion{#{ReVIEW::VERSION}}") @maker.basedir = Dir.pwd @maker.erb_config tmpl = @maker.template_content @@ -196,7 +197,7 @@ def test_colophon_history @config['pht'] = ['Mrs.Smith'] @config['language'] = 'ja' history = @maker.make_history_list - expect = ['2011年1月1日 発行'] + expect = ['2020年7月11日 発行'] assert_equal expect, history end @@ -207,9 +208,9 @@ def test_colophon_history_2 @config['language'] = 'ja' @config['history'] = [['2011-08-03 v1.0.0版発行', - '2012-02-15 v1.1.0版発行']] + '2020-07-11 v1.2.0版発行']] history = @maker.make_history_list - expect = ['2011年8月3日 v1.0.0版発行', '2012年2月15日 v1.1.0版発行'] + expect = ['2011年8月3日 v1.0.0版発行', '2020年7月11日 v1.2.0版発行'] assert_equal expect, history end diff --git a/test/test_pdfmaker_cmd.rb b/test/test_pdfmaker_cmd.rb index 6f8aadc86..7a6ed66c9 100644 --- a/test/test_pdfmaker_cmd.rb +++ b/test/test_pdfmaker_cmd.rb @@ -30,7 +30,7 @@ def common_buildpdf(bookdir, templatedir, configfile, targetpdffile, option = ni Dir.chdir(@tmpdir1) do _o, e, s = Open3.capture3("#{ruby_cmd} -S #{REVIEW_PDFMAKER} #{option} #{configfile}") if !e.empty? && !s.success? - STDERR.puts e + $stderr.puts e end assert s.success? end diff --git a/test/test_plaintextbuilder.rb b/test/test_plaintextbuilder.rb index 59f5d8547..df03b7be7 100644 --- a/test/test_plaintextbuilder.rb +++ b/test/test_plaintextbuilder.rb @@ -249,6 +249,17 @@ def @chapter.list(_id) foo bar +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//list[samplelist][this is @{test}<&>_]{\nfoo\nbar\n//}\n") + expected = <<-EOS +foo +bar + +リスト1.1 this is test<&>_ + EOS assert_equal expected, actual end @@ -264,6 +275,17 @@ def @chapter.list(_id) 1: foo 2: bar +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//listnum[test][this is @{test}<&>_]{\nfoo\nbar\n//}\n") + expected = <<-EOS + 1: foo + 2: bar + +リスト1.1 this is test<&>_ + EOS assert_equal expected, actual end @@ -277,6 +299,18 @@ def test_source buz +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//source[foo/bar/test.rb]{\nfoo\nbar\n\nbuz\n//}\n") + expected = <<-EOS +foo +bar + +buz +foo/bar/test.rb + EOS assert_equal expected, actual end @@ -308,6 +342,16 @@ def test_box foo bar +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//box[FOO]{\nfoo\nbar\n//}\n") + expected = <<-EOS +foo +bar +FOO + EOS assert_equal expected, actual end @@ -327,6 +371,16 @@ def test_cmd lineA lineB +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//cmd[cap1]{\nlineA\nlineB\n//}\n") + expected = <<-EOS +lineA +lineB +cap1 + EOS assert_equal expected, actual end @@ -348,6 +402,16 @@ def test_emlist_caption lineA lineB +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//emlist[cap1]{\nlineA\nlineB\n//}\n") + expected = <<-EOS +lineA +lineB +cap1 + EOS assert_equal expected, actual end @@ -359,6 +423,16 @@ def test_emlistnum 1: foo 2: bar +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//emlistnum[this is @{test}<&>_]{\nfoo\nbar\n//}\n") + expected = <<-EOS + 1: foo + 2: bar +this is test<&>_ + EOS assert_equal expected, actual end @@ -387,15 +461,25 @@ def test_table aaa\tbbb ccc\tddd<>& +EOS + assert_equal expected, actual + + @config['caption_position']['table'] = 'bottom' + actual = compile_block("//table[foo][FOO]{\naaa\tbbb\n------------\nccc\tddd<>&\n//}\n") + expected = <<-EOS +aaa\tbbb +ccc\tddd<>& + +表1.1 FOO EOS assert_equal expected, actual end def test_empty_table - e = assert_raises(ReVIEW::ApplicationError) { compile_block "//table{\n//}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("//table{\n//}\n") } assert_equal ':2: error: no rows in the table', e.message - e = assert_raises(ReVIEW::ApplicationError) { compile_block "//table{\n------------\n//}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("//table{\n------------\n//}\n") } assert_equal ':3: error: no rows in the table', e.message end @@ -512,6 +596,147 @@ def test_major_blocks assert_equal expected, actual end + def test_minicolumn_blocks + %w[note memo tip info warning important caution notice].each do |type| + @builder.doc_status.clear + src = <<-EOS +//#{type}[#{type}1]{ + +//} + +//#{type}[#{type}2]{ +//} +EOS + + expected = <<-EOS +#{type}1 + +#{type}2 + +EOS + assert_equal expected, compile_block(src) + + src = <<-EOS +//#{type}[#{type}2]{ + +//} + +//#{type}[#{type}3]{ + +//} + +//#{type}[#{type}4]{ + +//} + +//#{type}[#{type}5]{ + +//} + +//#{type}[#{type}6]{ + +//} +EOS + + expected = <<-EOS +#{type}2 + +#{type}3 + +#{type}4 + +#{type}5 + +#{type}6 + +EOS + assert_equal expected, compile_block(src) + + src = <<-EOS +//#{type}{ + + * A + + 1. B + +//} + +//#{type}[OMITEND1]{ + +//emlist{ +LIST +//} + +//} +//#{type}[OMITEND2]{ +//} +EOS + + expected = <<-EOS +A + +1 B + +OMITEND1 + +LIST + +OMITEND2 + +EOS + assert_equal expected, compile_block(src) + end + end + + def test_minicolumn_blocks_nest_error1 + %w[note memo tip info warning important caution notice].each do |type| + @builder.doc_status.clear + src = <<-EOS +//#{type}{ + +//#{type}{ +//} + +//} +EOS + e = assert_raises(ReVIEW::ApplicationError) { compile_block(src) } + assert_match(/minicolumn cannot be nested:/, e.message) + end + end + + def test_minicolumn_blocks_nest_error2 + %w[note memo tip info warning important caution notice].each do |type| + @builder.doc_status.clear + src = <<-EOS +//#{type}{ + +//#{type}{ + +//} + +//} +EOS + e = assert_raises(ReVIEW::ApplicationError) { compile_block(src) } + assert_match(/minicolumn cannot be nested:/, e.message) + end + end + + def test_minicolumn_blocks_nest_error3 + %w[memo tip info warning important caution notice].each do |type| + @builder.doc_status.clear + src = <<-EOS +//#{type}{ + +//note{ +//} + +//} +EOS + e = assert_raises(ReVIEW::ApplicationError) { compile_block(src) } + assert_match(/minicolumn cannot be nested:/, e.message) + end + end + def test_image def @chapter.image(_id) item = Book::Index::Item.new('sampleimg', 1) @@ -521,6 +746,10 @@ def @chapter.image(_id) actual = compile_block("//image[sampleimg][sample photo]{\nfoo\n//}\n") assert_equal %Q(図1.1 sample photo\n\n), actual + + @config['caption_position']['image'] = 'top' + actual = compile_block("//image[sampleimg][sample photo]{\nfoo\n//}\n") + assert_equal %Q(図1.1 sample photo\n\n), actual end def test_image_with_metric @@ -540,18 +769,18 @@ def test_texequation end def test_inline_unknown - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@{n}\n") } assert_equal ':1: error: unknown image: n', e.message - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@{n}\n") } assert_equal ':1: error: unknown footnote: n', e.message - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@{n}\n") } assert_equal ':1: error: unknown headline: n', e.message %w[list table column].each do |name| - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@<#{name}>{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@<#{name}>{n}\n") } assert_equal ":1: error: unknown #{name}: n", e.message end %w[chap chapref title].each do |name| - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@<#{name}>{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@<#{name}>{n}\n") } assert_equal ':1: error: key not found: "n"', e.message end end @@ -649,6 +878,17 @@ def test_texequation_with_caption 式1.1 The Equivalence of Mass and Energy e=mc^2 +EOS + actual = compile_block(src) + assert_equal expected, actual + + @config['caption_position']['equation'] = 'bottom' + expected = <<-EOS +式1.1 + +e=mc^2 +式1.1 The Equivalence of Mass and Energy + EOS actual = compile_block(src) assert_equal expected, actual diff --git a/test/test_review_ext.rb b/test/test_review_ext.rb index d5f931508..31d540cd1 100644 --- a/test/test_review_ext.rb +++ b/test/test_review_ext.rb @@ -11,7 +11,8 @@ def test_builder_init_on_review_ext module ReVIEW class HTMLBuilder attr_reader :builder_init_test - def builder_init + def initialize(strict = false) + super @builder_init_test = "test" end end diff --git a/test/test_rstbuilder.rb b/test/test_rstbuilder.rb index 862e5c5ea..b1326ebcb 100644 --- a/test/test_rstbuilder.rb +++ b/test/test_rstbuilder.rb @@ -1,7 +1,7 @@ require 'test_helper' require 'review/compiler' require 'review/book' -require 'review/topbuilder' +require 'review/rstbuilder' require 'review/i18n' class RSTBuidlerTest < Test::Unit::TestCase @@ -327,13 +327,16 @@ def test_major_blocks .. note:: A + B + .. note:: caption A + EOS assert_equal expected, actual @@ -342,13 +345,16 @@ def test_major_blocks .. memo:: A + B + .. memo:: caption A + EOS assert_equal expected, actual @@ -357,13 +363,16 @@ def test_major_blocks .. info:: A + B + .. info:: caption A + EOS assert_equal expected, actual @@ -372,13 +381,16 @@ def test_major_blocks .. important:: A + B + .. important:: caption A + EOS assert_equal expected, actual @@ -387,13 +399,16 @@ def test_major_blocks .. caution:: A + B + .. caution:: caption A + EOS assert_equal expected, actual @@ -402,13 +417,16 @@ def test_major_blocks .. notice:: A + B + .. notice:: caption A + EOS assert_equal expected, actual @@ -417,13 +435,16 @@ def test_major_blocks .. warning:: A + B + .. warning:: caption A + EOS assert_equal expected, actual @@ -432,13 +453,16 @@ def test_major_blocks .. tip:: A + B + .. tip:: caption A + EOS assert_equal expected, actual end diff --git a/test/test_sec_counter.rb b/test/test_sec_counter.rb new file mode 100644 index 000000000..83613f58b --- /dev/null +++ b/test/test_sec_counter.rb @@ -0,0 +1,156 @@ +require 'test_helper' +require 'book_test_helper' +require 'review/sec_counter' + +class SecCounterTest < Test::Unit::TestCase + include ReVIEW + + def setup + @book = Book::Base.new + @io = StringIO.new("= sample\n\n") + @chapter = Book::Chapter.new(@book, 1, 'foo', '-', @io) + @part = Book::Part.new(@book, 1, [], 'name') + I18n.setup + end + + def test_initialize + @sec_counter = SecCounter.new(5, @chapter) + assert_equal('1', @sec_counter.anchor(0)) + assert_equal('1', @sec_counter.anchor(1)) + assert_equal('1-0', @sec_counter.anchor(2)) + assert_equal('1-0-0', @sec_counter.anchor(3)) + end + + def test_anchor1 + @sec_counter = SecCounter.new(5, @chapter) + @sec_counter.inc(3) + assert_equal('1', @sec_counter.anchor(0)) + assert_equal('1', @sec_counter.anchor(1)) + assert_equal('1-0', @sec_counter.anchor(2)) + assert_equal('1-0-1', @sec_counter.anchor(3)) + end + + def test_anchor2 + @sec_counter = SecCounter.new(5, @chapter) + @sec_counter.inc(2) + @sec_counter.inc(3) + @sec_counter.inc(3) + @sec_counter.inc(4) + assert_equal('1', @sec_counter.anchor(0)) + assert_equal('1', @sec_counter.anchor(1)) + assert_equal('1-1', @sec_counter.anchor(2)) + assert_equal('1-1-2', @sec_counter.anchor(3)) + assert_equal('1-1-2-1', @sec_counter.anchor(4)) + end + + def test_anchor3 + @sec_counter = SecCounter.new(5, @chapter) + @sec_counter.inc(2) + @sec_counter.inc(3) + @sec_counter.inc(3) + @sec_counter.inc(4) + @sec_counter.inc(3) + assert_equal('1', @sec_counter.anchor(0)) + assert_equal('1', @sec_counter.anchor(1)) + assert_equal('1-1', @sec_counter.anchor(2)) + assert_equal('1-1-3', @sec_counter.anchor(3)) + assert_equal('1-1-3-0', @sec_counter.anchor(4)) + end + + def test_anchor4 + @sec_counter = SecCounter.new(5, @chapter) + @sec_counter.inc(2) + @sec_counter.inc(2) + @sec_counter.inc(2) + @sec_counter.inc(3) + @sec_counter.inc(3) + assert_equal('1', @sec_counter.anchor(0)) + assert_equal('1', @sec_counter.anchor(1)) + assert_equal('1-3', @sec_counter.anchor(2)) + assert_equal('1-3-2', @sec_counter.anchor(3)) + assert_equal('1-3-2-0', @sec_counter.anchor(4)) + end + + def test_anchor_part1 + @sec_counter = SecCounter.new(5, @part) + @sec_counter.inc(3) + assert_equal('1', @sec_counter.anchor(0)) + assert_equal('1', @sec_counter.anchor(1)) + assert_equal('1-0', @sec_counter.anchor(2)) + assert_equal('1-0-1', @sec_counter.anchor(3)) + end + + def test_anchor_part3 + @sec_counter = SecCounter.new(5, @part) + @sec_counter.inc(2) + @sec_counter.inc(3) + @sec_counter.inc(3) + @sec_counter.inc(4) + @sec_counter.inc(3) + assert_equal('1', @sec_counter.anchor(0)) + assert_equal('1', @sec_counter.anchor(1)) + assert_equal('1-1', @sec_counter.anchor(2)) + assert_equal('1-1-3', @sec_counter.anchor(3)) + assert_equal('1-1-3-0', @sec_counter.anchor(4)) + end + + def test_prefix1 + @sec_counter = SecCounter.new(5, @chapter) + @sec_counter.inc(2) + assert_equal('1.1 ', @sec_counter.prefix(2, 3)) + assert_equal('1.1.0 ', @sec_counter.prefix(3, 3)) + assert_equal('1.1.0 ', @sec_counter.prefix(3, 5)) + end + + def test_prefix2 + @sec_counter = SecCounter.new(5, @chapter) + @sec_counter.inc(2) + @sec_counter.inc(3) + @sec_counter.inc(3) + @sec_counter.inc(4) + assert_equal('1.1 ', @sec_counter.prefix(2, 5)) + assert_equal('1.1 ', @sec_counter.prefix(2, 5)) + assert_equal(nil, @sec_counter.prefix(2, 1)) + assert_equal('1.1.2 ', @sec_counter.prefix(3, 5)) + assert_equal('1.1.2 ', @sec_counter.prefix(3, 3)) + assert_equal(nil, @sec_counter.prefix(3, 2)) + end + + def test_prefix3 + @sec_counter = SecCounter.new(5, @chapter) + @sec_counter.inc(2) + @sec_counter.inc(3) + @sec_counter.inc(3) + @sec_counter.inc(4) + @sec_counter.inc(3) + assert_equal('1.1 ', @sec_counter.prefix(2, 5)) + assert_equal('1.1 ', @sec_counter.prefix(2, 5)) + assert_equal(nil, @sec_counter.prefix(2, 1)) + assert_equal('1.1.3 ', @sec_counter.prefix(3, 5)) + assert_equal('1.1.3 ', @sec_counter.prefix(3, 3)) + assert_equal(nil, @sec_counter.prefix(3, 2)) + assert_equal('1.1.3.0 ', @sec_counter.prefix(4, 5)) + end + + def test_prefix_part1 + @sec_counter = SecCounter.new(5, @part) + @sec_counter.inc(2) + assert_equal('I.1 ', @sec_counter.prefix(2, 3)) + assert_equal('I.1.0 ', @sec_counter.prefix(3, 3)) + assert_equal('I.1.0 ', @sec_counter.prefix(3, 5)) + end + + def test_prefix_part2 + @sec_counter = SecCounter.new(5, @part) + @sec_counter.inc(2) + @sec_counter.inc(3) + @sec_counter.inc(3) + @sec_counter.inc(4) + assert_equal('I.1 ', @sec_counter.prefix(2, 5)) + assert_equal('I.1 ', @sec_counter.prefix(2, 5)) + assert_equal(nil, @sec_counter.prefix(2, 1)) + assert_equal('I.1.2 ', @sec_counter.prefix(3, 5)) + assert_equal('I.1.2 ', @sec_counter.prefix(3, 3)) + assert_equal(nil, @sec_counter.prefix(3, 2)) + end +end diff --git a/test/test_textmaker_cmd.rb b/test/test_textmaker_cmd.rb index 77cc9e5b3..56de0643d 100644 --- a/test/test_textmaker_cmd.rb +++ b/test/test_textmaker_cmd.rb @@ -29,7 +29,7 @@ def common_buildtext(bookdir, configfile, targetfile, option) ruby_cmd = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name']) + RbConfig::CONFIG['EXEEXT'] Dir.chdir(@tmpdir1) do _o, e, s = Open3.capture3("#{ruby_cmd} -S #{REVIEW_TEXTMAKER} #{option} #{configfile}") - STDERR.puts e unless e.empty? + assert_equal '', e assert s.success? end assert File.exist?(File.join(@tmpdir1, targetfile)) diff --git a/test/test_topbuilder.rb b/test/test_topbuilder.rb index 3a0332760..4c04c2366 100644 --- a/test/test_topbuilder.rb +++ b/test/test_topbuilder.rb @@ -175,11 +175,11 @@ def test_dlist_beforeulol end def test_dt_inline - fn = Book::FootnoteIndex.parse(['//footnote[bar][bar]']) - @chapter.instance_eval { @footnote_index = fn } - actual = compile_block(" : foo@{bar}[]<>&@$\\alpha[]$\n") + actual = compile_block("//footnote[bar][bar]\n\n : foo@{bar}[]<>&@$\\alpha[]$\n") expected = <<-EOS +【注1】bar + ★foo【注1】[]<>&◆→TeX式ここから←◆\\alpha[]◆→TeX式ここまで←◆☆ @@ -295,6 +295,19 @@ def @chapter.list(_id) 2: bar ◆→終了:リスト←◆ +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//listnum[test][this is @{test}<&>_]{\nfoo\nbar\n//}\n") + expected = <<-EOS +◆→開始:リスト←◆ + 1: foo + 2: bar + +リスト1.1 this is ★test☆<&>_ +◆→終了:リスト←◆ + EOS assert_equal expected, actual end @@ -310,6 +323,20 @@ def test_source buz ◆→終了:ソースコードリスト←◆ +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//source[foo/bar/test.rb]{\nfoo\nbar\n\nbuz\n//}\n") + expected = <<-EOS +◆→開始:ソースコードリスト←◆ +foo +bar + +buz +■foo/bar/test.rb +◆→終了:ソースコードリスト←◆ + EOS assert_equal expected, actual end @@ -347,6 +374,18 @@ def test_box bar ◆→終了:書式←◆ +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//box[FOO]{\nfoo\nbar\n//}\n") + expected = <<-EOS +◆→開始:書式←◆ +foo +bar +■FOO +◆→終了:書式←◆ + EOS assert_equal expected, actual end @@ -370,6 +409,18 @@ def test_cmd lineB ◆→終了:コマンド←◆ +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//cmd[cap1]{\nlineA\nlineB\n//}\n") + expected = <<-EOS +◆→開始:コマンド←◆ +lineA +lineB +■cap1 +◆→終了:コマンド←◆ + EOS assert_equal expected, actual end @@ -395,6 +446,18 @@ def test_emlist_caption lineB ◆→終了:インラインリスト←◆ +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//emlist[cap1]{\nlineA\nlineB\n//}\n") + expected = <<-EOS +◆→開始:インラインリスト←◆ +lineA +lineB +■cap1 +◆→終了:インラインリスト←◆ + EOS assert_equal expected, actual end @@ -408,6 +471,18 @@ def test_emlistnum 2: bar ◆→終了:インラインリスト←◆ +EOS + assert_equal expected, actual + + @config['caption_position']['list'] = 'bottom' + actual = compile_block("//emlistnum[this is @{test}<&>_]{\nfoo\nbar\n//}\n") + expected = <<-EOS +◆→開始:インラインリスト←◆ + 1: foo + 2: bar +■this is ★test☆<&>_ +◆→終了:インラインリスト←◆ + EOS assert_equal expected, actual end @@ -440,15 +515,28 @@ def test_table ccc\tddd<>& ◆→終了:表←◆ +EOS + assert_equal expected, actual + + @config['caption_position']['table'] = 'bottom' + actual = compile_block("//table[foo][FOO]{\naaa\tbbb\n------------\nccc\tddd<>&\n//}\n") + expected = <<-EOS +◆→開始:表←◆ +★aaa☆\t★bbb☆ +ccc\tddd<>& + +表1.1 FOO +◆→終了:表←◆ + EOS assert_equal expected, actual end def test_empty_table - e = assert_raises(ReVIEW::ApplicationError) { compile_block "//table{\n//}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("//table{\n//}\n") } assert_equal ':2: error: no rows in the table', e.message - e = assert_raises(ReVIEW::ApplicationError) { compile_block "//table{\n------------\n//}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("//table{\n------------\n//}\n") } assert_equal ':3: error: no rows in the table', e.message end @@ -647,6 +735,183 @@ def test_major_blocks assert_equal expected, actual end + def test_minicolumn_blocks + titles = { + 'note' => 'ノート', + 'memo' => 'メモ', + 'important' => '重要', + 'info' => '情報', + 'notice' => '注意', + 'caution' => '警告', + 'warning' => '危険', + 'tip' => 'TIP' + } + + %w[note memo tip info warning important caution notice].each do |type| + @builder.doc_status.clear + src = <<-EOS +//#{type}[#{type}1]{ + +//} + +//#{type}[#{type}2]{ +//} +EOS + + expected = <<-EOS +◆→開始:#{titles[type]}←◆ +■#{type}1 +◆→終了:#{titles[type]}←◆ + +◆→開始:#{titles[type]}←◆ +■#{type}2 +◆→終了:#{titles[type]}←◆ + +EOS + assert_equal expected, compile_block(src) + + src = <<-EOS +//#{type}[#{type}2]{ + +//} + +//#{type}[#{type}3]{ + +//} + +//#{type}[#{type}4]{ + +//} + +//#{type}[#{type}5]{ + +//} + +//#{type}[#{type}6]{ + +//} +EOS + + expected = <<-EOS +◆→開始:#{titles[type]}←◆ +■#{type}2 +◆→終了:#{titles[type]}←◆ + +◆→開始:#{titles[type]}←◆ +■#{type}3 +◆→終了:#{titles[type]}←◆ + +◆→開始:#{titles[type]}←◆ +■#{type}4 +◆→終了:#{titles[type]}←◆ + +◆→開始:#{titles[type]}←◆ +■#{type}5 +◆→終了:#{titles[type]}←◆ + +◆→開始:#{titles[type]}←◆ +■#{type}6 +◆→終了:#{titles[type]}←◆ + +EOS + assert_equal expected, compile_block(src) + + src = <<-EOS +//#{type}{ + + * A + + 1. B + +//} + +//#{type}[OMITEND1]{ + +//emlist{ +LIST +//} + +//} +//#{type}[OMITEND2]{ +//} +EOS + + expected = <<-EOS +◆→開始:#{titles[type]}←◆ + +● A + +1 B + +◆→終了:#{titles[type]}←◆ + +◆→開始:#{titles[type]}←◆ +■OMITEND1 + +◆→開始:インラインリスト←◆ +LIST +◆→終了:インラインリスト←◆ + +◆→終了:#{titles[type]}←◆ + +◆→開始:#{titles[type]}←◆ +■OMITEND2 +◆→終了:#{titles[type]}←◆ + +EOS + assert_equal expected, compile_block(src) + end + end + + def test_minicolumn_blocks_nest_error1 + %w[note memo tip info warning important caution notice].each do |type| + @builder.doc_status.clear + src = <<-EOS +//#{type}{ + +//#{type}{ +//} + +//} +EOS + e = assert_raises(ReVIEW::ApplicationError) { compile_block(src) } + assert_match(/minicolumn cannot be nested:/, e.message) + end + end + + def test_minicolumn_blocks_nest_error2 + %w[note memo tip info warning important caution notice].each do |type| + @builder.doc_status.clear + src = <<-EOS +//#{type}{ + +//#{type}{ + +//} + +//} +EOS + e = assert_raises(ReVIEW::ApplicationError) { compile_block(src) } + assert_match(/minicolumn cannot be nested:/, e.message) + end + end + + def test_minicolumn_blocks_nest_error3 + %w[memo tip info warning important caution notice].each do |type| + @builder.doc_status.clear + src = <<-EOS +//#{type}{ + +//note{ +//} + +//} +EOS + e = assert_raises(ReVIEW::ApplicationError) { compile_block(src) } + assert_match(/minicolumn cannot be nested:/, e.message) + end + end + def test_image def @chapter.image(_id) item = Book::Index::Item.new('sampleimg', 1) @@ -657,6 +922,18 @@ def @chapter.image(_id) actual = compile_block("//image[sampleimg][sample photo]{\nfoo\n//}\n") expected = <<-EOS ◆→開始:図←◆ +◆→./images/chap1-sampleimg.png←◆ + +図1.1 sample photo +◆→終了:図←◆ + +EOS + assert_equal expected, actual + + @config['caption_position']['image'] = 'top' + actual = compile_block("//image[sampleimg][sample photo]{\nfoo\n//}\n") + expected = <<-EOS +◆→開始:図←◆ 図1.1 sample photo ◆→./images/chap1-sampleimg.png←◆ @@ -676,9 +953,9 @@ def @chapter.image(_id) actual = compile_block("//image[sampleimg][sample photo][scale=1.2]{\nfoo\n//}\n") expected = <<-EOS ◆→開始:図←◆ -図1.1 sample photo - ◆→./images/chap1-sampleimg.png scale=1.2←◆ + +図1.1 sample photo ◆→終了:図←◆ EOS @@ -716,18 +993,18 @@ def test_inline_w end def test_inline_unknown - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@{n}\n") } assert_equal ':1: error: unknown image: n', e.message - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@{n}\n") } assert_equal ':1: error: unknown footnote: n', e.message - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@{n}\n") } assert_equal ':1: error: unknown headline: n', e.message %w[list table column].each do |name| - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@<#{name}>{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@<#{name}>{n}\n") } assert_equal ":1: error: unknown #{name}: n", e.message end %w[chap chapref title].each do |name| - e = assert_raises(ReVIEW::ApplicationError) { compile_block "@<#{name}>{n}\n" } + e = assert_raises(ReVIEW::ApplicationError) { compile_block("@<#{name}>{n}\n") } assert_equal ':1: error: key not found: "n"', e.message end end @@ -829,6 +1106,20 @@ def test_texequation_with_caption e=mc^2 ◆→終了:TeX式←◆ +EOS + actual = compile_block(src) + assert_equal expected, actual + + @config['caption_position']['equation'] = 'bottom' + + expected = <<-EOS +式1.1 + +◆→開始:TeX式←◆ +e=mc^2 +式1.1 The Equivalence of Mass ▲and☆ Energy +◆→終了:TeX式←◆ + EOS actual = compile_block(src) assert_equal expected, actual diff --git a/test/test_update.rb b/test/test_update.rb index 5af3c748c..174fdde09 100644 --- a/test/test_update.rb +++ b/test/test_update.rb @@ -64,7 +64,7 @@ def test_check_old_catalogs io = StringIO.new @u.instance_eval{ @logger = ReVIEW::Logger.new(io) } assert_raise(ApplicationError) { @u.check_old_catalogs(@tmpdir) } - assert_match(/review\-catalog\-converter/, io.string) + assert_match(/review-catalog-converter/, io.string) File.unlink(File.join(@tmpdir, fname)) end @@ -86,7 +86,7 @@ def test_check_own_files_reviewext io = StringIO.new @u.instance_eval{ @logger = ReVIEW::Logger.new(io) } @u.check_own_files(@tmpdir) - assert_match(/There is review\-ext\.rb file/, io.string) + assert_match(/There is review-ext\.rb file/, io.string) end def test_update_version_older @@ -385,7 +385,7 @@ def test_update_stys_modified File.write(File.join(@tmpdir, 'sty/review-base.sty'), "% MODIFIED\n") @u.update_tex_stys('review-jsbook', @tmpdir) - assert_match(/review\-base\.sty will be overridden/, io.string) + assert_match(/review-base\.sty will be overridden/, io.string) assert_equal cont, File.read(File.join(@tmpdir, 'sty/review-base.sty')) end diff --git a/test/test_webtocprinter.rb b/test/test_webtocprinter.rb index f8607f2de..a94e998f4 100644 --- a/test/test_webtocprinter.rb +++ b/test/test_webtocprinter.rb @@ -11,7 +11,7 @@ def setup end def test_webtocprinter_null - dummy_book = ReVIEW::Book::Base.load + dummy_book = ReVIEW::Book::Base.new # chap = ReVIEW::Book::Chapter.new(dummy_book, 1, '-', nil, StringIO.new) str = WEBTOCPrinter.book_to_string(dummy_book) expect = <<-EOB @@ -28,9 +28,9 @@ def test_webtocprinter_with_chapters - ch1.re - ch2.re EOB - mktmpbookdir 'catalog.yml' => catalog_yml, + mktmpbookdir('catalog.yml' => catalog_yml, 'ch1.re' => "= ch. 1\n\n111\n", - 'ch2.re' => "= ch. 2\n\n222\n" do |_dir, book, _files| + 'ch2.re' => "= ch. 2\n\n222\n") do |_dir, book, _files| str = WEBTOCPrinter.book_to_string(book) expect = <<-EOB
    @@ -51,9 +51,9 @@ def test_webtocprinter_with_parts - part2: - ch2.re EOB - mktmpbookdir 'catalog.yml' => catalog_yml, + mktmpbookdir('catalog.yml' => catalog_yml, 'ch1.re' => "= ch. 1\n\n111\n", - 'ch2.re' => "= ch. 2\n\n222\n" do |_dir, book, _files| + 'ch2.re' => "= ch. 2\n\n222\n") do |_dir, book, _files| str = WEBTOCPrinter.book_to_string(book) expect = <<-EOB
      @@ -78,11 +78,11 @@ def test_webtocprinter_with_partfiles - p2.re: - ch2.re EOB - mktmpbookdir 'catalog.yml' => catalog_yml, + mktmpbookdir('catalog.yml' => catalog_yml, 'p1.re' => "= This is PART1\n\np111\n", 'p2.re' => "= This is PART2\n\np111\n", 'ch1.re' => "= ch. 1\n\n111\n", - 'ch2.re' => "= ch. 2\n\n222\n" do |_dir, book, _files| + 'ch2.re' => "= ch. 2\n\n222\n") do |_dir, book, _files| str = WEBTOCPrinter.book_to_string(book) expect = <<-EOB
        @@ -116,7 +116,7 @@ def test_webtocprinter_full - post1.re - post2.re EOB - mktmpbookdir 'catalog.yml' => catalog_yml, + mktmpbookdir('catalog.yml' => catalog_yml, 'pre1.re' => "= PRE1\n\npre111\n", 'pre2.re' => "= PRE2\n\npre222\n", 'app1.re' => "= APP1\n\napp111\n", @@ -126,7 +126,7 @@ def test_webtocprinter_full 'post1.re' => "= POST1\n\npo111\n", 'post2.re' => "= POST2\n\npo222\n", 'ch1.re' => "= ch. 1\n\n111\n", - 'ch2.re' => "= ch. 2\n\n222\n" do |_dir, book, _files| + 'ch2.re' => "= ch. 2\n\n222\n") do |_dir, book, _files| str = WEBTOCPrinter.book_to_string(book) expect = <<-EOB
          @@ -144,6 +144,50 @@ def test_webtocprinter_full
        • POST1
        • POST2
        +EOB + assert_equal expect, str + end + end + + def test_webtocprinter_nochapter + catalog_yml = <<-EOB +CHAPS: +EOB + mktmpbookdir('catalog.yml' => catalog_yml) do |_dir, book, _files| + str = WEBTOCPrinter.book_to_string(book) + expect = <<-EOB + +EOB + assert_equal expect, str + end + end + + def test_webtocprinter_noheadline + catalog_yml = <<-EOB +CHAPS: + - ch1.re + - ch2.re + - ch3.re + - ch4.re +EOB + mktmpbookdir('catalog.yml' => catalog_yml, + 'ch1.re' => "A\n", + 'ch2.re' => "B\n\n= C\n== D\n", + 'ch3.re' => "//emlist{\nLIST\n//}\n", + 'ch4.re' => "==[column] E\n\n= F") do |_dir, book, _files| + str = WEBTOCPrinter.book_to_string(book) + expect = <<-EOB + EOB assert_equal expect, str end