thinreports 0.6.0.pre3

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.
Files changed (146) hide show
  1. data/.yardopts +1 -0
  2. data/README.rdoc +100 -0
  3. data/Rakefile +10 -0
  4. data/doc/yardoc_templates/default/layout/html/footer.erb +10 -0
  5. data/lib/thinreports.rb +11 -0
  6. data/lib/thinreports/core.rb +20 -0
  7. data/lib/thinreports/core/errors.rb +58 -0
  8. data/lib/thinreports/core/events.rb +71 -0
  9. data/lib/thinreports/core/ext.rb +5 -0
  10. data/lib/thinreports/core/ext/array.rb +19 -0
  11. data/lib/thinreports/core/ext/hash.rb +19 -0
  12. data/lib/thinreports/core/ext/object.rb +25 -0
  13. data/lib/thinreports/core/format.rb +14 -0
  14. data/lib/thinreports/core/format/base.rb +93 -0
  15. data/lib/thinreports/core/format/builder.rb +65 -0
  16. data/lib/thinreports/core/ordered_hash.rb +39 -0
  17. data/lib/thinreports/core/page.rb +91 -0
  18. data/lib/thinreports/core/shape.rb +53 -0
  19. data/lib/thinreports/core/shape/base.rb +16 -0
  20. data/lib/thinreports/core/shape/base/interface.rb +38 -0
  21. data/lib/thinreports/core/shape/base/internal.rb +60 -0
  22. data/lib/thinreports/core/shape/basic.rb +15 -0
  23. data/lib/thinreports/core/shape/basic/format.rb +26 -0
  24. data/lib/thinreports/core/shape/basic/interface.rb +57 -0
  25. data/lib/thinreports/core/shape/basic/internal.rb +29 -0
  26. data/lib/thinreports/core/shape/list.rb +25 -0
  27. data/lib/thinreports/core/shape/list/configuration.rb +33 -0
  28. data/lib/thinreports/core/shape/list/events.rb +30 -0
  29. data/lib/thinreports/core/shape/list/format.rb +72 -0
  30. data/lib/thinreports/core/shape/list/manager.rb +209 -0
  31. data/lib/thinreports/core/shape/list/page.rb +118 -0
  32. data/lib/thinreports/core/shape/list/page_state.rb +40 -0
  33. data/lib/thinreports/core/shape/list/section_format.rb +32 -0
  34. data/lib/thinreports/core/shape/list/section_interface.rb +48 -0
  35. data/lib/thinreports/core/shape/list/section_internal.rb +29 -0
  36. data/lib/thinreports/core/shape/list/store.rb +34 -0
  37. data/lib/thinreports/core/shape/manager.rb +14 -0
  38. data/lib/thinreports/core/shape/manager/format.rb +28 -0
  39. data/lib/thinreports/core/shape/manager/internal.rb +87 -0
  40. data/lib/thinreports/core/shape/manager/target.rb +85 -0
  41. data/lib/thinreports/core/shape/tblock.rb +16 -0
  42. data/lib/thinreports/core/shape/tblock/format.rb +36 -0
  43. data/lib/thinreports/core/shape/tblock/formatter.rb +32 -0
  44. data/lib/thinreports/core/shape/tblock/formatter/basic.rb +38 -0
  45. data/lib/thinreports/core/shape/tblock/formatter/datetime.rb +21 -0
  46. data/lib/thinreports/core/shape/tblock/formatter/number.rb +49 -0
  47. data/lib/thinreports/core/shape/tblock/formatter/padding.rb +43 -0
  48. data/lib/thinreports/core/shape/tblock/interface.rb +52 -0
  49. data/lib/thinreports/core/shape/tblock/internal.rb +70 -0
  50. data/lib/thinreports/core/shape/text.rb +15 -0
  51. data/lib/thinreports/core/shape/text/format.rb +25 -0
  52. data/lib/thinreports/core/shape/text/interface.rb +16 -0
  53. data/lib/thinreports/core/shape/text/internal.rb +17 -0
  54. data/lib/thinreports/core/utils.rb +48 -0
  55. data/lib/thinreports/generator.rb +30 -0
  56. data/lib/thinreports/generator/base.rb +49 -0
  57. data/lib/thinreports/generator/pdf.rb +63 -0
  58. data/lib/thinreports/generator/pdf/document.rb +171 -0
  59. data/lib/thinreports/generator/pdf/document/draw_shape.rb +74 -0
  60. data/lib/thinreports/generator/pdf/document/font.rb +59 -0
  61. data/lib/thinreports/generator/pdf/document/graphics.rb +42 -0
  62. data/lib/thinreports/generator/pdf/document/graphics/attributes.rb +92 -0
  63. data/lib/thinreports/generator/pdf/document/graphics/basic.rb +132 -0
  64. data/lib/thinreports/generator/pdf/document/graphics/image.rb +51 -0
  65. data/lib/thinreports/generator/pdf/document/graphics/text.rb +130 -0
  66. data/lib/thinreports/generator/pdf/document/parse_color.rb +51 -0
  67. data/lib/thinreports/generator/pdf/document/parse_svg.rb +102 -0
  68. data/lib/thinreports/generator/pdf/drawer.rb +16 -0
  69. data/lib/thinreports/generator/pdf/drawer/base.rb +56 -0
  70. data/lib/thinreports/generator/pdf/drawer/list.rb +39 -0
  71. data/lib/thinreports/generator/pdf/drawer/list_section.rb +46 -0
  72. data/lib/thinreports/generator/pdf/drawer/page.rb +101 -0
  73. data/lib/thinreports/generator/pdf/prawn_ext.rb +36 -0
  74. data/lib/thinreports/generator/pxd.rb +75 -0
  75. data/lib/thinreports/generator/pxd/helper.rb +33 -0
  76. data/lib/thinreports/generator/pxd/list_renderer.rb +58 -0
  77. data/lib/thinreports/generator/pxd/page_renderer.rb +75 -0
  78. data/lib/thinreports/layout.rb +17 -0
  79. data/lib/thinreports/layout/base.rb +78 -0
  80. data/lib/thinreports/layout/configuration.rb +27 -0
  81. data/lib/thinreports/layout/format.rb +77 -0
  82. data/lib/thinreports/layout/version.rb +43 -0
  83. data/lib/thinreports/report.rb +31 -0
  84. data/lib/thinreports/report/base.rb +160 -0
  85. data/lib/thinreports/report/events.rb +32 -0
  86. data/lib/thinreports/report/internal.rb +131 -0
  87. data/resources/fonts/IPA_Font_License_Agreement_v1.0.txt +117 -0
  88. data/resources/fonts/ipag.ttf +0 -0
  89. data/resources/fonts/ipagp.ttf +0 -0
  90. data/resources/fonts/ipam.ttf +0 -0
  91. data/resources/fonts/ipamp.ttf +0 -0
  92. data/tasks/clean.rake +6 -0
  93. data/tasks/doc.rake +13 -0
  94. data/tasks/test.rake +18 -0
  95. data/test/unit/core/ext/array_spec.rb +29 -0
  96. data/test/unit/core/ext/hash_spec.rb +29 -0
  97. data/test/unit/core/ext/object_spec.rb +30 -0
  98. data/test/unit/core/format/test_base.rb +148 -0
  99. data/test/unit/core/format/test_builder.rb +114 -0
  100. data/test/unit/core/ordered_hash_spec.rb +51 -0
  101. data/test/unit/core/shape/base/test_interface.rb +52 -0
  102. data/test/unit/core/shape/base/test_internal.rb +109 -0
  103. data/test/unit/core/shape/basic/test_format.rb +59 -0
  104. data/test/unit/core/shape/basic/test_interface.rb +70 -0
  105. data/test/unit/core/shape/basic/test_internal.rb +42 -0
  106. data/test/unit/core/shape/list/test_configuration.rb +52 -0
  107. data/test/unit/core/shape/list/test_events.rb +28 -0
  108. data/test/unit/core/shape/list/test_format.rb +71 -0
  109. data/test/unit/core/shape/list/test_page.rb +10 -0
  110. data/test/unit/core/shape/list/test_page_state.rb +31 -0
  111. data/test/unit/core/shape/list/test_section_format.rb +46 -0
  112. data/test/unit/core/shape/list/test_section_interface.rb +52 -0
  113. data/test/unit/core/shape/list/test_section_internal.rb +30 -0
  114. data/test/unit/core/shape/list/test_store.rb +41 -0
  115. data/test/unit/core/shape/manager/test_format.rb +40 -0
  116. data/test/unit/core/shape/manager/test_internal.rb +137 -0
  117. data/test/unit/core/shape/manager/test_target.rb +109 -0
  118. data/test/unit/core/shape/tblock/formatter/test_basic.rb +24 -0
  119. data/test/unit/core/shape/tblock/formatter/test_datetime.rb +47 -0
  120. data/test/unit/core/shape/tblock/formatter/test_number.rb +76 -0
  121. data/test/unit/core/shape/tblock/formatter/test_padding.rb +72 -0
  122. data/test/unit/core/shape/tblock/test_format.rb +125 -0
  123. data/test/unit/core/shape/tblock/test_formatter.rb +28 -0
  124. data/test/unit/core/shape/tblock/test_interface.rb +40 -0
  125. data/test/unit/core/shape/tblock/test_internal.rb +139 -0
  126. data/test/unit/core/shape/text/test_format.rb +73 -0
  127. data/test/unit/core/shape/text/test_internal.rb +21 -0
  128. data/test/unit/core/test_events.rb +91 -0
  129. data/test/unit/core/test_shape.rb +35 -0
  130. data/test/unit/core/utils_spec.rb +56 -0
  131. data/test/unit/generator/pdf/document/graphics/test_attributes.rb +103 -0
  132. data/test/unit/generator/pdf/document/test_font.rb +54 -0
  133. data/test/unit/generator/pdf/document/test_graphics.rb +43 -0
  134. data/test/unit/generator/pdf/document/test_parse_color.rb +30 -0
  135. data/test/unit/generator/test_base.rb +51 -0
  136. data/test/unit/helper.rb +37 -0
  137. data/test/unit/layout/test_base.rb +92 -0
  138. data/test/unit/layout/test_configuration.rb +46 -0
  139. data/test/unit/layout/test_format.rb +104 -0
  140. data/test/unit/layout/test_version.rb +62 -0
  141. data/test/unit/report/test_base.rb +187 -0
  142. data/test/unit/report/test_events.rb +22 -0
  143. data/test/unit/report/test_internal.rb +234 -0
  144. data/test/unit/test_layout.rb +12 -0
  145. data/test/unit/test_report.rb +30 -0
  146. metadata +218 -0
