thinreports 0.6.0.pre3

Sign up to get free protection for your applications and to get access to all the features.
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,30 @@
1
+ # coding: utf-8
2
+
3
+ module ThinReports
4
+
5
+ module Generator
6
+ # @param [Symbol] type
7
+ # @param report (see ThinReports::Generator::Base#initialize)
8
+ # @param options (see ThinReports::Generator::Base#initialize)
9
+ def self.new(type, report, options = {})
10
+ unless generator = registry[type]
11
+ raise ThinReports::Errors::UnknownGeneratorType
12
+ end
13
+ generator.new(report, options)
14
+ end
15
+
16
+ # @private
17
+ def self.register(type, generator)
18
+ registry[type] = generator
19
+ end
20
+
21
+ # @private
22
+ def self.registry
23
+ @generators ||= {}
24
+ end
25
+ end
26
+ end
27
+
28
+ require 'thinreports/generator/base'
29
+ require 'thinreports/generator/pxd'
30
+ require 'thinreports/generator/pdf'
@@ -0,0 +1,49 @@
1
+ # coding: utf-8
2
+
3
+ module ThinReports
4
+ module Generator
5
+
6
+ # @abstract
7
+ class Base
8
+ # @return [ThinReports::Report::Base]
9
+ # @private
10
+ attr_reader :report
11
+
12
+ # @return [Hash]
13
+ # @private
14
+ attr_reader :options
15
+
16
+ # @private
17
+ def self.inherited(g)
18
+ Generator.register(g.name.split('::').last.downcase.to_sym, g);
19
+ end
20
+
21
+ # @param [ThinReports::Report::Base] report
22
+ # @param [Hash] options
23
+ def initialize(report, options = {})
24
+ report.finalize
25
+
26
+ @report = report.internal
27
+ @options = options || {}
28
+ end
29
+
30
+ # @return [String]
31
+ # @abstract
32
+ def generate
33
+ raise NotImplementedError
34
+ end
35
+
36
+ # @param [String] filename
37
+ # @abstract
38
+ def generate_file(filename)
39
+ raise NotImplementedError
40
+ end
41
+
42
+ # @private
43
+ def default_layout
44
+ report.default_layout
45
+ end
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,63 @@
1
+ # coding: utf-8
2
+
3
+ require 'prawn'
4
+
5
+ module ThinReports
6
+ module Generator
7
+
8
+ class Pdf < Base
9
+ # @param report (see ThinReports::Generator::Base#initialize)
10
+ # @param [Hash] options
11
+ # @option options [Hash] :security (nil)
12
+ # See Prawn::Document#encrypt_document
13
+ def initialize(report, options)
14
+ super
15
+ @pdf = Document.new(options)
16
+ @drawers = {}
17
+ @current_format = nil
18
+ end
19
+
20
+ # @see ThinReports::Generator::Base#generate
21
+ def generate
22
+ draw_report
23
+ @pdf.render
24
+ end
25
+
26
+ # @see ThinReports::Generator::Base#generate_file
27
+ def generate_file(filename)
28
+ draw_report
29
+ @pdf.render_file(filename)
30
+ end
31
+
32
+ private
33
+
34
+ def draw_report
35
+ report.pages.each do |page|
36
+ draw_page(page)
37
+ end
38
+ end
39
+
40
+ def draw_page(page)
41
+ return @pdf.add_blank_page if page.blank?
42
+
43
+ format = page.layout.format
44
+ # On format change.
45
+ unless @current_format == format.identifier
46
+ @pdf.start_new_page(format)
47
+ else
48
+ @pdf.start_new_page
49
+ end
50
+ drawer(format).draw(page.manager)
51
+ end
52
+
53
+ def drawer(format)
54
+ @drawers[format.identifier] ||= Drawer::Page.new(@pdf, format)
55
+ end
56
+ end
57
+
58
+ end
59
+ end
60
+
61
+ require 'thinreports/generator/pdf/prawn_ext'
62
+ require 'thinreports/generator/pdf/document'
63
+ require 'thinreports/generator/pdf/drawer'
@@ -0,0 +1,171 @@
1
+ # coding: utf-8
2
+
3
+ require 'thinreports/generator/pdf/document/font'
4
+ require 'thinreports/generator/pdf/document/parse_color'
5
+ require 'thinreports/generator/pdf/document/graphics'
6
+ require 'thinreports/generator/pdf/document/draw_shape'
7
+ require 'thinreports/generator/pdf/document/parse_svg'
8
+
9
+ module ThinReports
10
+ module Generator
11
+
12
+ # @private
13
+ class Pdf::Document
14
+ include Pdf::Font
15
+ include Pdf::ParseColor
16
+ include Pdf::Graphics
17
+ include Pdf::DrawShape
18
+ include Pdf::ParseSVG
19
+
20
+ # @param options (see ThinReports::Generator::Pdf#initialize)
21
+ def initialize(options = {})
22
+ @pdf = Prawn::Document.new(
23
+ :skip_page_creation => true,
24
+ :margin => [0, 0],
25
+ :info => {:Creator => 'ThinReports Generator for Ruby ' +
26
+ ThinReports::VERSION}
27
+ )
28
+ @format_stamp_registry = []
29
+
30
+ # Setup to Prawn::Document.
31
+ setup_fonts
32
+ setup_custom_graphic_states
33
+
34
+ # Encrypts the document.
35
+ if options[:security]
36
+ @pdf.encrypt_document(options[:security])
37
+ end
38
+ end
39
+
40
+ # @param [ThinReports::Layout::Format] format (nil)
41
+ def start_new_page(format = nil)
42
+ options = {}
43
+ format_id = format.identifier
44
+
45
+ if format
46
+ options[:layout] = format.page_orientation.to_sym
47
+ options[:size] = if format.user_paper_type?
48
+ [format.page_width.to_f, format.page_height.to_f]
49
+ else
50
+ format.page_paper_type
51
+ end
52
+ end
53
+
54
+ pdf.start_new_page(options)
55
+
56
+ # @note
57
+ # The best way is to create a template for each layout.
58
+ # However, in the latest Prawn library, because of problems handling
59
+ # the template that you want supported by future releases.
60
+ # @see https://github.com/sandal/prawn/issues/199
61
+ unless format_stamp_registry.include?(format_id)
62
+ create_format_stamp(format)
63
+ end
64
+ # Apply the static shapes of current format.
65
+ stamp(format_id.to_s)
66
+ end
67
+
68
+ def add_blank_page
69
+ pdf.start_new_page(pdf.page_count.zero? ? {:size => 'A4'} : {})
70
+ end
71
+
72
+ # Delegate to Prawn::Document#render
73
+ # @see Prawn::Document#render
74
+ def render
75
+ result = pdf.render
76
+ finalize
77
+ result
78
+ end
79
+
80
+ # Delegate to Prawn::Document#render_file
81
+ # @see Prawn::Document#render_file
82
+ def render_file(*args)
83
+ finalize
84
+ pdf.render_file(*args)
85
+ end
86
+
87
+ # @param [Numeric, String] x
88
+ # @param [Numeric, String] y
89
+ def translate(x, y, &block)
90
+ x, y = rpos(x, y)
91
+ pdf.translate(x, y, &block)
92
+ end
93
+
94
+ # @param [String] stamp_id
95
+ # @param [Array<Numeric>] at (nil)
96
+ def stamp(stamp_id, at = nil)
97
+ unless at.nil?
98
+ pdf.stamp_at(stamp_id, rpos(*at))
99
+ else
100
+ pdf.stamp(stamp_id)
101
+ end
102
+ end
103
+
104
+ # Delegate to Prawn::Document#create_stamp
105
+ # @param [String] id
106
+ # @see Prawn::Document#create_stamp
107
+ def create_stamp(id, &block)
108
+ pdf.create_stamp(id, &block)
109
+ end
110
+
111
+ # @private
112
+ # @see #pdf
113
+ def internal
114
+ @pdf
115
+ end
116
+
117
+ private
118
+
119
+ # @return [Prawn::Document]
120
+ attr_reader :pdf
121
+
122
+ # @return [Array<Symbol>]
123
+ attr_reader :format_stamp_registry
124
+
125
+ def finalize
126
+ clean_temp_images
127
+ end
128
+
129
+ # @param [ThinReports::Layout::Format] format
130
+ def create_format_stamp(format)
131
+ create_stamp(format.identifier.to_s) do
132
+ parse_svg(format.layout, '/svg/g')
133
+ end
134
+ format_stamp_registry << format.identifier
135
+ end
136
+
137
+ # @param [Array<String, Numeric>] values
138
+ # @return [Numeric, Array<Numeric>, nil]
139
+ def s2f(*values)
140
+ return nil if values.empty?
141
+
142
+ if values.size == 1
143
+ if value = values.first
144
+ value.is_a?(::Numeric) ? value : value.to_f
145
+ end
146
+ else
147
+ values.map {|v| s2f(v) }
148
+ end
149
+ end
150
+
151
+ # @param [Numeric, String] x
152
+ # @param [Numeric, String] y
153
+ # @return [Array<Float>]
154
+ def map_to_upper_left_relative_position(x, y)
155
+ x, y = s2f(x, y)
156
+ [x, -y]
157
+ end
158
+ alias_method :rpos, :map_to_upper_left_relative_position
159
+
160
+ # @param [Numeric, String] x
161
+ # @param [Numeric, String] y
162
+ # @return [Array<Float>]
163
+ def map_to_upper_left_position(x, y)
164
+ x, y = s2f(x, y)
165
+ [x, pdf.bounds.height - y]
166
+ end
167
+ alias_method :pos, :map_to_upper_left_position
168
+ end
169
+
170
+ end
171
+ end
@@ -0,0 +1,74 @@
1
+ # coding: utf-8
2
+
3
+ module ThinReports
4
+ module Generator
5
+
6
+ # @private
7
+ module Pdf::DrawShape
8
+ # @param [ThinReports::Core::Shape::Tblock::Internal] shape
9
+ def draw_shape_tblock(shape)
10
+ x, y, w, h = shape.box.values_at('x', 'y', 'width', 'height')
11
+
12
+ content = shape.real_value.to_s
13
+ unless shape.multiple?
14
+ content = content.gsub(/\n/, ' ')
15
+ end
16
+ text_box(content, x, y, w, h, common_text_attrs(shape.attributes))
17
+ end
18
+
19
+ # @param [ThinReports::Core::Shape::Basic::Internal] shape
20
+ def draw_shape_image(shape)
21
+ x, y, w, h = shape.svg_attrs.values_at('x', 'y', 'width', 'height')
22
+ base64image(extract_base64_string(shape.svg_attrs['xlink:href']),
23
+ x, y, w, h)
24
+ end
25
+
26
+ # @param [ThinReports::Core::Shape::Text::Internal] shape
27
+ def draw_shape_text(shape)
28
+ x, y, w, h = shape.box.values_at('x', 'y', 'width', 'height')
29
+ text(shape.text.join("\n"), x, y, w, h,
30
+ shape_text_attrs(shape))
31
+ end
32
+
33
+ # @param [ThinReports::Core::Shape::Basic::Internal] shape
34
+ def draw_shape_ellipse(shape)
35
+ args = shape.svg_attrs.values_at('cx', 'cy', 'rx', 'ry')
36
+ args << common_graphic_attrs(shape.attributes)
37
+ ellipse(*args)
38
+ end
39
+
40
+ # @param [ThinReports::Core::Shape::Basic::Internal] shape
41
+ def draw_shape_line(shape)
42
+ args = shape.svg_attrs.values_at('x1', 'y1', 'x2', 'y2')
43
+ args << common_graphic_attrs(shape.attributes)
44
+ line(*args)
45
+ end
46
+
47
+ # @param [ThinReports::Core::Shape::Basic::Internal] shape
48
+ def draw_shape_rect(shape)
49
+ args = shape.svg_attrs.values_at('x', 'y', 'width', 'height')
50
+ args << common_graphic_attrs(shape.attributes) do |attrs|
51
+ attrs[:radius] = shape.svg_attrs['rx']
52
+ end
53
+ rect(*args)
54
+ end
55
+
56
+ private
57
+
58
+ # @param [ThinReports::Core::Shape::Text::Internal]
59
+ # @return [Hash]
60
+ def shape_text_attrs(shape)
61
+ format = shape.format
62
+
63
+ common_text_attrs(shape.attributes) do |attrs|
64
+ # Set the :line_height option.
65
+ attrs[:line_height] = format.line_height unless format.line_height.blank?
66
+ # Set the :valign option.
67
+ attrs[:valign] = text_valign(format.valign)
68
+ end
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,59 @@
1
+ # coding: utf-8
2
+
3
+ module ThinReports
4
+ module Generator
5
+
6
+ # @private
7
+ module Pdf::Font
8
+ FONT_STORE = File.join(ThinReports::ROOTDIR, 'resources', 'fonts')
9
+
10
+ BUILTIN_FONTS = {
11
+ 'IPAMincho' => {:normal => File.join(FONT_STORE, 'ipam.ttf')},
12
+ 'IPAPMincho' => {:normal => File.join(FONT_STORE, 'ipamp.ttf')},
13
+ 'IPAGothic' => {:normal => File.join(FONT_STORE, 'ipag.ttf')},
14
+ 'IPAPGothic' => {:normal => File.join(FONT_STORE, 'ipagp.ttf')}
15
+ }
16
+
17
+ private
18
+
19
+ def setup_fonts
20
+ # Install built-in fonts.
21
+ pdf.font_families.update(BUILTIN_FONTS)
22
+
23
+ # Install fall-back font that IPAMincho.
24
+ fallback_font = BUILTIN_FONTS['IPAMincho'][:normal]
25
+ pdf.font_families['FallbackFont'] = {:normal => fallback_font,
26
+ :bold => fallback_font,
27
+ :italic => fallback_font,
28
+ :bold_italic => fallback_font}
29
+ # Setup fallback font.
30
+ pdf.fallback_fonts(['FallbackFont'])
31
+
32
+ # Create aliases from the font list provided by Prawn.
33
+ pdf.font_families.update(
34
+ 'Courier New' => pdf.font_families['Courier'],
35
+ 'Times New Roman' => pdf.font_families['Times-Roman']
36
+ )
37
+ end
38
+
39
+ # @return [String]
40
+ def default_family
41
+ 'Helvetica'
42
+ end
43
+
44
+ # @param [String] family
45
+ # @return [String]
46
+ def default_family_if_missing(family)
47
+ pdf.font_families.key?(family) ? family : default_family
48
+ end
49
+
50
+ # @param [String] font
51
+ # @param [Symbol] style
52
+ # @return [Boolean]
53
+ def font_has_style?(font, style)
54
+ (f = pdf.font_families[font]) && f.key?(style)
55
+ end
56
+ end
57
+
58
+ end
59
+ end