alphasights-prawn 0.10.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.
Files changed (244) hide show
  1. data/COPYING +340 -0
  2. data/HACKING +50 -0
  3. data/LICENSE +56 -0
  4. data/README +141 -0
  5. data/Rakefile +52 -0
  6. data/data/encodings/win_ansi.txt +29 -0
  7. data/data/fonts/Action Man.dfont +0 -0
  8. data/data/fonts/Activa.ttf +0 -0
  9. data/data/fonts/Chalkboard.ttf +0 -0
  10. data/data/fonts/Courier-Bold.afm +342 -0
  11. data/data/fonts/Courier-BoldOblique.afm +342 -0
  12. data/data/fonts/Courier-Oblique.afm +342 -0
  13. data/data/fonts/Courier.afm +342 -0
  14. data/data/fonts/DejaVuSans.ttf +0 -0
  15. data/data/fonts/Dustismo_Roman.ttf +0 -0
  16. data/data/fonts/Helvetica-Bold.afm +2827 -0
  17. data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
  18. data/data/fonts/Helvetica-Oblique.afm +3051 -0
  19. data/data/fonts/Helvetica.afm +3051 -0
  20. data/data/fonts/MustRead.html +19 -0
  21. data/data/fonts/Symbol.afm +213 -0
  22. data/data/fonts/Times-Bold.afm +2588 -0
  23. data/data/fonts/Times-BoldItalic.afm +2384 -0
  24. data/data/fonts/Times-Italic.afm +2667 -0
  25. data/data/fonts/Times-Roman.afm +2419 -0
  26. data/data/fonts/ZapfDingbats.afm +225 -0
  27. data/data/fonts/comicsans.ttf +0 -0
  28. data/data/fonts/gkai00mp.ttf +0 -0
  29. data/data/images/16bit.alpha +0 -0
  30. data/data/images/16bit.dat +0 -0
  31. data/data/images/16bit.png +0 -0
  32. data/data/images/arrow.png +0 -0
  33. data/data/images/arrow2.png +0 -0
  34. data/data/images/barcode_issue.png +0 -0
  35. data/data/images/dice.alpha +0 -0
  36. data/data/images/dice.dat +0 -0
  37. data/data/images/dice.png +0 -0
  38. data/data/images/dice_interlaced.png +0 -0
  39. data/data/images/fractal.jpg +0 -0
  40. data/data/images/letterhead.jpg +0 -0
  41. data/data/images/page_white_text.alpha +0 -0
  42. data/data/images/page_white_text.dat +0 -0
  43. data/data/images/page_white_text.png +0 -0
  44. data/data/images/pigs.jpg +0 -0
  45. data/data/images/rails.dat +0 -0
  46. data/data/images/rails.png +0 -0
  47. data/data/images/ruport.png +0 -0
  48. data/data/images/ruport_data.dat +0 -0
  49. data/data/images/ruport_transparent.png +0 -0
  50. data/data/images/ruport_type0.png +0 -0
  51. data/data/images/stef.jpg +0 -0
  52. data/data/images/tru256.bmp +0 -0
  53. data/data/images/web-links.dat +1 -0
  54. data/data/images/web-links.png +0 -0
  55. data/data/pdfs/complex_template.pdf +0 -0
  56. data/data/pdfs/contains_ttf_font.pdf +0 -0
  57. data/data/pdfs/encrypted.pdf +0 -0
  58. data/data/pdfs/hexagon.pdf +61 -0
  59. data/data/pdfs/indirect_reference.pdf +86 -0
  60. data/data/pdfs/nested_pages.pdf +118 -0
  61. data/data/pdfs/resources_as_indirect_object.pdf +83 -0
  62. data/data/pdfs/two_hexagons.pdf +90 -0
  63. data/data/pdfs/version_1_6.pdf +61 -0
  64. data/data/shift_jis_text.txt +1 -0
  65. data/examples/bounding_box/bounding_boxes.rb +43 -0
  66. data/examples/bounding_box/indentation.rb +34 -0
  67. data/examples/bounding_box/russian_boxes.rb +36 -0
  68. data/examples/bounding_box/stretched_nesting.rb +67 -0
  69. data/examples/builder/simple.rb +28 -0
  70. data/examples/example_helper.rb +4 -0
  71. data/examples/general/background.rb +23 -0
  72. data/examples/general/canvas.rb +15 -0
  73. data/examples/general/context_sensitive_headers.rb +37 -0
  74. data/examples/general/float.rb +11 -0
  75. data/examples/general/margin.rb +36 -0
  76. data/examples/general/measurement_units.rb +51 -0
  77. data/examples/general/metadata-info.rb +16 -0
  78. data/examples/general/multi_page_layout.rb +18 -0
  79. data/examples/general/outlines.rb +50 -0
  80. data/examples/general/page_geometry.rb +31 -0
  81. data/examples/general/page_numbering.rb +15 -0
  82. data/examples/general/repeaters.rb +47 -0
  83. data/examples/general/stamp.rb +41 -0
  84. data/examples/general/templates.rb +13 -0
  85. data/examples/graphics/basic_images.rb +23 -0
  86. data/examples/graphics/chunkable.rb +38 -0
  87. data/examples/graphics/cmyk.rb +12 -0
  88. data/examples/graphics/curves.rb +11 -0
  89. data/examples/graphics/hexagon.rb +13 -0
  90. data/examples/graphics/image_fit.rb +15 -0
  91. data/examples/graphics/image_flow.rb +37 -0
  92. data/examples/graphics/image_position.rb +17 -0
  93. data/examples/graphics/line.rb +32 -0
  94. data/examples/graphics/png_types.rb +22 -0
  95. data/examples/graphics/polygons.rb +16 -0
  96. data/examples/graphics/remote_images.rb +12 -0
  97. data/examples/graphics/rounded_polygons.rb +19 -0
  98. data/examples/graphics/rounded_rectangle.rb +20 -0
  99. data/examples/graphics/ruport_style_helpers.rb +19 -0
  100. data/examples/graphics/stroke_bounds.rb +20 -0
  101. data/examples/graphics/stroke_cap_and_join.rb +45 -0
  102. data/examples/graphics/stroke_dash.rb +42 -0
  103. data/examples/graphics/transformations.rb +52 -0
  104. data/examples/graphics/transparency.rb +26 -0
  105. data/examples/m17n/chinese_text_wrapping.rb +17 -0
  106. data/examples/m17n/euro.rb +15 -0
  107. data/examples/m17n/sjis.rb +28 -0
  108. data/examples/m17n/utf8.rb +13 -0
  109. data/examples/m17n/win_ansi_charset.rb +54 -0
  110. data/examples/security/hello_foo.rb +8 -0
  111. data/examples/table/bill.rb +53 -0
  112. data/examples/table/cell.rb +12 -0
  113. data/examples/table/checkerboard.rb +22 -0
  114. data/examples/table/header.rb +14 -0
  115. data/examples/table/inline_format_table.rb +12 -0
  116. data/examples/table/multi_page_table.rb +9 -0
  117. data/examples/table/simple_table.rb +24 -0
  118. data/examples/table/subtable.rb +12 -0
  119. data/examples/table/widths.rb +20 -0
  120. data/examples/text/alignment.rb +18 -0
  121. data/examples/text/character_spacing.rb +12 -0
  122. data/examples/text/dfont.rb +48 -0
  123. data/examples/text/family_based_styling.rb +24 -0
  124. data/examples/text/font_calculations.rb +91 -0
  125. data/examples/text/font_size.rb +33 -0
  126. data/examples/text/hyphenation.rb +45 -0
  127. data/examples/text/indent_paragraphs.rb +22 -0
  128. data/examples/text/inline_format.rb +103 -0
  129. data/examples/text/kerning.rb +30 -0
  130. data/examples/text/rotated.rb +98 -0
  131. data/examples/text/shaped_text_box.rb +31 -0
  132. data/examples/text/simple_text.rb +17 -0
  133. data/examples/text/simple_text_ttf.rb +17 -0
  134. data/examples/text/text_box.rb +88 -0
  135. data/examples/text/text_box_returning_excess.rb +51 -0
  136. data/examples/text/text_flow.rb +67 -0
  137. data/lib/prawn.rb +27 -0
  138. data/lib/prawn/canvas.rb +119 -0
  139. data/lib/prawn/chunkable.rb +37 -0
  140. data/lib/prawn/compatibility.rb +51 -0
  141. data/lib/prawn/core.rb +85 -0
  142. data/lib/prawn/core/annotations.rb +61 -0
  143. data/lib/prawn/core/byte_string.rb +9 -0
  144. data/lib/prawn/core/chunk.rb +36 -0
  145. data/lib/prawn/core/destinations.rb +90 -0
  146. data/lib/prawn/core/document_state.rb +78 -0
  147. data/lib/prawn/core/literal_string.rb +16 -0
  148. data/lib/prawn/core/name_tree.rb +165 -0
  149. data/lib/prawn/core/object_store.rb +236 -0
  150. data/lib/prawn/core/page.rb +179 -0
  151. data/lib/prawn/core/pdf_object.rb +108 -0
  152. data/lib/prawn/core/reference.rb +112 -0
  153. data/lib/prawn/core/text.rb +140 -0
  154. data/lib/prawn/core/text/formatted/arranger.rb +266 -0
  155. data/lib/prawn/core/text/formatted/line_wrap.rb +127 -0
  156. data/lib/prawn/core/text/formatted/wrap.rb +112 -0
  157. data/lib/prawn/core/text/line_wrap.rb +209 -0
  158. data/lib/prawn/core/text/wrap.rb +80 -0
  159. data/lib/prawn/document.rb +573 -0
  160. data/lib/prawn/document/bounding_box.rb +425 -0
  161. data/lib/prawn/document/graphics_state.rb +48 -0
  162. data/lib/prawn/document/internals.rb +170 -0
  163. data/lib/prawn/document/page_geometry.rb +136 -0
  164. data/lib/prawn/document/snapshot.rb +87 -0
  165. data/lib/prawn/document_builder.rb +51 -0
  166. data/lib/prawn/document_builder/command.rb +38 -0
  167. data/lib/prawn/document_builder/constructs.rb +2 -0
  168. data/lib/prawn/document_builder/constructs/flowing_text_construct.rb +18 -0
  169. data/lib/prawn/document_builder/constructs/path_construct.rb +9 -0
  170. data/lib/prawn/document_builder/layout.rb +25 -0
  171. data/lib/prawn/document_builder/modifications.rb +2 -0
  172. data/lib/prawn/document_builder/modifications/layout_modification.rb +9 -0
  173. data/lib/prawn/document_builder/modifications/path_modification.rb +9 -0
  174. data/lib/prawn/encoding.rb +121 -0
  175. data/lib/prawn/errors.rb +94 -0
  176. data/lib/prawn/font.rb +341 -0
  177. data/lib/prawn/font/afm.rb +225 -0
  178. data/lib/prawn/font/dfont.rb +42 -0
  179. data/lib/prawn/font/ttf.rb +350 -0
  180. data/lib/prawn/graphics.rb +325 -0
  181. data/lib/prawn/graphics/cap_style.rb +38 -0
  182. data/lib/prawn/graphics/color.rb +205 -0
  183. data/lib/prawn/graphics/dash.rb +71 -0
  184. data/lib/prawn/graphics/join_style.rb +38 -0
  185. data/lib/prawn/graphics/transformation.rb +156 -0
  186. data/lib/prawn/graphics/transparency.rb +99 -0
  187. data/lib/prawn/images.rb +348 -0
  188. data/lib/prawn/images/jpg.rb +46 -0
  189. data/lib/prawn/images/png.rb +226 -0
  190. data/lib/prawn/measurement_extensions.rb +46 -0
  191. data/lib/prawn/measurements.rb +71 -0
  192. data/lib/prawn/outline.rb +278 -0
  193. data/lib/prawn/repeater.rb +129 -0
  194. data/lib/prawn/security.rb +262 -0
  195. data/lib/prawn/security/arcfour.rb +51 -0
  196. data/lib/prawn/stamp.rb +126 -0
  197. data/lib/prawn/table.rb +421 -0
  198. data/lib/prawn/table/accessors.rb +180 -0
  199. data/lib/prawn/table/cell.rb +350 -0
  200. data/lib/prawn/table/cell/in_table.rb +27 -0
  201. data/lib/prawn/table/cell/subtable.rb +65 -0
  202. data/lib/prawn/table/cell/text.rb +125 -0
  203. data/lib/prawn/text.rb +449 -0
  204. data/lib/prawn/text/box.rb +392 -0
  205. data/lib/prawn/text/formatted.rb +4 -0
  206. data/lib/prawn/text/formatted/box.rb +228 -0
  207. data/lib/prawn/text/formatted/fragment.rb +181 -0
  208. data/lib/prawn/text/formatted/parser.rb +213 -0
  209. data/spec/annotations_spec.rb +90 -0
  210. data/spec/bounding_box_spec.rb +190 -0
  211. data/spec/cell_spec.rb +348 -0
  212. data/spec/destinations_spec.rb +15 -0
  213. data/spec/document_spec.rb +473 -0
  214. data/spec/font_spec.rb +324 -0
  215. data/spec/formatted_text_arranger_spec.rb +426 -0
  216. data/spec/formatted_text_box_spec.rb +756 -0
  217. data/spec/formatted_text_fragment_spec.rb +211 -0
  218. data/spec/graphics_spec.rb +446 -0
  219. data/spec/images_spec.rb +96 -0
  220. data/spec/inline_formatted_text_parser_spec.rb +502 -0
  221. data/spec/jpg_spec.rb +25 -0
  222. data/spec/line_wrap_spec.rb +341 -0
  223. data/spec/measurement_units_spec.rb +23 -0
  224. data/spec/name_tree_spec.rb +112 -0
  225. data/spec/object_store_spec.rb +160 -0
  226. data/spec/outline_spec.rb +269 -0
  227. data/spec/pdf_object_spec.rb +170 -0
  228. data/spec/png_spec.rb +237 -0
  229. data/spec/reference_spec.rb +82 -0
  230. data/spec/repeater_spec.rb +96 -0
  231. data/spec/security_spec.rb +120 -0
  232. data/spec/snapshot_spec.rb +138 -0
  233. data/spec/spec_helper.rb +26 -0
  234. data/spec/stamp_spec.rb +108 -0
  235. data/spec/stroke_styles_spec.rb +163 -0
  236. data/spec/table_spec.rb +598 -0
  237. data/spec/template_spec.rb +158 -0
  238. data/spec/text_at_spec.rb +119 -0
  239. data/spec/text_box_spec.rb +742 -0
  240. data/spec/text_spacing_spec.rb +75 -0
  241. data/spec/text_spec.rb +333 -0
  242. data/spec/text_with_inline_formatting_spec.rb +193 -0
  243. data/spec/transparency_spec.rb +89 -0
  244. metadata +331 -0