@@ -0,0 +1,42 @@
1
+ # coding: utf-8
2
+
3
+ module ThinReports
4
+ module Generator
5
+
6
+ # @private
7
+ module Pdf::Graphics
8
+
9
+ BASE_LINE_WIDTH = 0.9
10
+
11
+ private
12
+
13
+ # Change the default graphic states defined by Prawn.
14
+ def setup_custom_graphic_states
15
+ pdf.line_width(BASE_LINE_WIDTH)
16
+ end
17
+
18
+ # @param [Numeric] width
19
+ def line_width(width)
20
+ pdf.line_width(width * BASE_LINE_WIDTH)
21
+ end
22
+
23
+ # Delegate to Prawn::Document#save_graphic_state
24
+ # @see Prawn::Document#save_graphics_state
25
+ def save_graphics_state
26
+ pdf.save_graphics_state
27
+ end
28
+
29
+ # Delegate to Prawn::Document#restore_graphic_state
30
+ # @see Prawn::Document#restore_graphics_state
31
+ def restore_graphics_state
32
+ pdf.restore_graphics_state
33
+ end
34
+ end
35
+
36
+ end
37
+ end
38
+
39
+ require 'thinreports/generator/pdf/document/graphics/attributes'
40
+ require 'thinreports/generator/pdf/document/graphics/basic'
41
+ require 'thinreports/generator/pdf/document/graphics/image'
42
+ require 'thinreports/generator/pdf/document/graphics/text'
@@ -0,0 +1,92 @@
1
+ # coding: utf-8
2
+
3
+ module ThinReports
4
+ module Generator
5
+
6
+ module Pdf::Graphics
7
+ # @param [Hash] svg_attrs
8
+ # @yield [attrs]
9
+ # @yieldparam [Hash] attrs
10
+ # @return [Hash]
11
+ def common_graphic_attrs(svg_attrs, &block)
12
+ attrs = {:stroke => svg_attrs['stroke'],
13
+ :stroke_width => svg_attrs['stroke-width'],
14
+ :fill => svg_attrs['fill']}
15
+
16
+ # Set 0 to stroke_width if stroke_opacity is '0'.
17
+ if svg_attrs['stroke-opacity'] == '0'
18
+ attrs[:stroke_width] = 0
19
+ end
20
+
21
+ # Setting for stroke dash.
22
+ if (dash = svg_attrs['stroke-dasharray']) && dash != 'none'
23
+ attrs[:stroke_dash] = dash.split(',')
24
+ end
25
+ block.call(attrs) if block_given?
26
+ attrs
27
+ end
28
+
29
+ # @param [Hash] svg_attrs
30
+ # @yield [attrs]
31
+ # @yieldparam [Hash] attrs
32
+ # @return [Hash]
33
+ def common_text_attrs(svg_attrs, &block)
34
+ attrs = {:font => default_family_if_missing(svg_attrs['font-family']),
35
+ :size => svg_attrs['font-size'],
36
+ :color => svg_attrs['fill'],
37
+ :align => text_align(svg_attrs['text-anchor']),
38
+ :styles => font_styles(svg_attrs)}
39
+ # Letter Spacing.
40
+ if space = svg_attrs['letter-spacing']
41
+ attrs[:letter_spacing] = space unless space == 'normal'
42
+ end
43
+ block.call(attrs) if block_given?
44
+ attrs
45
+ end
46
+
47
+ # @param [Hash] svg_attrs
48
+ # @return [Array<Symbol>]
49
+ def font_styles(svg_attrs)
50
+ styles = []
51
+ styles << :bold if svg_attrs['font-weight'] == 'bold'
52
+ styles << :italic if svg_attrs['font-style'] == 'italic'
53
+
54
+ if (deco = svg_attrs['text-decoration']) && deco != 'none'
55
+ deco = deco.split(' ')
56
+ styles << :underline if deco.include?('underline')
57
+ styles << :strikethrough if deco.include?('line-through')
58
+ end
59
+ styles
60
+ end
61
+
62
+ # @param [String] svg_align
63
+ # @return [Symbol]
64
+ def text_align(svg_align)
65
+ case svg_align
66
+ when 'start' then :left
67
+ when 'middle' then :center
68
+ when 'end' then :right
69
+ else :left
70
+ end
71
+ end
72
+
73
+ # @param [String] svg_valign
74
+ # @return [Symbol]
75
+ def text_valign(svg_valign)
76
+ case svg_valign
77
+ when 'top' then :top
78
+ when 'center' then :center
79
+ when 'bottom' then :bottom
80
+ else :top
81
+ end
82
+ end
83
+
84
+ # @param [String] xlink
85
+ # @return [String]
86
+ def extract_base64_string(xlink)
87
+ xlink.sub(%r{^data:image/[a-z]+?;base64,}, '')
88
+ end
89
+ end
90
+
91
+ end
92
+ end
@@ -0,0 +1,132 @@
1
+ # coding: utf-8
2
+
3
+ module ThinReports
4
+ module Generator
5
+
6
+ module Pdf::Graphics
7
+ # @param [Numeric, String] x1
8
+ # @param [Numeric, String] y1
9
+ # @param [Numeric, String] x2
10
+ # @param [Numeric, String] y2
11
+ # @param [Hash] attrs ({})
12
+ # @option attrs [String] :stroke
13
+ # @option attrs [Numeric, String] :stroke_width
14
+ # @option attrs [Array<Integer, String>] :stroke_dash
15
+ def line(x1, y1, x2, y2, attrs = {})
16
+ with_graphic_styles(attrs) do
17
+ pdf.line(pos(x1, y1), pos(x2, y2))
18
+ end
19
+ end
20
+
21
+ # @param [Numeric, String] x
22
+ # @param [Numeric, String] y
23
+ # @param [Numeric, String] w width
24
+ # @param [Numeric, String] h height
25
+ # @param [Hash] attrs ({})
26
+ # @option attrs [Integer, String] :radius
27
+ # @option attrs [String] :stroke
28
+ # @option attrs [Numeric, String] :stroke_width
29
+ # @option attrs [Array<Integer, String>] :stroke_dash
30
+ # @option attrs [String] :fill
31
+ def rect(x, y, w, h, attrs = {})
32
+ w, h = s2f(w, h)
33
+ radius = s2f(attrs[:radius])
34
+
35
+ with_graphic_styles(attrs) do
36
+ if radius && !radius.zero?
37
+ pdf.rounded_rectangle(pos(x, y), w, h, radius)
38
+ else
39
+ pdf.rectangle(pos(x, y), w, h)
40
+ end
41
+ end
42
+ end
43
+
44
+ # @param [Numeric, String] x center-x
45
+ # @param [Numeric, String] y center-y
46
+ # @param [Numeric, String] rx
47
+ # @param [Numeric, String] ry
48
+ # @param [Hash] attrs ({})
49
+ # @option attrs [String] :stroke
50
+ # @option attrs [Numeric, String] :stroke_width
51
+ # @option attrs [Array<Integer, String>] :stroke_dash
52
+ # @option attrs [String] :fill
53
+ def ellipse(x, y, rx, ry, attrs = {})
54
+ rx, ry = s2f(rx, ry)
55
+
56
+ with_graphic_styles(attrs) do
57
+ pdf.ellipse(pos(x, y), rx, ry)
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ # @param [Hash] attrs
64
+ def with_graphic_styles(attrs, &block)
65
+ stroke = build_stroke_styles(attrs)
66
+ fill = build_fill_styles(attrs)
67
+
68
+ # Do not draw if no colors given.
69
+ return unless fill || stroke
70
+
71
+ save_graphics_state
72
+
73
+ # Apply stroke-dashed.
74
+ if stroke && stroke[:dash]
75
+ length, space = stroke[:dash]
76
+ pdf.dash(length, :space => space)
77
+ end
78
+
79
+ case
80
+ # Draw with fill and stroke.
81
+ when fill && stroke
82
+ pdf.fill_and_stroke {
83
+ line_width(stroke[:width])
84
+ pdf.fill_color(fill[:color])
85
+ pdf.stroke_color(stroke[:color])
86
+ block.call
87
+ }
88
+ # Draw only with fill.
89
+ when fill
90
+ pdf.fill {
91
+ pdf.fill_color(fill[:color])
92
+ block.call
93
+ }
94
+ # Draw only with stroke.
95
+ when stroke
96
+ pdf.stroke {
97
+ line_width(stroke[:width])
98
+ pdf.stroke_color(stroke[:color])
99
+ block.call
100
+ }
101
+ end
102
+
103
+ restore_graphics_state
104
+ end
105
+
106
+ # @param [Hash] styles
107
+ # @return [Hash, nil]
108
+ def build_stroke_styles(styles)
109
+ color = styles[:stroke]
110
+ width = styles[:stroke_width]
111
+
112
+ if color && color != 'none' && width && width != 0
113
+ {:color => parse_color(color),
114
+ :width => s2f(width),
115
+ :dash => s2f(*styles[:stroke_dash])}
116
+ end
117
+ end
118
+
119
+ # @param [Hash] styles
120
+ # @return [Hash, nil]
121
+ def build_fill_styles(styles)
122
+ color = styles[:fill]
123
+
124
+ if color && color != 'none'
125
+ {:color => parse_color(color)}
126
+ end
127
+ end
128
+
129
+ end
130
+
131
+ end
132
+ end
@@ -0,0 +1,51 @@
1
+ # coding: utf-8
2
+
3
+ require 'tempfile'
4
+ require 'base64'
5
+
6
+ module ThinReports
7
+ module Generator
8
+
9
+ module Pdf::Graphics
10
+ # @param [String] filename
11
+ # @param [Numeric, Strng] x
12
+ # @param [Numeric, Strng] y
13
+ # @param [Numeric, Strng] w
14
+ # @param [Numeric, Strng] h
15
+ def image(filename, x, y, w, h)
16
+ w, h = s2f(w, h)
17
+ pdf.image(filename, :at => pos(x, y), :width => w, :height => h)
18
+ end
19
+
20
+ # @param [String] base64
21
+ # @param x, y, w, h (see #image)
22
+ def base64image(base64, x, y, w, h)
23
+ image = create_temp_imagefile(base64)
24
+ image(image.path, x, y, w, h)
25
+ end
26
+
27
+ private
28
+
29
+ def clean_temp_images
30
+ temp_image_registry.each {|tmp| tmp.unlink }
31
+ end
32
+
33
+ def temp_image_registry
34
+ @temp_image_registry ||= []
35
+ end
36
+
37
+ # @param [String] base64
38
+ # @return [Tempfile]
39
+ def create_temp_imagefile(base64)
40
+ file = Tempfile.new('trg-tmp-img')
41
+ file.binmode
42
+ file.write(Base64.decode64(base64))
43
+ temp_image_registry << file
44
+ file
45
+ ensure
46
+ file.close
47
+ end
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,130 @@
1
+ # coding: utf-8
2
+
3
+ module ThinReports
4
+ module Generator
5
+
6
+ module Pdf::Graphics
7
+ # @param [String] content
8
+ # @param [Numeric, String] x
9
+ # @param [Numeric, String] y
10
+ # @param [Numeric, String] w
11
+ # @param [Numeric, String] h
12
+ # @param [Hash] attrs ({})
13
+ # @option attrs [String] :font
14
+ # @option attrs [Numeric, String] :size
15
+ # @option attrs [String] :color
16
+ # @option attrs [Array<:bold, :italic, :underline, :strikethrough>]
17
+ # :styles (nil)
18
+ # @option attrs [:left, :center, :right] :align (:left)
19
+ # @option attrs [:top, :center, :bottom] :valign (:top)
20
+ # @option attrs [Numeric, String] :line_height The total height of an text line.
21
+ # @option attrs [Numeric, String] :letter_spacing
22
+ def text_box(content, x, y, w, h, attrs = {})
23
+ w, h = s2f(w, h)
24
+ with_text_styles(attrs) do |built_attrs, font_styles|
25
+ pdf.formatted_text_box([{:text => text_without_line_wrap(content),
26
+ :styles => font_styles}],
27
+ built_attrs.merge(:at => pos(x, y),
28
+ :width => w,
29
+ :height => h))
30
+ end
31
+ end
32
+
33
+ # @see #text_box
34
+ def text(content, x, y, w, h, attrs = {})
35
+ # Set the :overflow property to :shirink_to_fit.
36
+ text_box(content, x, y, w, h, attrs.merge(:overflow => :shrink_to_fit))
37
+ end
38
+
39
+ private
40
+
41
+ # @param attrs (see #text)
42
+ # @yield [built_attrs, font_styles]
43
+ # @yieldparam [Hash] built_attrs The finalized attributes.
44
+ # @yieldparam [Array] font_styles The finalized styles.
45
+ def with_text_styles(attrs, &block)
46
+ # When no color is given, do not draw.
47
+ return unless attrs.key?(:color) && attrs[:color] != 'none'
48
+
49
+ save_graphics_state
50
+
51
+ fontinfo = {:name => attrs.delete(:font).to_s,
52
+ :color => parse_color(attrs.delete(:color)),
53
+ :size => s2f(attrs.delete(:size))}
54
+
55
+ # Add the specified value to :leading option.
56
+ if line_height = attrs.delete(:line_height)
57
+ attrs[:leading] = text_line_leading(s2f(line_height),
58
+ :name => fontinfo[:name],
59
+ :size => fontinfo[:size])
60
+ end
61
+
62
+ # Set the :character_spacing option.
63
+ if space = attrs.delete(:letter_spacing)
64
+ attrs[:character_spacing] = s2f(space)
65
+ end
66
+
67
+ # Or... with_font_styles(attrs, fontinfo, &block)
68
+ with_font_styles(attrs, fontinfo) do |modified_attrs, styles|
69
+ block.call(modified_attrs, styles)
70
+ end
71
+
72
+ restore_graphics_state
73
+ end
74
+
75
+ # @param [Numeric] line_height
76
+ # @param [Hash] font
77
+ # @option font [String] :name Name of font.
78
+ # @option font [Numeric] :size Size of font.
79
+ # @return [Numeric]
80
+ def text_line_leading(line_height, font)
81
+ line_height - pdf.font(font[:name], :size => font[:size]).height
82
+ end
83
+
84
+ # @param [String] content
85
+ # @return [String]
86
+ def text_without_line_wrap(content)
87
+ content.gsub(/ /, Prawn::Text::NBSP)
88
+ end
89
+
90
+ # @param [Hash] attrs
91
+ # @param [Hash] font
92
+ # @option font [String] :color
93
+ # @option font [Numeric] :size
94
+ # @option font [String] :name
95
+ # @yield [attributes, styles]
96
+ # @yieldparam [Hash] modified_attrs
97
+ # @yieldparam [Array] styles
98
+ def with_font_styles(attrs, font, &block)
99
+ # Building font styles.
100
+ if styles = attrs.delete(:styles)
101
+ manual, styles = styles.partition do |style|
102
+ [:bold, :italic].include?(style) && !font_has_style?(font[:name], style)
103
+ end
104
+ end
105
+
106
+ # Emulate bold style.
107
+ if manual.include?(:bold)
108
+ pdf.stroke_color(font[:color])
109
+ pdf.line_width(font[:size] * 0.025)
110
+
111
+ # Change rendering mode to :fill_stroke.
112
+ attrs[:mode] = :fill_stroke
113
+ end
114
+
115
+ # Emulate italic style.
116
+ if manual.include?(:italic)
117
+ # FIXME
118
+ # pdf.transformation_matrix(1, 0, 0.26, 1, 0, 0)
119
+ end
120
+
121
+ pdf.font(font[:name], :size => font[:size]) do
122
+ pdf.fill_color(font[:color])
123
+ block.call(attrs, styles || [])
124
+ end
125
+ end
126
+
127
+ end
128
+
129
+ end
130
+ end