├── .gitignore ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── bin └── imponer ├── jekyll-pandoc-multiple-formats.gemspec ├── lib ├── jekyll-pandoc-multiple-formats.rb └── jekyll-pandoc-multiple-formats │ ├── binder.rb │ ├── config.rb │ ├── converter.rb │ ├── generator.rb │ ├── imposition.rb │ ├── pandoc_file.rb │ ├── printer.rb │ ├── unite.rb │ └── version.rb └── test ├── fixtures └── test.pdf ├── source ├── _layouts │ └── nil.html └── _posts │ ├── 2014-01-01-test.markdown │ └── 2015-01-01-another_post.markdown ├── test_helper.rb ├── test_pandoc_file.rb └── test_printer.rb /.gitignore: -------------------------------------------------------------------------------- 1 | test/destination/ 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2015 Nicolás Reynolds 2 | 2012-2013 Mauricio Pasquier Juan 3 | 2013 Brian Candler 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Another pandoc plugin for jekyll 2 | 3 | This jekyll plugin was inspired by [jekyll-pandoc-plugin][1] but it was changed 4 | to generate multiple outputs, rather than just using pandoc to generate jekyll 5 | html posts. Besides, it doesn't require the 'pandoc-ruby' gem. 6 | 7 | It's used on [En Defensa del Software Libre][0]. Please check [our 8 | repo](https://github.com/edsl/endefensadelsl.org) if you like to see how 9 | it works in production. 10 | 11 | [0]: http://endefensadelsl.org 12 | [1]: https://github.com/dsanson/jekyll-pandoc-plugin 13 | 14 | 15 | ## What does it do 16 | 17 | It replaces the html generation for pandoc. This means you will have 18 | support for pandoc's markdown extensions, like ~strikethrough~ and 19 | [@cite], tables and [a lot more stuff](http://pandoc.org/README.html). 20 | 21 | It'll also generate the post in other formats you like, so your 22 | blog can be made available in different formats at the same time. Epub 23 | for ebook readers, mediawiki for copy&paste to wikis, etc. 24 | 25 | If instructed, this plugin will also generate pdfs in ready for print 26 | format. 27 | 28 | 29 | ## Configuration 30 | 31 | Add to `_config.yml`: 32 | 33 | ```yaml 34 | 35 | markdown: pandoc 36 | pandoc: 37 | skip: 38 | full: false 39 | posts: false 40 | categories: false 41 | bundle_permalink: ':output_ext/:slug.:output_ext' 42 | papersize: 'a5paper' 43 | sheetsize: 'a4paper' 44 | imposition: true 45 | binder: true 46 | covers_dir: assets/covers 47 | signature: 20 48 | full_file: true 49 | 50 | flags: '--smart' 51 | site_flags: '--toc' 52 | outputs: 53 | latex: 54 | pdf: '--latex-engine=xelatex' 55 | epub: '--epub-chapter-level=2' 56 | lang: 57 | ar: 58 | all: '-V mainfont="Amiri"' 59 | pdf: '--include-in-header=_layouts/rtl.tex' 60 | ``` 61 | 62 | * `markdown: pandoc` will instruct jekyll to use the pandoc html 63 | converter. 64 | 65 | * `skip` allows you to skip the other formats generation and proceed 66 | with the regular jekyll site build. You can skip some of the 67 | generation process or all of it. Older versions of this plugin 68 | required `true` or `false` to skip the process altogether. 69 | 70 | * `full_flags` if `full_file` is defined, these flags are used on it. 71 | By default are set to `--top-level-division=part` so each category is 72 | a different book part. 73 | 74 | * `site_flags` are flags applied to the html generation 75 | 76 | * `flags` is a string with the flags you will normally pass to `pandoc` on cli. 77 | It's used on all output types. 78 | 79 | * `outputs` is a hash of output formats (even markdown!). You can add 80 | output-specific flags. 81 | 82 | * `imposition` creates ready to print PDFs if you're creating PDF 83 | output. 84 | 85 | * `binder` creates ready to print PDFs 86 | 87 | * `bundle_permalink` is the path of the bundled articles 88 | 89 | * `papersize` is the page size for PDF. You can also use this option on 90 | the front matter. 91 | 92 | * `sheetsize` is the page size for ready the print PDF. You can also 93 | use this option on the front matter. 94 | 95 | * `covers_dir` the directory where covers are stored. If you have a 96 | `lang` defined, it will append the language to the `covers_dir` when 97 | looking for a category/full site cover, so you can have localized 98 | covers. 99 | 100 | * `signature` is the amount of pages per fold on the imposition version. 101 | Specify `0` for a single fold of all the pages. You can also use this 102 | option on the front matter. 103 | 104 | * `full_file` generates a single file containing all articles, sectioned 105 | by their main category (the first one defined if many). 106 | 107 | * `lang` is a hash where you can define per-language flags. If you have 108 | a `lang` attribute in your site config, this plugin will add the 109 | `-V lang=XX` flag and any language-specific flag you want. You can 110 | define language flags for `all` formats or for specific formats. 111 | 112 | **IMPORTANT**: As of version 0.1.0 the syntax of the config changed. 113 | Please upgrade your `_config.yml` accordingly. 114 | 115 | 116 | ## Front Matter 117 | 118 | ### Covers 119 | 120 | Support for epub covers has been added. You can add the path to 121 | a cover on the front matter of the article to have pandoc add a cover 122 | image on the epub result. 123 | 124 | --- 125 | cover: images/awesome.png 126 | --- 127 | 128 | For categories or posts without a cover specified, the plugin looks for 129 | a PNG file inside the `covers_dir` whose file name will be the 130 | category/post slug. 131 | 132 | Since 0.2.0, there's also support for PDF covers. If you have a PNG 133 | cover, it will get converted to PDF. You can also provide a PDF cover 134 | as long as it's the same file name as the PNG cover. 135 | 136 | * Category cover: `assets/covers/the_category_slug.png` 137 | * PDF cover: `assets/covers/the_slug.pdf` 138 | 139 | ### Paper sizes 140 | 141 | For PDFs, each article can have a `papersize` and a `sheetsize`. The 142 | `papersize` indicates the page size, and the `sheetsize` indicates the 143 | pages per fold size. 144 | 145 | Only A* sizes from A7 to A0 are supported for now. 146 | 147 | --- 148 | papersize: a5paper 149 | sheesize: a4paper 150 | --- 151 | 152 | This example will generate a 2 pages per A4 sheet. 153 | 154 | ### Bundled articles 155 | 156 | If articles share a category, the generator will create a PDF book 157 | including all of them. The name of the category will become the title 158 | of the book. 159 | 160 | --- 161 | category: [ 'En Defensa del Software Libre #0' ] 162 | --- 163 | 164 | The papersize will be the `papersize` of the first article found or the 165 | default papersize on the `_config.yml` file. Same applies for 166 | `sheetsize`. 167 | 168 | NOTE: Authorship will be set to empty. This could change in the future. 169 | 170 | ## Bibliography 171 | 172 | If you have bibliography, pandoc recommends leaving an empty 173 | section at the end of the document if you want to have a separate 174 | section for it. For bundled articles, this plugin will remove the extra 175 | sections and create one for everything at the end. 176 | 177 | # Bibliography 178 | 179 | 180 | 181 | You can also use the underlined version of the section title (aka 182 | `settext style` vs `atx style`). 183 | 184 | 185 | ## Layout 186 | 187 | Add this liquid snippet on your `_layout/post.html` to generate links to the 188 | other available formats from the post itself: 189 | 190 |
    191 | {% for format in site.pandoc.outputs %} 192 | {% capture extension %}{{ format | first }}{% endcapture %} 193 |
  • 194 | 195 | {{ extension }} 196 | 197 |
  • 198 | {% endfor %} 199 |
200 | 201 | ## How to install 202 | 203 | Add this snippet to your `_config.yml` on jekyll 1.3 204 | 205 | gems: [ 'jekyll-pandoc-multiple-formats' ] 206 | 207 | Alternative, see 208 | [here](https://github.com/fauno/jekyll-pandoc-multiple-formats/issues/7). 209 | 210 | 211 | ## How to run 212 | 213 | Execute `jekyll build` normally :D 214 | 215 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | Bundler.setup(:default, :development, :test) 3 | 4 | require 'rake' 5 | require 'rake/testtask' 6 | 7 | Rake::TestTask.new(:test) do |test| 8 | test.libs << 'lib' << 'test' 9 | test.pattern = 'test/**/test_*.rb' 10 | test.verbose = true 11 | end 12 | 13 | task :default => 'test' 14 | -------------------------------------------------------------------------------- /bin/imponer: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # Copyright (c) 2012-2015 Nicolás Reynolds 3 | # 2012-2013 Mauricio Pasquier Juan 4 | # 2013 Brian Candler 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining 7 | # a copy of this software and associated documentation files (the 8 | # "Software"), to deal in the Software without restriction, including 9 | # without limitation the rights to use, copy, modify, merge, publish, 10 | # distribute, sublicense, and/or sell copies of the Software, and to 11 | # permit persons to whom the Software is furnished to do so, subject to 12 | # the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be 15 | # included in all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | 26 | file = ARGV.first 27 | exit if not File.exist? file 28 | 29 | # Los temporales tiene un sufijo -tmp 30 | file_tmp = file.gsub(/\.pdf$/, '-tmp.pdf') 31 | # Los definitivos -tmp-imposed 32 | file_imp = file.gsub(/\.pdf$/, '-tmp-imposed.pdf') 33 | 34 | # Cantidad de páginas 35 | pages = `pdfinfo '#{file}' | grep '^Pages:' | cut -d: -f2 | tr -d ' '`.to_i 36 | 37 | # Encontramos el múltiplo de 4 más cercano a la cantidad de páginas 38 | pages4 = ((pages+3)/4)*4.to_i 39 | # y luego el múltiplo de 4 más cercano al dividir la cantidad de páginas 40 | # por dos (porque cada hoja tiene dos mitades del libro) 41 | pages8 = ((pages4/2+3)/4*4).to_i 42 | 43 | # Creamos la imposición de páginas para A5 44 | `pdfjam --vanilla \ 45 | --outfile "#{File.dirname file}" \ 46 | --paper a5paper \ 47 | --suffix tmp \ 48 | --landscape \ 49 | --signature #{pages4} \ 50 | "#{file}"` 51 | 52 | # Y luego lo dividimos por la mitad y hacemos 2x2 en A4 53 | `pdfjam --vanilla \ 54 | --outfile "#{File.dirname file}" \ 55 | --paper a4paper \ 56 | --suffix imposed \ 57 | --no-landscape \ 58 | --signature #{pages8} \ 59 | "#{file_tmp}"` 60 | 61 | `rm -v "#{file_tmp}"` 62 | `mv -v "#{file_imp}" "#{file_imp.gsub /-tmp/, ''}"` 63 | -------------------------------------------------------------------------------- /jekyll-pandoc-multiple-formats.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'jekyll-pandoc-multiple-formats/version' 5 | 6 | Gem::Specification.new do |gem| 7 | gem.name = 'jekyll-pandoc-multiple-formats' 8 | gem.version = JekyllPandocMultipleFormats::VERSION 9 | gem.authors = ['Mauricio Pasquier Juan', 'Nicolás Reynolds'] 10 | gem.email = ['mauricio@pasquierjuan.com.ar', 'fauno@endefensadelsl.org'] 11 | gem.description = %q{This jekyll plugin was inspired by 12 | jekyll-pandoc-plugin but it was changed to generate multiple outputs, 13 | rather than just using pandoc to generate jekyll html posts. Besides, 14 | it doesn't require the 'pandoc-ruby' gem.} 15 | gem.summary = %q{Use pandoc on jekyll to generate posts in multiple formats} 16 | gem.homepage = 'https://github.com/fauno/jekyll-pandoc-multiple-formats' 17 | gem.license = 'MIT' 18 | 19 | gem.files = `git ls-files`.split($/) 20 | gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } 21 | gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) 22 | gem.require_paths = ['lib'] 23 | 24 | gem.add_dependency('jekyll', '~> 3.7.0') 25 | gem.add_dependency('pdf_info', '~> 0.5.0') 26 | gem.add_dependency('rtex', '~> 2.1.0') 27 | gem.add_development_dependency('rake', '~> 10.5.0') 28 | gem.add_development_dependency('minitest', '~> 5.8.0') 29 | gem.add_development_dependency('shoulda', '~> 3.5.0') 30 | end 31 | -------------------------------------------------------------------------------- /lib/jekyll-pandoc-multiple-formats.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012-2015 Nicolás Reynolds 2 | # 2012-2013 Mauricio Pasquier Juan 3 | # 2013 Brian Candler 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining 6 | # a copy of this software and associated documentation files (the 7 | # "Software"), to deal in the Software without restriction, including 8 | # without limitation the rights to use, copy, modify, merge, publish, 9 | # distribute, sublicense, and/or sell copies of the Software, and to 10 | # permit persons to whom the Software is furnished to do so, subject to 11 | # the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be 14 | # included in all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | require 'open3' 25 | 26 | require 'jekyll-pandoc-multiple-formats/version' 27 | require 'jekyll-pandoc-multiple-formats/config' 28 | 29 | # TODO this may go to a separate gem 30 | require 'jekyll-pandoc-multiple-formats/printer' 31 | require 'jekyll-pandoc-multiple-formats/imposition' 32 | require 'jekyll-pandoc-multiple-formats/binder' 33 | require 'jekyll-pandoc-multiple-formats/unite' 34 | 35 | require 'jekyll-pandoc-multiple-formats/pandoc_file' 36 | require 'jekyll-pandoc-multiple-formats/generator' 37 | require 'jekyll-pandoc-multiple-formats/converter' 38 | -------------------------------------------------------------------------------- /lib/jekyll-pandoc-multiple-formats/binder.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012-2015 Nicolás Reynolds 2 | # 2012-2013 Mauricio Pasquier Juan 3 | # 2013 Brian Candler 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining 6 | # a copy of this software and associated documentation files (the 7 | # "Software"), to deal in the Software without restriction, including 8 | # without limitation the rights to use, copy, modify, merge, publish, 9 | # distribute, sublicense, and/or sell copies of the Software, and to 10 | # permit persons to whom the Software is furnished to do so, subject to 11 | # the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be 14 | # included in all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | module JekyllPandocMultipleFormats 25 | class Binder < Printer 26 | def initialize(file, papersize = nil, sheetsize = nil, extra_options = nil) 27 | super 28 | @output_file = file.gsub(/\.pdf\Z/, '-binder.pdf') 29 | 30 | render_template 31 | self 32 | end 33 | 34 | def to_nup 35 | @pages.times.map{|i|i+1}.map do |page| 36 | sheet=[] 37 | @nup.times do 38 | sheet << page 39 | end 40 | 41 | sheet 42 | end.flatten 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/jekyll-pandoc-multiple-formats/config.rb: -------------------------------------------------------------------------------- 1 | module JekyllPandocMultipleFormats 2 | class Config 3 | DEFAULTS = { 4 | 'skip' => { 5 | 'posts' => false, 6 | 'categories' => false, 7 | 'full' => false 8 | }, 9 | 'bundle_permalink' => ':output_ext/:slug.:output_ext', 10 | 'papersize' => 'a5paper', 11 | 'sheetsize' => 'a4paper', 12 | 'imposition' => true, 13 | 'binder' => true, 14 | 'signature' => 20, 15 | 'flags' => '--smart', 16 | 'site_flags' => '--toc', 17 | 'outputs' => {}, 18 | 'covers_dir' => 'images/', 19 | 'title' => nil, 20 | 'full_flags' => '--top-level-division=part' 21 | } 22 | 23 | attr_accessor :config 24 | 25 | def initialize(config = {}) 26 | @config = Jekyll::Utils.deep_merge_hashes(DEFAULTS, config) 27 | end 28 | 29 | def skip? 30 | @config['skip'].values.all? 31 | end 32 | 33 | def imposition? 34 | @config['imposition'] 35 | end 36 | 37 | def binder? 38 | @config['binder'] 39 | end 40 | 41 | # TODO magic 42 | def outputs 43 | @config['outputs'] 44 | end 45 | 46 | def generate_posts? 47 | !@config.dig('skip', 'posts') 48 | end 49 | 50 | def generate_categories? 51 | !@config.dig('skip', 'categories') 52 | end 53 | 54 | def generate_full_file? 55 | !@config.dig('skip', 'full') 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/jekyll-pandoc-multiple-formats/converter.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012-2015 Nicolás Reynolds 2 | # 2012-2013 Mauricio Pasquier Juan 3 | # 2013 Brian Candler 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining 6 | # a copy of this software and associated documentation files (the 7 | # "Software"), to deal in the Software without restriction, including 8 | # without limitation the rights to use, copy, modify, merge, publish, 9 | # distribute, sublicense, and/or sell copies of the Software, and to 10 | # permit persons to whom the Software is furnished to do so, subject to 11 | # the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be 14 | # included in all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | # Namespacing and metaprogramming FTW 25 | module JekyllPandocMultipleFormats 26 | # Determines the correct module where it lives the converter 27 | def self.namespace 28 | Jekyll::VERSION >= '1.0.0' ? Jekyll::Converters : Jekyll 29 | end 30 | 31 | # Determines the correct class name. Jekyll has the converters class kinda 32 | # hardcoded 33 | def self.class_name 34 | Jekyll::VERSION >= '1.0.0' ? 'Markdown' : 'MarkdownConverter' 35 | end 36 | 37 | def self.build 38 | namespace::const_get(class_name).send :include, ConverterMethods 39 | end 40 | 41 | # When included in the correspondant markdown class this module redefines the 42 | # three needed Converter instance methods 43 | module ConverterMethods 44 | def self.included(base) 45 | base.class_eval do 46 | # Just return html5 47 | def convert(content) 48 | lang = @config.dig('lang') 49 | flags = [] 50 | flags << @config.dig('pandoc', 'flags') 51 | flags << @config.dig('pandoc', 'site_flags') 52 | flags << @config.dig('pandoc', 'lang', lang, 'all') 53 | 54 | output = '' 55 | Dir::chdir(@config['source']) do 56 | cmd = "pandoc -t html5 #{flags.compact.join(' ')}" 57 | Open3::popen3(cmd) do |stdin, stdout, stderr, thread| 58 | stdin.puts content 59 | stdin.close 60 | 61 | output = stdout.read.strip 62 | STDERR.print stderr.read 63 | 64 | # Wait for the process to finish 65 | thread.value 66 | end 67 | end 68 | 69 | output 70 | end 71 | 72 | def matches(ext) 73 | rgx = '(' + @config['markdown_ext'].gsub(',','|') +')' 74 | ext =~ Regexp.new(rgx, Regexp::IGNORECASE) 75 | end 76 | 77 | def output_ext(ext) 78 | '.html' 79 | end 80 | end 81 | end 82 | end 83 | end 84 | 85 | # Conjures the metamagic 86 | JekyllPandocMultipleFormats.build 87 | -------------------------------------------------------------------------------- /lib/jekyll-pandoc-multiple-formats/generator.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012-2018 Nicolás Reynolds 2 | # 2012-2013 Mauricio Pasquier Juan 3 | # 2013 Brian Candler 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining 6 | # a copy of this software and associated documentation files (the 7 | # "Software"), to deal in the Software without restriction, including 8 | # without limitation the rights to use, copy, modify, merge, publish, 9 | # distribute, sublicense, and/or sell copies of the Software, and to 10 | # permit persons to whom the Software is furnished to do so, subject to 11 | # the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be 14 | # included in all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | module Jekyll 25 | 26 | class PandocGenerator < Generator 27 | safe true 28 | 29 | attr_accessor :site, :config 30 | 31 | def generate_post_for_output(post, output) 32 | Jekyll.logger.debug 'Pandoc:', post.data['title'] 33 | 34 | pandoc_file = PandocFile.new(@site, output, post) 35 | return unless pandoc_file.write 36 | 37 | @site.keep_files << pandoc_file.relative_path 38 | @pandoc_files << pandoc_file 39 | end 40 | 41 | def generate_category_for_output(category, posts, output) 42 | Jekyll.logger.info 'Pandoc:', "Generating category #{category}" 43 | posts.sort! 44 | pandoc_file = PandocFile.new(@site, output, posts, category) 45 | 46 | if @site.keep_files.include? pandoc_file.relative_path 47 | Jekyll.logger.warn 'Pandoc:', 48 | "#{pandoc_file.relative_path} is a category file AND a post file. Change the category name to fix this" 49 | return 50 | end 51 | 52 | return unless pandoc_file.write 53 | 54 | @site.keep_files << pandoc_file.relative_path 55 | @pandoc_files << pandoc_file 56 | end 57 | 58 | def general_full_for_output(output) 59 | title = @site.config.dig('title') 60 | Jekyll.logger.info 'Pandoc:', "Generating full file #{title}" 61 | # For parts to make sense, we order articles by date and then by 62 | # category, so each category is ordered by date. 63 | # 64 | # cat1 - art1 65 | # cat1 - art3 66 | # cat2 - art2 67 | full = @site.posts.docs.reject { |p| p.data.dig('full') }.sort_by do |p| 68 | [ p.data['date'], p.data['categories'].first.to_s ] 69 | end 70 | 71 | full_file = PandocFile.new(@site, output, full, title, { full: true }) 72 | full_file.write 73 | @site.keep_files << full_file.relative_path 74 | @pandoc_files << full_file 75 | end 76 | 77 | def generate(site) 78 | @site ||= site 79 | @config ||= JekyllPandocMultipleFormats::Config.new(@site.config['pandoc']) 80 | 81 | return if @config.skip? 82 | 83 | # we create a single array of files 84 | @pandoc_files = [] 85 | 86 | @config.outputs.each_pair do |output, _| 87 | Jekyll.logger.info 'Pandoc:', "Generating #{output}" 88 | @site.posts.docs.each do |post| 89 | Jekyll::Hooks.trigger :posts, :pre_render, post, { format: output } 90 | generate_post_for_output(post, output) if @config.generate_posts? 91 | Jekyll::Hooks.trigger :posts, :post_render, post, { format: output } 92 | end 93 | 94 | if @config.generate_categories? 95 | @site.post_attr_hash('categories').each_pair do |title, posts| 96 | generate_category_for_output title, posts, output 97 | end 98 | end 99 | 100 | general_full_for_output(output) if @config.generate_full_file? 101 | end 102 | 103 | @pandoc_files.each do |pandoc_file| 104 | # If output is PDF, we also create the imposed PDF 105 | next unless pandoc_file.pdf? 106 | 107 | if @config.imposition? 108 | 109 | imposed_file = JekyllPandocMultipleFormats::Imposition 110 | .new(pandoc_file.path, pandoc_file.papersize, 111 | pandoc_file.sheetsize, pandoc_file.signature) 112 | 113 | imposed_file.write 114 | @site.keep_files << imposed_file.relative_path(@site.dest) 115 | end 116 | 117 | # If output is PDF, we also create the imposed PDF 118 | if @config.binder? 119 | 120 | binder_file = JekyllPandocMultipleFormats::Binder 121 | .new(pandoc_file.path, pandoc_file.papersize, 122 | pandoc_file.sheetsize) 123 | 124 | binder_file.write 125 | @site.keep_files << binder_file.relative_path(@site.dest) 126 | end 127 | 128 | # Add covers to PDFs after building ready for print files 129 | if pandoc_file.has_cover? 130 | # Generate the cover 131 | next unless pandoc_file.pdf_cover! 132 | united_output = pandoc_file.path.gsub(/\.pdf\Z/, '-cover.pdf') 133 | united_file = JekyllPandocMultipleFormats::Unite 134 | .new(united_output, [pandoc_file.pdf_cover,pandoc_file.path]) 135 | 136 | if united_file.write 137 | # Replace the original file with the one with cover 138 | FileUtils.rm_f(pandoc_file.path) 139 | FileUtils.mv(united_output, pandoc_file.path) 140 | end 141 | end 142 | end 143 | end 144 | end 145 | end 146 | -------------------------------------------------------------------------------- /lib/jekyll-pandoc-multiple-formats/imposition.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012-2015 Nicolás Reynolds 2 | # 2012-2013 Mauricio Pasquier Juan 3 | # 2013 Brian Candler 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining 6 | # a copy of this software and associated documentation files (the 7 | # "Software"), to deal in the Software without restriction, including 8 | # without limitation the rights to use, copy, modify, merge, publish, 9 | # distribute, sublicense, and/or sell copies of the Software, and to 10 | # permit persons to whom the Software is furnished to do so, subject to 11 | # the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be 14 | # included in all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | module JekyllPandocMultipleFormats 25 | class Imposition < Printer 26 | 27 | attr_accessor :rounded_pages, :blank_pages, :signature 28 | 29 | def initialize(file, papersize = nil, sheetsize = nil, signature = nil, extra_options = nil) 30 | super(file, papersize, sheetsize, extra_options) 31 | @output_file = file.gsub(/\.pdf\Z/, '-imposed.pdf') 32 | # Total pages must be modulo 4 33 | @rounded_pages = round_to_nearest(@pages, 4) 34 | @blank_pages = @rounded_pages - @pages 35 | # If we don't use a signature, make a single fold 36 | @signature = signature || @rounded_pages 37 | # Also if we specified 0 38 | @signature = @rounded_pages if signature == 0 39 | 40 | render_template 41 | self 42 | end 43 | 44 | def to_nup 45 | # 14 pages example: 46 | # [ {}, 1, 2, {}, 14, 3, 4, 13, 12, 5, 6, 11, 10, 7, 8, 9 ] 47 | # 48 | # * Add {} to missing pages 49 | # [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, {}, {}] 50 | # * Take first half [ 1, 2, 3, 4, 5, 6, 7, 8 ] 51 | # * Reverse second half [ {}, {}, 14, 13, 12, 11, 10, 9 ] 52 | # * Intercalate the first half into the second half by two 53 | # elements 54 | # [ {}, 1, 2, {}, 14, 3, 4, 13, 12, 5, 6, 11, 10, 7, 8, 9 ] 55 | # 56 | # An array of numbered pages padded with blank pages ('{}') 57 | # 58 | # [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 ] + [ '{}', '{}' ] 59 | padded = @pages.times.map{|i|i+1} + Array.new(@blank_pages, '{}') 60 | 61 | # If we have a signature, we have to split in groups up to the 62 | # amount of pages per signature, and then continue with the rest 63 | # 64 | # If we have no signature, we assume it's equal to the total 65 | # amount of pages, so you only have one fold 66 | signed = padded.each_slice(@signature).to_a 67 | folds = [] 68 | signed.each do |fold| 69 | # 70 | # Split in halves 71 | # [ [ 1, 2, 3, 4, 5, 6, 7, 8 ], 72 | # [ 9, 10, 11, 12, 13, 14, '{}', '{}' ] ] 73 | halved = fold.each_slice(fold.size / 2).to_a 74 | # Add a nil as last page. When we reverse it and intercalate by 75 | # two pages, we'll have [nil, last_page] instead of 76 | # [last_page,second_to_last_page] 77 | # 78 | # [ [ 1, 2, 3, 4, 5, 6, 7, 8 ], 79 | # [ 9, 10, 11, 12, 13, 14, '{}', '{}', nil ] ] 80 | halved.last << nil 81 | # Reverse the second half and intercalate by two pages into the 82 | # first one. Then remove nil elements and flatten the array. 83 | # 84 | # [ [ 1, 2, 3, 4, 5, 6, 7, 8 ], 85 | # [ nil, '{}', '{}', 14, 13, 12, 11, 10 ] ] 86 | # 87 | # [ {}, 1, 2, {}, 14, 3, 4, 13, 12, 5, 6, 11, 10, 7, 8, 9 ] 88 | folds << halved.last.reverse.each_slice(2).zip(halved.first.each_slice(2).to_a).flatten.compact 89 | end 90 | 91 | # Create the matrix of pages (N-Up) per fold 92 | # 93 | # ["{}", 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] 94 | folds.map { |o| o.each_slice(2).map{ |i| a=[]; (@nup/2).times { a = a+i }; a }}.flatten 95 | end 96 | end 97 | end 98 | -------------------------------------------------------------------------------- /lib/jekyll-pandoc-multiple-formats/pandoc_file.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012-2018 Nicolás Reynolds 2 | # 2012-2013 Mauricio Pasquier Juan 3 | # 2013 Brian Candler 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining 6 | # a copy of this software and associated documentation files (the 7 | # "Software"), to deal in the Software without restriction, including 8 | # without limitation the rights to use, copy, modify, merge, publish, 9 | # distribute, sublicense, and/or sell copies of the Software, and to 10 | # permit persons to whom the Software is furnished to do so, subject to 11 | # the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be 14 | # included in all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | module Jekyll 25 | class PandocFile 26 | include Convertible 27 | 28 | attr_reader :format, :site, :config, :flags, :posts, :slug, :title, :url 29 | attr_reader :papersize, :sheetsize, :signature, :sources 30 | 31 | def initialize(site, format, posts, title = nil, extra = {}) 32 | @site = site 33 | @config = JekyllPandocMultipleFormats::Config.new(@site.config['pandoc']).config 34 | @format = format 35 | @flags = [] 36 | @last_cat = nil 37 | @extra = extra 38 | 39 | if posts.is_a? Array 40 | @single_post = false 41 | @posts = posts 42 | 43 | raise ArgumentError.new "'title' argument is required for multipost file" unless title 44 | 45 | @title = title 46 | @slug = Utils.slugify(@title) 47 | else 48 | @single_post = true 49 | @posts = [posts] 50 | @title = title || posts.data['title'] 51 | @slug = posts.data['slug'] 52 | end 53 | 54 | self 55 | end 56 | 57 | def path 58 | # path is full destination path with all elements of permalink 59 | path = @site.in_dest_dir(relative_path) 60 | end 61 | 62 | def path_with_quotes 63 | "\"#{path}\"" 64 | end 65 | 66 | def relative_path 67 | path = URL.unescape_path(url) 68 | path.gsub! /^\//, '' 69 | 70 | # but if the post is going to be index.html, use slug + format 71 | # (ie /year/month/slug/slug.pdf) 72 | if url.end_with? '/' 73 | path = File.join(path, @slug) 74 | path << '.' 75 | path << @format 76 | end 77 | 78 | path 79 | end 80 | 81 | # if it's just one article, the url is the same except it's not html 82 | # otherwise we use the permalink template with some placeholder 83 | def url 84 | @url ||= if single_post? 85 | single_post.url.gsub(/\.html$/, ".#{@format}") 86 | else 87 | URL.new({ 88 | template: @config['bundle_permalink'], 89 | placeholders: url_placeholders, 90 | permalink: nil }).to_s 91 | end 92 | end 93 | 94 | def url_placeholders 95 | { output_ext: @format, 96 | slug: @slug, 97 | title: @title } 98 | end 99 | 100 | # adds post metadata as yaml metadata 101 | def yaml_metadata 102 | if single_post? 103 | 104 | # if we were to merge config to data, the default options would 105 | # take precedence 106 | data = Jekyll::Utils.deep_merge_hashes @config, single_post.data 107 | single_post.merge_data! data 108 | 109 | 110 | # we extract the excerpt because it serializes as an object and 111 | # breaks pandoc 112 | metadata = single_post.data.reject{ |k| k == 'excerpt' } 113 | 114 | if @config['date_format'] 115 | metadata['date'] = metadata['date'].strftime(@config['date_format']) 116 | else 117 | metadata.delete('date') 118 | end 119 | else 120 | # we have to use this fugly syntax because jekyll doesn't do 121 | # symbols 122 | metadata = { 123 | 'date' => @config['date_format'] ? Date.today.strftime(@config['date_format']) : nil, 124 | 'title' => @title, 125 | 'author' => nil, 126 | 'papersize' => papersize, 127 | 'sheetsize' => sheetsize, 128 | 'signature' => signature 129 | } 130 | end 131 | 132 | # fix page sizes, pandoc uses 'A4' while printer.rb uses 133 | # 'a4paper' 134 | %w[papersize sheetsize].each do |size| 135 | metadata[size] = fix_size metadata[size] 136 | end 137 | 138 | metadata.to_yaml << "\n---\n" 139 | end 140 | 141 | def content 142 | # add base url from config if we're creating a subdir site 143 | relative_re = /\(#{@site.config.dig('baseurl')}\/(.*)\)/ 144 | if single_post? 145 | # make all images relative to source dir 146 | single_post.content.gsub(relative_re, '(\1)') 147 | else 148 | header_re = /^(#+.*\n*|.*\n[=-]+\n*)\Z/ 149 | bib_title = "" 150 | @posts.map do |post| 151 | bib_title = post.content.match(header_re).to_s if bib_title.empty? 152 | # remove bibliography titles 153 | # since pandoc does it's own bibliography output, it recommends 154 | # leaving an empty chapter title to mark it as such 155 | content = post.content.gsub(header_re, '') 156 | # make all images relative to source dir 157 | content = content.gsub(relative_re, '(\1)') 158 | 159 | # if the file contains all the articles, we make each category 160 | # a different part by adding a first level title out of it 161 | if full? 162 | # make all titles down a level 163 | # TODO have sixth level titles into account 164 | content = content.gsub(/^#/, '##') 165 | 166 | # we don't use all the categories otherwise the article 167 | # would be repeated 168 | cat = post.data['categories'].first 169 | # if we already set the category part, we just skip it 170 | if @last_cat == cat 171 | content 172 | # otherwise add the category title 173 | else 174 | @last_cat = cat 175 | "# #{cat}\n\n#{content}" 176 | end 177 | else 178 | # or we just return the content 179 | content 180 | end 181 | # we add the first bibliography title we can find in the end 182 | end.join("\n\n\n") << bib_title 183 | end 184 | end 185 | 186 | def write 187 | unless rebuild? 188 | Jekyll.logger.info "#{relative_path} doesn't need to be rebuilt" 189 | Jekyll.logger.debug sources.join(' ') 190 | return true 191 | end 192 | 193 | FileUtils.mkdir_p(File.dirname(path)) 194 | # Remove the file before creating it 195 | FileUtils.rm_f(path) 196 | # Move to the source dir since everything will be relative to that 197 | Dir::chdir(@site.config['source']) do 198 | # Do the stuff 199 | Jekyll.logger.debug command 200 | e = Open3::popen3(command) do |stdin, stdout, stderr, thread| 201 | stdin.puts yaml_metadata 202 | stdin.puts content 203 | stdin.close 204 | STDERR.print stderr.read 205 | 206 | # Wait for the process to finish 207 | thread.value 208 | end 209 | 210 | Jekyll.logger.warn 'Pandoc:', "Failed generating #{path}" if e.to_i > 0 211 | end 212 | 213 | File.exists?(path) 214 | end 215 | 216 | # Returns a cover, without checking if it exists 217 | # 218 | # It assumes covers are in PNG format 219 | def cover 220 | cover_path = [ @site.config['source'] ] 221 | if single_post? && single_post.data['cover'] 222 | cover_path << single_post.data['cover'] 223 | elsif site_lang? 224 | cover_path << @config['covers_dir'] 225 | cover_path << site_lang 226 | cover_path << "#{@slug}.png" 227 | else 228 | cover_path << @config['covers_dir'] 229 | cover_path << "#{@slug}.png" 230 | end 231 | 232 | File.join(cover_path) 233 | end 234 | 235 | def cover_with_quotes 236 | "\"#{cover}\"" 237 | end 238 | 239 | # Returns a PDF cover 240 | def pdf_cover 241 | cover.gsub(/\.[^\.]+\Z/, '.pdf') 242 | end 243 | 244 | def pdf_cover! 245 | if has_cover? && !File.exists?(pdf_cover) 246 | Open3::popen3("convert \"#{cover}\" \"#{pdf_cover}\"") do |stdin, stdout, stderr, thread| 247 | STDERR.print stderr.read 248 | 249 | # Wait for the process to finish 250 | thread.value 251 | end 252 | end 253 | 254 | File.exists?(pdf_cover) 255 | end 256 | 257 | def flags 258 | return @flags.join ' ' unless @flags.empty? 259 | 260 | @flags << @config['flags'] 261 | @flags << @config['outputs'][@format] if @config['outputs'].key?(@format) 262 | @flags << '-o' 263 | @flags << path_with_quotes 264 | 265 | # Binary formats don't need a -t flag 266 | unless binary? 267 | @flags << '-t' 268 | @flags << @format 269 | end 270 | 271 | if epub? && has_cover? 272 | @flags << '--epub-cover-image' 273 | @flags << cover_with_quotes 274 | end 275 | 276 | if full? 277 | @flags << @config['full_flags'] 278 | end 279 | 280 | if site_lang? 281 | @flags << "-V lang=#{site_lang}" 282 | @flags << @config.dig('lang', site_lang, 'all') 283 | @flags << @config.dig('lang', site_lang, @format) 284 | end 285 | 286 | @flags.compact.join ' ' 287 | end 288 | 289 | def command 290 | 'pandoc ' << flags 291 | end 292 | 293 | def site_lang 294 | @site.config.dig('lang') 295 | end 296 | 297 | def site_lang? 298 | !site_lang.nil? 299 | end 300 | 301 | def full? 302 | @extra[:full] 303 | end 304 | 305 | def pdf? 306 | @format == 'pdf' 307 | end 308 | 309 | def epub? 310 | %w[epub epub3].include? @format 311 | end 312 | 313 | # These formats are binary files and must use the -o flag 314 | def binary? 315 | %w[pdf epub epub3 odt docx].include? @format 316 | end 317 | 318 | def single_post? 319 | @single_post 320 | end 321 | 322 | def has_cover? 323 | e = File.exists? cover 324 | Jekyll.logger.warn "Cover:", "#{cover} missing for #{relative_path}" unless e 325 | 326 | e 327 | end 328 | 329 | def papersize 330 | @papersize ||= find_option 'papersize' 331 | end 332 | 333 | def sheetsize 334 | @sheetsize ||= find_option 'sheetsize' 335 | end 336 | 337 | def signature 338 | @signature ||= find_option 'signature' 339 | end 340 | 341 | # Finds the source files for this pandoc file 342 | def sources 343 | return @sources if @sources 344 | 345 | @sources = ['_config.yml'] 346 | @sources << flags.split(' ').map do |flag| 347 | file = File.join(@site.config['source'], flag.split('=').last) 348 | file if File.exist? file 349 | end 350 | 351 | @sources << posts.map(&:path) 352 | 353 | @sources = @sources.flatten.compact 354 | end 355 | 356 | def rebuild? 357 | !File.exist?(path) || sources.map do |f| 358 | File.ctime(f) > File.ctime(path) 359 | end.any? 360 | end 361 | 362 | private 363 | 364 | def single_post 365 | @posts.first 366 | end 367 | 368 | def find_option(name) 369 | if @posts.any? { |p| p.data.key? name } 370 | @posts.select { |p| p.data.key? name }.first.data[name] 371 | else 372 | @config[name] 373 | end 374 | end 375 | 376 | def fix_size(size) 377 | size.gsub /paper$/, '' 378 | end 379 | end 380 | end 381 | -------------------------------------------------------------------------------- /lib/jekyll-pandoc-multiple-formats/printer.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012-2015 Nicolás Reynolds 2 | # 2012-2013 Mauricio Pasquier Juan 3 | # 2013 Brian Candler 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining 6 | # a copy of this software and associated documentation files (the 7 | # "Software"), to deal in the Software without restriction, including 8 | # without limitation the rights to use, copy, modify, merge, publish, 9 | # distribute, sublicense, and/or sell copies of the Software, and to 10 | # permit persons to whom the Software is furnished to do so, subject to 11 | # the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be 14 | # included in all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | require 'pdf/info' 25 | require 'rtex' 26 | 27 | module JekyllPandocMultipleFormats 28 | class Printer 29 | TEMPLATE = <<-EOT.gsub(/^\s+/, '') 30 | \\documentclass[@@sheetsize@@,10pt]{article} 31 | 32 | \\usepackage{pgfpages} 33 | \\usepackage{pdfpages} 34 | 35 | \\pgfpagesuselayout{@@nup@@ on 1}[@@sheetsize@@,@@extra_options@@] 36 | 37 | \\begin{document} 38 | \\includepdf[pages={@@pages@@}]{@@document@@} 39 | \\end{document} 40 | EOT 41 | 42 | # TODO allow custom sheet sizes 43 | SHEET_SIZES = { 44 | a7paper: 2, 45 | a6paper: 4, 46 | a5paper: 8, 47 | a4paper: 16, 48 | a3paper: 32, 49 | a2paper: 64, 50 | a1paper: 128, 51 | a0paper: 256 52 | } 53 | 54 | attr_accessor :output_file, :original_file, :pages, :template, 55 | :papersize, :sheetsize, :nup, :extra_options, :relative_path 56 | 57 | def initialize(file, papersize = nil, sheetsize = nil, extra_options = nil) 58 | return unless /\.pdf\Z/ =~ file 59 | return unless pdf = PDF::Info.new(file) 60 | 61 | @original_file = File.realpath(file) 62 | @papersize = papersize || 'a5paper' 63 | @sheetsize = sheetsize || 'a4paper' 64 | @pages = pdf.metadata[:page_count] 65 | @nup = SHEET_SIZES[@sheetsize.to_sym] / SHEET_SIZES[@papersize.to_sym] 66 | @extra_options = extra_options || '' 67 | 68 | # These layouts require a landscape page 69 | @extra_options << 'landscape' if is_landscape? 70 | 71 | self 72 | end 73 | 74 | def relative_path(from) 75 | @relative_path ||= Pathname.new(output_file).relative_path_from(Pathname.new(from)).to_s 76 | end 77 | 78 | def write 79 | # Create the imposed file 80 | pdflatex = RTeX::Document.new(template) 81 | pdflatex.to_pdf do |pdf_file| 82 | FileUtils.cp pdf_file, @output_file 83 | end 84 | 85 | File.exists? @output_file 86 | end 87 | 88 | def is_landscape? 89 | [2,8,32,128].include? @nup 90 | end 91 | 92 | def round_to_nearest(int, near) 93 | (int + (near - 1)) / near * near 94 | end 95 | 96 | def render_template 97 | @template = TEMPLATE 98 | .gsub('@@nup@@', @nup.to_s) 99 | .gsub('@@sheetsize@@', @sheetsize) 100 | .gsub('@@extra_options@@', @extra_options) 101 | .gsub('@@document@@', @original_file) 102 | .gsub('@@pages@@', to_nup * ',') 103 | end 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /lib/jekyll-pandoc-multiple-formats/unite.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012-2015 Nicolás Reynolds 2 | # 2012-2013 Mauricio Pasquier Juan 3 | # 2013 Brian Candler 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining 6 | # a copy of this software and associated documentation files (the 7 | # "Software"), to deal in the Software without restriction, including 8 | # without limitation the rights to use, copy, modify, merge, publish, 9 | # distribute, sublicense, and/or sell copies of the Software, and to 10 | # permit persons to whom the Software is furnished to do so, subject to 11 | # the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be 14 | # included in all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | module JekyllPandocMultipleFormats 25 | class Unite < Printer 26 | TEMPLATE = <<-EOT.gsub(/^\s+/, '') 27 | \\documentclass{article} 28 | \\usepackage{pdfpages} 29 | 30 | \\begin{document} 31 | @@include@@ 32 | \\end{document} 33 | EOT 34 | 35 | INCLUDE_TEMPLATE = '\\includepdf[fitpaper=true,pages=-]{@@document@@}' 36 | 37 | attr_accessor :files, :template 38 | 39 | def initialize(output_file, files) 40 | raise ArgumentError.new 'An array of filenames is required' unless files.is_a? Array 41 | 42 | @output_file = output_file 43 | self.files = files 44 | 45 | render_template 46 | self 47 | end 48 | 49 | def <<(file) 50 | @files ||= [] 51 | @files << File.realpath(file) if /\.pdf\Z/ =~ file 52 | end 53 | 54 | def files=(file_array) 55 | return unless file_array.respond_to? :each 56 | 57 | file_array.each do |f| 58 | self << f 59 | end 60 | end 61 | 62 | def render_template 63 | includes = @files.map do |f| 64 | INCLUDE_TEMPLATE.gsub(/@@document@@/, f) 65 | end 66 | 67 | @template = TEMPLATE.gsub('@@include@@', includes.join("\n")) 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /lib/jekyll-pandoc-multiple-formats/version.rb: -------------------------------------------------------------------------------- 1 | module JekyllPandocMultipleFormats 2 | VERSION = '0.4.0' 3 | end 4 | -------------------------------------------------------------------------------- /test/fixtures/test.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fauno/jekyll-pandoc-multiple-formats/8403a7452aa74e503331640da963a9bfba720446/test/fixtures/test.pdf -------------------------------------------------------------------------------- /test/source/_layouts/nil.html: -------------------------------------------------------------------------------- 1 | {{ yield }} 2 | -------------------------------------------------------------------------------- /test/source/_posts/2014-01-01-test.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: nil 3 | title: A Title 4 | author: minitest 5 | categories: [ test ] 6 | --- 7 | 8 | # A Title 9 | 10 | Some text. 11 | -------------------------------------------------------------------------------- /test/source/_posts/2015-01-01-another_post.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: nil 3 | title: Another Title 4 | author: minitest 5 | categories: [ test ] 6 | --- 7 | 8 | # Another Title 9 | 10 | Some text. 11 | 12 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 2 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 3 | 4 | TEST_PDF = File.join(File.dirname(__FILE__),'fixtures/test.pdf') 5 | TEST_IMPOSED_PDF = File.join(File.dirname(__FILE__),'fixtures/test-imposed.pdf') 6 | TEST_BINDER_PDF = File.join(File.dirname(__FILE__),'fixtures/test-binder.pdf') 7 | 8 | TEST_DIR = File.expand_path("../", __FILE__) 9 | SOURCE_DIR = File.expand_path("source", TEST_DIR) 10 | DEST_DIR = File.expand_path("destination", TEST_DIR) 11 | 12 | require 'rubygems' 13 | require 'minitest/autorun' 14 | require 'shoulda' 15 | require 'jekyll' 16 | require 'jekyll-pandoc-multiple-formats' 17 | 18 | 19 | # Copied from jekyll-archives (c) Alfred Xing, licensed under MIT with 20 | # the following note: 21 | # Taken from jekyll-mentions (Copyright (c) 2014 GitHub, Inc. Licensened under the MIT). 22 | class Minitest::Test 23 | def fixture_site(config = {}) 24 | Jekyll::Site.new( 25 | Jekyll::Utils.deep_merge_hashes( 26 | Jekyll::Utils.deep_merge_hashes( 27 | Jekyll::Configuration::DEFAULTS, 28 | { 29 | "source" => SOURCE_DIR, 30 | "destination" => DEST_DIR 31 | } 32 | ), 33 | config 34 | ) 35 | ) 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /test/test_pandoc_file.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class TestPandocFile < MiniTest::Test 4 | context 'single post' do 5 | setup do 6 | @site = fixture_site({ 'pandoc' => { }}) 7 | @site.read 8 | @pandoc_file = Jekyll::PandocFile.new(@site, 'pdf', @site.posts.docs.first) 9 | end 10 | 11 | should 'be a single post' do 12 | assert @pandoc_file.single_post? 13 | end 14 | 15 | should 'have a format' do 16 | assert_equal 'pdf', @pandoc_file.format 17 | end 18 | 19 | should 'have an extension' do 20 | assert @pandoc_file.path.end_with?('pdf') 21 | end 22 | 23 | should 'share the same path with different extension' do 24 | assert @pandoc_file.path.end_with?(@site.posts.docs.first.url.gsub(/html\Z/, 'pdf')) 25 | end 26 | 27 | should 'share the same title' do 28 | assert_equal @site.posts.docs.first.data['title'], @pandoc_file.title 29 | end 30 | 31 | should 'have an url' do 32 | assert_equal @site.posts.docs.first.url.gsub(/html\Z/, 'pdf'), @pandoc_file.url 33 | end 34 | 35 | should 'have metadata in yaml format' do 36 | assert @pandoc_file.yaml_metadata.start_with?('---') 37 | assert @pandoc_file.yaml_metadata.end_with?("---\n") 38 | end 39 | 40 | should 'have content' do 41 | assert @pandoc_file.content.is_a?(String) 42 | assert_equal @site.posts.docs.first.content, @pandoc_file.content 43 | end 44 | 45 | should 'be a pdf' do 46 | assert @pandoc_file.pdf? 47 | assert @pandoc_file.binary? 48 | refute @pandoc_file.epub? 49 | end 50 | 51 | should 'not have a cover' do 52 | refute @pandoc_file.has_cover? 53 | refute @pandoc_file.cover 54 | end 55 | 56 | should 'have a papersize' do 57 | refute @pandoc_file.papersize.empty? 58 | end 59 | 60 | should 'have a sheetsize' do 61 | refute @pandoc_file.sheetsize.empty? 62 | end 63 | 64 | should 'have flags' do 65 | assert @pandoc_file.flags.is_a?(String) 66 | assert @pandoc_file.flags.length > 0 67 | end 68 | 69 | should 'create a file' do 70 | assert @pandoc_file.write 71 | assert File.exists?(@pandoc_file.path) 72 | end 73 | 74 | end 75 | 76 | context 'several posts' do 77 | setup do 78 | @site = fixture_site({ 'pandoc' => { }}) 79 | @site.read 80 | @pandoc_file = Jekyll::PandocFile.new(@site, 'pdf', @site.posts.docs, 'Multipost test') 81 | end 82 | 83 | should 'be multipost' do 84 | refute @pandoc_file.single_post? 85 | end 86 | 87 | should 'have a title' do 88 | assert_raises(ArgumentError) { Jekyll::PandocFile.new(@site, 'pdf', @site.posts.docs) } 89 | refute @pandoc_file.title.empty? 90 | end 91 | 92 | should 'have a path' do 93 | assert @pandoc_file.path.end_with?("#{@pandoc_file.slug}.#{@pandoc_file.format}") 94 | end 95 | 96 | should 'have metadata in yaml format' do 97 | assert @pandoc_file.yaml_metadata.start_with?('---') 98 | assert @pandoc_file.yaml_metadata.end_with?("---\n") 99 | end 100 | 101 | should 'create a file' do 102 | assert @pandoc_file.write 103 | assert File.exists?(@pandoc_file.path) 104 | end 105 | end 106 | end 107 | -------------------------------------------------------------------------------- /test/test_printer.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class TestJekyllPandocMultipleFormats < MiniTest::Test 4 | context 'imposition' do 5 | setup do 6 | @imposition = JekyllPandocMultipleFormats::Imposition.new(TEST_PDF) 7 | end 8 | 9 | should 'have a new file name' do 10 | assert_equal TEST_IMPOSED_PDF, @imposition.output_file 11 | end 12 | 13 | should 'write a new filename' do 14 | assert @imposition.write 15 | assert File.exists?(TEST_IMPOSED_PDF) 16 | assert FileUtils.rm(TEST_IMPOSED_PDF) 17 | end 18 | 19 | should 'have number of pages' do 20 | assert_equal 23, @imposition.pages 21 | end 22 | 23 | should 'have a sheet size' do 24 | assert_equal 'a4paper', @imposition.sheetsize 25 | end 26 | 27 | should 'have a page size' do 28 | assert_equal 'a5paper', @imposition.papersize 29 | end 30 | 31 | should 'have rounded pages' do 32 | assert_equal 24, @imposition.rounded_pages 33 | end 34 | 35 | should 'have blank pages' do 36 | assert_equal 1, @imposition.blank_pages 37 | end 38 | 39 | should 'have a signature' do 40 | assert_equal 24, @imposition.signature 41 | end 42 | 43 | should 'have ordered pages' do 44 | 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 45 | end 46 | end 47 | 48 | context 'signed imposition' do 49 | setup do 50 | @imposition = JekyllPandocMultipleFormats::Imposition.new(TEST_PDF, 'a5paper', 'a4paper', 12) 51 | end 52 | 53 | should 'have a sheet size' do 54 | assert_equal 'a4paper', @imposition.sheetsize 55 | end 56 | 57 | should 'have a page size' do 58 | assert_equal 'a5paper', @imposition.papersize 59 | end 60 | 61 | should 'have a signature' do 62 | assert_equal 12, @imposition.signature 63 | end 64 | 65 | should 'have ordered pages' do 66 | 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 67 | end 68 | end 69 | 70 | context '2x1 imposition' do 71 | setup do 72 | @impositions = [] 73 | 74 | [ 75 | { p: 'a7paper', s: 'a6paper' }, 76 | { p: 'a6paper', s: 'a5paper' }, 77 | { p: 'a5paper', s: 'a4paper' }, 78 | { p: 'a4paper', s: 'a3paper' }, 79 | { p: 'a3paper', s: 'a2paper' }, 80 | { p: 'a2paper', s: 'a1paper' }, 81 | { p: 'a1paper', s: 'a0paper' } 82 | ].each do |i| 83 | @impositions << JekyllPandocMultipleFormats::Imposition.new(TEST_PDF, i[:p], i[:s]) 84 | end 85 | end 86 | 87 | should 'have ordered pages' do 88 | @impositions.each do |i| 89 | 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 90 | end 91 | end 92 | end 93 | 94 | context '4x1 imposition' do 95 | setup do 96 | @impositions = [] 97 | 98 | [ 99 | { p: 'a7paper', s: 'a5paper' }, 100 | { p: 'a6paper', s: 'a4paper' }, 101 | { p: 'a5paper', s: 'a3paper' }, 102 | { p: 'a4paper', s: 'a2paper' }, 103 | { p: 'a3paper', s: 'a1paper' }, 104 | { p: 'a2paper', s: 'a0paper' } 105 | ].each do |i| 106 | @impositions << JekyllPandocMultipleFormats::Imposition.new(TEST_PDF, i[:p], i[:s]) 107 | end 108 | end 109 | 110 | should 'have ordered pages' do 111 | @impositions.each do |i| 112 | assert_equal ["{}", 1, "{}", 1, 113 | 2, 23, 2, 23, 114 | 22, 3, 22, 3, 115 | 4, 21, 4, 21, 116 | 20, 5, 20, 5, 117 | 6, 19, 6, 19, 118 | 18, 7, 18, 7, 119 | 8, 17, 8, 17, 120 | 16, 9, 16, 9, 121 | 10, 15, 10, 15, 122 | 14, 11, 14, 11, 123 | 12, 13, 12, 13], 124 | i.to_nup 125 | end 126 | end 127 | end 128 | 129 | context '8x1 imposition' do 130 | setup do 131 | @impositions = [] 132 | 133 | [ 134 | { p: 'a7paper', s: 'a4paper' }, 135 | { p: 'a6paper', s: 'a3paper' }, 136 | { p: 'a5paper', s: 'a2paper' }, 137 | { p: 'a4paper', s: 'a1paper' }, 138 | { p: 'a3paper', s: 'a0paper' } 139 | ].each do |i| 140 | @impositions << JekyllPandocMultipleFormats::Imposition.new(TEST_PDF, i[:p], i[:s]) 141 | end 142 | end 143 | 144 | should 'have ordered pages' do 145 | @impositions.each do |i| 146 | assert_equal [ 147 | "{}", 1, "{}", 1, "{}", 1, "{}", 1, 148 | 2, 23, 2, 23, 2, 23, 2, 23, 149 | 22, 3, 22, 3, 22, 3, 22, 3, 150 | 4, 21, 4, 21, 4, 21, 4, 21, 151 | 20, 5, 20, 5, 20, 5, 20, 5, 152 | 6, 19, 6, 19, 6, 19, 6, 19, 153 | 18, 7, 18, 7, 18, 7, 18, 7, 154 | 8, 17, 8, 17, 8, 17, 8, 17, 155 | 16, 9, 16, 9, 16, 9, 16, 9, 156 | 10, 15, 10, 15, 10, 15, 10, 15, 157 | 14, 11, 14, 11, 14, 11, 14, 11, 158 | 12, 13, 12, 13, 12, 13, 12, 13], 159 | i.to_nup 160 | end 161 | end 162 | end 163 | 164 | context '16x1 imposition' do 165 | setup do 166 | @impositions = [] 167 | 168 | [ 169 | { p: 'a7paper', s: 'a3paper' }, 170 | { p: 'a6paper', s: 'a2paper' }, 171 | { p: 'a5paper', s: 'a1paper' }, 172 | { p: 'a4paper', s: 'a0paper' } 173 | ].each do |i| 174 | @impositions << JekyllPandocMultipleFormats::Imposition.new(TEST_PDF, i[:p], i[:s]) 175 | end 176 | end 177 | 178 | should 'have ordered pages' do 179 | @impositions.each do |i| 180 | assert_equal [ 181 | "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, 182 | 2, 23, 2, 23, 2, 23, 2, 23, 2, 23, 2, 23, 2, 23, 2, 23, 183 | 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 184 | 4, 21, 4, 21, 4, 21, 4, 21, 4, 21, 4, 21, 4, 21, 4, 21, 185 | 20, 5, 20, 5, 20, 5, 20, 5, 20, 5, 20, 5, 20, 5, 20, 5, 186 | 6, 19, 6, 19, 6, 19, 6, 19, 6, 19, 6, 19, 6, 19, 6, 19, 187 | 18, 7, 18, 7, 18, 7, 18, 7, 18, 7, 18, 7, 18, 7, 18, 7, 188 | 8, 17, 8, 17, 8, 17, 8, 17, 8, 17, 8, 17, 8, 17, 8, 17, 189 | 16, 9, 16, 9, 16, 9, 16, 9, 16, 9, 16, 9, 16, 9, 16, 9, 190 | 10, 15, 10, 15, 10, 15, 10, 15, 10, 15, 10, 15, 10, 15, 10, 15, 191 | 14, 11, 14, 11, 14, 11, 14, 11, 14, 11, 14, 11, 14, 11, 14, 11, 192 | 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13], 193 | i.to_nup 194 | end 195 | end 196 | end 197 | 198 | context '32x1 imposition' do 199 | setup do 200 | @impositions = [] 201 | 202 | [ 203 | { p: 'a7paper', s: 'a2paper' }, 204 | { p: 'a6paper', s: 'a1paper' }, 205 | { p: 'a5paper', s: 'a0paper' } 206 | ].each do |i| 207 | @impositions << JekyllPandocMultipleFormats::Imposition.new(TEST_PDF, i[:p], i[:s]) 208 | end 209 | end 210 | 211 | should 'have ordered pages' do 212 | @impositions.each do |i| 213 | assert_equal [ 214 | "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, "{}", 1, 215 | 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, 216 | 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, 217 | 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, 218 | 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, 219 | 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, 220 | 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, 221 | 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, 222 | 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, 223 | 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, 224 | 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, 225 | 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], 226 | i.to_nup 227 | end 228 | end 229 | end 230 | 231 | context 'binder' do 232 | setup do 233 | @binder = JekyllPandocMultipleFormats::Binder.new(TEST_PDF) 234 | end 235 | 236 | should 'write a new filename' do 237 | assert @binder.write 238 | assert File.exists?(TEST_BINDER_PDF) 239 | assert FileUtils.rm(TEST_BINDER_PDF) 240 | end 241 | 242 | should 'have number of pages' do 243 | assert_equal 23, @binder.pages 244 | end 245 | 246 | should 'have a sheet size' do 247 | assert_equal 'a4paper', @binder.sheetsize 248 | end 249 | 250 | should 'have a page size' do 251 | assert_equal 'a5paper', @binder.papersize 252 | end 253 | end 254 | 255 | context '2x1 binder' do 256 | setup do 257 | @binder = [] 258 | 259 | [ 260 | { p: 'a7paper', s: 'a6paper' }, 261 | { p: 'a6paper', s: 'a5paper' }, 262 | { p: 'a5paper', s: 'a4paper' }, 263 | { p: 'a4paper', s: 'a3paper' }, 264 | { p: 'a3paper', s: 'a2paper' }, 265 | { p: 'a2paper', s: 'a1paper' }, 266 | { p: 'a1paper', s: 'a0paper' } 267 | ].each do |i| 268 | @binder << JekyllPandocMultipleFormats::Binder.new(TEST_PDF, i[:p], i[:s]) 269 | end 270 | end 271 | 272 | should 'have ordered pages' do 273 | @binder.each do |i| 274 | assert_equal [ 275 | 1, 1, 276 | 2, 2, 277 | 3, 3, 278 | 4, 4, 279 | 5, 5, 280 | 6, 6, 281 | 7, 7, 282 | 8, 8, 283 | 9, 9, 284 | 10, 10, 285 | 11, 11, 286 | 12, 12, 287 | 13, 13, 288 | 14, 14, 289 | 15, 15, 290 | 16, 16, 291 | 17, 17, 292 | 18, 18, 293 | 19, 19, 294 | 20, 20, 295 | 21, 21, 296 | 22, 22, 297 | 23, 23 ], i.to_nup 298 | end 299 | end 300 | end 301 | 302 | context '4x1 binder' do 303 | setup do 304 | @binder = [] 305 | 306 | [ 307 | { p: 'a7paper', s: 'a5paper' }, 308 | { p: 'a6paper', s: 'a4paper' }, 309 | { p: 'a5paper', s: 'a3paper' }, 310 | { p: 'a4paper', s: 'a2paper' }, 311 | { p: 'a3paper', s: 'a1paper' }, 312 | { p: 'a2paper', s: 'a0paper' } 313 | ].each do |i| 314 | @binder << JekyllPandocMultipleFormats::Binder.new(TEST_PDF, i[:p], i[:s]) 315 | end 316 | end 317 | 318 | should 'have ordered pages' do 319 | @binder.each do |i| 320 | assert_equal [ 321 | 1, 1, 1, 1, 322 | 2, 2, 2, 2, 323 | 3, 3, 3, 3, 324 | 4, 4, 4, 4, 325 | 5, 5, 5, 5, 326 | 6, 6, 6, 6, 327 | 7, 7, 7, 7, 328 | 8, 8, 8, 8, 329 | 9, 9, 9, 9, 330 | 10, 10, 10, 10, 331 | 11, 11, 11, 11, 332 | 12, 12, 12, 12, 333 | 13, 13, 13, 13, 334 | 14, 14, 14, 14, 335 | 15, 15, 15, 15, 336 | 16, 16, 16, 16, 337 | 17, 17, 17, 17, 338 | 18, 18, 18, 18, 339 | 19, 19, 19, 19, 340 | 20, 20, 20, 20, 341 | 21, 21, 21, 21, 342 | 22, 22, 22, 22, 343 | 23, 23, 23, 23], 344 | i.to_nup 345 | end 346 | end 347 | end 348 | 349 | context '8x1 binder' do 350 | setup do 351 | @binder = [] 352 | 353 | [ 354 | { p: 'a7paper', s: 'a4paper' }, 355 | { p: 'a6paper', s: 'a3paper' }, 356 | { p: 'a5paper', s: 'a2paper' }, 357 | { p: 'a4paper', s: 'a1paper' }, 358 | { p: 'a3paper', s: 'a0paper' } 359 | ].each do |i| 360 | @binder << JekyllPandocMultipleFormats::Binder.new(TEST_PDF, i[:p], i[:s]) 361 | end 362 | end 363 | 364 | should 'have ordered pages' do 365 | @binder.each do |i| 366 | assert_equal [ 367 | 1, 1, 1, 1, 1, 1, 1, 1, 368 | 2, 2, 2, 2, 2, 2, 2, 2, 369 | 3, 3, 3, 3, 3, 3, 3, 3, 370 | 4, 4, 4, 4, 4, 4, 4, 4, 371 | 5, 5, 5, 5, 5, 5, 5, 5, 372 | 6, 6, 6, 6, 6, 6, 6, 6, 373 | 7, 7, 7, 7, 7, 7, 7, 7, 374 | 8, 8, 8, 8, 8, 8, 8, 8, 375 | 9, 9, 9, 9, 9, 9, 9, 9, 376 | 10, 10, 10, 10, 10, 10, 10, 10, 377 | 11, 11, 11, 11, 11, 11, 11, 11, 378 | 12, 12, 12, 12, 12, 12, 12, 12, 379 | 13, 13, 13, 13, 13, 13, 13, 13, 380 | 14, 14, 14, 14, 14, 14, 14, 14, 381 | 15, 15, 15, 15, 15, 15, 15, 15, 382 | 16, 16, 16, 16, 16, 16, 16, 16, 383 | 17, 17, 17, 17, 17, 17, 17, 17, 384 | 18, 18, 18, 18, 18, 18, 18, 18, 385 | 19, 19, 19, 19, 19, 19, 19, 19, 386 | 20, 20, 20, 20, 20, 20, 20, 20, 387 | 21, 21, 21, 21, 21, 21, 21, 21, 388 | 22, 22, 22, 22, 22, 22, 22, 22, 389 | 23, 23, 23, 23, 23, 23, 23, 23], 390 | i.to_nup 391 | end 392 | end 393 | end 394 | 395 | context '16x1 binder' do 396 | setup do 397 | @binder = [] 398 | 399 | [ 400 | { p: 'a7paper', s: 'a3paper' }, 401 | { p: 'a6paper', s: 'a2paper' }, 402 | { p: 'a5paper', s: 'a1paper' }, 403 | { p: 'a4paper', s: 'a0paper' } 404 | ].each do |i| 405 | @binder << JekyllPandocMultipleFormats::Binder.new(TEST_PDF, i[:p], i[:s]) 406 | end 407 | end 408 | 409 | should 'have ordered pages' do 410 | @binder.each do |i| 411 | assert_equal [ 412 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 413 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 414 | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 415 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 416 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 417 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 418 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 419 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 420 | 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 421 | 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 422 | 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 423 | 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 424 | 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 425 | 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 426 | 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 427 | 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 428 | 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 429 | 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 430 | 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 431 | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 432 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 433 | 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 434 | 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23], 435 | i.to_nup 436 | end 437 | end 438 | end 439 | 440 | context '32x1 binder' do 441 | setup do 442 | @binder = [] 443 | 444 | [ 445 | { p: 'a7paper', s: 'a2paper' }, 446 | { p: 'a6paper', s: 'a1paper' }, 447 | { p: 'a5paper', s: 'a0paper' } 448 | ].each do |i| 449 | @binder << JekyllPandocMultipleFormats::Binder.new(TEST_PDF, i[:p], i[:s]) 450 | end 451 | end 452 | 453 | should 'have ordered pages' do 454 | @binder.each do |i| 455 | assert_equal [ 456 | 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, 457 | 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, 458 | 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, 459 | 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, 460 | 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, 461 | 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, 462 | 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, 463 | 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, 464 | 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, 465 | 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, 466 | 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, 467 | 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, 468 | 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, 469 | 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, 470 | 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, 471 | 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, 472 | 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, 473 | 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, 474 | 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, 475 | 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, 476 | 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, 477 | 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, 478 | 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], 479 | i.to_nup 480 | end 481 | end 482 | end 483 | end 484 | --------------------------------------------------------------------------------