mdown2pdf 0.1.0

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.
@@ -0,0 +1,66 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <xsl:stylesheet version="2.0"
3
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
4
+ xmlns:outline="http://wkhtmltopdf.org/outline"
5
+ xmlns="http://www.w3.org/1999/xhtml">
6
+ <xsl:output doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
7
+ doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
8
+ indent="yes" />
9
+ <xsl:template match="outline:outline">
10
+ <html>
11
+ <head>
12
+ <title>Table of Contents</title>
13
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
14
+ <style>
15
+ body {
16
+ color: #333;
17
+ }
18
+
19
+ h1 {
20
+ text-align: center;
21
+ font-size: 20px;
22
+ font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
23
+ }
24
+ div {
25
+ border-bottom: 1px dotted #555;
26
+ }
27
+ span {float: right;}
28
+ li {list-style: none;}
29
+ ul {
30
+ font-size: 15px;
31
+ font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
32
+ }
33
+ ul {padding-left: 0em;}
34
+ ul ul { padding-left: 1em; }
35
+ a {text-decoration:none; color: #4078c0;}
36
+ </style>
37
+ </head>
38
+ <body>
39
+ <h1>Table des matières</h1>
40
+ <ul><xsl:apply-templates select="outline:item/outline:item"/></ul>
41
+ </body>
42
+ </html>
43
+ </xsl:template>
44
+ <xsl:template match="outline:item">
45
+ <li>
46
+ <xsl:if test="@title!=''">
47
+ <div>
48
+ <a>
49
+ <xsl:if test="@link">
50
+ <xsl:attribute name="href"><xsl:value-of select="@link"/></xsl:attribute>
51
+ </xsl:if>
52
+ <xsl:if test="@backLink">
53
+ <xsl:attribute name="name"><xsl:value-of select="@backLink"/></xsl:attribute>
54
+ </xsl:if>
55
+ <xsl:value-of select="@title" />
56
+ </a>
57
+ <span> <xsl:value-of select="@page" /> </span>
58
+ </div>
59
+ </xsl:if>
60
+ <ul>
61
+ <xsl:comment>added to prevent self-closing tags in QtXmlPatterns</xsl:comment>
62
+ <xsl:apply-templates select="outline:item"/>
63
+ </ul>
64
+ </li>
65
+ </xsl:template>
66
+ </xsl:stylesheet>
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "mdown2pdf"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
3
+
4
+ require "mdown2pdf"
5
+
6
+ class MD2PDFConvert
7
+ def self.convert(args)
8
+ if args.include?('-h') || args.include?('--help')
9
+ help
10
+ exit
11
+ end
12
+
13
+ @@args = args
14
+
15
+ Mdown2PDF.temporary_html_files_for(file) do |output, cover|
16
+ Mdown2PDF.output(
17
+ file: output,
18
+ name: destination,
19
+ cover: cover,
20
+ toc: toc
21
+ )
22
+ end
23
+ end
24
+
25
+ def self.help
26
+ puts "Usage: md2pdf file [options]"
27
+ puts
28
+ puts "Available options:"
29
+ puts
30
+ puts "-o, [--output] Specify the output file name. By default it"
31
+ puts " is inferred with the given Markdown file name."
32
+ puts
33
+ puts "--skip-toc Flag to specify whether the table of contents"
34
+ puts " should be skipped from the output. Disabled by"
35
+ puts " default."
36
+ end
37
+
38
+ def self.args
39
+ @@args
40
+ end
41
+
42
+ def self.destination
43
+ @@destination ||= begin
44
+ if args.include?('--output') || args.include?('-o')
45
+ position = (args.index('--output') || args.index('-o')) + 1
46
+ args[position]
47
+ else
48
+ file
49
+ end
50
+ end
51
+ end
52
+
53
+ def self.file
54
+ @@file ||= begin
55
+ if !args[0]
56
+ puts "No input file given. The path to a Markdown file is required."
57
+ exit
58
+ end
59
+
60
+ args[0]
61
+ end
62
+ end
63
+
64
+ def self.toc
65
+ !args.include?('--skip-toc')
66
+ end
67
+ end
68
+
69
+ MD2PDFConvert.convert(ARGV)
@@ -0,0 +1,112 @@
1
+ require "mdown2pdf/pseudo_code_lexer"
2
+ require "mdown2pdf/render"
3
+ require "mdown2pdf/version"
4
+
5
+ module Mdown2PDF
6
+ extend self
7
+
8
+ # Creates two temporary files:
9
+ #
10
+ # * One to represent the eventual cover of the PDF file.
11
+ # * One with the document translated to HTML.
12
+ #
13
+ # These two temporary files will be used by Wkhtmltopdf to
14
+ # generate the final PDF file.
15
+ #
16
+ # This method requires a block that will yield a path for each
17
+ # file. For example:
18
+ #
19
+ # temporary_files_for("a_file.md") do |output, cover|
20
+ # # Here `output` and `cover` are paths.
21
+ # end
22
+ #
23
+ # To distinguish the cover from the rest of the document, the
24
+ # user must put an h-rule inside their document and the first
25
+ # one will be used as a separator.
26
+ def temporary_html_files_for(file)
27
+ seed = (rand * 100).round
28
+ output_file = "/tmp/output-#{seed}.html"
29
+ cover_file = "/tmp/cover-#{seed}.html"
30
+
31
+ document = markdown_for(file)
32
+ first_hr = document.index('<hr>')
33
+
34
+ if first_hr
35
+ cover = document[0...first_hr]
36
+ document = document[first_hr+4..-1]
37
+ end
38
+
39
+ File.open(output_file, "w") do |f|
40
+ f.write(File.read(asset('output.html')) % { content: document })
41
+ end
42
+
43
+ if cover
44
+ File.open(cover_file, "w") do |f|
45
+ f.write(File.read(asset('output.html')) % { content: cover })
46
+ end
47
+
48
+ yield(output_file, cover_file)
49
+ else
50
+ yield(output_file, nil)
51
+ end
52
+ ensure
53
+ File.delete(output_file)
54
+ File.delete(cover_file) if File.exist?(cover_file)
55
+ end
56
+
57
+ # Actually outputs a given HTML file as a PDF for a given name
58
+ # through the `wkhtmltopdf` command line tool.
59
+ #
60
+ # - The given `:file` key represents the HTML file to translate
61
+ # to PDF.
62
+ # - `:name` will be used to infer the PDF file's name.
63
+ # - `:cover` can be used if a document should be used as a cover.
64
+ # - `:toc` specifies whether a table of contents should be generated
65
+ # or not for the document.
66
+ def output(file: , name: , cover: nil, toc: false)
67
+ stylesheet = ["--user-style-sheet", asset('style.css')]
68
+ arguments = ["wkhtmltopdf"]
69
+
70
+ if cover
71
+ arguments.push("cover", cover)
72
+ arguments.push(*stylesheet)
73
+ end
74
+
75
+ arguments.push("toc", "--xsl-style-sheet", asset("toc.xsl")) if toc
76
+
77
+ arguments.push(
78
+ file,
79
+ "--include-in-outline",
80
+ "--enable-internal-links",
81
+ *stylesheet,
82
+ name.sub(/.(md|mdwon)/, '.pdf')
83
+ )
84
+
85
+ `#{arguments.join(" ")}`
86
+ end
87
+
88
+ # Converts a given Markdown file to an HTML document. The underlying
89
+ # implementation uses Redcarpet but you should check out the +Render+
90
+ # class for further information.
91
+ def markdown_for(file)
92
+ html_for(File.read(file))
93
+ end
94
+
95
+ # Converts a given Markdown string to HTML.
96
+ def html_for(markdown)
97
+ parser = Redcarpet::Markdown.new(Render, {
98
+ fenced_code_blocks: true,
99
+ tables: true,
100
+ superscript: true,
101
+ strikethrough: true,
102
+ autolink: true
103
+ })
104
+
105
+ parser.render(markdown)
106
+ end
107
+
108
+ # Returns an absolute path for a given asset.
109
+ def asset(name)
110
+ File.expand_path(File.join(__dir__, '..', 'assets', name))
111
+ end
112
+ end
@@ -0,0 +1,84 @@
1
+ require 'rouge'
2
+
3
+ module Mdown2PDF
4
+ class PseudoCodeLexer < Rouge::RegexLexer
5
+ tag 'pseudo'
6
+ title 'Pseudo Code'
7
+
8
+ def self.keywords
9
+ @keywords ||= Set.new %w(
10
+ fonction programme tant que faire si alors sinon fin jusqu à
11
+ ce début procédure pour allant de pas et chaque non dans
12
+ )
13
+ end
14
+
15
+ def self.declaration_keywords
16
+ @declaration_keywords ||= Set.new %w(
17
+ var Entrée Sortie
18
+ )
19
+ end
20
+
21
+ def self.types
22
+ @types ||= Set.new %w(
23
+ Graphe Sommet
24
+ )
25
+ end
26
+
27
+ state :whitespace do
28
+ rule /\s+/m, Text
29
+ rule %r{(//).*$\n}, Comment::Single
30
+ end
31
+
32
+ state :literal do
33
+ rule /"(\\.|.)*?"/, Str::Double
34
+ rule /'(\\.|.)*?'/, Str::Single
35
+
36
+ rule /(\.\.)/, Punctuation
37
+
38
+ rule %r((\d+[.]\d+)), Num::Float
39
+ rule /\d+/, Num
40
+
41
+ rule /(\+|\-|\*|\/\/?|\*\*?)/, Operator
42
+
43
+ rule /(<=?|>=?|==|!=)/, Operator
44
+ rule %r{∞}, Operator
45
+
46
+ rule /,/, Punctuation
47
+ rule /:/, Punctuation
48
+ rule /\[/, Punctuation
49
+ rule /\]/, Punctuation
50
+ rule /\(/, Punctuation
51
+ rule /\)/, Punctuation
52
+ rule /;/, Punctuation
53
+ rule /\|\_/, Punctuation
54
+ rule /\_\|/, Punctuation
55
+ rule /\|/, Punctuation
56
+ rule %r{'}, Punctuation
57
+ rule %r{=}, Punctuation
58
+ rule %r{\{|\}}, Punctuation
59
+
60
+ rule /(\+|\-|\*|\/\/?|\*\*?)/, Operator
61
+ end
62
+
63
+ state :root do
64
+ mixin :whitespace
65
+ mixin :literal
66
+
67
+ rule /(vrai|faux|nul|Nil)\b/i, Name::Builtin
68
+
69
+ rule /([[:alpha:]]([[:alnum:]](_[[:alnum:]])?)*)/ do |m|
70
+ name = m[0]
71
+
72
+ if self.class.keywords.include? name
73
+ token Keyword
74
+ elsif self.class.declaration_keywords.include? name
75
+ token Keyword::Declaration
76
+ elsif self.class.types.include? name
77
+ token Keyword::Type
78
+ else
79
+ token Name
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,49 @@
1
+ require 'redcarpet'
2
+
3
+ module Mdown2PDF
4
+ # Custom Redcarpet render object that adds the following to the
5
+ # default stack:
6
+ #
7
+ # * Code highlighting (through Rouge).
8
+ # * Automatic expanding for image paths (since Wkhtmltopdf requires
9
+ # absolute paths).
10
+ # * Anchor generation for titles.
11
+ # * Possibility to use "`#hexcode`" to generate a div with the specified
12
+ # value as the background color.
13
+ #
14
+ # It is a drop-in replacement for any Redcarpet render object that procuces
15
+ # HTML.
16
+ class Render < Redcarpet::Render::HTML
17
+ def block_code(code, lang)
18
+ lexer = Rouge::Lexer.find_fancy(lang, code) || Rouge::Lexers::PlainText
19
+
20
+ if lexer.tag == 'make'
21
+ code.gsub! /^ /, "\t"
22
+ end
23
+
24
+ %(<div class="highlight"><pre>#{Rouge::Formatters::HTML.new.format(lexer.lex(code))}</pre></div>)
25
+ end
26
+
27
+ def codespan(code)
28
+ if code.start_with?("#")
29
+ %(<div style="height: 20px; margin: auto; width: 40px; background: #{code}"></div>)
30
+ else
31
+ %(<code>#{code}</code>)
32
+ end
33
+ end
34
+
35
+ def header(text, level)
36
+ alphabet = ['a'..'z'] + []
37
+
38
+ anchor = text.chars.keep_if do |c|
39
+ c =~ /[[:alpha:]]/ || c == ''
40
+ end.join("")
41
+
42
+ %(<h#{level} id="#{anchor.downcase.gsub(' ', '-')}">#{text}</h#{level}>)
43
+ end
44
+
45
+ def image(link, title, text)
46
+ %(<img src="#{Dir.pwd}/#{link}">)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,3 @@
1
+ module Mdown2PDF
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,38 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "mdown2pdf/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "mdown2pdf"
8
+ spec.version = Mdown2PDF::VERSION
9
+ spec.authors = ["Robin Dupret"]
10
+ spec.email = ["robin.dupret@gmail.com"]
11
+
12
+ spec.summary = %q{Easily convert Markdown documents to PDF files.}
13
+ spec.homepage = "https://github.com/robin850/mdown2pdf"
14
+ spec.license = "MIT"
15
+
16
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
18
+ # if spec.respond_to?(:metadata)
19
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
20
+ #else
21
+ # raise "RubyGems 2.0 or newer is required to protect against " \
22
+ # "public gem pushes."
23
+ #end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
26
+ f.match(%r{^(test|spec|features)/})
27
+ end
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ spec.add_runtime_dependency "rouge", "~> 2.1.0"
33
+ spec.add_runtime_dependency "redcarpet", "~> 3.4.0"
34
+
35
+ spec.add_development_dependency "bundler", "~> 1.15"
36
+ spec.add_development_dependency "rake", "~> 10.0"
37
+ spec.add_development_dependency "minitest", "~> 5.0"
38
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mdown2pdf
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Robin Dupret
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-12-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rouge
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.1.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.1.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: redcarpet
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 3.4.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 3.4.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.15'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.15'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '5.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '5.0'
83
+ description:
84
+ email:
85
+ - robin.dupret@gmail.com
86
+ executables:
87
+ - md2pdf
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - Gemfile
92
+ - Gemfile.lock
93
+ - LICENSE
94
+ - README.md
95
+ - Rakefile
96
+ - assets/output.html
97
+ - assets/style.css
98
+ - assets/toc.xsl
99
+ - bin/console
100
+ - exe/md2pdf
101
+ - lib/mdown2pdf.rb
102
+ - lib/mdown2pdf/pseudo_code_lexer.rb
103
+ - lib/mdown2pdf/render.rb
104
+ - lib/mdown2pdf/version.rb
105
+ - mdown2pdf.gemspec
106
+ homepage: https://github.com/robin850/mdown2pdf
107
+ licenses:
108
+ - MIT
109
+ metadata: {}
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 2.6.12
127
+ signing_key:
128
+ specification_version: 4
129
+ summary: Easily convert Markdown documents to PDF files.
130
+ test_files: []