philomath 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: bb70795a33712bfa57b1199014c8c8e47db6b01939a20b2076f8893f687cf126
4
+ data.tar.gz: 97dace31e67170cd2c76e5618be7263df4a57e9ff6c02bd8632cd99587164685
5
+ SHA512:
6
+ metadata.gz: a416eceddfbf958bc994dd221e9844bc4886f65bea2da18553a74d786c364b061860ad72fce0c345f78d85a6f15d2dd7327e678df50c3517806517837c19b048
7
+ data.tar.gz: afa5784f2b3c04aee3dd1f4cc4a0b1f2aec86a22ac6f73d18bdca7a4247fa6680a0145d40cd0d0b51210ec9c4f516e012f4fd45040968f6dc9af5d96d120ed24
@@ -0,0 +1,21 @@
1
+ module Philomath
2
+ module Rendering
3
+ class RenderCallback
4
+ CALLBACKS = [
5
+ Philomath::Rendering::RenderCodeInline,
6
+ Philomath::Rendering::RenderWithoutEntities,
7
+ Philomath::Rendering::RenderStyledLink
8
+ ]
9
+
10
+ def call(value)
11
+ parsed_value = value
12
+
13
+ CALLBACKS.each do |callback|
14
+ parsed_value = callback.call(parsed_value)
15
+ end
16
+
17
+ parsed_value
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ module Philomath
2
+ module Rendering
3
+ class RenderCodeInline
4
+ def self.call(value)
5
+ if value.kind_of?(Array)
6
+ value.map { |v| v.gsub("<code>", "<color rgb='7d8182'>").gsub("</code>", "</color>") }
7
+ else
8
+ value.gsub("<code>", "<color rgb='7d8182'>").gsub("</code>", "</color>")
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ module Philomath
2
+ module Rendering
3
+ class RenderStyledLink
4
+ class << self
5
+ def call(value)
6
+ if value.kind_of?(Array)
7
+ value.map { |v| replace_links(v) }
8
+ else
9
+ replace_links(value)
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def replace_links(value)
16
+ fragment = Nokogiri::HTML.fragment(value)
17
+ fragment.css("a").wrap("<color rgb='0000FF'><u>")
18
+ fragment.to_html
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ module Philomath
2
+ module Rendering
3
+ class RenderWithoutEntities
4
+ def self.call(value)
5
+ if value.kind_of?(Array)
6
+ value.map { |v| v.gsub("&quot;", '"') }
7
+ else
8
+ value.gsub("&quot;", '"')
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module Philomath
2
+ module Version
3
+ module_function
4
+
5
+ def to_s
6
+ "0.0.1"
7
+ end
8
+ end
9
+ end
data/lib/philomath.rb ADDED
@@ -0,0 +1,143 @@
1
+ require 'markdownlyze'
2
+ require 'prawn'
3
+ require 'prawn_components'
4
+ require 'nokogiri'
5
+
6
+ require_relative 'philomath/rendering/render_code_inline'
7
+ require_relative 'philomath/rendering/render_styled_link'
8
+ require_relative 'philomath/rendering/render_without_entities'
9
+ require_relative 'philomath/rendering/render_callback'
10
+
11
+ module Philomath
12
+ class << self
13
+ def render(chapters:)
14
+ table_of_contents = {}
15
+ contents = {}
16
+ pdf_chapters = []
17
+ pdf = Prawn::Document.new
18
+ pdf.default_leading(10)
19
+
20
+ PrawnComponents.initialize_fonts(pdf: pdf)
21
+ pdf.font('Inter')
22
+
23
+ chapters.each do |chapter|
24
+ pdf_chapters << {
25
+ name: chapter[:name],
26
+ parsed_elements: Markdownlyze.parse(chapter[:content])
27
+ }
28
+ end
29
+
30
+ callback = Rendering::RenderCallback.new
31
+
32
+ toc_pages_count = get_toc_pages_count(chapters: pdf_chapters.map { |c| c[:parsed_elements] })
33
+ toc_pages_count.times { pdf.start_new_page }
34
+
35
+ pdf_chapters.each_with_index do |chapter_conf, chapter_conf_i|
36
+ chapter_elements = chapter_conf[:parsed_elements]
37
+
38
+ table_of_contents[chapter_conf[:name]] = {
39
+ page: pdf.page_number, headings: {}
40
+ }
41
+
42
+ chapter_elements.each do |node|
43
+ case node[:element]
44
+ when :h1
45
+ pdf.public_send("h1", node[:value], callback: callback)
46
+ when :h2
47
+ table_of_contents[chapter_conf[:name]][:headings][node[:value]] = pdf.page_number
48
+ pdf.public_send("h2", node[:value], callback: callback)
49
+ when :h3
50
+ pdf.public_send("h3", node[:value], callback: callback)
51
+ when :h4
52
+ pdf.public_send("h4", node[:value], callback: callback)
53
+ when :paragraph
54
+ pdf.public_send("paragraph", callback.call(node[:value]))
55
+ when :code_block
56
+ pdf.public_send("code_block", node[:value], node[:language])
57
+ when :blank_line
58
+ pdf.move_down(3)
59
+ when :ol
60
+ pdf.public_send("ol", node[:value], callback: callback)
61
+ when :ul
62
+ pdf.public_send("ul", node[:value], callback: callback)
63
+ when :image
64
+ pdf.move_down(6)
65
+ pdf.image(chapter.images[node[:value]], position: :center, width: pdf.bounds.width - 25)
66
+ pdf.move_down(6)
67
+ when :quote
68
+ pdf.public_send("quote", node[:value], callback: callback)
69
+ pdf.move_down(6)
70
+ end
71
+ end
72
+
73
+ pdf.start_new_page unless chapter_conf_i == pdf_chapters.size - 1
74
+ end
75
+
76
+ toc_page = 1
77
+
78
+ pdf.go_to_page(toc_page)
79
+
80
+ pdf.public_send("h1", 'Table of contents')
81
+ pdf.move_down(50)
82
+
83
+ pdf.public_send("table_of_contents", table_of_contents)
84
+
85
+ # Generate clickable table of contents
86
+ pdf.outline.define do |outline|
87
+ table_of_contents.each do |chapter_data|
88
+ toc_chapter_title = chapter_data.first
89
+ toc_chapter_page = chapter_data.last[:page]
90
+ toc_chapter_headings = chapter_data.last[:headings]
91
+
92
+ outline.section(toc_chapter_title, destination: toc_chapter_page) do
93
+ toc_chapter_headings.each_pair do |heading_title, heading_page|
94
+ outline.page(title: heading_title, destination: heading_page)
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ options = {
101
+ start_count_at: 3,
102
+ page_filter: lambda{ |pg| pg > 2 },
103
+ at: [0, 0],
104
+ align: :center,
105
+ size: 13
106
+ }
107
+
108
+ pdf.number_pages('<page>', options)
109
+
110
+ pdf.render
111
+ end
112
+
113
+ private
114
+
115
+ def get_toc_pages_count(chapters:)
116
+ toc_list_item_type = :h2
117
+ subheading_height = 29
118
+ heading_height = 43
119
+ height_to_use_on_page = 592
120
+
121
+ toc_metrics = chapters.map do |c|
122
+ c.count { |m| m[:element] == toc_list_item_type }
123
+ end
124
+
125
+ sum = toc_metrics.map do |n|
126
+ heading_height + (subheading_height * n)
127
+ end
128
+
129
+ toc_page_length = 1
130
+ toc_page_pos = 0
131
+ sum.each do |chap_sum|
132
+ if (toc_page_pos + chap_sum) > height_to_use_on_page
133
+ toc_page_length += 1
134
+ toc_page_pos = 0
135
+ else
136
+ toc_page_pos += chap_sum
137
+ end
138
+ end
139
+
140
+ toc_page_length
141
+ end
142
+ end
143
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: philomath
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Paweł Dąbrowski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-11-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pry
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: markdownlyze
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.0.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 0.0.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: prawn_components
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 0.0.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 0.0.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: prawn
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 2.2.2
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 2.2.2
69
+ - !ruby/object:Gem::Dependency
70
+ name: nokogiri
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '='
74
+ - !ruby/object:Gem::Version
75
+ version: 1.16.7
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '='
81
+ - !ruby/object:Gem::Version
82
+ version: 1.16.7
83
+ description: Turn markdown files into PDF book
84
+ email: contact@paweldabrowski.com
85
+ executables: []
86
+ extensions: []
87
+ extra_rdoc_files: []
88
+ files:
89
+ - lib/philomath.rb
90
+ - lib/philomath/rendering/render_callback.rb
91
+ - lib/philomath/rendering/render_code_inline.rb
92
+ - lib/philomath/rendering/render_styled_link.rb
93
+ - lib/philomath/rendering/render_without_entities.rb
94
+ - lib/philomath/version.rb
95
+ homepage:
96
+ licenses: []
97
+ metadata: {}
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ requirements: []
113
+ rubygems_version: 3.4.10
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: Turn markdown files into PDF book
117
+ test_files: []