asciidoctor-pdf 1.5.0.alpha.16 → 1.5.0.alpha.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +5 -5
  2. data/.yardopts +12 -0
  3. data/CHANGELOG.adoc +66 -0
  4. data/LICENSE.adoc +1 -1
  5. data/README.adoc +221 -68
  6. data/asciidoctor-pdf.gemspec +41 -42
  7. data/bin/asciidoctor-pdf +3 -3
  8. data/data/fonts/mplus1p-regular-fallback.ttf +0 -0
  9. data/data/fonts/notoserif-bold-subset.ttf +0 -0
  10. data/data/fonts/notoserif-bold_italic-subset.ttf +0 -0
  11. data/data/fonts/notoserif-italic-subset.ttf +0 -0
  12. data/data/fonts/notoserif-regular-subset.ttf +0 -0
  13. data/data/themes/default-theme.yml +6 -3
  14. data/docs/theming-guide.adoc +162 -23
  15. data/lib/asciidoctor-pdf.rb +2 -1
  16. data/lib/asciidoctor-pdf/asciidoctor_ext.rb +1 -0
  17. data/lib/asciidoctor-pdf/asciidoctor_ext/logging_shim.rb +19 -0
  18. data/lib/asciidoctor-pdf/converter.rb +408 -186
  19. data/lib/asciidoctor-pdf/core_ext/array.rb +0 -6
  20. data/lib/asciidoctor-pdf/core_ext/numeric.rb +21 -12
  21. data/lib/asciidoctor-pdf/core_ext/ostruct.rb +3 -12
  22. data/lib/asciidoctor-pdf/core_ext/string.rb +1 -1
  23. data/lib/asciidoctor-pdf/formatted_text.rb +1 -0
  24. data/lib/asciidoctor-pdf/formatted_text/formatter.rb +8 -2
  25. data/lib/asciidoctor-pdf/formatted_text/inline_destination_marker.rb +1 -1
  26. data/lib/asciidoctor-pdf/formatted_text/inline_image_arranger.rb +18 -32
  27. data/lib/asciidoctor-pdf/formatted_text/inline_image_renderer.rb +3 -3
  28. data/lib/asciidoctor-pdf/formatted_text/inline_text_aligner.rb +20 -0
  29. data/lib/asciidoctor-pdf/formatted_text/parser.rb +124 -38
  30. data/lib/asciidoctor-pdf/formatted_text/parser.treetop +17 -10
  31. data/lib/asciidoctor-pdf/formatted_text/transform.rb +30 -20
  32. data/lib/asciidoctor-pdf/implicit_header_processor.rb +2 -2
  33. data/lib/asciidoctor-pdf/index_catalog.rb +25 -23
  34. data/lib/asciidoctor-pdf/measurements.rb +1 -1
  35. data/lib/asciidoctor-pdf/pdf-core_ext/pdf_object.rb +1 -1
  36. data/lib/asciidoctor-pdf/pdfmark.rb +13 -13
  37. data/lib/asciidoctor-pdf/prawn-svg_ext.rb +2 -2
  38. data/lib/asciidoctor-pdf/prawn-svg_ext/interface.rb +2 -2
  39. data/lib/asciidoctor-pdf/prawn-table_ext.rb +1 -0
  40. data/lib/asciidoctor-pdf/prawn-table_ext/cell.rb +60 -0
  41. data/lib/asciidoctor-pdf/prawn-table_ext/cell/text.rb +3 -3
  42. data/lib/asciidoctor-pdf/prawn_ext/coderay_encoder.rb +3 -3
  43. data/lib/asciidoctor-pdf/prawn_ext/extensions.rb +39 -14
  44. data/lib/asciidoctor-pdf/prawn_ext/formatted_text/fragment.rb +9 -10
  45. data/lib/asciidoctor-pdf/prawn_ext/images.rb +2 -2
  46. data/lib/asciidoctor-pdf/roman_numeral.rb +7 -7
  47. data/lib/asciidoctor-pdf/rouge_ext.rb +2 -2
  48. data/lib/asciidoctor-pdf/rouge_ext/formatters/prawn.rb +20 -9
  49. data/lib/asciidoctor-pdf/rouge_ext/themes/{pastie.rb → asciidoctor_pdf_default.rb} +5 -5
  50. data/lib/asciidoctor-pdf/rouge_ext/themes/bw.rb +38 -0
  51. data/lib/asciidoctor-pdf/sanitizer.rb +36 -23
  52. data/lib/asciidoctor-pdf/temporary_path.rb +1 -1
  53. data/lib/asciidoctor-pdf/theme_loader.rb +17 -14
  54. data/lib/asciidoctor-pdf/version.rb +3 -2
  55. data/lib/asciidoctor/pdf.rb +1 -0
  56. data/lib/asciidoctor/pdf/version.rb +1 -0
  57. metadata +113 -84
  58. data/Gemfile +0 -22
  59. data/Rakefile +0 -81
  60. data/lib/asciidoctor-pdf/rouge_ext/css_theme.rb +0 -15
@@ -1,6 +1,6 @@
1
1
  # regenerate parser.rb using `tt parser.treetop`
2
2
  module Asciidoctor
3
- module Pdf
3
+ module PDF
4
4
  module FormattedText
5
5
  grammar Markup
6
6
  rule text
@@ -8,7 +8,7 @@ grammar Markup
8
8
  end
9
9
 
10
10
  rule complex
11
- (cdata / element / entity)* {
11
+ (cdata / element / charref)* {
12
12
  def content
13
13
  elements.map {|e| e.content }
14
14
  end
@@ -95,25 +95,32 @@ grammar Markup
95
95
  }
96
96
  end
97
97
 
98
- rule entity
99
- '&' ('#' entity_number / entity_name) ';' {
98
+ rule charref
99
+ '&' ('#' character_decimal / '#x' character_hex / character_name) ';' {
100
100
  def content
101
- if (entity_value = elements[1]).terminal?
102
- { type: :entity, name: entity_value.text_value.to_sym }
101
+ if (ref_data = elements[1]).terminal?
102
+ { type: :charref, reference_type: :name, value: ref_data.text_value.to_sym }
103
+ elsif ref_data.elements[0].text_value == '#'
104
+ { type: :charref, reference_type: :decimal, value: ref_data.elements[1].text_value.to_i }
103
105
  else
104
- { type: :entity, number: entity_value.elements[1].text_value.to_i }
106
+ { type: :charref, reference_type: :hex, value: ref_data.elements[1].text_value }
105
107
  end
106
108
  end
107
109
  }
108
110
  end
109
111
 
110
- rule entity_number
112
+ rule character_decimal
111
113
  # NOTE 6 decimals only supported in Asciidoctor 1.5.5 and up
112
114
  [0-9] 2..6
113
115
  end
114
116
 
115
- rule entity_name
116
- 'amp' / 'apos' / 'gt' / 'lt' / 'quot'
117
+ rule character_hex
118
+ # NOTE 5 hexadecimals only supported in Asciidoctor 1.5.5 and up
119
+ [0-9a-f] 2..5
120
+ end
121
+
122
+ rule character_name
123
+ 'amp' / 'apos' / 'gt' / 'lt' / 'nbsp' / 'quot'
117
124
  end
118
125
 
119
126
  rule spaces
@@ -1,18 +1,20 @@
1
1
  module Asciidoctor
2
- module Pdf
2
+ module PDF
3
3
  module FormattedText
4
4
  class Transform
5
- LF = %(\n)
5
+ LF = ?\n
6
+ ZeroWidthSpace = ?\u200b
6
7
  CharEntityTable = {
7
- lt: '<',
8
- gt: '>',
9
- amp: '&',
10
- quot: '"',
11
- apos: '\''
8
+ amp: ?&,
9
+ apos: ?',
10
+ gt: ?>,
11
+ lt: ?<,
12
+ nbsp: ?\u00a0,
13
+ quot: ?",
12
14
  }
13
- CharRefRx = /&(?:#(\d{2,6})|(#{CharEntityTable.keys * '|'}));/
15
+ CharRefRx = /&(?:(#{CharEntityTable.keys * ?|})|#(?:(\d\d\d{0,4})|x([a-f\d][a-f\d][a-f\d]{0,3})));/
14
16
  TextDecorationTable = { 'underline' => :underline, 'line-through' => :strikethrough }
15
- #DummyText = %(\u0000)
17
+ #DummyText = ?\u0000
16
18
 
17
19
  def initialize(options = {})
18
20
  @merge_adjacent_text_nodes = options[:merge_adjacent_text_nodes]
@@ -75,7 +77,8 @@ class Transform
75
77
  fragment = {
76
78
  image_path: attributes[:tmp] == 'true' ? attributes[:src].extend(TemporaryPath) : attributes[:src],
77
79
  image_format: attributes[:format],
78
- text: attributes[:alt],
80
+ # a zero-width space in the text will cause the image to be duplicated
81
+ text: (attributes[:alt].delete ZeroWidthSpace),
79
82
  callback: InlineImageRenderer
80
83
  }
81
84
  if (img_w = attributes[:width])
@@ -86,22 +89,22 @@ class Transform
86
89
  end
87
90
  end
88
91
  when :text
89
- text = node[:value]
90
- # NOTE the remaining logic is shared with :entity
91
92
  if @merge_adjacent_text_nodes && previous_fragment_is_text
92
- fragments << { text: %(#{fragments.pop[:text]}#{text}) }
93
+ fragments << { text: %(#{fragments.pop[:text]}#{node[:value]}) }
93
94
  else
94
- fragments << { text: text }
95
+ fragments << { text: node[:value] }
95
96
  end
96
97
  previous_fragment_is_text = true
97
- when :entity
98
- if (name = node[:name])
99
- text = CharEntityTable[name]
98
+ when :charref
99
+ if (ref_type = node[:reference_type]) == :name
100
+ text = CharEntityTable[node[:value]]
101
+ elsif ref_type == :decimal
102
+ # FIXME AFM fonts do not include a thin space glyph; set fallback_fonts to allow glyph to be resolved
103
+ text = [node[:value]].pack('U1')
100
104
  else
101
105
  # FIXME AFM fonts do not include a thin space glyph; set fallback_fonts to allow glyph to be resolved
102
- text = [node[:number]].pack('U*')
106
+ text = [(node[:value].to_i 16)].pack('U1')
103
107
  end
104
- # NOTE the remaining logic is shared with :text
105
108
  if @merge_adjacent_text_nodes && previous_fragment_is_text
106
109
  fragments << { text: %(#{fragments.pop[:text]}#{text}) }
107
110
  else
@@ -161,6 +164,13 @@ class Transform
161
164
  fragment[:size] = value
162
165
  end
163
166
  end
167
+ if (value = attrs[:width])
168
+ fragment[:width] = value
169
+ if (value = attrs[:align])
170
+ fragment[:align] = value.to_sym
171
+ fragment[:callback] = InlineTextAligner
172
+ end
173
+ end
164
174
  #if !fragment[:character_spacing] && (value = attrs[:character_spacing])
165
175
  # fragment[:character_spacing] = value.to_f
166
176
  #end
@@ -173,7 +183,7 @@ class Transform
173
183
  fragment[:anchor] = value
174
184
  elsif (value = attrs[:href])
175
185
  fragment[:link] = value.include?(';') ? value.gsub(CharRefRx) {
176
- $2 ? CharEntityTable[$2.to_sym] : [$1.to_i].pack('U*')
186
+ $1 ? CharEntityTable[$1.to_sym] : [$2 ? $2.to_i : ($3.to_i 16)].pack('U1')
177
187
  } : value
178
188
  elsif (value = attrs[:name])
179
189
  # NOTE text is null character, which is used as placeholder text so Prawn doesn't drop fragment
@@ -1,7 +1,7 @@
1
1
  require 'asciidoctor/extensions'
2
2
 
3
3
  module Asciidoctor
4
- module Pdf
4
+ module PDF
5
5
  # An include processor that skips the implicit author line below
6
6
  # the document title within include documents.
7
7
  class ImplicitHeaderProcessor < ::Asciidoctor::Extensions::IncludeProcessor
@@ -59,5 +59,5 @@ end
59
59
  end
60
60
 
61
61
  Asciidoctor::Extensions.register :pdf do
62
- include_processor Asciidoctor::Pdf::ImplicitHeaderProcessor if @document.backend == 'pdf'
62
+ include_processor Asciidoctor::PDF::ImplicitHeaderProcessor if @document.backend == 'pdf'
63
63
  end
@@ -1,5 +1,7 @@
1
- module Asciidoctor; module Pdf
1
+ module Asciidoctor; module PDF
2
2
  class IndexCatalog
3
+ LeadingAlphaRx = /^\p{Alpha}/
4
+
3
5
  attr_accessor :start_page_number
4
6
 
5
7
  def initialize
@@ -7,7 +9,7 @@ module Asciidoctor; module Pdf
7
9
  @start_page_number = 1
8
10
  @dests = {}
9
11
  end
10
-
12
+
11
13
  def store_term names, dest = nil
12
14
  if (num_terms = names.size) > 2
13
15
  store_tertiary_term names[0], names[1], names[2], dest
@@ -17,27 +19,27 @@ module Asciidoctor; module Pdf
17
19
  store_primary_term names[0], dest
18
20
  end
19
21
  end
20
-
22
+
21
23
  def store_primary_term name, dest = nil
22
24
  store_dest dest if dest
23
25
  (init_category name.chr.upcase).store_term name, dest
24
26
  end
25
-
27
+
26
28
  def store_secondary_term primary_name, secondary_name, dest = nil
27
29
  store_dest dest if dest
28
30
  (store_primary_term primary_name).store_term secondary_name, dest
29
31
  end
30
-
32
+
31
33
  def store_tertiary_term primary_name, secondary_name, tertiary_name, dest = nil
32
34
  store_dest dest if dest
33
35
  (store_secondary_term primary_name, secondary_name).store_term tertiary_name, dest
34
36
  end
35
-
37
+
36
38
  def init_category name
37
- name = '@' if (ord = name.ord) < 65 || ord > 90
39
+ name = '@' unless LeadingAlphaRx =~ name
38
40
  @categories[name] ||= (IndexTermCategory.new name)
39
41
  end
40
-
42
+
41
43
  def find_category name
42
44
  @categories[name]
43
45
  end
@@ -51,7 +53,7 @@ module Asciidoctor; module Pdf
51
53
  dest[:page] = physical_page_number - (@start_page_number - 1)
52
54
  end
53
55
  end
54
-
56
+
55
57
  def empty?
56
58
  @categories.empty?
57
59
  end
@@ -60,26 +62,26 @@ module Asciidoctor; module Pdf
60
62
  @categories.empty? ? [] : @categories.values.sort
61
63
  end
62
64
  end
63
-
65
+
64
66
  class IndexTermGroup
65
67
  include Comparable
66
68
  attr_reader :name
67
-
69
+
68
70
  def initialize name
69
- @name = name
71
+ @name = name
70
72
  @terms = {}
71
73
  end
72
-
74
+
73
75
  def store_term name, dest = nil
74
76
  term = (@terms[name] ||= (IndexTerm.new name))
75
77
  term.add_dest dest if dest
76
78
  term
77
79
  end
78
-
80
+
79
81
  def find_term name
80
82
  @terms[name]
81
83
  end
82
-
84
+
83
85
  def terms
84
86
  @terms.empty? ? [] : @terms.values.sort
85
87
  end
@@ -88,30 +90,30 @@ module Asciidoctor; module Pdf
88
90
  @name <=> other.name
89
91
  end
90
92
  end
91
-
93
+
92
94
  class IndexTermCategory < IndexTermGroup; end
93
-
95
+
94
96
  class IndexTerm < IndexTermGroup
95
97
  def initialize name
96
98
  super
97
99
  @dests = ::Set.new
98
100
  end
99
-
100
- alias :subterms :terms
101
-
101
+
102
+ alias subterms terms
103
+
102
104
  def add_dest dest
103
105
  @dests << dest
104
106
  self
105
107
  end
106
-
108
+
107
109
  def dests
108
110
  @dests.select {|d| d.key? :page }.sort {|a, b| a[:page] <=> b[:page] }
109
111
  end
110
-
112
+
111
113
  def container?
112
114
  @dests.empty? || @dests.none? {|d| d.key? :page }
113
115
  end
114
-
116
+
115
117
  def leaf?
116
118
  @terms.empty?
117
119
  end
@@ -1,4 +1,4 @@
1
- module Asciidoctor; module Pdf
1
+ module Asciidoctor; module PDF
2
2
  module Measurements
3
3
  MeasurementValueRx = /(\d+|\d*\.\d+)(in|mm|cm|p[txc])?$/
4
4
  InsetMeasurementValueRx = /(?<=^| |\()(-?\d+(?:\.\d+)?)(in|mm|cm|p[txc])(?=$| |\))/
@@ -1,6 +1,6 @@
1
1
  unless (defined? PDF::Core.pdf_object) == 'method'
2
2
  module PDF::Core
3
- alias :pdf_object :PdfObject
3
+ alias pdf_object PdfObject
4
4
  module_function :pdf_object
5
5
  end
6
6
  end
@@ -1,7 +1,7 @@
1
1
  module Asciidoctor
2
- module Pdf
2
+ module PDF
3
3
  class Pdfmark
4
- include ::Asciidoctor::Pdf::Sanitizer
4
+ include ::Asciidoctor::PDF::Sanitizer
5
5
 
6
6
  def initialize doc
7
7
  @doc = doc
@@ -10,23 +10,23 @@ class Pdfmark
10
10
  def generate
11
11
  doc = @doc
12
12
  # FIXME use sanitize: :plain_text once available
13
- content = <<-EOS
14
- [ /Title #{sanitize(doc.doctitle use_fallback: true).to_pdf}
15
- /Author #{(doc.attr 'authors').to_pdf}
16
- /Subject #{(doc.attr 'subject').to_pdf}
17
- /Keywords #{(doc.attr 'keywords').to_pdf}
18
- /ModDate #{date = ::Time.now.to_pdf}
19
- /CreationDate #{date}
20
- /Creator (Asciidoctor PDF #{::Asciidoctor::Pdf::VERSION}, based on Prawn #{::Prawn::VERSION})
21
- /Producer #{(doc.attr 'publisher').to_pdf}
22
- /DOCINFO pdfmark
13
+ content = <<~EOS
14
+ [ /Title #{sanitize(doc.doctitle use_fallback: true).to_pdf}
15
+ /Author #{(doc.attr 'authors').to_pdf}
16
+ /Subject #{(doc.attr 'subject').to_pdf}
17
+ /Keywords #{(doc.attr 'keywords').to_pdf}
18
+ /ModDate #{date = ::Time.now.to_pdf}
19
+ /CreationDate #{date}
20
+ /Creator (Asciidoctor PDF #{::Asciidoctor::PDF::VERSION}, based on Prawn #{::Prawn::VERSION})
21
+ /Producer #{(doc.attr 'publisher').to_pdf}
22
+ /DOCINFO pdfmark
23
23
  EOS
24
24
  content
25
25
  end
26
26
 
27
27
  def generate_file pdf_file
28
28
  # QUESTION should we use the extension pdfmeta to be more clear?
29
- ::IO.write %(#{pdf_file}mark), generate
29
+ ::File.write %(#{pdf_file}mark), generate
30
30
  end
31
31
  end
32
32
  end
@@ -1,4 +1,4 @@
1
- require 'prawn-svg' unless defined? Prawn::Svg::VERSION
1
+ require 'prawn-svg' unless defined? Prawn::SVG::Interface
2
2
  require_relative 'prawn-svg_ext/interface'
3
3
  # NOTE disable system fonts since they're non-portable
4
- Prawn::Svg::Interface.font_path.clear
4
+ Prawn::SVG::Interface.font_path.clear
@@ -1,4 +1,4 @@
1
- module Prawn; module Svg
1
+ module Prawn; module SVG
2
2
  class Interface
3
3
  def resize opts = {}
4
4
  sizing = document.sizing
@@ -7,4 +7,4 @@ module Prawn; module Svg
7
7
  sizing.calculate
8
8
  end
9
9
  end
10
- end; end unless Prawn::Svg::Interface.method_defined? :resize
10
+ end; end unless Prawn::SVG::Interface.method_defined? :resize
@@ -1,3 +1,4 @@
1
1
  require 'prawn/table' unless defined? Prawn::Table::VERSION
2
+ require_relative 'prawn-table_ext/cell'
2
3
  require_relative 'prawn-table_ext/cell/asciidoc'
3
4
  require_relative 'prawn-table_ext/cell/text'
@@ -0,0 +1,60 @@
1
+ class Prawn::Table::Cell
2
+ remove_method :draw_borders
3
+ # Draws borders around the cell. Borders are centered on the bounds of
4
+ # the cell outside of any padding, so the caller is responsible for
5
+ # setting appropriate padding to ensure the border does not overlap with
6
+ # cell content.
7
+ #
8
+ def draw_borders(pt)
9
+ x, y = pt
10
+
11
+ @pdf.mask(:line_width, :stroke_color) do
12
+ @borders.each do |border|
13
+ idx = { top: 0, right: 1, bottom: 2, left: 3 }[border]
14
+ border_color = @border_colors[idx]
15
+ border_width = @border_widths[idx]
16
+ border_line = @border_lines[idx]
17
+
18
+ next unless border_width > 0
19
+
20
+ # Left and right borders are drawn one-half border beyond the center
21
+ # of the corner, so that the corners end up square.
22
+ from, to = case border
23
+ when :top
24
+ [[x, y], [x+width, y]]
25
+ when :bottom
26
+ [[x, y-height], [x+width, y-height]]
27
+ when :left
28
+ [[x, y + (border_top_width / 2.0)],
29
+ [x, y - height - (border_bottom_width / 2.0)]]
30
+ when :right
31
+ [[x+width, y + (border_top_width / 2.0)],
32
+ [x+width, y - height - (border_bottom_width / 2.0)]]
33
+ end
34
+
35
+ case border_line
36
+ when :dashed
37
+ @pdf.dash border_width * 4
38
+ when :dotted
39
+ @pdf.dash border_width, space: border_width * 2
40
+ when :solid
41
+ # normal line style
42
+ else
43
+ raise ::ArgumentError, 'border_line must be :solid, :dotted or :dashed'
44
+ end
45
+
46
+ @pdf.line_width = border_width
47
+ if border_color == 'transparent'
48
+ @pdf.stroke_color = '000000'
49
+ @pdf.transparent 0 do
50
+ @pdf.stroke_line(from, to)
51
+ end
52
+ else
53
+ @pdf.stroke_color = border_color
54
+ @pdf.stroke_line(from, to)
55
+ end
56
+ @pdf.undash
57
+ end
58
+ end
59
+ end
60
+ end
@@ -4,9 +4,9 @@ class Prawn::Table::Cell::Text
4
4
  def draw_content
5
5
  with_font do
6
6
  with_text_color do
7
- (text_box :width => spanned_content_width + FPTolerance,
8
- :height => spanned_content_height + FPTolerance,
9
- :at => [0, @pdf.cursor]).render
7
+ (text_box width: spanned_content_width + FPTolerance,
8
+ height: spanned_content_height + FPTolerance,
9
+ at: [0, @pdf.cursor]).render
10
10
  end
11
11
  end
12
12
  end