prawn-git 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
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