mdown2pdf 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []