jekyll-printing-press 1.0.0rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +222 -0
- data/lib/jekyll/converters/markdown/pandoc.rb +47 -0
- data/lib/jekyll/pandoc/configuration.rb +120 -0
- data/lib/jekyll/pandoc/document.rb +202 -0
- data/lib/jekyll/pandoc/documents/bound.rb +58 -0
- data/lib/jekyll/pandoc/documents/imposed.rb +61 -0
- data/lib/jekyll/pandoc/documents/multiple.rb +76 -0
- data/lib/jekyll/pandoc/generator.rb +94 -0
- data/lib/jekyll/pandoc/generators/binder.rb +45 -0
- data/lib/jekyll/pandoc/generators/category.rb +32 -0
- data/lib/jekyll/pandoc/generators/imposition.rb +45 -0
- data/lib/jekyll/pandoc/generators/multiple.rb +62 -0
- data/lib/jekyll/pandoc/generators/posts.rb +54 -0
- data/lib/jekyll/pandoc/generators/site.rb +32 -0
- data/lib/jekyll/pandoc/paru_helper.rb +24 -0
- data/lib/jekyll/pandoc/printing.latex.liquid +10 -0
- data/lib/jekyll/pandoc/printing.rb +167 -0
- data/lib/jekyll/pandoc/renderer.rb +62 -0
- data/lib/jekyll/pandoc/renderers/binder.rb +32 -0
- data/lib/jekyll/pandoc/renderers/imposition.rb +82 -0
- data/lib/jekyll/pandoc/utils.rb +95 -0
- data/lib/jekyll-pandoc-multiple-formats.rb +16 -0
- metadata +311 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'securerandom'
|
4
|
+
require_relative '../document'
|
5
|
+
require_relative '../renderers/imposition'
|
6
|
+
|
7
|
+
module Jekyll
|
8
|
+
module Pandoc
|
9
|
+
module Documents
|
10
|
+
# An imposed document is a PDF with pages arranged for printing
|
11
|
+
# and folding.
|
12
|
+
class Imposed < Jekyll::Pandoc::Document
|
13
|
+
# Do nothing, content is not read, but Jekyll expects a String
|
14
|
+
#
|
15
|
+
# @return [nil]
|
16
|
+
def read_content(**)
|
17
|
+
self.content = ''
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
# Adds relations to source documents
|
22
|
+
#
|
23
|
+
# @return [nil]
|
24
|
+
def read_post_data
|
25
|
+
if data.key? 'uuid'
|
26
|
+
require 'securerandom'
|
27
|
+
data['uuid'] = SecureRandom.uuid
|
28
|
+
end
|
29
|
+
|
30
|
+
source_document.source_document.data['imposed'] =
|
31
|
+
source_document.data['imposed'] = self
|
32
|
+
|
33
|
+
source_document.source_document.data['formats'] << self
|
34
|
+
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
# Imposition
|
39
|
+
#
|
40
|
+
# @return [Jekyll::Pandoc::Renderers::Imposition]
|
41
|
+
def renderer
|
42
|
+
@renderer ||= Jekyll::Pandoc::Renderers::Imposition.new(site, self)
|
43
|
+
end
|
44
|
+
|
45
|
+
# The file is always binary
|
46
|
+
#
|
47
|
+
# @return [TrueClass]
|
48
|
+
def binary?
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
# PDFs can't be rendered with Liquid
|
53
|
+
#
|
54
|
+
# @return [FalseClass]
|
55
|
+
def render_with_liquid?
|
56
|
+
false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'securerandom'
|
4
|
+
require 'jekyll/utils'
|
5
|
+
require_relative '../document'
|
6
|
+
require_relative '../utils'
|
7
|
+
|
8
|
+
module Jekyll
|
9
|
+
module Pandoc
|
10
|
+
module Documents
|
11
|
+
# A document composed of several documents
|
12
|
+
class Multiple < Jekyll::Pandoc::Document
|
13
|
+
# Documents group
|
14
|
+
#
|
15
|
+
# @return [Array]
|
16
|
+
attr_reader :source_documents
|
17
|
+
|
18
|
+
# @param path [String]
|
19
|
+
# @param relations [Hash]
|
20
|
+
def initialize(path, relations = {})
|
21
|
+
@source_documents = relations[:source_documents]
|
22
|
+
relations[:source_document] = self
|
23
|
+
super
|
24
|
+
|
25
|
+
Jekyll::Utils.deep_merge_hashes!(data, relations[:data])
|
26
|
+
end
|
27
|
+
|
28
|
+
# Data must be provided externally
|
29
|
+
#
|
30
|
+
# @return [Hash]
|
31
|
+
def data
|
32
|
+
@data ||= { 'multiple' => true }
|
33
|
+
end
|
34
|
+
|
35
|
+
# The content is a concatenated string of source_documents
|
36
|
+
# content with their titles.
|
37
|
+
#
|
38
|
+
# The ID is attached to a title so you can map IDs to metadata
|
39
|
+
# on a Pandoc filter later.
|
40
|
+
#
|
41
|
+
# @return [nil]
|
42
|
+
def read_content(**)
|
43
|
+
self.content = source_documents.map do |doc|
|
44
|
+
["\n\n# #{doc['title']} {##{extract_id(doc)} data-chapter-title=true}", doc.content]
|
45
|
+
end.flatten.join("\n\n")
|
46
|
+
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
|
50
|
+
# Generates a data hash from source documents so they can be
|
51
|
+
# accessed later as Pandoc metadata. For instance, to generate
|
52
|
+
# authors per chapter.
|
53
|
+
#
|
54
|
+
# @return [nil]
|
55
|
+
def read_post_data
|
56
|
+
data['uuid'] ||= SecureRandom.uuid
|
57
|
+
|
58
|
+
source_documents.each do |doc|
|
59
|
+
data[extract_id(doc)] = Jekyll::Pandoc::Utils.sanitize_data doc.data
|
60
|
+
end
|
61
|
+
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# Extracts an ID from a document
|
68
|
+
#
|
69
|
+
# @return [String]
|
70
|
+
def extract_id(document)
|
71
|
+
document['uuid'] || document.id.tr('/', '-').sub(/\A-/, '')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jekyll
|
4
|
+
module Pandoc
|
5
|
+
# Generates all Pandoc documents and adds them to the site. The
|
6
|
+
# documents are not written, just prepared for later rendering and
|
7
|
+
# writing following Jekyll's process.
|
8
|
+
#
|
9
|
+
# @see Jekyll::Site#generate
|
10
|
+
# @see Jekyll::Generator
|
11
|
+
class Generator < ::Jekyll::Generator
|
12
|
+
safe true
|
13
|
+
priority :highest
|
14
|
+
|
15
|
+
# Site
|
16
|
+
#
|
17
|
+
# @return [Jekyll::Site]
|
18
|
+
attr_reader :site
|
19
|
+
|
20
|
+
# Pandoc collections
|
21
|
+
#
|
22
|
+
# @return [Hash]
|
23
|
+
attr_reader :collections
|
24
|
+
|
25
|
+
# Generate documents for every format.
|
26
|
+
#
|
27
|
+
# @return [nil]
|
28
|
+
def generate(site)
|
29
|
+
@site = site
|
30
|
+
@collections = {}
|
31
|
+
|
32
|
+
return unless generate?
|
33
|
+
|
34
|
+
setup
|
35
|
+
generate_documents!
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# Do nothing for now
|
41
|
+
def setup; end
|
42
|
+
|
43
|
+
# Source documents. Every generator know how to fetch its own
|
44
|
+
# documents.
|
45
|
+
#
|
46
|
+
# @return [Array]
|
47
|
+
def source_documents
|
48
|
+
raise NotImplementedError
|
49
|
+
end
|
50
|
+
|
51
|
+
# This method knows how to collect documents, and generate
|
52
|
+
# collections and Pandoc documents.
|
53
|
+
#
|
54
|
+
# @return [nil]
|
55
|
+
def generate_documents!
|
56
|
+
raise NotImplementedError
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Hash]
|
60
|
+
def config
|
61
|
+
site.config['pandoc']
|
62
|
+
end
|
63
|
+
|
64
|
+
# Jekyll runs every Jekyll::Generator descendant, but we don't
|
65
|
+
# want to run this one, just use it as a template.
|
66
|
+
#
|
67
|
+
# @return [Boolean]
|
68
|
+
def generate?
|
69
|
+
self.class != Jekyll::Pandoc::Generator
|
70
|
+
end
|
71
|
+
|
72
|
+
# Creates or finds a collection by a label, performs basic
|
73
|
+
# configuration and brings everything else from site
|
74
|
+
# configuration.
|
75
|
+
#
|
76
|
+
# @param label [String]
|
77
|
+
# @return [Hash]
|
78
|
+
def collection_for(label)
|
79
|
+
site.collections[label] ||=
|
80
|
+
Jekyll::Collection.new(site, label).tap do |col|
|
81
|
+
# Allow to configure the collection
|
82
|
+
# @see https://jekyllrb.com/docs/collections/
|
83
|
+
col.metadata['output'] = true unless col.metadata.key? 'output'
|
84
|
+
# Follow the same permalink structure unless otherwise
|
85
|
+
# specified.
|
86
|
+
unless site.config.dig('collections', label, 'permalink')
|
87
|
+
col.metadata['permalink'] =
|
88
|
+
site.posts.url_template
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../generator'
|
4
|
+
require_relative '../documents/bound'
|
5
|
+
|
6
|
+
module Jekyll
|
7
|
+
module Pandoc
|
8
|
+
module Generators
|
9
|
+
# Generates a ready for print PDF for use in a binding machine.
|
10
|
+
class Binder < Jekyll::Pandoc::Generator
|
11
|
+
priority :high
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# Only generate if the site asks for imposition
|
16
|
+
#
|
17
|
+
# @return [Boolean]
|
18
|
+
def generate?
|
19
|
+
site.config.dig('pandoc', 'printing', 'formats')&.include? 'binder'
|
20
|
+
end
|
21
|
+
|
22
|
+
# Only PDFs are imposed
|
23
|
+
#
|
24
|
+
# @return [Array]
|
25
|
+
def source_documents
|
26
|
+
@source_documents ||= site.collections['pdf'].docs
|
27
|
+
end
|
28
|
+
|
29
|
+
# Generate a {Jekyll::Pandoc::Documents::Bound} per document
|
30
|
+
#
|
31
|
+
# @return [nil]
|
32
|
+
def generate_documents!
|
33
|
+
source_documents.each do |document|
|
34
|
+
collection_for('bound').tap do |col|
|
35
|
+
col.docs << Jekyll::Pandoc::Documents::Bound.new(document.path, site: site, collection: col,
|
36
|
+
source_document: document).tap(&:read)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../generator'
|
4
|
+
require_relative 'multiple'
|
5
|
+
|
6
|
+
module Jekyll
|
7
|
+
module Pandoc
|
8
|
+
module Generators
|
9
|
+
class Category < Jekyll::Pandoc::Generator
|
10
|
+
include Multiple
|
11
|
+
|
12
|
+
priority :high
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
# Group posts by categories
|
17
|
+
#
|
18
|
+
# @return [String]
|
19
|
+
def generator_type
|
20
|
+
@generator_type ||= 'categories'
|
21
|
+
end
|
22
|
+
|
23
|
+
# Groups documents by category
|
24
|
+
#
|
25
|
+
# @return [Array]
|
26
|
+
def source_documents
|
27
|
+
@source_documents ||= site.categories.to_a
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../generator'
|
4
|
+
require_relative '../documents/imposed'
|
5
|
+
|
6
|
+
module Jekyll
|
7
|
+
module Pandoc
|
8
|
+
module Generators
|
9
|
+
# Generates a ready for print PDF by creating 4-pages sheets.
|
10
|
+
class Imposition < Jekyll::Pandoc::Generator
|
11
|
+
priority :high
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# Only generate if the site asks for imposition
|
16
|
+
#
|
17
|
+
# @return [Boolean]
|
18
|
+
def generate?
|
19
|
+
site.config.dig('pandoc', 'printing', 'formats')&.include? 'imposition'
|
20
|
+
end
|
21
|
+
|
22
|
+
# Only PDFs are imposed
|
23
|
+
#
|
24
|
+
# @return [Array]
|
25
|
+
def source_documents
|
26
|
+
@source_documents ||= site.collections['pdf'].docs
|
27
|
+
end
|
28
|
+
|
29
|
+
# Generate a {Jekyll::Pandoc::Documents::Imposed} per document
|
30
|
+
#
|
31
|
+
# @return [nil]
|
32
|
+
def generate_documents!
|
33
|
+
source_documents.each do |document|
|
34
|
+
collection_for('imposed').tap do |col|
|
35
|
+
col.docs << Jekyll::Pandoc::Documents::Imposed.new(document.path, site: site, collection: col,
|
36
|
+
source_document: document).tap(&:read)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'tempfile'
|
4
|
+
require 'jekyll/utils'
|
5
|
+
require_relative '../documents/multiple'
|
6
|
+
|
7
|
+
module Jekyll
|
8
|
+
module Pandoc
|
9
|
+
module Generators
|
10
|
+
# Mixin for multiple-file generators
|
11
|
+
module Multiple
|
12
|
+
private
|
13
|
+
|
14
|
+
# Generator type
|
15
|
+
#
|
16
|
+
# @return [String]
|
17
|
+
def generator_type
|
18
|
+
raise NotImplementedError
|
19
|
+
end
|
20
|
+
|
21
|
+
# Must return an Array of grouped documents (an Array of Arrays)
|
22
|
+
#
|
23
|
+
# @return [Array]
|
24
|
+
def source_documents
|
25
|
+
raise NotImplementedError
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [Boolean]
|
29
|
+
def generate?
|
30
|
+
config['documents']&.include? generator_type
|
31
|
+
end
|
32
|
+
|
33
|
+
# Generates a temporary file on site source per each document
|
34
|
+
# group and passes data as a relation.
|
35
|
+
#
|
36
|
+
# @return [nil]
|
37
|
+
def generate_documents!
|
38
|
+
source_documents.each do |(title, documents)|
|
39
|
+
config.available_formats.each do |format|
|
40
|
+
next if format == :html5
|
41
|
+
|
42
|
+
data = {
|
43
|
+
'title' => title,
|
44
|
+
'slug' => Jekyll::Utils.slugify(title, mode: 'pretty'),
|
45
|
+
'data' => Time.now
|
46
|
+
}
|
47
|
+
label = format.to_s
|
48
|
+
file = Tempfile.new([data['slug'], '.markdown'], site.source)
|
49
|
+
|
50
|
+
collection_for(label).tap do |col|
|
51
|
+
col.docs << Jekyll::Pandoc::Documents::Multiple.new(file.path, site: site, collection: col,
|
52
|
+
source_documents: documents, data: data).tap(&:read)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../document'
|
4
|
+
require_relative '../generator'
|
5
|
+
|
6
|
+
module Jekyll
|
7
|
+
module Pandoc
|
8
|
+
class Generator
|
9
|
+
# Generates Pandoc documents from Jekyll posts collection. Each
|
10
|
+
# format is a collection.
|
11
|
+
class Posts < Generator
|
12
|
+
priority :highest
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
# Only generate if aggregating posts is enabled
|
17
|
+
#
|
18
|
+
# @return [Boolean]
|
19
|
+
def generate?
|
20
|
+
config['documents']&.include? 'posts'
|
21
|
+
end
|
22
|
+
|
23
|
+
# Documents to generate from
|
24
|
+
#
|
25
|
+
# @return [Array]
|
26
|
+
def source_documents
|
27
|
+
@source_documents ||= site.posts.docs.reject(&:asset_file?)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Generate a Pandoc document for each format and adds it to
|
31
|
+
# a collection.
|
32
|
+
#
|
33
|
+
# HTML5 is already generated by
|
34
|
+
# {Jekyll::Converters::Markdown::Pandoc}
|
35
|
+
#
|
36
|
+
# @return [nil]
|
37
|
+
def generate_documents!
|
38
|
+
source_documents.each do |document|
|
39
|
+
config.available_formats.each do |format|
|
40
|
+
next if format == :html5
|
41
|
+
|
42
|
+
collection_for(format.to_s).tap do |col|
|
43
|
+
col.docs << Document.new(document.path, site: site, collection: col,
|
44
|
+
source_document: document).tap(&:read)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../generator'
|
4
|
+
require_relative 'multiple'
|
5
|
+
|
6
|
+
module Jekyll
|
7
|
+
module Pandoc
|
8
|
+
module Generators
|
9
|
+
class Site < Jekyll::Pandoc::Generator
|
10
|
+
include Multiple
|
11
|
+
|
12
|
+
priority :high
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
# All posts
|
17
|
+
#
|
18
|
+
# @return [String]
|
19
|
+
def generator_type
|
20
|
+
@generator_type ||= 'site'
|
21
|
+
end
|
22
|
+
|
23
|
+
# All posts
|
24
|
+
#
|
25
|
+
# @return [Array]
|
26
|
+
def source_documents
|
27
|
+
@source_documents ||= [ [ site.config['title'], site.posts.docs.reject(&:asset_file?) ] ]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'paru/pandoc'
|
4
|
+
|
5
|
+
module Jekyll
|
6
|
+
module Pandoc
|
7
|
+
# Creates a Paru::Pandoc with options from configuration
|
8
|
+
module ParuHelper
|
9
|
+
extend self
|
10
|
+
|
11
|
+
# Returns a Paru::Pandoc with options set
|
12
|
+
#
|
13
|
+
# @param options [Hash]
|
14
|
+
# @return [Paru::Pandoc]
|
15
|
+
def from(options)
|
16
|
+
Paru::Pandoc.new.tap do |paru|
|
17
|
+
options.each do |option, value|
|
18
|
+
paru.send option, value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
\documentclass[{{ sheetsize }},10pt]{article}
|
2
|
+
|
3
|
+
\usepackage{pgfpages}
|
4
|
+
\usepackage{pdfpages}
|
5
|
+
|
6
|
+
\pgfpagesuselayout{ {{- nup }} on 1}[{{ sheetsize }},{{ options }}]
|
7
|
+
|
8
|
+
\begin{document}
|
9
|
+
\includepdf[pages={ {{- pages | join: ',' -}} }]{ {{- path -}} }
|
10
|
+
\end{document}
|