diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..1bdd128379ed3bcb1524715655336ea6cb5d48a0 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +test/destination/ diff --git a/README.md b/README.md index 4667c5eb91f89bb292b387507dee48566dedeb60..6d4d882daa655e34afeab02b42f202bcf56c52b0 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ This jekyll plugin was inspired by [jekyll-pandoc-plugin][1] but it was changed to generate multiple outputs, rather than just using pandoc to generate jekyll html posts. Besides, it doesn't require the 'pandoc-ruby' gem. -It's used on [En Defensa del Software Libre][0]. +It's used on [En Defensa del Software Libre][0]. Please check [our +repo](https://github.com/edsl/endefensadelsl.org) if you like to see how +it works in production. [0]: http://endefensadelsl.org [1]: https://github.com/dsanson/jekyll-pandoc-plugin @@ -12,58 +14,127 @@ It's used on [En Defensa del Software Libre][0]. ## What does it do -First, it replaces the html generation for pandoc. This means you will have -support for pandoc's markdown extensions, like ~strikethrough~ and [@cite]. +It replaces the html generation for pandoc. This means you will have +support for pandoc's markdown extensions, like ~strikethrough~ and +[@cite], tables and [a lot more stuff](http://pandoc.org/README.html). -Second, it'll also generate the post in other formats you like, so your blog -can be made available in different formats at the same time. Epub for ebook -readers, mediawiki for copy&paste to wikis, etc. +It'll also generate the post in other formats you like, so your +blog can be made available in different formats at the same time. Epub +for ebook readers, mediawiki for copy&paste to wikis, etc. + +If instructed, this plugin will also generate pdfs in ready for print +format. ## Configuration Add to `_config.yml`: - pandoc: - skip: false - impose: false - output: ./tmp - flags: '--smart --bibliography=ref.bib' - site_flags: '--toc' - outputs: - pdf: '--latex-engine=latex' - epub: - markdown: +```yaml + +markdown: pandoc +pandoc: + skip: false + bundle_permalink: ':output_ext/:slug.:output_ext' + papersize: 'a5paper' + sheetsize: 'a4paper' + imposition: true + binder: true + + flags: '--smart' + site_flags: '--toc' + outputs: + latex: + pdf: '--latex-engine=xelatex' + epub: '--epub-chapter-level=2' + +``` + +* `markdown: pandoc` will instruct jekyll to use the pandoc html + converter. * `skip` allows you to skip the other formats generation and proceed with the regular jekyll site build. +* `site_flags` are flags applied to the html generation + * `flags` is a string with the flags you will normally pass to `pandoc` on cli. - It's used with all output types. + It's used on all output types. * `outputs` is a hash of output formats (even markdown!). You can add output-specific flags. -* `impose` creates ready to print PDFs if you're creating PDF output. +* `imposition` creates ready to print PDFs if you're creating PDF + output. + +* `binder` creates ready to print PDFs + +* `bundle_permalink` is the path of the bundled articles -**IMPORTANT**: If you were using this plugin before 2013-07-17 you have -to change your _config.yml syntax, change pandoc.outputs from array to -hashes (see the example :) +* `papersize` is the page size for PDF + +* `sheetsize` is the page size for ready the print PDF + +**IMPORTANT**: As of version 0.1.0 the syntax of the config changed. +Please upgrade your `_config.yml` accordingly. ## Front Matter -Support for epub covers has been added. You can add the path to a cover -on the front matter of the article to have pandoc add a cover image on -the epub result. +### Covers + +Support for epub covers has been added. You can add the path to +a cover on the front matter of the article to have pandoc add a cover +image on the epub result. --- - author: you - title: awesome stuff cover: images/awesome.png --- - etc... +### Paper sizes + +For PDFs, each article can have a `papersize` and a `sheetsize`. The +`papersize` indicates the page size, and the `sheetsize` indicates the +pages per fold size. + +Only A* sizes from A7 to A0 are supported for now. + + --- + papersize: a5paper + sheesize: a4paper + --- + +This example will generate a 2 pages per A4 sheet. + +### Bundled articles + +If articles share a category, the generator will create a PDF book +including all of them. The name of the category will become the title +of the book. + + --- + category: [ 'En Defensa del Software Libre #0' ] + --- + +The papersize will be the `papersize` of the first article found or the +default papersize on the `_config.yml` file. Same applies for +`sheetsize`. + +NOTE: Authorship will be set to empty. This could change in the future. + +## Bibliography + +If you have bibliography, pandoc recommends leaving an empty +section at the end of the document if you want to have a separate +section for it. For bundled articles, this plugin will remove the extra +sections and create one for everything at the end. + + # Bibliography + + <EOF> + +You can also use the underlined version of the section title (aka +`settext style` vs `atx style`). ## Layout @@ -75,7 +146,7 @@ other available formats from the post itself: {% for format in site.pandoc.outputs %} {% capture extension %}{{ format | first }}{% endcapture %} <li> - <a href="{{ extension }}{{ page.url | remove:'.html' }}.{{ extension }}"> + <a href="{{ page.url | remove:'.html' }}.{{ extension }}"> {{ extension }} </a> </li> @@ -84,13 +155,7 @@ other available formats from the post itself: ## How to install -Download a release and expand the archive, or clone the repository from GitHub. Go -into the generated directory and install as follows: - - gem build jekyll-pandoc-multiple-formats.gemspec - gem install --local jekyll-pandoc-multiple-formats.gem - -Add this snippet to your `_config.yml` on jekyll >=1.3 +Add this snippet to your `_config.yml` on jekyll 1.3 gems: [ 'jekyll-pandoc-multiple-formats' ] @@ -101,3 +166,4 @@ Alternative, see ## How to run Execute `jekyll build` normally :D + diff --git a/Rakefile b/Rakefile index 29955274e0d42e164337c411ad9144e8ffd7e46e..e690159c3c5aee77c592415e857acfda7426dd79 100644 --- a/Rakefile +++ b/Rakefile @@ -1 +1,13 @@ require "bundler/gem_tasks" +Bundler.setup(:default, :development, :test) + +require 'rake' +require 'rake/testtask' + +Rake::TestTask.new(:test) do |test| + test.libs << 'lib' << 'test' + test.pattern = 'test/**/test_*.rb' + test.verbose = true +end + +task :default => 'test' diff --git a/jekyll-pandoc-multiple-formats.gemspec b/jekyll-pandoc-multiple-formats.gemspec index 54efc127a81192bd9a86ce70a82ba89e13284db2..950dbaae33569e057fba0c82a7991d31aea1efbb 100644 --- a/jekyll-pandoc-multiple-formats.gemspec +++ b/jekyll-pandoc-multiple-formats.gemspec @@ -21,7 +21,10 @@ Gem::Specification.new do |gem| gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.require_paths = ['lib'] - gem.add_dependency('jekyll') - gem.add_dependency('pdf_info') - gem.add_dependency('rtex') + gem.add_dependency('jekyll', '~> 3.0.0') + gem.add_dependency('pdf_info', '~> 0.5.0') + gem.add_dependency('rtex', '~> 2.1.0') + gem.add_development_dependency('rake', '~> 10.5.0') + gem.add_development_dependency('minitest', '~> 5.8.0') + gem.add_development_dependency('shoulda', '~> 3.5.0') end diff --git a/lib/jekyll-pandoc-multiple-formats.rb b/lib/jekyll-pandoc-multiple-formats.rb index 257e131850e9bf7c101c2cf3e4f831c2755520cf..2596807b756eb00eb1b082201e5f5007ead6efbf 100644 --- a/lib/jekyll-pandoc-multiple-formats.rb +++ b/lib/jekyll-pandoc-multiple-formats.rb @@ -22,7 +22,16 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. require 'open3' + require 'jekyll-pandoc-multiple-formats/version' +require 'jekyll-pandoc-multiple-formats/config' + +# TODO this may go to a separate gem +require 'jekyll-pandoc-multiple-formats/printer' require 'jekyll-pandoc-multiple-formats/imposition' +require 'jekyll-pandoc-multiple-formats/binder' +require 'jekyll-pandoc-multiple-formats/unite' + +require 'jekyll-pandoc-multiple-formats/pandoc_file' require 'jekyll-pandoc-multiple-formats/generator' require 'jekyll-pandoc-multiple-formats/converter' diff --git a/lib/jekyll-pandoc-multiple-formats/binder.rb b/lib/jekyll-pandoc-multiple-formats/binder.rb new file mode 100644 index 0000000000000000000000000000000000000000..7df250e284e5ac5906419a3482241e3cc2bba84b --- /dev/null +++ b/lib/jekyll-pandoc-multiple-formats/binder.rb @@ -0,0 +1,45 @@ +# Copyright (c) 2012-2015 Nicolás Reynolds <fauno@endefensadelsl.org> +# 2012-2013 Mauricio Pasquier Juan <mpj@endefensadelsl.org> +# 2013 Brian Candler <b.candler@pobox.com> +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +module JekyllPandocMultipleFormats + class Binder < Printer + def initialize(file, papersize = nil, sheetsize = nil, extra_options = nil) + super + @output_file = file.gsub(/\.pdf\Z/, '-binder.pdf') + + render_template + self + end + + def to_nup + @pages.times.map{|i|i+1}.map do |page| + sheet=[] + @nup.times do + sheet << page + end + + sheet + end.flatten + end + end +end diff --git a/lib/jekyll-pandoc-multiple-formats/config.rb b/lib/jekyll-pandoc-multiple-formats/config.rb new file mode 100644 index 0000000000000000000000000000000000000000..b88f8bbe446e244d32bd9f0c812151b28b693920 --- /dev/null +++ b/lib/jekyll-pandoc-multiple-formats/config.rb @@ -0,0 +1,40 @@ +module JekyllPandocMultipleFormats + class Config + DEFAULTS = { + 'skip' => false, + 'bundle_permalink' => ':output_ext/:slug.:output_ext', + 'papersize' => 'a5paper', + 'sheetsize' => 'a4paper', + 'imposition' => true, + 'binder' => true, + 'signature' => 20, + 'flags' => '--smart', + 'site_flags' => '--toc', + 'outputs' => {}, + 'covers_dir' => 'images/' + } + + attr_accessor :config + + def initialize(config = {}) + @config = Jekyll::Utils.deep_merge_hashes(DEFAULTS, config) + end + + def skip? + @config['skip'] + end + + def imposition? + @config['imposition'] + end + + def binder? + @config['binder'] + end + + # TODO magic + def outputs + @config['outputs'] + end + end +end diff --git a/lib/jekyll-pandoc-multiple-formats/converter.rb b/lib/jekyll-pandoc-multiple-formats/converter.rb index 2c98a4a62ad6240cb6b8713bffb96f058ca90c12..7123226227b8ffee4b4089642e3b03e32c391ada 100644 --- a/lib/jekyll-pandoc-multiple-formats/converter.rb +++ b/lib/jekyll-pandoc-multiple-formats/converter.rb @@ -49,13 +49,15 @@ module JekyllPandocMultipleFormats output = '' Dir::chdir(@config['source']) do - Open3::popen3("pandoc -t html5 #{flags}") do |stdin, stdout, stderr| + Open3::popen3("pandoc -t html5 #{flags}") do |stdin, stdout, stderr, thread| stdin.puts content stdin.close output = stdout.read.strip STDERR.print stderr.read + # Wait for the process to finish + thread.value end end @@ -68,7 +70,7 @@ module JekyllPandocMultipleFormats end def output_ext(ext) - ".html" + '.html' end end end diff --git a/lib/jekyll-pandoc-multiple-formats/generator.rb b/lib/jekyll-pandoc-multiple-formats/generator.rb index b2be5c2e7e66adc757fa5ea416f1d0ff4035ca46..41821c9fd91cd12748d6da50dbe4e203147e00a0 100644 --- a/lib/jekyll-pandoc-multiple-formats/generator.rb +++ b/lib/jekyll-pandoc-multiple-formats/generator.rb @@ -24,79 +24,80 @@ module Jekyll class PandocGenerator < Generator - def generate(site) - outputs = site.config['pandoc']['outputs'] - flags = site.config['pandoc']['flags'] + safe true - outputs.each_pair do |output, extra_flags| + attr_accessor :site, :config - # Skip conversion if we're skipping, but still cleanup the outputs hash - next if site.config['pandoc']['skip'] + def generate(site) + @site ||= site + @config ||= JekyllPandocMultipleFormats::Config.new(@site.config['pandoc']) - # If there isn't a config entry for pandoc's output throw it with the rest - base_dir = File.join(site.source, site.config['pandoc']['output']) || site.source + return if @config.skip? - site.posts.each do |post| + # we create a single array of files + @pandoc_files = [] - post_path = File.join(base_dir, output, File.dirname(post.url)) + @config.outputs.each_pair do |output, extra_flags| + @site.posts.docs.each do |post| - puts "Creating #{post_path}" - FileUtils.mkdir_p(post_path) + pandoc_file = PandocFile.new(@site, output, post) + next unless pandoc_file.write - filename = post.url.gsub(/\.html$/, ".#{output}") - # Have a filename! - filename = "#{post.url.gsub(/\//, "-").gsub(/-$/, "")}.#{output}" if filename =~ /\/$/ + @site.keep_files << pandoc_file.relative_path + @pandoc_files << pandoc_file + end - filename_with_path = File.join(base_dir, output, filename) + @site.post_attr_hash('categories').each_pair do |title, posts| + posts.sort! + pandoc_file = PandocFile.new(@site, output, posts, title) + next unless pandoc_file.write - # Special cases, stdout is disabled for these - if ['pdf', 'epub', 'odt', 'docx'].include?(output) - output_flag = "-o #{filename_with_path}" - else - output_flag = "-t #{output} -o #{filename_with_path}" - end - - # Add cover if epub - if output == "epub" and not post.data['cover'].nil? - output_flag << " --epub-cover-image=#{post.data['cover']}" - end + @site.keep_files << pandoc_file.relative_path + @pandoc_files << pandoc_file + end + end - # The command - # Move to the source dir since everything will be relative to # that - pandoc = "pandoc #{flags} #{output_flag} #{extra_flags}" + @pandoc_files.each do |pandoc_file| + # If output is PDF, we also create the imposed PDF + if pandoc_file.pdf? - # Inform what's being done - puts pandoc + if @config.imposition? - # Make the markdown header so pandoc receives metadata - content = "% #{post.data['title']}\n" - content << "% #{post.data['author']}\n" - content << post.content + imposed_file = JekyllPandocMultipleFormats::Imposition + .new(pandoc_file.path, pandoc_file.papersize, + pandoc_file.sheetsize, pandoc_file.signature) - # Do the stuff - Dir::chdir(site.config['source']) do - Open3::popen3(pandoc) do |stdin, stdout, stderr| - stdin.puts content - stdin.close - STDERR.print stderr.read - end + imposed_file.write + @site.keep_files << imposed_file.relative_path(@site.dest) end - # Skip failed files - next if not File.exist? filename_with_path - # If output is PDF, we also create the imposed PDF - if output == 'pdf' and site.config['pandoc']['impose'] + if @config.binder? - imposed_file = JekyllPandocMultipleFormats::Imposition.write(filename) + binder_file = JekyllPandocMultipleFormats::Binder + .new(pandoc_file.path, pandoc_file.papersize, + pandoc_file.sheetsize) - site.static_files << StaticFile.new(site, base_dir, output, imposed_file) + binder_file.write + @site.keep_files << binder_file.relative_path(@site.dest) end - # Add them to the static files list - site.static_files << StaticFile.new(site, base_dir, output, filename) + # Add covers to PDFs after building ready for print files + if pandoc_file.has_cover? + # Generate the cover + next unless pandoc_file.pdf_cover! + united_output = pandoc_file.path.gsub(/\.pdf\Z/, '-cover.pdf') + united_file = JekyllPandocMultipleFormats::Unite + .new(united_output, [pandoc_file.pdf_cover,pandoc_file.path], pandoc_file.papersize) + + if united_file.write + # Replace the original file with the one with cover + FileUtils.rm_f(pandoc_file.path) + FileUtils.mv(united_output, pandoc_file.path) + end + end end end - end + end end end diff --git a/lib/jekyll-pandoc-multiple-formats/imposition.rb b/lib/jekyll-pandoc-multiple-formats/imposition.rb index 44428c3beef6680f0399e038dbff50dadfd913d1..7326aaadc3e0c9db0e724d1108c322cb64cd0943 100644 --- a/lib/jekyll-pandoc-multiple-formats/imposition.rb +++ b/lib/jekyll-pandoc-multiple-formats/imposition.rb @@ -21,42 +21,25 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -require 'pdf/info' -require 'rtex' - module JekyllPandocMultipleFormats - class Imposition - - # Imposition template - TEMPLATE = <<-EOT.gsub(/^\s+/, '') - \\documentclass[@@papersize@@,10pt]{article} - - \\usepackage{pgfpages} - \\usepackage{pdfpages} + class Imposition < Printer - \\pgfpagesuselayout{@@nup@@ on 1}[@@papersize@@@@extra@@] + attr_accessor :rounded_pages, :blank_pages, :signature - \\begin{document} - \\includepdf[pages={@@pages@@}]{@@document@@} - \\end{document} - EOT + def initialize(file, papersize = nil, sheetsize = nil, signature = nil, extra_options = nil) + super(file, papersize, sheetsize, extra_options) + @output_file = file.gsub(/\.pdf\Z/, '-imposed.pdf') + # Total pages must be modulo 4 + @rounded_pages = round_to_nearest(@pages, 4) + @blank_pages = @rounded_pages - @pages + # If we don't use a signature, make a single fold + @signature = signature || @rounded_pages - def round_to_nearest(int, near) - (int + (near - 1)) / near * near + render_template + self end - def write(file, papersize = 'a4paper', nup = 4, extra_options = nil) - return unless /\.pdf\Z/ =~ file - return unless pdf = PDF::Info.new(file) - - file = File.realpath(file) - - # Pages - pages = pdf.metadata[:page_count] - # Total pages must be modulo 4 - rounded_pages = round_to_nearest(pages, 4) - blank_pages = rounded_pages - pages - + def to_nup # 14 pages example: # [ {}, 1, 2, {}, 14, 3, 4, 13, 12, 5, 6, 11, 10, 7, 8, 9 ] # @@ -71,44 +54,42 @@ module JekyllPandocMultipleFormats # An array of numbered pages padded with blank pages ('{}') # # [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 ] + [ '{}', '{}' ] - padded = pages.times.map{|i|i+1} + Array.new(blank_pages, '{}') - # Split in halves - # [ [ 1, 2, 3, 4, 5, 6, 7, 8 ], - # [ 9, 10, 11, 12, 13, 14, '{}', '{}' ] ] - halved = padded.each_slice(rounded_pages / 2).to_a - # Add a nil as last page. When we reverse it and intercalate by - # two pages, we'll have [nil, last_page] instead of - # [last_page,second_to_last_page] - # - # [ [ 1, 2, 3, 4, 5, 6, 7, 8 ], - # [ 9, 10, 11, 12, 13, 14, '{}', '{}', nil ] ] - halved.last << nil - # Reverse the second half and intercalate by two pages into the - # first one. Then remove nil elements and flatten the array. - # - # [ [ 1, 2, 3, 4, 5, 6, 7, 8 ], - # [ nil, '{}', '{}', 14, 13, 12, 11, 10 ] ] - # - # [ {}, 1, 2, {}, 14, 3, 4, 13, 12, 5, 6, 11, 10, 7, 8, 9 ] - order = halved.last.reverse.each_slice(2).zip(halved.first.each_slice(2).to_a).flatten.compact + padded = @pages.times.map{|i|i+1} + Array.new(@blank_pages, '{}') - # Create the matrix of pages (N-Up) + # If we have a signature, we have to split in groups up to the + # amount of pages per signature, and then continue with the rest # - # ["{}", 1, "{}", 1, 2, "{}", 2, "{}", 14, 3, 14, 3, 4, 13, 4, 13, 12, 5, 12, 5, 6, 11, 6, 11, 10, 7, 10, 7, 8, 9, 8, 9] - nup_pages = order.each_slice(2).map{ |i| ((nup/2)-1).times { i = i+i }; i }.flatten - - template = TEMPLATE.gsub('@@nup@@', nup) - .gsub('@@papersize@@', papersize) - .gsub('@@extra@@', extra_options) - - # Create the imposed file - file_name = file.gsub(/\.pdf\Z/, '-imposed.pdf') - pdflatex = RTeX::Document.new(template.gsub('@@document@@', file).gsub('@@pages@@', nup_pages * ',')) - pdflatex.to_pdf do |pdf_file| - FileUtils.cp pdf_file, file_name + # If we have no signature, we assume it's equal to the total + # amount of pages, so you only have one fold + signed = padded.each_slice(@signature).to_a + folds = [] + signed.each do |fold| + # + # Split in halves + # [ [ 1, 2, 3, 4, 5, 6, 7, 8 ], + # [ 9, 10, 11, 12, 13, 14, '{}', '{}' ] ] + halved = fold.each_slice(@signature / 2).to_a + # Add a nil as last page. When we reverse it and intercalate by + # two pages, we'll have [nil, last_page] instead of + # [last_page,second_to_last_page] + # + # [ [ 1, 2, 3, 4, 5, 6, 7, 8 ], + # [ 9, 10, 11, 12, 13, 14, '{}', '{}', nil ] ] + halved.last << nil + # Reverse the second half and intercalate by two pages into the + # first one. Then remove nil elements and flatten the array. + # + # [ [ 1, 2, 3, 4, 5, 6, 7, 8 ], + # [ nil, '{}', '{}', 14, 13, 12, 11, 10 ] ] + # + # [ {}, 1, 2, {}, 14, 3, 4, 13, 12, 5, 6, 11, 10, 7, 8, 9 ] + folds << halved.last.reverse.each_slice(2).zip(halved.first.each_slice(2).to_a).flatten.compact end - file_name + # Create the matrix of pages (N-Up) per fold + # + # ["{}", 1, "{}", 1, 2, "{}", 2, "{}", 14, 3, 14, 3, 4, 13, 4, 13, 12, 5, 12, 5, 6, 11, 6, 11, 10, 7, 10, 7, 8, 9, 8, 9] + folds.map { |o| o.each_slice(2).map{ |i| a=[]; (@nup/2).times { a = a+i }; a }}.flatten end end end diff --git a/lib/jekyll-pandoc-multiple-formats/pandoc_file.rb b/lib/jekyll-pandoc-multiple-formats/pandoc_file.rb new file mode 100644 index 0000000000000000000000000000000000000000..9932d692ae16789d68b96f01f8e98f7c6aa94e19 --- /dev/null +++ b/lib/jekyll-pandoc-multiple-formats/pandoc_file.rb @@ -0,0 +1,277 @@ +# Copyright (c) 2012-2015 Nicolás Reynolds <fauno@endefensadelsl.org> +# 2012-2013 Mauricio Pasquier Juan <mpj@endefensadelsl.org> +# 2013 Brian Candler <b.candler@pobox.com> +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +module Jekyll + class PandocFile + include Convertible + + + attr_reader :format, :site, :config, :flags, :posts, :slug, :title, :url + attr_reader :papersize, :sheetsize, :signature + + def initialize(site, format, posts, title = nil) + @site = site + @config = JekyllPandocMultipleFormats::Config.new(@site.config['pandoc']).config + @format = format + @flags = [] + + if posts.is_a? Array + @posts = posts + + raise ArgumentError.new "'title' argument is required for multipost file" unless title + + @title = title + @slug = Utils.slugify(title) + else + @posts = [posts] + @slug = posts.data['slug'] + @title = posts.data['title'] unless title + end + end + + def path + # path is full destination path with all elements of permalink + path = @site.in_dest_dir(relative_path) + end + + def relative_path + path = URL.unescape_path(url) + + # but if the post is going to be index.html, use slug + format + # (ie /year/month/slug/slug.pdf) + if url.end_with? '/' + path = File.join(path, @slug) + path << '.' + path << @format + end + + path + end + + # if it's just one article, the url is the same except it's not html + # otherwise we use the permalink template with some placeholder + def url + @url = if single_post? + single_post.url.gsub(/\.html$/, ".#{@format}") + else + URL.new({ + template: @config['bundle_permalink'], + placeholders: url_placeholders, + permalink: nil }).to_s + end + end + + def url_placeholders + { output_ext: @format, + slug: @slug, + title: @title } + end + + # adds post metadata as yaml metadata + def yaml_metadata + if single_post? + + # if we were to merge config to data, the default options would + # take precedence + data = Jekyll::Utils.deep_merge_hashes @config, single_post.data + single_post.merge_data! data + + + # we extract the excerpt because it serializes as an object and + # breaks pandoc + metadata = single_post.data.reject{ |k| k == 'excerpt' } + + if @config['date_format'] + metadata['date'] = metadata['date'].strftime(@config['date_format']) + else + metadata.delete('date') + end + else + # we have to use this fugly syntax because jekyll doesn't do + # symbols + metadata = { + 'date' => @config['date_format'] ? Date.today.strftime(@config['date_format']) : nil, + 'title' => @title, + 'author' => nil, + 'papersize' => papersize, + 'sheetsize' => sheetsize, + 'signature' => signature + } + end + + # fix page sizes, pandoc uses 'A4' while printer.rb uses + # 'a4paper' + %w[papersize sheetsize].each do |size| + metadata[size] = fix_size metadata[size] + end + + metadata.to_yaml << "\n---\n" + end + + def content + if single_post? + single_post.content + else + header_re = /^(#+.*\n*|.*\n[=-]+\n*)\Z/ + bib_title = "" + @posts.map do |post| + bib_title = post.content.match(header_re).to_s if bib_title.empty? + # remove bibliography titles + # since pandoc does it's own bibliography output, it recommends + # leaving an empty chapter title to mark it as such + post.content.gsub(header_re, '') + # we add the first bibliography title we can find in the end + end.join("\n\n\n") << bib_title + end + end + + def write + FileUtils.mkdir_p(File.dirname(path)) + # Remove the file before creating it + FileUtils.rm_f(path) + # Move to the source dir since everything will be relative to that + Dir::chdir(@site.config['source']) do + # Do the stuff + Open3::popen3(command) do |stdin, stdout, stderr, thread| + stdin.puts yaml_metadata + stdin.puts content + stdin.close + STDERR.print stderr.read + + # Wait for the process to finish + thread.value + end + end + + File.exists?(path) + end + + # Returns a cover, without checking if it exists + # + # It assumes covers are in PNG format + def cover + if single_post? && single_post.data['cover'] + File.join(@site.config['source'], single_post.data['cover']) + else + File.join(@site.config['source'], @config['covers_dir'], "#{@slug}.png") + end + end + + # Returns a PDF cover + def pdf_cover + cover.gsub(/\.[^\.]+\Z/, '.pdf') + end + + def pdf_cover! + if has_cover? && !File.exists?(pdf_cover) + Dir::chdir(@site.config['source']) do + Open3::popen3("convert \"#{cover}\" \"#{pdf_cover}\"") do |stdin, stdout, stderr, thread| + STDERR.print stderr.read + + # Wait for the process to finish + thread.value + end + end + end + + File.exists?(pdf_cover) + end + + def flags + return @flags.join ' ' unless @flags.empty? + + @flags << @config['flags'] + @flags << @config['outputs'][@format] if @config['outputs'].key?(@format) + @flags << '-o' + @flags << path + + # Binary formats don't need a -t flag + unless binary? + @flags << '-t' + @flags << @format + end + + if epub? && has_cover? + @flags << '--epub-cover-image' + @flags << cover + end + + @flags.join ' ' + end + + def command + 'pandoc ' << flags + end + + def pdf? + @format == 'pdf' + end + + def epub? + %w[epub epub3].include? @format + end + + # These formats are binary files and must use the -o flag + def binary? + %w[pdf epub epub3 odt docx].include? @format + end + + def single_post? + @posts.count == 1 + end + + def has_cover? + File.exists? cover + end + + def papersize + @papersize ||= find_option 'papersize' + end + + def sheetsize + @sheetsize ||= find_option 'sheetsize' + end + + def signature + @signature ||= find_option 'signature' + end + + private + + def single_post + @posts.first + end + + def find_option(name) + if @posts.any? { |p| p.data.key? name } + @posts.select { |p| p.data.key? name }.first.data[name] + else + @config[name] + end + end + + def fix_size(size) + size.gsub /paper$/, '' + end + end +end diff --git a/lib/jekyll-pandoc-multiple-formats/printer.rb b/lib/jekyll-pandoc-multiple-formats/printer.rb new file mode 100644 index 0000000000000000000000000000000000000000..9ee45a967356a3485ca7dd8286088410efb6d6e5 --- /dev/null +++ b/lib/jekyll-pandoc-multiple-formats/printer.rb @@ -0,0 +1,105 @@ +# Copyright (c) 2012-2015 Nicolás Reynolds <fauno@endefensadelsl.org> +# 2012-2013 Mauricio Pasquier Juan <mpj@endefensadelsl.org> +# 2013 Brian Candler <b.candler@pobox.com> +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +require 'pdf/info' +require 'rtex' + +module JekyllPandocMultipleFormats + class Printer + TEMPLATE = <<-EOT.gsub(/^\s+/, '') + \\documentclass[@@sheetsize@@,10pt]{article} + + \\usepackage{pgfpages} + \\usepackage{pdfpages} + + \\pgfpagesuselayout{@@nup@@ on 1}[@@sheetsize@@,@@extra_options@@] + + \\begin{document} + \\includepdf[pages={@@pages@@}]{@@document@@} + \\end{document} + EOT + + # TODO allow custom sheet sizes + SHEET_SIZES = { + a7paper: 2, + a6paper: 4, + a5paper: 8, + a4paper: 16, + a3paper: 32, + a2paper: 64, + a1paper: 128, + a0paper: 256 + } + + attr_accessor :output_file, :original_file, :pages, :template, + :papersize, :sheetsize, :nup, :extra_options, :relative_path + + def initialize(file, papersize = nil, sheetsize = nil, extra_options = nil) + return unless /\.pdf\Z/ =~ file + return unless pdf = PDF::Info.new(file) + + @original_file = File.realpath(file) + @papersize = papersize || 'a5paper' + @sheetsize = sheetsize || 'a4paper' + @pages = pdf.metadata[:page_count] + @nup = SHEET_SIZES[@sheetsize.to_sym] / SHEET_SIZES[@papersize.to_sym] + @extra_options = extra_options || '' + + # These layouts require a landscape page + @extra_options << 'landscape' if is_landscape? + + self + end + + def relative_path(from) + @relative_path ||= Pathname.new(output_file).relative_path_from(Pathname.new(from)).to_s + end + + def write + # Create the imposed file + pdflatex = RTeX::Document.new(template) + pdflatex.to_pdf do |pdf_file| + FileUtils.cp pdf_file, @output_file + end + + File.exists? @output_file + end + + def is_landscape? + [2,8,32,128].include? @nup + end + + def round_to_nearest(int, near) + (int + (near - 1)) / near * near + end + + def render_template + @template = TEMPLATE + .gsub('@@nup@@', @nup.to_s) + .gsub('@@sheetsize@@', @sheetsize) + .gsub('@@extra_options@@', @extra_options) + .gsub('@@document@@', @original_file) + .gsub('@@pages@@', to_nup * ',') + end + end +end diff --git a/lib/jekyll-pandoc-multiple-formats/unite.rb b/lib/jekyll-pandoc-multiple-formats/unite.rb new file mode 100644 index 0000000000000000000000000000000000000000..2df3cd33dc7c957943c4d156181bcd6dedb21786 --- /dev/null +++ b/lib/jekyll-pandoc-multiple-formats/unite.rb @@ -0,0 +1,69 @@ +# Copyright (c) 2012-2015 Nicolás Reynolds <fauno@endefensadelsl.org> +# 2012-2013 Mauricio Pasquier Juan <mpj@endefensadelsl.org> +# 2013 Brian Candler <b.candler@pobox.com> +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +module JekyllPandocMultipleFormats + class Unite < Printer + TEMPLATE = <<-EOT.gsub(/^\s+/, '') + \\documentclass{article} + \\usepackage{pdfpages} + + \\begin{document} + @@include@@ + \\end{document} + EOT + + INCLUDE_TEMPLATE = '\\includepdf[fitpaper=true,pages=-]{@@document@@}' + + attr_accessor :files, :template + + def initialize(output_file, files) + raise ArgumentError.new 'An array of filenames is required' unless files.is_a? Array + + @output_file = output_file + @files = files + + render_template + self + end + + def <<(file) + @files << File.realpath(file) if /\.pdf\Z/ =~ file + end + + def files=(file_array) + return unless file_array.is_a? Array + + file_array.each do |f| + self << f + end + end + + def render_template + includes = @files.map do |f| + INCLUDE_TEMPLATE.gsub(/@@document@@/, f) + end + + @template = TEMPLATE.gsub('@@include@@', includes.join("\n")) + end + end +end diff --git a/lib/jekyll-pandoc-multiple-formats/version.rb b/lib/jekyll-pandoc-multiple-formats/version.rb index e7afdc1c5429ca1d554f5166fab0283c420e5038..a47004e3578c9b866af907908d4939117c8dadad 100644 --- a/lib/jekyll-pandoc-multiple-formats/version.rb +++ b/lib/jekyll-pandoc-multiple-formats/version.rb @@ -1,3 +1,3 @@ module JekyllPandocMultipleFormats - VERSION = "0.0.7" + VERSION = '0.2.0' end diff --git a/test/fixtures/test.pdf b/test/fixtures/test.pdf new file mode 100644 index 0000000000000000000000000000000000000000..57338139d8d44aa84cd841218b2de0d3a25e3061 Binary files /dev/null and b/test/fixtures/test.pdf differ diff --git a/test/source/_layouts/nil.html b/test/source/_layouts/nil.html new file mode 100644 index 0000000000000000000000000000000000000000..57f3fe16625c8ff95eb894f33270b136f2088f1a --- /dev/null +++ b/test/source/_layouts/nil.html @@ -0,0 +1 @@ +{{ yield }} diff --git a/test/source/_posts/2014-01-01-test.markdown b/test/source/_posts/2014-01-01-test.markdown new file mode 100644 index 0000000000000000000000000000000000000000..5b9160112deab9dd5afffee5a418f7216f3e31df --- /dev/null +++ b/test/source/_posts/2014-01-01-test.markdown @@ -0,0 +1,10 @@ +--- +layout: nil +title: A Title +author: minitest +categories: [ test ] +--- + +# A Title + +Some text. diff --git a/test/source/_posts/2015-01-01-another_post.markdown b/test/source/_posts/2015-01-01-another_post.markdown new file mode 100644 index 0000000000000000000000000000000000000000..9028409958810fe79fbcd9d3338ebf3e4b708c86 --- /dev/null +++ b/test/source/_posts/2015-01-01-another_post.markdown @@ -0,0 +1,11 @@ +--- +layout: nil +title: Another Title +author: minitest +categories: [ test ] +--- + +# Another Title + +Some text. + diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 0000000000000000000000000000000000000000..398d8555f3d7d0cdb03b3219354b284f3b2e70a0 --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,37 @@ +$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) +$LOAD_PATH.unshift(File.dirname(__FILE__)) + +TEST_PDF = File.join(File.dirname(__FILE__),'fixtures/test.pdf') +TEST_IMPOSED_PDF = File.join(File.dirname(__FILE__),'fixtures/test-imposed.pdf') +TEST_BINDER_PDF = File.join(File.dirname(__FILE__),'fixtures/test-binder.pdf') + +TEST_DIR = File.expand_path("../", __FILE__) +SOURCE_DIR = File.expand_path("source", TEST_DIR) +DEST_DIR = File.expand_path("destination", TEST_DIR) + +require 'rubygems' +require 'minitest/autorun' +require 'shoulda' +require 'jekyll' +require 'jekyll-pandoc-multiple-formats' + + +# Copied from jekyll-archives (c) Alfred Xing, licensed under MIT with +# the following note: +# Taken from jekyll-mentions (Copyright (c) 2014 GitHub, Inc. Licensened under the MIT). +class Minitest::Test + def fixture_site(config = {}) + Jekyll::Site.new( + Jekyll::Utils.deep_merge_hashes( + Jekyll::Utils.deep_merge_hashes( + Jekyll::Configuration::DEFAULTS, + { + "source" => SOURCE_DIR, + "destination" => DEST_DIR + } + ), + config + ) + ) + end +end diff --git a/test/test_pandoc_file.rb b/test/test_pandoc_file.rb new file mode 100644 index 0000000000000000000000000000000000000000..ed81bc29593cb0df91697b6062de84ba7d4a3b50 --- /dev/null +++ b/test/test_pandoc_file.rb @@ -0,0 +1,106 @@ +require 'test_helper' + +class TestPandocFile < MiniTest::Test + context 'single post' do + setup do + @site = fixture_site({ 'pandoc' => { }}) + @site.read + @pandoc_file = Jekyll::PandocFile.new(@site, 'pdf', @site.posts.docs.first) + end + + should 'be a single post' do + assert @pandoc_file.single_post? + end + + should 'have a format' do + assert_equal 'pdf', @pandoc_file.format + end + + should 'have an extension' do + assert @pandoc_file.path.end_with?('pdf') + end + + should 'share the same path with different extension' do + assert @pandoc_file.path.end_with?(@site.posts.docs.first.url.gsub(/html\Z/, 'pdf')) + end + + should 'share the same title' do + assert_equal @site.posts.docs.first.data['title'], @pandoc_file.title + end + + should 'have an url' do + assert_equal @site.posts.docs.first.url.gsub(/html\Z/, 'pdf'), @pandoc_file.url + end + + should 'have metadata in yaml format' do + assert @pandoc_file.yaml_metadata.start_with?('---') + assert @pandoc_file.yaml_metadata.end_with?("---\n") + end + + should 'have content' do + assert @pandoc_file.content.is_a?(String) + assert_equal @site.posts.docs.first.content, @pandoc_file.content + end + + should 'be a pdf' do + assert @pandoc_file.pdf? + assert @pandoc_file.binary? + refute @pandoc_file.epub? + end + + should 'not have a cover' do + refute @pandoc_file.has_cover? + refute @pandoc_file.cover + end + + should 'have a papersize' do + refute @pandoc_file.papersize.empty? + end + + should 'have a sheetsize' do + refute @pandoc_file.sheetsize.empty? + end + + should 'have flags' do + assert @pandoc_file.flags.is_a?(String) + assert @pandoc_file.flags.length > 0 + end + + should 'create a file' do + assert @pandoc_file.write + assert File.exists?(@pandoc_file.path) + end + + end + + context 'several posts' do + setup do + @site = fixture_site({ 'pandoc' => { }}) + @site.read + @pandoc_file = Jekyll::PandocFile.new(@site, 'pdf', @site.posts.docs, 'Multipost test') + end + + should 'be multipost' do + refute @pandoc_file.single_post? + end + + should 'have a title' do + assert_raises(ArgumentError) { Jekyll::PandocFile.new(@site, 'pdf', @site.posts.docs) } + refute @pandoc_file.title.empty? + end + + should 'have a path' do + assert @pandoc_file.path.end_with?("#{@pandoc_file.slug}.#{@pandoc_file.format}") + end + + should 'have metadata in yaml format' do + assert @pandoc_file.yaml_metadata.start_with?('---') + assert @pandoc_file.yaml_metadata.end_with?("---\n") + end + + should 'create a file' do + assert @pandoc_file.write + assert File.exists?(@pandoc_file.path) + end + end +end diff --git a/test/test_printer.rb b/test/test_printer.rb new file mode 100644 index 0000000000000000000000000000000000000000..1192121e45d65acee8eed0ab24360c4a61c469cc --- /dev/null +++ b/test/test_printer.rb @@ -0,0 +1,483 @@ +require 'test_helper' + +class TestJekyllPandocMultipleFormats < MiniTest::Test + context 'imposition' do + setup do + @imposition = JekyllPandocMultipleFormats::Imposition.new(TEST_PDF) + end + + should 'have a new file name' do + assert_equal TEST_IMPOSED_PDF, @imposition.output_file + end + + should 'write a new filename' do + assert @imposition.write + assert File.exists?(TEST_IMPOSED_PDF) + assert FileUtils.rm(TEST_IMPOSED_PDF) + end + + should 'have number of pages' do + assert_equal 23, @imposition.pages + end + + should 'have a sheet size' do + assert_equal 'a4paper', @imposition.sheetsize + end + + should 'have a page size' do + assert_equal 'a5paper', @imposition.papersize + end + + should 'have rounded pages' do + assert_equal 24, @imposition.rounded_pages + end + + should 'have blank pages' do + assert_equal 1, @imposition.blank_pages + end + + should 'have a signature' do + assert_equal 24, @imposition.signature + end + + should 'have ordered pages' do + assert_equal ["{}", 1, 2, 23, 22, 3, 4, 21, 20, 5, 6, 19, 18, 7, 8, 17, 16, 9, 10, 15, 14, 11, 12, 13], @imposition.to_nup + end + end + + context 'signed imposition' do + setup do + @imposition = JekyllPandocMultipleFormats::Imposition.new(TEST_PDF, 'a5paper', 'a4paper', 12) + end + + should 'have a sheet size' do + assert_equal 'a4paper', @imposition.sheetsize + end + + should 'have a page size' do + assert_equal 'a5paper', @imposition.papersize + end + + should 'have a signature' do + assert_equal 12, @imposition.signature + end + + should 'have ordered pages' do + assert_equal [12, 1, 2, 11, 10, 3, 4, 9, 8, 5, 6, 7, "{}", 13, 14, 23, 22, 15, 16, 21, 20, 17, 18, 19], @imposition.to_nup + end + end + + context '2x1 imposition' do + setup do + @impositions = [] + + [ + { p: 'a7paper', s: 'a6paper' }, + { p: 'a6paper', s: 'a5paper' }, + { p: 'a5paper', s: 'a4paper' }, + { p: 'a4paper', s: 'a3paper' }, + { p: 'a3paper', s: 'a2paper' }, + { p: 'a2paper', s: 'a1paper' }, + { p: 'a1paper', s: 'a0paper' } + ].each do |i| + @impositions << JekyllPandocMultipleFormats::Imposition.new(TEST_PDF, i[:p], i[:s]) + end + end + + should 'have ordered pages' do + @impositions.each do |i| + assert_equal ["{}", 1, 2, 23, 22, 3, 4, 21, 20, 5, 6, 19, 18, 7, 8, 17, 16, 9, 10, 15, 14, 11, 12, 13], i.to_nup + end + end + end + + context '4x1 imposition' do + setup do + @impositions = [] + + [ + { p: 'a7paper', s: 'a5paper' }, + { p: 'a6paper', s: 'a4paper' }, + { p: 'a5paper', s: 'a3paper' }, + { p: 'a4paper', s: 'a2paper' }, + { p: 'a3paper', s: 'a1paper' }, + { p: 'a2paper', s: 'a0paper' } + ].each do |i| + @impositions << JekyllPandocMultipleFormats::Imposition.new(TEST_PDF, i[:p], i[:s]) + end + end + + should 'have ordered pages' do + @impositions.each do |i| + assert_equal ["{}", 1, "{}", 1, + 2, 23, 2, 23, + 22, 3, 22, 3, + 4, 21, 4, 21, + 20, 5, 20, 5, + 6, 19, 6, 19, + 18, 7, 18, 7, + 8, 17, 8, 17, + 16, 9, 16, 9, + 10, 15, 10, 15, + 14, 11, 14, 11, + 12, 13, 12, 13], + i.to_nup + end + end + end + + context '8x1 imposition' do + setup do + @impositions = [] + + [ + { p: 'a7paper', s: 'a4paper' }, + { p: 'a6paper', s: 'a3paper' }, + { p: 'a5paper', s: 'a2paper' }, + { p: 'a4paper', s: 'a1paper' }, + { p: 'a3paper', s: 'a0paper' } + ].each do |i| + @impositions << JekyllPandocMultipleFormats::Imposition.new(TEST_PDF, i[:p], i[:s]) + end + end + + should 'have ordered pages' do + @impositions.each do |i| + assert_equal [ + "{}", 1, "{}", 1, "{}", 1, "{}", 1, + 2, 23, 2, 23, 2, 23, 2, 23, + 22, 3, 22, 3, 22, 3, 22, 3, + 4, 21, 4, 21, 4, 21, 4, 21, + 20, 5, 20, 5, 20, 5, 20, 5, + 6, 19, 6, 19, 6, 19, 6, 19, + 18, 7, 18, 7, 18, 7, 18, 7, + 8, 17, 8, 17, 8, 17, 8, 17, + 16, 9, 16, 9, 16, 9, 16, 9, + 10, 15, 10, 15, 10, 15, 10, 15, + 14, 11, 14, 11, 14, 11, 14, 11, + 12, 13, 12, 13, 12, 13, 12, 13], + i.to_nup + end + end + end + + context '16x1 imposition' do + setup do + @impositions = [] + + [ + { p: 'a7paper', s: 'a3paper' }, + { p: 'a6paper', s: 'a2paper' }, + { p: 'a5paper', s: 'a1paper' }, + { p: 'a4paper', s: 'a0paper' } + ].each do |i| + @impositions << JekyllPandocMultipleFormats::Imposition.new(TEST_PDF, i[:p], i[:s]) + end + end + + should 'have ordered pages' do + @impositions.each do |i| + assert_equal [ + "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, + 2, 23, 2, 23, 2, 23, 2, 23, 2, 23, 2, 23, 2, 23, 2, 23, + 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, + 4, 21, 4, 21, 4, 21, 4, 21, 4, 21, 4, 21, 4, 21, 4, 21, + 20, 5, 20, 5, 20, 5, 20, 5, 20, 5, 20, 5, 20, 5, 20, 5, + 6, 19, 6, 19, 6, 19, 6, 19, 6, 19, 6, 19, 6, 19, 6, 19, + 18, 7, 18, 7, 18, 7, 18, 7, 18, 7, 18, 7, 18, 7, 18, 7, + 8, 17, 8, 17, 8, 17, 8, 17, 8, 17, 8, 17, 8, 17, 8, 17, + 16, 9, 16, 9, 16, 9, 16, 9, 16, 9, 16, 9, 16, 9, 16, 9, + 10, 15, 10, 15, 10, 15, 10, 15, 10, 15, 10, 15, 10, 15, 10, 15, + 14, 11, 14, 11, 14, 11, 14, 11, 14, 11, 14, 11, 14, 11, 14, 11, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13], + i.to_nup + end + end + end + + context '32x1 imposition' do + setup do + @impositions = [] + + [ + { p: 'a7paper', s: 'a2paper' }, + { p: 'a6paper', s: 'a1paper' }, + { p: 'a5paper', s: 'a0paper' } + ].each do |i| + @impositions << JekyllPandocMultipleFormats::Imposition.new(TEST_PDF, i[:p], i[:s]) + end + end + + should 'have ordered pages' do + @impositions.each do |i| + assert_equal [ + "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, + 2, 23, 2, 23, 2, 23, 2, 23, 2, 23, 2, 23, 2, 23, 2, 23, 2, 23, 2, 23, 2, 23, 2, 23, 2, 23, 2, 23, 2, 23, 2, 23, + 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, + 4, 21, 4, 21, 4, 21, 4, 21, 4, 21, 4, 21, 4, 21, 4, 21, 4, 21, 4, 21, 4, 21, 4, 21, 4, 21, 4, 21, 4, 21, 4, 21, + 20, 5, 20, 5, 20, 5, 20, 5, 20, 5, 20, 5, 20, 5, 20, 5, 20, 5, 20, 5, 20, 5, 20, 5, 20, 5, 20, 5, 20, 5, 20, 5, + 6, 19, 6, 19, 6, 19, 6, 19, 6, 19, 6, 19, 6, 19, 6, 19, 6, 19, 6, 19, 6, 19, 6, 19, 6, 19, 6, 19, 6, 19, 6, 19, + 18, 7, 18, 7, 18, 7, 18, 7, 18, 7, 18, 7, 18, 7, 18, 7, 18, 7, 18, 7, 18, 7, 18, 7, 18, 7, 18, 7, 18, 7, 18, 7, + 8, 17, 8, 17, 8, 17, 8, 17, 8, 17, 8, 17, 8, 17, 8, 17, 8, 17, 8, 17, 8, 17, 8, 17, 8, 17, 8, 17, 8, 17, 8, 17, + 16, 9, 16, 9, 16, 9, 16, 9, 16, 9, 16, 9, 16, 9, 16, 9, 16, 9, 16, 9, 16, 9, 16, 9, 16, 9, 16, 9, 16, 9, 16, 9, + 10, 15, 10, 15, 10, 15, 10, 15, 10, 15, 10, 15, 10, 15, 10, 15, 10, 15, 10, 15, 10, 15, 10, 15, 10, 15, 10, 15, 10, 15, 10, 15, + 14, 11, 14, 11, 14, 11, 14, 11, 14, 11, 14, 11, 14, 11, 14, 11, 14, 11, 14, 11, 14, 11, 14, 11, 14, 11, 14, 11, 14, 11, 14, 11, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13], + i.to_nup + end + end + end + + context 'binder' do + setup do + @binder = JekyllPandocMultipleFormats::Binder.new(TEST_PDF) + end + + should 'write a new filename' do + assert @binder.write + assert File.exists?(TEST_BINDER_PDF) + assert FileUtils.rm(TEST_BINDER_PDF) + end + + should 'have number of pages' do + assert_equal 23, @binder.pages + end + + should 'have a sheet size' do + assert_equal 'a4paper', @binder.sheetsize + end + + should 'have a page size' do + assert_equal 'a5paper', @binder.papersize + end + end + + context '2x1 binder' do + setup do + @binder = [] + + [ + { p: 'a7paper', s: 'a6paper' }, + { p: 'a6paper', s: 'a5paper' }, + { p: 'a5paper', s: 'a4paper' }, + { p: 'a4paper', s: 'a3paper' }, + { p: 'a3paper', s: 'a2paper' }, + { p: 'a2paper', s: 'a1paper' }, + { p: 'a1paper', s: 'a0paper' } + ].each do |i| + @binder << JekyllPandocMultipleFormats::Binder.new(TEST_PDF, i[:p], i[:s]) + end + end + + should 'have ordered pages' do + @binder.each do |i| + assert_equal [ + 1, 1, + 2, 2, + 3, 3, + 4, 4, + 5, 5, + 6, 6, + 7, 7, + 8, 8, + 9, 9, + 10, 10, + 11, 11, + 12, 12, + 13, 13, + 14, 14, + 15, 15, + 16, 16, + 17, 17, + 18, 18, + 19, 19, + 20, 20, + 21, 21, + 22, 22, + 23, 23 ], i.to_nup + end + end + end + + context '4x1 binder' do + setup do + @binder = [] + + [ + { p: 'a7paper', s: 'a5paper' }, + { p: 'a6paper', s: 'a4paper' }, + { p: 'a5paper', s: 'a3paper' }, + { p: 'a4paper', s: 'a2paper' }, + { p: 'a3paper', s: 'a1paper' }, + { p: 'a2paper', s: 'a0paper' } + ].each do |i| + @binder << JekyllPandocMultipleFormats::Binder.new(TEST_PDF, i[:p], i[:s]) + end + end + + should 'have ordered pages' do + @binder.each do |i| + assert_equal [ + 1, 1, 1, 1, + 2, 2, 2, 2, + 3, 3, 3, 3, + 4, 4, 4, 4, + 5, 5, 5, 5, + 6, 6, 6, 6, + 7, 7, 7, 7, + 8, 8, 8, 8, + 9, 9, 9, 9, + 10, 10, 10, 10, + 11, 11, 11, 11, + 12, 12, 12, 12, + 13, 13, 13, 13, + 14, 14, 14, 14, + 15, 15, 15, 15, + 16, 16, 16, 16, + 17, 17, 17, 17, + 18, 18, 18, 18, + 19, 19, 19, 19, + 20, 20, 20, 20, + 21, 21, 21, 21, + 22, 22, 22, 22, + 23, 23, 23, 23], + i.to_nup + end + end + end + + context '8x1 binder' do + setup do + @binder = [] + + [ + { p: 'a7paper', s: 'a4paper' }, + { p: 'a6paper', s: 'a3paper' }, + { p: 'a5paper', s: 'a2paper' }, + { p: 'a4paper', s: 'a1paper' }, + { p: 'a3paper', s: 'a0paper' } + ].each do |i| + @binder << JekyllPandocMultipleFormats::Binder.new(TEST_PDF, i[:p], i[:s]) + end + end + + should 'have ordered pages' do + @binder.each do |i| + assert_equal [ + 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 13, 13, + 14, 14, 14, 14, 14, 14, 14, 14, + 15, 15, 15, 15, 15, 15, 15, 15, + 16, 16, 16, 16, 16, 16, 16, 16, + 17, 17, 17, 17, 17, 17, 17, 17, + 18, 18, 18, 18, 18, 18, 18, 18, + 19, 19, 19, 19, 19, 19, 19, 19, + 20, 20, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, + 22, 22, 22, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 23, 23, 23, 23], + i.to_nup + end + end + end + + context '16x1 binder' do + setup do + @binder = [] + + [ + { p: 'a7paper', s: 'a3paper' }, + { p: 'a6paper', s: 'a2paper' }, + { p: 'a5paper', s: 'a1paper' }, + { p: 'a4paper', s: 'a0paper' } + ].each do |i| + @binder << JekyllPandocMultipleFormats::Binder.new(TEST_PDF, i[:p], i[:s]) + end + end + + should 'have ordered pages' do + @binder.each do |i| + assert_equal [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23], + i.to_nup + end + end + end + + context '32x1 binder' do + setup do + @binder = [] + + [ + { p: 'a7paper', s: 'a2paper' }, + { p: 'a6paper', s: 'a1paper' }, + { p: 'a5paper', s: 'a0paper' } + ].each do |i| + @binder << JekyllPandocMultipleFormats::Binder.new(TEST_PDF, i[:p], i[:s]) + end + end + + should 'have ordered pages' do + @binder.each do |i| + assert_equal [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23], + i.to_nup + end + end + end +end