@@ -0,0 +1,71 @@
1
+ # encoding: utf-8
2
+
3
+ # dash.rb : Implements stroke dashing
4
+ #
5
+ # Contributed by Daniel Nelson. October, 2009
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ #
9
+ module Prawn
10
+ module Graphics
11
+ module Dash
12
+
13
+ # Sets the dash pattern for stroked lines and curves
14
+ #
15
+ # length is the length of the dash. If options is not present,
16
+ # or options[:space] is nil, then length is also the length of
17
+ # the space between dashes
18
+ #
19
+ # options may contain :space and :phase
20
+ # :space is the space between the dashes
21
+ # :phase is where in the cycle to begin dashing. For
22
+ # example, a phase of 0 starts at the beginning of
23
+ # the dash; whereas, if the phase is equal to the
24
+ # length of the dash, then stroking will begin at
25
+ # the beginning of the space. Default is 0
26
+ #
27
+ # integers or floats may be used for length and the options
28
+ #
29
+ # dash units are in PDF points ( 1/72 in )
30
+ #
31
+ def dash(length=nil, options={})
32
+ return @dash || undash_hash if length.nil?
33
+
34
+ @dash = { :dash => length,
35
+ :space => options[:space] || length,
36
+ :phase => options[:phase] || 0 }
37
+
38
+ write_stroke_dash
39
+ end
40
+
41
+ alias_method :dash=, :dash
42
+
43
+ # Stops dashing, restoring solid stroked lines and curves
44
+ #
45
+ def undash
46
+ @dash = undash_hash
47
+ write_stroke_dash
48
+ end
49
+
50
+ # Returns when stroke is dashed, false otherwise
51
+ #
52
+ def dashed?
53
+ dash != undash_hash
54
+ end
55
+
56
+ private
57
+
58
+ def undash_hash
59
+ { :dash => nil, :space => nil, :phase => 0 }
60
+ end
61
+
62
+ def write_stroke_dash
63
+ if @dash[:dash].nil?
64
+ add_content "[] 0 d"
65
+ return
66
+ end
67
+ add_content "[#{@dash[:dash]} #{@dash[:space]}] #{@dash[:phase]} d"
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,38 @@
1
+ # encoding: utf-8
2
+
3
+ # join_style.rb : Implements stroke join styling
4
+ #
5
+ # Contributed by Daniel Nelson. October, 2009
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ #
9
+ module Prawn
10
+ module Graphics
11
+ module JoinStyle
12
+ JOIN_STYLES = { :miter => 0, :round => 1, :bevel => 2 }
13
+
14
+ # Sets the join style for stroked lines and curves
15
+ #
16
+ # style is one of :miter, :round, or :bevel
17
+ #
18
+ # NOTE: if this method is never called, :miter will be used for join style
19
+ # throughout the document
20
+ #
21
+ def join_style(style=nil)
22
+ return @join_style || :miter if style.nil?
23
+
24
+ @join_style = style
25
+
26
+ write_stroke_join_style
27
+ end
28
+
29
+ alias_method :join_style=, :join_style
30
+
31
+ private
32
+
33
+ def write_stroke_join_style
34
+ add_content "#{JOIN_STYLES[@join_style]} j"
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,156 @@
1
+ # encoding: utf-8
2
+ #
3
+ # transformation.rb: Implements rotate, translate, skew, scale and a generic
4
+ # transformation_matrix
5
+ #
6
+ # Copyright January 2010, Michael Witrant. All Rights Reserved.
7
+ #
8
+ # This is free software. Please see the LICENSE and COPYING files for details.
9
+
10
+ module Prawn
11
+ module Graphics
12
+ module Transformation
13
+
14
+ # Rotate the user space. If a block is not provided, then you must save
15
+ # and restore the graphics state yourself.
16
+ #
17
+ # == Options
18
+ # <tt>:origin</tt>:: <tt>[number, number]</tt>. The point around which to
19
+ # rotate. A block must be provided if using the :origin
20
+ #
21
+ # raises <tt>Prawn::Errors::BlockRequired</tt> if an :origin option is
22
+ # provided, but no block is given
23
+ #
24
+ # Example without a block:
25
+ #
26
+ # save_graphics_state
27
+ # rotate 30
28
+ # text "rotated text"
29
+ # restore_graphics_state
30
+ #
31
+ # Example with a block: rotating a rectangle around its upper-left corner
32
+ #
33
+ # x = 300
34
+ # y = 300
35
+ # width = 150
36
+ # height = 200
37
+ # angle = 30
38
+ # pdf.rotate(angle, :origin => [x, y]) do
39
+ # pdf.stroke_rectangle([x, y], width, height)
40
+ # end
41
+ #
42
+ def rotate(angle, options={}, &block)
43
+ Prawn.verify_options(:origin, options)
44
+ rad = degree_to_rad(angle)
45
+ cos = Math.cos(rad)
46
+ sin = Math.sin(rad)
47
+ if options[:origin].nil?
48
+ transformation_matrix(cos, sin, -sin, cos, 0, 0, &block)
49
+ else
50
+ raise Prawn::Errors::BlockRequired unless block_given?
51
+ x = options[:origin][0] + bounds.absolute_left
52
+ y = options[:origin][1] + bounds.absolute_bottom
53
+ x_prime = x * cos - y * sin
54
+ y_prime = x * sin + y * cos
55
+ translate(x - x_prime, y - y_prime) do
56
+ transformation_matrix(cos, sin, -sin, cos, 0, 0, &block)
57
+ end
58
+ end
59
+ end
60
+
61
+ # Translate the user space. If a block is not provided, then you must save
62
+ # and restore the graphics state yourself.
63
+ #
64
+ # Example without a block: move the text up and over 10
65
+ #
66
+ # save_graphics_state
67
+ # translate(10, 10)
68
+ # text "scaled text"
69
+ # restore_graphics_state
70
+ #
71
+ # Example with a block: draw a rectangle with its upper-left corner at
72
+ # x + 10, y + 10
73
+ #
74
+ # x = 300
75
+ # y = 300
76
+ # width = 150
77
+ # height = 200
78
+ # pdf.translate(10, 10) do
79
+ # pdf.stroke_rectangle([x, y], width, height)
80
+ # end
81
+ #
82
+ def translate(x, y, &block)
83
+ transformation_matrix(1, 0, 0, 1, x, y, &block)
84
+ end
85
+
86
+ # Scale the user space. If a block is not provided, then you must save
87
+ # and restore the graphics state yourself.
88
+ #
89
+ # == Options
90
+ # <tt>:origin</tt>:: <tt>[number, number]</tt>. The point from which to
91
+ # scale. A block must be provided if using the :origin
92
+ #
93
+ # raises <tt>Prawn::Errors::BlockRequired</tt> if an :origin option is
94
+ # provided, but no block is given
95
+ #
96
+ # Example without a block:
97
+ #
98
+ # save_graphics_state
99
+ # scale 1.5
100
+ # text "scaled text"
101
+ # restore_graphics_state
102
+ #
103
+ # Example with a block: scale a rectangle from its upper-left corner
104
+ #
105
+ # x = 300
106
+ # y = 300
107
+ # width = 150
108
+ # height = 200
109
+ # factor = 1.5
110
+ # pdf.scale(angle, :origin => [x, y]) do
111
+ # pdf.stroke_rectangle([x, y], width, height)
112
+ # end
113
+ #
114
+ def scale(factor, options={}, &block)
115
+ Prawn.verify_options(:origin, options)
116
+ if options[:origin].nil?
117
+ transformation_matrix(factor, 0, 0, factor, 0, 0, &block)
118
+ else
119
+ raise Prawn::Errors::BlockRequired unless block_given?
120
+ x = options[:origin][0] + bounds.absolute_left
121
+ y = options[:origin][1] + bounds.absolute_bottom
122
+ x_prime = factor * x
123
+ y_prime = factor * y
124
+ translate(x - x_prime, y - y_prime) do
125
+ transformation_matrix(factor, 0, 0, factor, 0, 0, &block)
126
+ end
127
+ end
128
+ end
129
+
130
+ # The following definition of skew would only work in a clearly
131
+ # predicatable manner when if the document had no margin. don't provide
132
+ # this shortcut until it behaves in a clearly understood manner
133
+ #
134
+ # def skew(a, b, &block)
135
+ # transformation_matrix(1,
136
+ # Math.tan(degree_to_rad(a)),
137
+ # Math.tan(degree_to_rad(b)),
138
+ # 1, 0, 0, &block)
139
+ # end
140
+
141
+ # Transform the user space (see notes for rotate regarding graphics state)
142
+ # Generally, one would use the rotate, scale, translate, and skew
143
+ # convenience methods instead of calling transformation_matrix directly
144
+ def transformation_matrix(a, b, c, d, e, f)
145
+ values = [a, b, c, d, e, f].map { |x| "%.5f" % x }.join(" ")
146
+ save_graphics_state if block_given?
147
+ add_content "#{values} cm"
148
+ if block_given?
149
+ yield
150
+ restore_graphics_state
151
+ end
152
+ end
153
+
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,99 @@
1
+ # encoding: utf-8
2
+ #
3
+ # transparency.rb : Implements transparency
4
+ #
5
+ # Copyright October 2009, Daniel Nelson. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ #
9
+
10
+ module Prawn
11
+ module Graphics
12
+
13
+ # The Prawn::Transparency module is used to place transparent
14
+ # content on the page. It has the capacity for separate
15
+ # transparency values for stroked content and all other content.
16
+ #
17
+ # Example:
18
+ # # both the fill and stroke will be at 50% opacity
19
+ # pdf.transparent(0.5) do
20
+ # pdf.text("hello world")
21
+ # pdf.fill_and_stroke_circle_at([x, y], :radius => 25)
22
+ # end
23
+ #
24
+ # # the fill will be at 50% opacity, but the stroke will
25
+ # # be at 75% opacity
26
+ # pdf.transparent(0.5, 0.75) do
27
+ # pdf.text("hello world")
28
+ # pdf.fill_and_stroke_circle_at([x, y], :radius => 25)
29
+ # end
30
+ #
31
+ module Transparency
32
+
33
+ # Sets the <tt>opacity</tt> and <tt>stroke_opacity</tt> for all
34
+ # the content within the <tt>block</tt>
35
+ # If <tt>stroke_opacity</tt> is not provided, then it takes on
36
+ # the same value as <tt>opacity</tt>
37
+ #
38
+ # Valid ranges for both paramters are 0.0 to 1.0
39
+ #
40
+ # Example:
41
+ # # both the fill and stroke will be at 50% opacity
42
+ # pdf.transparent(0.5) do
43
+ # pdf.text("hello world")
44
+ # pdf.fill_and_stroke_circle_at([x, y], :radius => 25)
45
+ # end
46
+ #
47
+ # # the fill will be at 50% opacity, but the stroke will
48
+ # # be at 75% opacity
49
+ # pdf.transparent(0.5, 0.75) do
50
+ # pdf.text("hello world")
51
+ # pdf.fill_and_stroke_circle_at([x, y], :radius => 25)
52
+ # end
53
+ #
54
+ def transparent(opacity, stroke_opacity=opacity, &block)
55
+ min_version(1.4)
56
+
57
+ opacity = [[opacity, 0.0].max, 1.0].min
58
+ stroke_opacity = [[stroke_opacity, 0.0].max, 1.0].min
59
+
60
+ save_graphics_state
61
+ add_content "/#{opacity_dictionary_name(opacity, stroke_opacity)} gs"
62
+ yield
63
+ restore_graphics_state
64
+ end
65
+
66
+ private
67
+
68
+ def opacity_dictionary_registry
69
+ @opacity_dictionary_registry ||= {}
70
+ end
71
+
72
+ def next_opacity_dictionary_id
73
+ opacity_dictionary_registry.length + 1
74
+ end
75
+
76
+ def opacity_dictionary_name(opacity, stroke_opacity)
77
+ key = "#{opacity}_#{stroke_opacity}"
78
+
79
+ if opacity_dictionary_registry[key]
80
+ dictionary = opacity_dictionary_registry[key][:obj]
81
+ dictionary_name = opacity_dictionary_registry[key][:name]
82
+ else
83
+ dictionary = ref!(:Type => :ExtGState,
84
+ :CA => stroke_opacity,
85
+ :ca => opacity
86
+ )
87
+
88
+ dictionary_name = "Tr#{next_opacity_dictionary_id}"
89
+ opacity_dictionary_registry[key] = { :name => dictionary_name,
90
+ :obj => dictionary }
91
+ end
92
+
93
+ page.ext_gstates.merge!(dictionary_name => dictionary)
94
+ dictionary_name
95
+ end
96
+
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,348 @@
1
+ # encoding: ASCII-8BIT
2
+ # images.rb : Implements PDF image embedding
3
+ #
4
+ # Copyright April 2008, James Healy, Gregory Brown. All Rights Reserved.
5
+ #
6
+ # This is free software. Please see the LICENSE and COPYING files for details.
7
+
8
+ require 'digest/sha1'
9
+
10
+ module Prawn
11
+
12
+ module Images
13
+
14
+ # Add the image at filename to the current page. Currently only
15
+ # JPG and PNG files are supported.
16
+ #
17
+ # NOTE: Prawn is very slow at rendering PNGs with alpha channels. The
18
+ # workaround for those who don't mind installing RMagick is to use:
19
+ #
20
+ # http://github.com/amberbit/prawn-fast-png
21
+ #
22
+ # Arguments:
23
+ # <tt>file</tt>:: path to file or an object that responds to #read
24
+ #
25
+ # Options:
26
+ # <tt>:at</tt>:: an array [x,y] with the location of the top left corner of the image.
27
+ # <tt>:position</tt>:: One of (:left, :center, :right) or an x-offset
28
+ # <tt>:vposition</tt>:: One of (:top, :center, :center) or an y-offset
29
+ # <tt>:height</tt>:: the height of the image [actual height of the image]
30
+ # <tt>:width</tt>:: the width of the image [actual width of the image]
31
+ # <tt>:scale</tt>:: scale the dimensions of the image proportionally
32
+ # <tt>:fit</tt>:: scale the dimensions of the image proportionally to fit inside [width,height]
33
+ #
34
+ # Prawn::Document.generate("image2.pdf", :page_layout => :landscape) do
35
+ # pigs = "#{Prawn::BASEDIR}/data/images/pigs.jpg"
36
+ # image pigs, :at => [50,450], :width => 450
37
+ #
38
+ # dice = "#{Prawn::BASEDIR}/data/images/dice.png"
39
+ # image dice, :at => [50, 450], :scale => 0.75
40
+ # end
41
+ #
42
+ # If only one of :width / :height are provided, the image will be scaled
43
+ # proportionally. When both are provided, the image will be stretched to
44
+ # fit the dimensions without maintaining the aspect ratio.
45
+ #
46
+ #
47
+ # If :at is provided, the image will be place in the current page but
48
+ # the text position will not be changed.
49
+ #
50
+ #
51
+ # If instead of an explicit filename, an object with a read method is
52
+ # passed as +file+, you can embed images from IO objects and things
53
+ # that act like them (including Tempfiles and open-uri objects).
54
+ #
55
+ # require "open-uri"
56
+ #
57
+ # Prawn::Document.generate("remote_images.pdf") do
58
+ # image open("http://prawn.majesticseacreature.com/media/prawn_logo.png")
59
+ # end
60
+ #
61
+ # This method returns an image info object which can be used to check the
62
+ # dimensions of an image object if needed.
63
+ # (See also: Prawn::Images::PNG , Prawn::Images::JPG)
64
+ #
65
+ def image(file, options={})
66
+ Prawn.verify_options [:at, :position, :vposition, :height,
67
+ :width, :scale, :fit], options
68
+
69
+ if file.respond_to?(:read)
70
+ image_content = file.read
71
+ else
72
+ raise ArgumentError, "#{file} not found" unless File.file?(file)
73
+ image_content = File.binread(file)
74
+ end
75
+
76
+ image_sha1 = Digest::SHA1.hexdigest(image_content)
77
+
78
+ # if this image has already been embedded, just reuse it
79
+ if image_registry[image_sha1]
80
+ info = image_registry[image_sha1][:info]
81
+ image_obj = image_registry[image_sha1][:obj]
82
+ else
83
+ # build the image object and embed the raw data
84
+ image_obj = case detect_image_format(image_content)
85
+ when :jpg then
86
+ info = Prawn::Images::JPG.new(image_content)
87
+ build_jpg_object(image_content, info)
88
+ when :png then
89
+ info = Prawn::Images::PNG.new(image_content)
90
+
91
+ # 16-bit color only supported in 1.5+ (ISO 32000-1:2008 8.9.5.1)
92
+ if info.bits > 8
93
+ min_version 1.5
94
+ end
95
+
96
+ build_png_object(image_content, info)
97
+ end
98
+ image_registry[image_sha1] = {:obj => image_obj, :info => info}
99
+ end
100
+
101
+ # find where the image will be placed and how big it will be
102
+ w,h = calc_image_dimensions(info, options)
103
+
104
+ if options[:at]
105
+ x,y = map_to_absolute(options[:at])
106
+ else
107
+ x,y = image_position(w,h,options)
108
+ move_text_position h
109
+ end
110
+
111
+ # add a reference to the image object to the current page
112
+ # resource list and give it a label
113
+ label = "I#{next_image_id}"
114
+ state.page.xobjects.merge!( label => image_obj )
115
+
116
+ # add the image to the current page
117
+ instruct = "\nq\n%.3f 0 0 %.3f %.3f %.3f cm\n/%s Do\nQ"
118
+ add_content instruct % [ w, h, x, y - h, label ]
119
+
120
+ return info
121
+ end
122
+
123
+ private
124
+
125
+ def image_position(w,h,options)
126
+ options[:position] ||= :left
127
+
128
+ x = case options[:position]
129
+ when :left
130
+ bounds.absolute_left
131
+ when :center
132
+ bounds.absolute_left + (bounds.width - w) / 2.0
133
+ when :right
134
+ bounds.absolute_right - w
135
+ when Numeric
136
+ options[:position] + bounds.absolute_left
137
+ end
138
+
139
+ y = case options[:vposition]
140
+ when :top
141
+ bounds.absolute_top
142
+ when :center
143
+ bounds.absolute_top - (bounds.height - h) / 2.0
144
+ when :bottom
145
+ bounds.absolute_bottom + h
146
+ when Numeric
147
+ bounds.absolute_top - options[:vposition]
148
+ else
149
+ self.y
150
+ end
151
+ return [x,y]
152
+ end
153
+
154
+ def build_jpg_object(data, jpg)
155
+ color_space = case jpg.channels
156
+ when 1
157
+ :DeviceGray
158
+ when 3
159
+ :DeviceRGB
160
+ when 4
161
+ :DeviceCMYK
162
+ else
163
+ raise ArgumentError, 'JPG uses an unsupported number of channels'
164
+ end
165
+ obj = ref!(:Type => :XObject,
166
+ :Subtype => :Image,
167
+ :Filter => :DCTDecode,
168
+ :ColorSpace => color_space,
169
+ :BitsPerComponent => jpg.bits,
170
+ :Width => jpg.width,
171
+ :Height => jpg.height,
172
+ :Length => data.size )
173
+
174
+ # add extra decode params for CMYK images. By swapping the
175
+ # min and max values from the default, we invert the colours. See
176
+ # section 4.8.4 of the spec.
177
+ if color_space == :DeviceCMYK
178
+ obj.data[:Decode] = [ 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0 ]
179
+ end
180
+
181
+ obj << data
182
+ return obj
183
+ end
184
+
185
+ def build_png_object(data, png)
186
+
187
+ if png.compression_method != 0
188
+ raise Errors::UnsupportedImageType, 'PNG uses an unsupported compression method'
189
+ end
190
+
191
+ if png.filter_method != 0
192
+ raise Errors::UnsupportedImageType, 'PNG uses an unsupported filter method'
193
+ end
194
+
195
+ if png.interlace_method != 0
196
+ raise Errors::UnsupportedImageType, 'PNG uses unsupported interlace method'
197
+ end
198
+
199
+ # some PNG types store the colour and alpha channel data together,
200
+ # which the PDF spec doesn't like, so split it out.
201
+ png.split_alpha_channel!
202
+
203
+ case png.colors
204
+ when 1
205
+ color = :DeviceGray
206
+ when 3
207
+ color = :DeviceRGB
208
+ else
209
+ raise Errors::UnsupportedImageType, "PNG uses an unsupported number of colors (#{png.colors})"
210
+ end
211
+
212
+ # build the image dict
213
+ obj = ref!(:Type => :XObject,
214
+ :Subtype => :Image,
215
+ :Height => png.height,
216
+ :Width => png.width,
217
+ :BitsPerComponent => png.bits,
218
+ :Length => png.img_data.size,
219
+ :Filter => :FlateDecode
220
+ )
221
+
222
+ unless png.alpha_channel
223
+ obj.data[:DecodeParms] = {:Predictor => 15,
224
+ :Colors => png.colors,
225
+ :BitsPerComponent => png.bits,
226
+ :Columns => png.width}
227
+ end
228
+
229
+ # append the actual image data to the object as a stream
230
+ obj << png.img_data
231
+
232
+ # sort out the colours of the image
233
+ if png.palette.empty?
234
+ obj.data[:ColorSpace] = color
235
+ else
236
+ # embed the colour palette in the PDF as a object stream
237
+ palette_obj = ref!(:Length => png.palette.size)
238
+ palette_obj << png.palette
239
+
240
+ # build the color space array for the image
241
+ obj.data[:ColorSpace] = [:Indexed,
242
+ :DeviceRGB,
243
+ (png.palette.size / 3) -1,
244
+ palette_obj]
245
+ end
246
+
247
+ # *************************************
248
+ # add transparency data if necessary
249
+ # *************************************
250
+
251
+ # For PNG color types 0, 2 and 3, the transparency data is stored in
252
+ # a dedicated PNG chunk, and is exposed via the transparency attribute
253
+ # of the PNG class.
254
+ if png.transparency[:grayscale]
255
+ # Use Color Key Masking (spec section 4.8.5)
256
+ # - An array with N elements, where N is two times the number of color
257
+ # components.
258
+ val = png.transparency[:grayscale]
259
+ obj.data[:Mask] = [val, val]
260
+ elsif png.transparency[:rgb]
261
+ # Use Color Key Masking (spec section 4.8.5)
262
+ # - An array with N elements, where N is two times the number of color
263
+ # components.
264
+ rgb = png.transparency[:rgb]
265
+ obj.data[:Mask] = rgb.collect { |x| [x,x] }.flatten
266
+ elsif png.transparency[:indexed]
267
+ # TODO: broken. I was attempting to us Color Key Masking, but I think
268
+ # we need to construct an SMask i think. Maybe do it inside
269
+ # the PNG class, and store it in alpha_channel
270
+ #obj.data[:Mask] = png.transparency[:indexed]
271
+ end
272
+
273
+ # For PNG color types 4 and 6, the transparency data is stored as a alpha
274
+ # channel mixed in with the main image data. The PNG class seperates
275
+ # it out for us and makes it available via the alpha_channel attribute
276
+ if png.alpha_channel
277
+ min_version 1.4
278
+ smask_obj = ref!(:Type => :XObject,
279
+ :Subtype => :Image,
280
+ :Height => png.height,
281
+ :Width => png.width,
282
+ :BitsPerComponent => png.alpha_channel_bits,
283
+ :Length => png.alpha_channel.size,
284
+ :Filter => :FlateDecode,
285
+ :ColorSpace => :DeviceGray,
286
+ :Decode => [0, 1]
287
+ )
288
+ smask_obj << png.alpha_channel
289
+ obj.data[:SMask] = smask_obj
290
+ end
291
+
292
+ return obj
293
+ end
294
+
295
+ def calc_image_dimensions(info, options)
296
+ w = options[:width] || info.width
297
+ h = options[:height] || info.height
298
+
299
+ if options[:width] && !options[:height]
300
+ wp = w / info.width.to_f
301
+ w = info.width * wp
302
+ h = info.height * wp
303
+ elsif options[:height] && !options[:width]
304
+ hp = h / info.height.to_f
305
+ w = info.width * hp
306
+ h = info.height * hp
307
+ elsif options[:scale]
308
+ w = info.width * options[:scale]
309
+ h = info.height * options[:scale]
310
+ elsif options[:fit]
311
+ bw, bh = options[:fit]
312
+ bp = bw / bh.to_f
313
+ ip = info.width / info.height.to_f
314
+ if ip > bp
315
+ w = bw
316
+ h = bw / ip
317
+ else
318
+ h = bh
319
+ w = bh * ip
320
+ end
321
+ end
322
+ info.scaled_width = w
323
+ info.scaled_height = h
324
+ [w,h]
325
+ end
326
+
327
+ def detect_image_format(content)
328
+ top = content[0,128]
329
+
330
+ if top[0, 3] == "\xff\xd8\xff"
331
+ return :jpg
332
+ elsif top[0, 8] == "\x89PNG\x0d\x0a\x1a\x0a"
333
+ return :png
334
+ else
335
+ raise Errors::UnsupportedImageType, "image file is an unrecognised format"
336
+ end
337
+ end
338
+
339
+ def image_registry
340
+ @image_registry ||= {}
341
+ end
342
+
343
+ def next_image_id
344
+ @image_counter ||= 0
345
+ @image_counter += 1
346
+ end
347
+ end
348
+ end