prawn-git 2.0.1

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 (252) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +10 -0
  3. data/COPYING +2 -0
  4. data/GPLv2 +340 -0
  5. data/GPLv3 +674 -0
  6. data/Gemfile +11 -0
  7. data/LICENSE +56 -0
  8. data/Rakefile +55 -0
  9. data/data/fonts/Courier-Bold.afm +342 -0
  10. data/data/fonts/Courier-BoldOblique.afm +342 -0
  11. data/data/fonts/Courier-Oblique.afm +342 -0
  12. data/data/fonts/Courier.afm +342 -0
  13. data/data/fonts/Helvetica-Bold.afm +2827 -0
  14. data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
  15. data/data/fonts/Helvetica-Oblique.afm +3051 -0
  16. data/data/fonts/Helvetica.afm +3051 -0
  17. data/data/fonts/MustRead.html +19 -0
  18. data/data/fonts/Symbol.afm +213 -0
  19. data/data/fonts/Times-Bold.afm +2588 -0
  20. data/data/fonts/Times-BoldItalic.afm +2384 -0
  21. data/data/fonts/Times-Italic.afm +2667 -0
  22. data/data/fonts/Times-Roman.afm +2419 -0
  23. data/data/fonts/ZapfDingbats.afm +225 -0
  24. data/data/images/16bit.alpha +0 -0
  25. data/data/images/16bit.color +0 -0
  26. data/data/images/16bit.png +0 -0
  27. data/data/images/arrow.png +0 -0
  28. data/data/images/arrow2.png +0 -0
  29. data/data/images/dice.alpha +0 -0
  30. data/data/images/dice.color +0 -0
  31. data/data/images/dice.png +0 -0
  32. data/data/images/dice_interlaced.png +0 -0
  33. data/data/images/fractal.jpg +0 -0
  34. data/data/images/indexed_color.dat +0 -0
  35. data/data/images/indexed_color.png +0 -0
  36. data/data/images/letterhead.jpg +0 -0
  37. data/data/images/license.md +8 -0
  38. data/data/images/page_white_text.alpha +0 -0
  39. data/data/images/page_white_text.color +0 -0
  40. data/data/images/page_white_text.png +0 -0
  41. data/data/images/pal_bk.png +0 -0
  42. data/data/images/pigs.jpg +0 -0
  43. data/data/images/prawn.png +0 -0
  44. data/data/images/ruport.png +0 -0
  45. data/data/images/ruport_data.dat +0 -0
  46. data/data/images/ruport_transparent.png +0 -0
  47. data/data/images/ruport_type0.png +0 -0
  48. data/data/images/stef.jpg +0 -0
  49. data/data/images/tru256.bmp +0 -0
  50. data/data/images/web-links.dat +1 -0
  51. data/data/images/web-links.png +0 -0
  52. data/data/pdfs/complex_template.pdf +0 -0
  53. data/data/pdfs/contains_ttf_font.pdf +0 -0
  54. data/data/pdfs/encrypted.pdf +0 -0
  55. data/data/pdfs/form.pdf +820 -0
  56. data/data/pdfs/hexagon.pdf +61 -0
  57. data/data/pdfs/indirect_reference.pdf +86 -0
  58. data/data/pdfs/multipage_template.pdf +127 -0
  59. data/data/pdfs/nested_pages.pdf +118 -0
  60. data/data/pdfs/page_without_mediabox.pdf +193 -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/lib/prawn.rb +89 -0
  66. data/lib/prawn/document.rb +706 -0
  67. data/lib/prawn/document/bounding_box.rb +539 -0
  68. data/lib/prawn/document/column_box.rb +144 -0
  69. data/lib/prawn/document/internals.rb +58 -0
  70. data/lib/prawn/document/span.rb +57 -0
  71. data/lib/prawn/encoding.rb +87 -0
  72. data/lib/prawn/errors.rb +80 -0
  73. data/lib/prawn/font.rb +413 -0
  74. data/lib/prawn/font/afm.rb +256 -0
  75. data/lib/prawn/font/dfont.rb +43 -0
  76. data/lib/prawn/font/ttf.rb +355 -0
  77. data/lib/prawn/font_metric_cache.rb +46 -0
  78. data/lib/prawn/graphics.rb +646 -0
  79. data/lib/prawn/graphics/cap_style.rb +47 -0
  80. data/lib/prawn/graphics/color.rb +232 -0
  81. data/lib/prawn/graphics/dash.rb +109 -0
  82. data/lib/prawn/graphics/join_style.rb +49 -0
  83. data/lib/prawn/graphics/patterns.rb +126 -0
  84. data/lib/prawn/graphics/transformation.rb +157 -0
  85. data/lib/prawn/graphics/transparency.rb +101 -0
  86. data/lib/prawn/grid.rb +279 -0
  87. data/lib/prawn/image_handler.rb +44 -0
  88. data/lib/prawn/images.rb +199 -0
  89. data/lib/prawn/images/image.rb +49 -0
  90. data/lib/prawn/images/jpg.rb +91 -0
  91. data/lib/prawn/images/png.rb +290 -0
  92. data/lib/prawn/measurement_extensions.rb +50 -0
  93. data/lib/prawn/measurements.rb +77 -0
  94. data/lib/prawn/outline.rb +289 -0
  95. data/lib/prawn/repeater.rb +124 -0
  96. data/lib/prawn/security.rb +288 -0
  97. data/lib/prawn/security/arcfour.rb +54 -0
  98. data/lib/prawn/soft_mask.rb +94 -0
  99. data/lib/prawn/stamp.rb +136 -0
  100. data/lib/prawn/text.rb +437 -0
  101. data/lib/prawn/text/box.rb +141 -0
  102. data/lib/prawn/text/formatted.rb +7 -0
  103. data/lib/prawn/text/formatted/arranger.rb +290 -0
  104. data/lib/prawn/text/formatted/box.rb +614 -0
  105. data/lib/prawn/text/formatted/fragment.rb +264 -0
  106. data/lib/prawn/text/formatted/line_wrap.rb +277 -0
  107. data/lib/prawn/text/formatted/parser.rb +224 -0
  108. data/lib/prawn/text/formatted/wrap.rb +160 -0
  109. data/lib/prawn/utilities.rb +46 -0
  110. data/lib/prawn/version.rb +5 -0
  111. data/lib/prawn/view.rb +91 -0
  112. data/manual/absolute_position.pdf +0 -0
  113. data/manual/basic_concepts/adding_pages.rb +27 -0
  114. data/manual/basic_concepts/basic_concepts.rb +36 -0
  115. data/manual/basic_concepts/creation.rb +39 -0
  116. data/manual/basic_concepts/cursor.rb +33 -0
  117. data/manual/basic_concepts/measurement.rb +25 -0
  118. data/manual/basic_concepts/origin.rb +38 -0
  119. data/manual/basic_concepts/other_cursor_helpers.rb +40 -0
  120. data/manual/basic_concepts/view.rb +42 -0
  121. data/manual/bounding_box/bounding_box.rb +39 -0
  122. data/manual/bounding_box/bounds.rb +49 -0
  123. data/manual/bounding_box/canvas.rb +24 -0
  124. data/manual/bounding_box/creation.rb +23 -0
  125. data/manual/bounding_box/indentation.rb +46 -0
  126. data/manual/bounding_box/nesting.rb +45 -0
  127. data/manual/bounding_box/russian_boxes.rb +40 -0
  128. data/manual/bounding_box/stretchy.rb +31 -0
  129. data/manual/contents.rb +29 -0
  130. data/manual/cover.rb +39 -0
  131. data/manual/document_and_page_options/background.rb +27 -0
  132. data/manual/document_and_page_options/document_and_page_options.rb +32 -0
  133. data/manual/document_and_page_options/metadata.rb +23 -0
  134. data/manual/document_and_page_options/page_margins.rb +38 -0
  135. data/manual/document_and_page_options/page_size.rb +34 -0
  136. data/manual/document_and_page_options/print_scaling.rb +20 -0
  137. data/manual/example_helper.rb +7 -0
  138. data/manual/graphics/circle_and_ellipse.rb +22 -0
  139. data/manual/graphics/color.rb +24 -0
  140. data/manual/graphics/common_lines.rb +30 -0
  141. data/manual/graphics/fill_and_stroke.rb +42 -0
  142. data/manual/graphics/fill_rules.rb +37 -0
  143. data/manual/graphics/gradients.rb +37 -0
  144. data/manual/graphics/graphics.rb +58 -0
  145. data/manual/graphics/helper.rb +24 -0
  146. data/manual/graphics/line_width.rb +35 -0
  147. data/manual/graphics/lines_and_curves.rb +41 -0
  148. data/manual/graphics/polygon.rb +29 -0
  149. data/manual/graphics/rectangle.rb +21 -0
  150. data/manual/graphics/rotate.rb +28 -0
  151. data/manual/graphics/scale.rb +41 -0
  152. data/manual/graphics/soft_masks.rb +46 -0
  153. data/manual/graphics/stroke_cap.rb +31 -0
  154. data/manual/graphics/stroke_dash.rb +48 -0
  155. data/manual/graphics/stroke_join.rb +30 -0
  156. data/manual/graphics/translate.rb +29 -0
  157. data/manual/graphics/transparency.rb +35 -0
  158. data/manual/how_to_read_this_manual.rb +40 -0
  159. data/manual/images/absolute_position.rb +23 -0
  160. data/manual/images/fit.rb +21 -0
  161. data/manual/images/horizontal.rb +25 -0
  162. data/manual/images/images.rb +40 -0
  163. data/manual/images/plain_image.rb +18 -0
  164. data/manual/images/scale.rb +22 -0
  165. data/manual/images/vertical.rb +28 -0
  166. data/manual/images/width_and_height.rb +25 -0
  167. data/manual/layout/boxes.rb +27 -0
  168. data/manual/layout/content.rb +25 -0
  169. data/manual/layout/layout.rb +28 -0
  170. data/manual/layout/simple_grid.rb +23 -0
  171. data/manual/outline/add_subsection_to.rb +61 -0
  172. data/manual/outline/insert_section_after.rb +47 -0
  173. data/manual/outline/outline.rb +32 -0
  174. data/manual/outline/sections_and_pages.rb +67 -0
  175. data/manual/repeatable_content/alternate_page_numbering.rb +32 -0
  176. data/manual/repeatable_content/page_numbering.rb +54 -0
  177. data/manual/repeatable_content/repeatable_content.rb +32 -0
  178. data/manual/repeatable_content/repeater.rb +55 -0
  179. data/manual/repeatable_content/stamp.rb +41 -0
  180. data/manual/security/encryption.rb +31 -0
  181. data/manual/security/permissions.rb +38 -0
  182. data/manual/security/security.rb +28 -0
  183. data/manual/table.rb +16 -0
  184. data/manual/text/alignment.rb +44 -0
  185. data/manual/text/color.rb +24 -0
  186. data/manual/text/column_box.rb +32 -0
  187. data/manual/text/fallback_fonts.rb +37 -0
  188. data/manual/text/font.rb +41 -0
  189. data/manual/text/font_size.rb +45 -0
  190. data/manual/text/font_style.rb +23 -0
  191. data/manual/text/formatted_callbacks.rb +60 -0
  192. data/manual/text/formatted_text.rb +50 -0
  193. data/manual/text/free_flowing_text.rb +51 -0
  194. data/manual/text/inline.rb +41 -0
  195. data/manual/text/kerning_and_character_spacing.rb +39 -0
  196. data/manual/text/leading.rb +25 -0
  197. data/manual/text/line_wrapping.rb +41 -0
  198. data/manual/text/paragraph_indentation.rb +34 -0
  199. data/manual/text/positioned_text.rb +38 -0
  200. data/manual/text/registering_families.rb +48 -0
  201. data/manual/text/rendering_and_color.rb +37 -0
  202. data/manual/text/right_to_left_text.rb +47 -0
  203. data/manual/text/rotation.rb +43 -0
  204. data/manual/text/single_usage.rb +37 -0
  205. data/manual/text/text.rb +73 -0
  206. data/manual/text/text_box_excess.rb +32 -0
  207. data/manual/text/text_box_extensions.rb +45 -0
  208. data/manual/text/text_box_overflow.rb +48 -0
  209. data/manual/text/utf8.rb +28 -0
  210. data/manual/text/win_ansi_charset.rb +60 -0
  211. data/prawn.gemspec +45 -0
  212. data/spec/acceptance/png.rb +25 -0
  213. data/spec/annotations_spec.rb +74 -0
  214. data/spec/bounding_box_spec.rb +510 -0
  215. data/spec/column_box_spec.rb +65 -0
  216. data/spec/data/curves.pdf +66 -0
  217. data/spec/destinations_spec.rb +15 -0
  218. data/spec/document_spec.rb +748 -0
  219. data/spec/extensions/encoding_helpers.rb +11 -0
  220. data/spec/extensions/mocha.rb +46 -0
  221. data/spec/font_metric_cache_spec.rb +52 -0
  222. data/spec/font_spec.rb +474 -0
  223. data/spec/formatted_text_arranger_spec.rb +421 -0
  224. data/spec/formatted_text_box_spec.rb +705 -0
  225. data/spec/formatted_text_fragment_spec.rb +298 -0
  226. data/spec/graphics_spec.rb +683 -0
  227. data/spec/grid_spec.rb +96 -0
  228. data/spec/image_handler_spec.rb +54 -0
  229. data/spec/images_spec.rb +153 -0
  230. data/spec/inline_formatted_text_parser_spec.rb +564 -0
  231. data/spec/jpg_spec.rb +25 -0
  232. data/spec/line_wrap_spec.rb +367 -0
  233. data/spec/measurement_units_spec.rb +25 -0
  234. data/spec/outline_spec.rb +430 -0
  235. data/spec/png_spec.rb +245 -0
  236. data/spec/reference_spec.rb +25 -0
  237. data/spec/repeater_spec.rb +160 -0
  238. data/spec/security_spec.rb +158 -0
  239. data/spec/soft_mask_spec.rb +79 -0
  240. data/spec/span_spec.rb +44 -0
  241. data/spec/spec_helper.rb +54 -0
  242. data/spec/stamp_spec.rb +160 -0
  243. data/spec/stroke_styles_spec.rb +211 -0
  244. data/spec/text_at_spec.rb +143 -0
  245. data/spec/text_box_spec.rb +1043 -0
  246. data/spec/text_rendering_mode_spec.rb +45 -0
  247. data/spec/text_spacing_spec.rb +93 -0
  248. data/spec/text_spec.rb +557 -0
  249. data/spec/text_with_inline_formatting_spec.rb +35 -0
  250. data/spec/transparency_spec.rb +91 -0
  251. data/spec/view_spec.rb +43 -0
  252. metadata +509 -0
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+
3
+ # ImageHandler provides a way to register image processors with Prawn
4
+ #
5
+ # Contributed by Evan Sharp in November 2013.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+
9
+ module Prawn
10
+ # @group Extension API
11
+
12
+ def self.image_handler
13
+ @image_handler ||= ImageHandler.new
14
+ end
15
+
16
+ class ImageHandler
17
+ def initialize
18
+ @handlers = []
19
+ end
20
+
21
+ def register(handler)
22
+ @handlers.delete(handler)
23
+ @handlers.push handler
24
+ end
25
+
26
+ def register!(handler)
27
+ @handlers.delete(handler)
28
+ @handlers.unshift handler
29
+ end
30
+
31
+ def unregister(handler)
32
+ @handlers.reject!{ |h| h == handler }
33
+ end
34
+
35
+ def find(image_blob)
36
+ handler = @handlers.find{ |h| h.can_render? image_blob }
37
+
38
+ return handler if handler
39
+
40
+ raise Prawn::Errors::UnsupportedImageType,
41
+ "image file is an unrecognised format"
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,199 @@
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
+ require 'pathname'
10
+
11
+ module Prawn
12
+
13
+ module Images
14
+ # @group Stable API
15
+
16
+ # Add the image at filename to the current page. Currently only
17
+ # JPG and PNG files are supported. (Note that processing PNG
18
+ # images with alpha channels can be processor and memory intensive.)
19
+ #
20
+ # Arguments:
21
+ # <tt>file</tt>:: path to file or an object that responds to #read and #rewind
22
+ #
23
+ # Options:
24
+ # <tt>:at</tt>:: an array [x,y] with the location of the top left corner of the image.
25
+ # <tt>:position</tt>:: One of (:left, :center, :right) or an x-offset
26
+ # <tt>:vposition</tt>:: One of (:top, :center, :center) or an y-offset
27
+ # <tt>:height</tt>:: the height of the image [actual height of the image]
28
+ # <tt>:width</tt>:: the width of the image [actual width of the image]
29
+ # <tt>:scale</tt>:: scale the dimensions of the image proportionally
30
+ # <tt>:fit</tt>:: scale the dimensions of the image proportionally to fit inside [width,height]
31
+ #
32
+ # Prawn::Document.generate("image2.pdf", :page_layout => :landscape) do
33
+ # pigs = "#{Prawn::DATADIR}/images/pigs.jpg"
34
+ # image pigs, :at => [50,450], :width => 450
35
+ #
36
+ # dice = "#{Prawn::DATADIR}/images/dice.png"
37
+ # image dice, :at => [50, 450], :scale => 0.75
38
+ # end
39
+ #
40
+ # If only one of :width / :height are provided, the image will be scaled
41
+ # proportionally. When both are provided, the image will be stretched to
42
+ # fit the dimensions without maintaining the aspect ratio.
43
+ #
44
+ #
45
+ # If :at is provided, the image will be place in the current page but
46
+ # the text position will not be changed.
47
+ #
48
+ #
49
+ # If instead of an explicit filename, an object with a read method is
50
+ # passed as +file+, you can embed images from IO objects and things
51
+ # that act like them (including Tempfiles and open-uri objects).
52
+ #
53
+ # require "open-uri"
54
+ #
55
+ # Prawn::Document.generate("remote_images.pdf") do
56
+ # image open("http://prawn.majesticseacreature.com/media/prawn_logo.png")
57
+ # end
58
+ #
59
+ # This method returns an image info object which can be used to check the
60
+ # dimensions of an image object if needed.
61
+ # (See also: Prawn::Images::PNG , Prawn::Images::JPG)
62
+ #
63
+ def image(file, options={})
64
+ Prawn.verify_options [:at, :position, :vposition, :height,
65
+ :width, :scale, :fit], options
66
+
67
+ pdf_obj, info = build_image_object(file)
68
+ embed_image(pdf_obj, info, options)
69
+
70
+ info
71
+ end
72
+
73
+
74
+ # Builds an info object (Prawn::Images::*) and a PDF reference representing
75
+ # the given image. Return a pair: [pdf_obj, info].
76
+ #
77
+ # @private
78
+ def build_image_object(file)
79
+ io = verify_and_open_image(file)
80
+ image_content = io.read
81
+ image_sha1 = Digest::SHA1.hexdigest(image_content)
82
+
83
+ # if this image has already been embedded, just reuse it
84
+ if image_registry[image_sha1]
85
+ info = image_registry[image_sha1][:info]
86
+ image_obj = image_registry[image_sha1][:obj]
87
+ else
88
+ # Build the image object
89
+ info = Prawn.image_handler.find(image_content).new(image_content)
90
+
91
+ # Bump PDF version if the image requires it
92
+ renderer.min_version(info.min_pdf_version) if info.respond_to?(:min_pdf_version)
93
+
94
+ # Add the image to the PDF and register it in case we see it again.
95
+ image_obj = info.build_pdf_object(self)
96
+ image_registry[image_sha1] = {:obj => image_obj, :info => info}
97
+ end
98
+
99
+ [image_obj, info]
100
+ end
101
+
102
+ # Given a PDF image resource <tt>pdf_obj</tt> that has been added to the
103
+ # page's resources and an <tt>info</tt> object (the pair returned from
104
+ # build_image_object), embed the image according to the <tt>options</tt>
105
+ # given.
106
+ #
107
+ # @private
108
+ def embed_image(pdf_obj, info, options)
109
+ # find where the image will be placed and how big it will be
110
+ w,h = info.calc_image_dimensions(options)
111
+
112
+ if options[:at]
113
+ x,y = map_to_absolute(options[:at])
114
+ else
115
+ x,y = image_position(w,h,options)
116
+ move_text_position h
117
+ end
118
+
119
+ # add a reference to the image object to the current page
120
+ # resource list and give it a label
121
+ label = "I#{next_image_id}"
122
+ state.page.xobjects.merge!(label => pdf_obj)
123
+
124
+ cm_params = PDF::Core.real_params([ w, 0, 0, h, x, y - h])
125
+ renderer.add_content("\nq\n#{cm_params} cm\n/#{label} Do\nQ")
126
+ end
127
+
128
+ private
129
+
130
+ def verify_and_open_image(io_or_path)
131
+ # File or IO
132
+ if io_or_path.respond_to?(:rewind)
133
+ io = io_or_path
134
+ # Rewind if the object we're passed is an IO, so that multiple embeds of
135
+ # the same IO object will work
136
+ io.rewind
137
+ # read the file as binary so the size is calculated correctly
138
+ # guard binmode because some objects acting io-like don't implement it
139
+ io.binmode if io.respond_to?(:binmode)
140
+ return io
141
+ end
142
+ # String or Pathname
143
+ io_or_path = Pathname.new(io_or_path)
144
+ raise ArgumentError, "#{io_or_path} not found" unless io_or_path.file?
145
+ io = io_or_path.open('rb')
146
+ io
147
+ end
148
+
149
+ def image_position(w,h,options)
150
+ options[:position] ||= :left
151
+
152
+ y = case options[:vposition]
153
+ when :top
154
+ bounds.absolute_top
155
+ when :center
156
+ bounds.absolute_top - (bounds.height - h) / 2.0
157
+ when :bottom
158
+ bounds.absolute_bottom + h
159
+ when Numeric
160
+ bounds.absolute_top - options[:vposition]
161
+ else
162
+ determine_y_with_page_flow(h)
163
+ end
164
+
165
+ x = case options[:position]
166
+ when :left
167
+ bounds.left_side
168
+ when :center
169
+ bounds.left_side + (bounds.width - w) / 2.0
170
+ when :right
171
+ bounds.right_side - w
172
+ when Numeric
173
+ options[:position] + bounds.left_side
174
+ end
175
+
176
+ return [x,y]
177
+ end
178
+
179
+ def determine_y_with_page_flow(h)
180
+ if overruns_page?(h)
181
+ bounds.move_past_bottom
182
+ end
183
+ self.y
184
+ end
185
+
186
+ def overruns_page?(h)
187
+ (self.y - h) < reference_bounds.absolute_bottom
188
+ end
189
+
190
+ def image_registry
191
+ @image_registry ||= {}
192
+ end
193
+
194
+ def next_image_id
195
+ @image_counter ||= 0
196
+ @image_counter += 1
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+ # image.rb : Base class for image info objects
3
+ #
4
+ # Copyright September 2011, Brad Ediger. 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
+ module Images
12
+ class Image
13
+ # @group Extension API
14
+
15
+ def calc_image_dimensions(options)
16
+ w = options[:width] || width
17
+ h = options[:height] || height
18
+
19
+ if options[:width] && !options[:height]
20
+ wp = w / width.to_f
21
+ w = width * wp
22
+ h = height * wp
23
+ elsif options[:height] && !options[:width]
24
+ hp = h / height.to_f
25
+ w = width * hp
26
+ h = height * hp
27
+ elsif options[:scale]
28
+ w = width * options[:scale]
29
+ h = height * options[:scale]
30
+ elsif options[:fit]
31
+ bw, bh = options[:fit]
32
+ bp = bw / bh.to_f
33
+ ip = width / height.to_f
34
+ if ip > bp
35
+ w = bw
36
+ h = bw / ip
37
+ else
38
+ h = bh
39
+ w = bh * ip
40
+ end
41
+ end
42
+ self.scaled_width = w
43
+ self.scaled_height = h
44
+
45
+ [w,h]
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,91 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ # jpg.rb : Extracts the data from a JPG that is needed for embedding
4
+ #
5
+ # Copyright April 2008, James Healy. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+
9
+ require 'stringio'
10
+
11
+ module Prawn
12
+ module Images
13
+
14
+ # A convenience class that wraps the logic for extracting the parts
15
+ # of a JPG image that we need to embed them in a PDF
16
+ #
17
+ class JPG < Image
18
+ # @group Extension API
19
+
20
+ attr_reader :width, :height, :bits, :channels
21
+ attr_accessor :scaled_width, :scaled_height
22
+
23
+ JPEG_SOF_BLOCKS = [0xC0, 0xC1, 0xC2, 0xC3, 0xC5, 0xC6, 0xC7, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF]
24
+
25
+ def self.can_render?(image_blob)
26
+ image_blob[0, 3].unpack("C*") == [255, 216, 255]
27
+ end
28
+
29
+ # Process a new JPG image
30
+ #
31
+ # <tt>:data</tt>:: A binary string of JPEG data
32
+ #
33
+ def initialize(data)
34
+ @data = data
35
+ d = StringIO.new(@data)
36
+ d.binmode
37
+
38
+ c_marker = 0xff # Section marker.
39
+ d.seek(2) # Skip the first two bytes of JPEG identifier.
40
+ loop do
41
+ marker, code, length = d.read(4).unpack('CCn')
42
+ raise "JPEG marker not found!" if marker != c_marker
43
+
44
+ if JPEG_SOF_BLOCKS.include?(code)
45
+ @bits, @height, @width, @channels = d.read(6).unpack("CnnC")
46
+ break
47
+ end
48
+
49
+ d.seek(length - 2, IO::SEEK_CUR)
50
+ end
51
+ end
52
+
53
+ # Build a PDF object representing this image in +document+, and return
54
+ # a Reference to it.
55
+ #
56
+ def build_pdf_object(document)
57
+ color_space = case channels
58
+ when 1
59
+ :DeviceGray
60
+ when 3
61
+ :DeviceRGB
62
+ when 4
63
+ :DeviceCMYK
64
+ else
65
+ raise ArgumentError, 'JPG uses an unsupported number of channels'
66
+ end
67
+
68
+ obj = document.ref!(
69
+ :Type => :XObject,
70
+ :Subtype => :Image,
71
+ :ColorSpace => color_space,
72
+ :BitsPerComponent => bits,
73
+ :Width => width,
74
+ :Height => height
75
+ )
76
+
77
+ # add extra decode params for CMYK images. By swapping the
78
+ # min and max values from the default, we invert the colours. See
79
+ # section 4.8.4 of the spec.
80
+ if color_space == :DeviceCMYK
81
+ obj.data[:Decode] = [ 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0 ]
82
+ end
83
+
84
+ obj.stream << @data
85
+ obj.stream.filters << :DCTDecode
86
+ obj
87
+ end
88
+
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,290 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ # png.rb : Extracts the data from a PNG that is needed for embedding
4
+ #
5
+ # Based on some similar code in PDF::Writer by Austin Ziegler
6
+ #
7
+ # Copyright April 2008, James Healy. All Rights Reserved.
8
+ #
9
+ # This is free software. Please see the LICENSE and COPYING files for details.
10
+
11
+ require 'stringio'
12
+ require 'enumerator'
13
+
14
+ module Prawn
15
+ module Images
16
+ # A convenience class that wraps the logic for extracting the parts
17
+ # of a PNG image that we need to embed them in a PDF
18
+ #
19
+ class PNG < Image
20
+ # @group Extension API
21
+
22
+ attr_reader :palette, :img_data, :transparency
23
+ attr_reader :width, :height, :bits
24
+ attr_reader :color_type, :compression_method, :filter_method
25
+ attr_reader :interlace_method, :alpha_channel
26
+ attr_accessor :scaled_width, :scaled_height
27
+
28
+ def self.can_render?(image_blob)
29
+ image_blob[0, 8].unpack("C*") == [137, 80, 78, 71, 13, 10, 26, 10]
30
+ end
31
+
32
+ # Process a new PNG image
33
+ #
34
+ # <tt>data</tt>:: A binary string of PNG data
35
+ #
36
+ def initialize(data)
37
+ data = StringIO.new(data.dup)
38
+
39
+ data.read(8) # Skip the default header
40
+
41
+ @palette = ""
42
+ @img_data = ""
43
+ @transparency = {}
44
+
45
+ loop do
46
+ chunk_size = data.read(4).unpack("N")[0]
47
+ section = data.read(4)
48
+ case section
49
+ when 'IHDR'
50
+ # we can grab other interesting values from here (like width,
51
+ # height, etc)
52
+ values = data.read(chunk_size).unpack("NNCCCCC")
53
+
54
+ @width = values[0]
55
+ @height = values[1]
56
+ @bits = values[2]
57
+ @color_type = values[3]
58
+ @compression_method = values[4]
59
+ @filter_method = values[5]
60
+ @interlace_method = values[6]
61
+ when 'PLTE'
62
+ @palette << data.read(chunk_size)
63
+ when 'IDAT'
64
+ @img_data << data.read(chunk_size)
65
+ when 'tRNS'
66
+ # This chunk can only occur once and it must occur after the
67
+ # PLTE chunk and before the IDAT chunk
68
+ @transparency = {}
69
+ case @color_type
70
+ when 3
71
+ raise Errors::UnsupportedImageType,
72
+ "Pallete-based transparency in PNG is not currently supported.\n" +
73
+ "See https://github.com/prawnpdf/prawn/issues/783"
74
+ when 0
75
+ # Greyscale. Corresponding to entries in the PLTE chunk.
76
+ # Grey is two bytes, range 0 .. (2 ^ bit-depth) - 1
77
+ grayval = data.read(chunk_size).unpack("n").first
78
+ @transparency[:grayscale] = grayval
79
+ when 2
80
+ # True colour with proper alpha channel.
81
+ @transparency[:rgb] = data.read(chunk_size).unpack("nnn")
82
+ end
83
+ when 'IEND'
84
+ # we've got everything we need, exit the loop
85
+ break
86
+ else
87
+ # unknown (or un-important) section, skip over it
88
+ data.seek(data.pos + chunk_size)
89
+ end
90
+
91
+ data.read(4) # Skip the CRC
92
+ end
93
+
94
+ @img_data = Zlib::Inflate.inflate(@img_data)
95
+ end
96
+
97
+ # number of color components to each pixel
98
+ #
99
+ def colors
100
+ case self.color_type
101
+ when 0, 3, 4
102
+ return 1
103
+ when 2, 6
104
+ return 3
105
+ end
106
+ end
107
+
108
+ # split the alpha channel data from the raw image data in images
109
+ # where it's required.
110
+ #
111
+ def split_alpha_channel!
112
+ split_image_data if alpha_channel?
113
+ end
114
+
115
+ def alpha_channel?
116
+ @color_type == 4 || @color_type == 6
117
+ end
118
+
119
+ # Build a PDF object representing this image in +document+, and return
120
+ # a Reference to it.
121
+ #
122
+ def build_pdf_object(document)
123
+ if compression_method != 0
124
+ raise Errors::UnsupportedImageType,
125
+ 'PNG uses an unsupported compression method'
126
+ end
127
+
128
+ if filter_method != 0
129
+ raise Errors::UnsupportedImageType,
130
+ 'PNG uses an unsupported filter method'
131
+ end
132
+
133
+ if interlace_method != 0
134
+ raise Errors::UnsupportedImageType,
135
+ 'PNG uses unsupported interlace method'
136
+ end
137
+
138
+ # some PNG types store the colour and alpha channel data together,
139
+ # which the PDF spec doesn't like, so split it out.
140
+ split_alpha_channel!
141
+
142
+ case colors
143
+ when 1
144
+ color = :DeviceGray
145
+ when 3
146
+ color = :DeviceRGB
147
+ else
148
+ raise Errors::UnsupportedImageType,
149
+ "PNG uses an unsupported number of colors (#{png.colors})"
150
+ end
151
+
152
+ # build the image dict
153
+ obj = document.ref!(
154
+ :Type => :XObject,
155
+ :Subtype => :Image,
156
+ :Height => height,
157
+ :Width => width,
158
+ :BitsPerComponent => bits
159
+ )
160
+
161
+ # append the actual image data to the object as a stream
162
+ obj << img_data
163
+
164
+ obj.stream.filters << {
165
+ :FlateDecode => {
166
+ :Predictor => 15,
167
+ :Colors => colors,
168
+ :BitsPerComponent => bits,
169
+ :Columns => width
170
+ }
171
+ }
172
+
173
+ # sort out the colours of the image
174
+ if palette.empty?
175
+ obj.data[:ColorSpace] = color
176
+ else
177
+ # embed the colour palette in the PDF as a object stream
178
+ palette_obj = document.ref!({})
179
+ palette_obj << palette
180
+
181
+ # build the color space array for the image
182
+ obj.data[:ColorSpace] = [:Indexed,
183
+ :DeviceRGB,
184
+ (palette.size / 3) -1,
185
+ palette_obj]
186
+ end
187
+
188
+ # *************************************
189
+ # add transparency data if necessary
190
+ # *************************************
191
+
192
+ # For PNG color types 0, 2 and 3, the transparency data is stored in
193
+ # a dedicated PNG chunk, and is exposed via the transparency attribute
194
+ # of the PNG class.
195
+ if transparency[:grayscale]
196
+ # Use Color Key Masking (spec section 4.8.5)
197
+ # - An array with N elements, where N is two times the number of color
198
+ # components.
199
+ val = transparency[:grayscale]
200
+ obj.data[:Mask] = [val, val]
201
+ elsif transparency[:rgb]
202
+ # Use Color Key Masking (spec section 4.8.5)
203
+ # - An array with N elements, where N is two times the number of color
204
+ # components.
205
+ rgb = transparency[:rgb]
206
+ obj.data[:Mask] = rgb.collect { |x| [x,x] }.flatten
207
+ end
208
+
209
+ # For PNG color types 4 and 6, the transparency data is stored as a alpha
210
+ # channel mixed in with the main image data. The PNG class seperates
211
+ # it out for us and makes it available via the alpha_channel attribute
212
+ if alpha_channel?
213
+ smask_obj = document.ref!(
214
+ :Type => :XObject,
215
+ :Subtype => :Image,
216
+ :Height => height,
217
+ :Width => width,
218
+ :BitsPerComponent => bits,
219
+ :ColorSpace => :DeviceGray,
220
+ :Decode => [0, 1]
221
+ )
222
+ smask_obj.stream << alpha_channel
223
+
224
+ smask_obj.stream.filters << {
225
+ :FlateDecode => {
226
+ :Predictor => 15,
227
+ :Colors => 1,
228
+ :BitsPerComponent => bits,
229
+ :Columns => width
230
+ }
231
+ }
232
+ obj.data[:SMask] = smask_obj
233
+ end
234
+
235
+ obj
236
+ end
237
+
238
+ # Returns the minimum PDF version required to support this image.
239
+ def min_pdf_version
240
+ if bits > 8
241
+ # 16-bit color only supported in 1.5+ (ISO 32000-1:2008 8.9.5.1)
242
+ 1.5
243
+ elsif alpha_channel?
244
+ # Need transparency for SMask
245
+ 1.4
246
+ else
247
+ 1.0
248
+ end
249
+ end
250
+
251
+ private
252
+
253
+ def split_image_data
254
+ alpha_bytes = bits / 8
255
+ color_bytes = colors * bits / 8
256
+
257
+ scanline_length = (color_bytes + alpha_bytes) * self.width + 1
258
+ scanlines = @img_data.bytesize / scanline_length
259
+ pixels = self.width * self.height
260
+
261
+ data = StringIO.new(@img_data)
262
+ data.binmode
263
+
264
+ color_data = [0x00].pack('C') * (pixels * color_bytes + scanlines)
265
+ color = StringIO.new(color_data)
266
+ color.binmode
267
+
268
+ @alpha_channel = [0x00].pack('C') * (pixels * alpha_bytes + scanlines)
269
+ alpha = StringIO.new(@alpha_channel)
270
+ alpha.binmode
271
+
272
+ scanlines.times do |line|
273
+ data.seek(line * scanline_length)
274
+
275
+ filter = data.getbyte
276
+
277
+ color.putc filter
278
+ alpha.putc filter
279
+
280
+ self.width.times do
281
+ color.write data.read(color_bytes)
282
+ alpha.write data.read(alpha_bytes)
283
+ end
284
+ end
285
+
286
+ @img_data = color_data
287
+ end
288
+ end
289
+ end
290
+ end