prawn 0.11.1 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (353) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +10 -0
  3. data/COPYING +2 -2
  4. data/GPLv2 +340 -0
  5. data/GPLv3 +674 -0
  6. data/Gemfile +11 -0
  7. data/LICENSE +1 -1
  8. data/Rakefile +29 -38
  9. data/data/images/16bit.alpha +0 -0
  10. data/data/images/16bit.color +0 -0
  11. data/data/images/dice.alpha +0 -0
  12. data/data/images/dice.color +0 -0
  13. data/data/images/indexed_color.dat +0 -0
  14. data/data/images/indexed_color.png +0 -0
  15. data/data/images/page_white_text.alpha +0 -0
  16. data/data/images/page_white_text.color +0 -0
  17. data/data/pdfs/nested_pages.pdf +13 -13
  18. data/lib/prawn/document/bounding_box.rb +87 -12
  19. data/lib/prawn/document/column_box.rb +57 -28
  20. data/lib/prawn/document/graphics_state.rb +11 -74
  21. data/lib/prawn/document/internals.rb +25 -23
  22. data/lib/prawn/document/snapshot.rb +11 -8
  23. data/lib/prawn/document/span.rb +12 -10
  24. data/lib/prawn/document.rb +250 -194
  25. data/lib/prawn/encoding.rb +9 -10
  26. data/lib/prawn/errors.rb +18 -29
  27. data/lib/prawn/font/afm.rb +52 -41
  28. data/lib/prawn/font/dfont.rb +4 -3
  29. data/lib/prawn/font/ttf.rb +44 -48
  30. data/lib/prawn/font.rb +138 -88
  31. data/lib/prawn/font_metric_cache.rb +47 -0
  32. data/lib/prawn/graphics/cap_style.rb +4 -3
  33. data/lib/prawn/graphics/color.rb +13 -5
  34. data/lib/prawn/graphics/dash.rb +53 -31
  35. data/lib/prawn/graphics/join_style.rb +9 -7
  36. data/lib/prawn/graphics/patterns.rb +138 -0
  37. data/lib/prawn/graphics/transformation.rb +10 -9
  38. data/lib/prawn/graphics/transparency.rb +3 -1
  39. data/lib/prawn/graphics.rb +316 -61
  40. data/lib/prawn/image_handler.rb +36 -0
  41. data/lib/prawn/images/image.rb +49 -0
  42. data/lib/prawn/images/jpg.rb +21 -15
  43. data/lib/prawn/images/png.rb +62 -119
  44. data/lib/prawn/images.rb +89 -108
  45. data/lib/prawn/layout/grid.rb +66 -54
  46. data/lib/prawn/layout.rb +10 -15
  47. data/lib/prawn/measurement_extensions.rb +10 -6
  48. data/lib/prawn/measurements.rb +27 -21
  49. data/lib/prawn/outline.rb +6 -308
  50. data/lib/prawn/repeater.rb +11 -9
  51. data/lib/prawn/security/arcfour.rb +1 -0
  52. data/lib/prawn/security.rb +55 -33
  53. data/lib/prawn/soft_mask.rb +96 -0
  54. data/lib/prawn/stamp.rb +5 -3
  55. data/lib/prawn/table/cell/image.rb +69 -0
  56. data/lib/prawn/table/cell/in_table.rb +4 -2
  57. data/lib/prawn/table/cell/span_dummy.rb +93 -0
  58. data/lib/prawn/table/cell/subtable.rb +2 -2
  59. data/lib/prawn/table/cell/text.rb +44 -26
  60. data/lib/prawn/table/cell.rb +302 -50
  61. data/lib/prawn/table/cells.rb +147 -49
  62. data/lib/prawn/table/column_width_calculator.rb +61 -0
  63. data/lib/prawn/table.rb +297 -118
  64. data/lib/prawn/text/box.rb +21 -5
  65. data/lib/prawn/text/formatted/arranger.rb +290 -0
  66. data/lib/prawn/text/formatted/box.rb +103 -59
  67. data/lib/prawn/text/formatted/fragment.rb +34 -23
  68. data/lib/prawn/text/formatted/line_wrap.rb +266 -0
  69. data/lib/prawn/text/formatted/parser.rb +15 -5
  70. data/lib/prawn/text/formatted/wrap.rb +150 -0
  71. data/lib/prawn/text/formatted.rb +5 -4
  72. data/lib/prawn/text.rb +38 -24
  73. data/lib/prawn/utilities.rb +46 -0
  74. data/lib/prawn.rb +85 -20
  75. data/manual/basic_concepts/adding_pages.rb +27 -0
  76. data/manual/basic_concepts/basic_concepts.rb +34 -0
  77. data/manual/basic_concepts/creation.rb +39 -0
  78. data/manual/basic_concepts/cursor.rb +33 -0
  79. data/manual/basic_concepts/measurement.rb +25 -0
  80. data/manual/basic_concepts/origin.rb +38 -0
  81. data/manual/basic_concepts/other_cursor_helpers.rb +40 -0
  82. data/manual/bounding_box/bounding_box.rb +39 -0
  83. data/manual/bounding_box/bounds.rb +49 -0
  84. data/manual/bounding_box/canvas.rb +24 -0
  85. data/manual/bounding_box/creation.rb +23 -0
  86. data/manual/bounding_box/indentation.rb +46 -0
  87. data/manual/bounding_box/nesting.rb +45 -0
  88. data/manual/bounding_box/russian_boxes.rb +40 -0
  89. data/manual/bounding_box/stretchy.rb +31 -0
  90. data/manual/document_and_page_options/background.rb +27 -0
  91. data/manual/document_and_page_options/document_and_page_options.rb +32 -0
  92. data/manual/document_and_page_options/metadata.rb +23 -0
  93. data/manual/document_and_page_options/page_margins.rb +38 -0
  94. data/manual/document_and_page_options/page_size.rb +34 -0
  95. data/manual/document_and_page_options/print_scaling.rb +20 -0
  96. data/manual/example_file.rb +111 -0
  97. data/manual/example_helper.rb +411 -0
  98. data/manual/example_package.rb +53 -0
  99. data/manual/example_section.rb +46 -0
  100. data/manual/graphics/circle_and_ellipse.rb +22 -0
  101. data/manual/graphics/color.rb +24 -0
  102. data/manual/graphics/common_lines.rb +30 -0
  103. data/manual/graphics/fill_and_stroke.rb +42 -0
  104. data/manual/graphics/fill_rules.rb +37 -0
  105. data/manual/graphics/gradients.rb +37 -0
  106. data/manual/graphics/graphics.rb +58 -0
  107. data/manual/graphics/helper.rb +24 -0
  108. data/manual/graphics/line_width.rb +35 -0
  109. data/manual/graphics/lines_and_curves.rb +41 -0
  110. data/manual/graphics/polygon.rb +29 -0
  111. data/manual/graphics/rectangle.rb +21 -0
  112. data/manual/graphics/rotate.rb +28 -0
  113. data/manual/graphics/scale.rb +41 -0
  114. data/manual/graphics/soft_masks.rb +46 -0
  115. data/manual/graphics/stroke_cap.rb +31 -0
  116. data/manual/graphics/stroke_dash.rb +48 -0
  117. data/manual/graphics/stroke_join.rb +30 -0
  118. data/manual/graphics/translate.rb +29 -0
  119. data/manual/graphics/transparency.rb +35 -0
  120. data/manual/images/absolute_position.rb +23 -0
  121. data/manual/images/fit.rb +21 -0
  122. data/manual/images/horizontal.rb +25 -0
  123. data/manual/images/images.rb +40 -0
  124. data/manual/images/plain_image.rb +18 -0
  125. data/manual/images/scale.rb +22 -0
  126. data/manual/images/vertical.rb +28 -0
  127. data/manual/images/width_and_height.rb +25 -0
  128. data/manual/layout/boxes.rb +27 -0
  129. data/manual/layout/content.rb +25 -0
  130. data/manual/layout/layout.rb +28 -0
  131. data/manual/layout/simple_grid.rb +23 -0
  132. data/manual/manual/cover.rb +36 -0
  133. data/manual/manual/foreword.rb +85 -0
  134. data/manual/manual/how_to_read_this_manual.rb +41 -0
  135. data/manual/manual/manual.rb +34 -0
  136. data/manual/outline/add_subsection_to.rb +61 -0
  137. data/manual/outline/insert_section_after.rb +47 -0
  138. data/manual/outline/outline.rb +32 -0
  139. data/manual/outline/sections_and_pages.rb +67 -0
  140. data/manual/repeatable_content/page_numbering.rb +54 -0
  141. data/manual/repeatable_content/repeatable_content.rb +31 -0
  142. data/manual/repeatable_content/repeater.rb +55 -0
  143. data/manual/repeatable_content/stamp.rb +41 -0
  144. data/manual/security/encryption.rb +31 -0
  145. data/manual/security/permissions.rb +38 -0
  146. data/manual/security/security.rb +28 -0
  147. data/manual/syntax_highlight.rb +52 -0
  148. data/manual/table/basic_block.rb +53 -0
  149. data/manual/table/before_rendering_page.rb +26 -0
  150. data/manual/table/cell_border_lines.rb +24 -0
  151. data/manual/table/cell_borders_and_bg.rb +31 -0
  152. data/manual/table/cell_dimensions.rb +30 -0
  153. data/manual/table/cell_text.rb +38 -0
  154. data/manual/table/column_widths.rb +30 -0
  155. data/manual/table/content_and_subtables.rb +39 -0
  156. data/manual/table/creation.rb +27 -0
  157. data/manual/table/filtering.rb +36 -0
  158. data/manual/table/flow_and_header.rb +17 -0
  159. data/manual/table/image_cells.rb +33 -0
  160. data/manual/table/position.rb +29 -0
  161. data/manual/table/row_colors.rb +20 -0
  162. data/manual/table/span.rb +30 -0
  163. data/manual/table/style.rb +22 -0
  164. data/manual/table/table.rb +52 -0
  165. data/manual/table/width.rb +27 -0
  166. data/manual/text/alignment.rb +44 -0
  167. data/manual/text/color.rb +24 -0
  168. data/manual/text/column_box.rb +32 -0
  169. data/manual/text/fallback_fonts.rb +37 -0
  170. data/manual/text/font.rb +41 -0
  171. data/manual/text/font_size.rb +45 -0
  172. data/manual/text/font_style.rb +23 -0
  173. data/manual/text/formatted_callbacks.rb +60 -0
  174. data/manual/text/formatted_text.rb +54 -0
  175. data/manual/text/free_flowing_text.rb +51 -0
  176. data/manual/text/group.rb +31 -0
  177. data/manual/text/inline.rb +43 -0
  178. data/manual/text/kerning_and_character_spacing.rb +39 -0
  179. data/manual/text/leading.rb +25 -0
  180. data/manual/text/line_wrapping.rb +41 -0
  181. data/manual/text/paragraph_indentation.rb +26 -0
  182. data/manual/text/positioned_text.rb +38 -0
  183. data/manual/text/registering_families.rb +48 -0
  184. data/manual/text/rendering_and_color.rb +37 -0
  185. data/manual/text/right_to_left_text.rb +43 -0
  186. data/manual/text/rotation.rb +43 -0
  187. data/manual/text/single_usage.rb +37 -0
  188. data/manual/text/text.rb +75 -0
  189. data/manual/text/text_box_excess.rb +32 -0
  190. data/manual/text/text_box_extensions.rb +45 -0
  191. data/manual/text/text_box_overflow.rb +44 -0
  192. data/manual/text/utf8.rb +28 -0
  193. data/{examples/m17n → manual/text}/win_ansi_charset.rb +14 -10
  194. data/prawn.gemspec +27 -17
  195. data/spec/acceptance/png.rb +23 -0
  196. data/spec/annotations_spec.rb +16 -32
  197. data/spec/bounding_box_spec.rb +284 -2
  198. data/spec/cell_spec.rb +169 -38
  199. data/spec/column_box_spec.rb +65 -0
  200. data/spec/data/curves.pdf +66 -0
  201. data/spec/destinations_spec.rb +5 -5
  202. data/spec/document_spec.rb +212 -113
  203. data/spec/extensions/encoding_helpers.rb +9 -0
  204. data/spec/extensions/mocha.rb +2 -3
  205. data/spec/font_metric_cache_spec.rb +52 -0
  206. data/spec/font_spec.rb +205 -95
  207. data/spec/formatted_text_arranger_spec.rb +43 -43
  208. data/spec/formatted_text_box_spec.rb +63 -24
  209. data/spec/formatted_text_fragment_spec.rb +8 -8
  210. data/spec/graphics_spec.rb +175 -68
  211. data/spec/grid_spec.rb +26 -15
  212. data/spec/image_handler_spec.rb +54 -0
  213. data/spec/images_spec.rb +58 -30
  214. data/spec/inline_formatted_text_parser_spec.rb +73 -19
  215. data/spec/jpg_spec.rb +4 -4
  216. data/spec/line_wrap_spec.rb +28 -28
  217. data/spec/measurement_units_spec.rb +6 -6
  218. data/spec/object_store_spec.rb +17 -106
  219. data/spec/outline_spec.rb +103 -63
  220. data/spec/png_spec.rb +25 -25
  221. data/spec/reference_spec.rb +8 -65
  222. data/spec/repeater_spec.rb +25 -11
  223. data/spec/security_spec.rb +44 -12
  224. data/spec/snapshot_spec.rb +38 -6
  225. data/spec/soft_mask_spec.rb +117 -0
  226. data/spec/span_spec.rb +10 -15
  227. data/spec/spec_helper.rb +32 -8
  228. data/spec/stamp_spec.rb +29 -30
  229. data/spec/stroke_styles_spec.rb +36 -18
  230. data/spec/table/span_dummy_spec.rb +17 -0
  231. data/spec/table_spec.rb +850 -104
  232. data/spec/text_at_spec.rb +19 -33
  233. data/spec/text_box_spec.rb +117 -64
  234. data/spec/text_rendering_mode_spec.rb +5 -5
  235. data/spec/text_spacing_spec.rb +20 -2
  236. data/spec/text_spec.rb +111 -59
  237. data/spec/transparency_spec.rb +5 -5
  238. metadata +477 -328
  239. data/HACKING +0 -50
  240. data/README +0 -141
  241. data/data/fonts/Action Man.dfont +0 -0
  242. data/data/fonts/Activa.ttf +0 -0
  243. data/data/fonts/Chalkboard.ttf +0 -0
  244. data/data/fonts/DejaVuSans.ttf +0 -0
  245. data/data/fonts/Dustismo_Roman.ttf +0 -0
  246. data/data/fonts/comicsans.ttf +0 -0
  247. data/data/fonts/gkai00mp.ttf +0 -0
  248. data/data/images/16bit.dat +0 -0
  249. data/data/images/dice.dat +0 -0
  250. data/data/images/page_white_text.dat +0 -0
  251. data/data/images/rails.dat +0 -0
  252. data/data/images/rails.png +0 -0
  253. data/examples/bounding_box/bounding_boxes.rb +0 -44
  254. data/examples/bounding_box/indentation.rb +0 -35
  255. data/examples/bounding_box/russian_boxes.rb +0 -37
  256. data/examples/bounding_box/stretched_nesting.rb +0 -68
  257. data/examples/example_helper.rb +0 -8
  258. data/examples/general/background.rb +0 -24
  259. data/examples/general/canvas.rb +0 -16
  260. data/examples/general/context_sensitive_headers.rb +0 -38
  261. data/examples/general/float.rb +0 -12
  262. data/examples/general/margin.rb +0 -37
  263. data/examples/general/measurement_units.rb +0 -52
  264. data/examples/general/metadata-info.rb +0 -17
  265. data/examples/general/multi_page_layout.rb +0 -19
  266. data/examples/general/outlines.rb +0 -67
  267. data/examples/general/page_geometry.rb +0 -32
  268. data/examples/general/page_numbering.rb +0 -40
  269. data/examples/general/page_templates.rb +0 -20
  270. data/examples/general/repeaters.rb +0 -48
  271. data/examples/general/stamp.rb +0 -42
  272. data/examples/general/templates.rb +0 -14
  273. data/examples/graphics/basic_images.rb +0 -24
  274. data/examples/graphics/cmyk.rb +0 -13
  275. data/examples/graphics/curves.rb +0 -12
  276. data/examples/graphics/gradient.rb +0 -23
  277. data/examples/graphics/hexagon.rb +0 -14
  278. data/examples/graphics/image_fit.rb +0 -16
  279. data/examples/graphics/image_flow.rb +0 -38
  280. data/examples/graphics/image_position.rb +0 -18
  281. data/examples/graphics/line.rb +0 -33
  282. data/examples/graphics/png_types.rb +0 -23
  283. data/examples/graphics/polygons.rb +0 -17
  284. data/examples/graphics/remote_images.rb +0 -13
  285. data/examples/graphics/rounded_polygons.rb +0 -20
  286. data/examples/graphics/rounded_rectangle.rb +0 -21
  287. data/examples/graphics/ruport_style_helpers.rb +0 -20
  288. data/examples/graphics/stroke_bounds.rb +0 -21
  289. data/examples/graphics/stroke_cap_and_join.rb +0 -46
  290. data/examples/graphics/stroke_dash.rb +0 -43
  291. data/examples/graphics/transformations.rb +0 -53
  292. data/examples/graphics/transparency.rb +0 -27
  293. data/examples/grid/bounding_boxes.rb +0 -22
  294. data/examples/grid/column_gutter_grid.rb +0 -21
  295. data/examples/grid/multi_boxes.rb +0 -52
  296. data/examples/grid/show_grid.rb +0 -14
  297. data/examples/grid/simple_grid.rb +0 -21
  298. data/examples/m17n/chinese_text_wrapping.rb +0 -18
  299. data/examples/m17n/euro.rb +0 -16
  300. data/examples/m17n/full_win_ansi_character_list.rb +0 -20
  301. data/examples/m17n/sjis.rb +0 -29
  302. data/examples/m17n/utf8.rb +0 -14
  303. data/examples/security/hello_foo.rb +0 -9
  304. data/examples/table/bill.rb +0 -54
  305. data/examples/table/borders.rb +0 -25
  306. data/examples/table/cell.rb +0 -13
  307. data/examples/table/checkerboard.rb +0 -23
  308. data/examples/table/header.rb +0 -15
  309. data/examples/table/inline_format_table.rb +0 -13
  310. data/examples/table/multi_page_table.rb +0 -10
  311. data/examples/table/simple_table.rb +0 -25
  312. data/examples/table/subtable.rb +0 -13
  313. data/examples/table/widths.rb +0 -21
  314. data/examples/text/alignment.rb +0 -19
  315. data/examples/text/character_spacing.rb +0 -13
  316. data/examples/text/dfont.rb +0 -49
  317. data/examples/text/family_based_styling.rb +0 -25
  318. data/examples/text/font_calculations.rb +0 -92
  319. data/examples/text/font_size.rb +0 -34
  320. data/examples/text/hyphenation.rb +0 -45
  321. data/examples/text/indent_paragraphs.rb +0 -24
  322. data/examples/text/inline_format.rb +0 -104
  323. data/examples/text/kerning.rb +0 -31
  324. data/examples/text/rendering_mode.rb +0 -21
  325. data/examples/text/rotated.rb +0 -99
  326. data/examples/text/shaped_text_box.rb +0 -32
  327. data/examples/text/simple_text.rb +0 -18
  328. data/examples/text/simple_text_ttf.rb +0 -18
  329. data/examples/text/span.rb +0 -30
  330. data/examples/text/text_box.rb +0 -90
  331. data/examples/text/text_box_returning_excess.rb +0 -52
  332. data/examples/text/text_flow.rb +0 -68
  333. data/lib/prawn/compatibility.rb +0 -51
  334. data/lib/prawn/core/annotations.rb +0 -61
  335. data/lib/prawn/core/byte_string.rb +0 -9
  336. data/lib/prawn/core/destinations.rb +0 -90
  337. data/lib/prawn/core/document_state.rb +0 -78
  338. data/lib/prawn/core/literal_string.rb +0 -16
  339. data/lib/prawn/core/name_tree.rb +0 -165
  340. data/lib/prawn/core/object_store.rb +0 -264
  341. data/lib/prawn/core/page.rb +0 -213
  342. data/lib/prawn/core/pdf_object.rb +0 -108
  343. data/lib/prawn/core/reference.rb +0 -112
  344. data/lib/prawn/core/text/formatted/arranger.rb +0 -293
  345. data/lib/prawn/core/text/formatted/line_wrap.rb +0 -272
  346. data/lib/prawn/core/text/formatted/wrap.rb +0 -149
  347. data/lib/prawn/core/text.rb +0 -268
  348. data/lib/prawn/core.rb +0 -85
  349. data/lib/prawn/document/page_geometry.rb +0 -136
  350. data/lib/prawn/graphics/gradient.rb +0 -84
  351. data/spec/name_tree_spec.rb +0 -112
  352. data/spec/pdf_object_spec.rb +0 -170
  353. data/spec/template_spec.rb +0 -291
@@ -16,13 +16,19 @@ module Prawn
16
16
  # A convenience class that wraps the logic for extracting the parts
17
17
  # of a PNG image that we need to embed them in a PDF
18
18
  #
19
- class PNG
19
+ class PNG < Image
20
+ # @group Extension API
21
+
20
22
  attr_reader :palette, :img_data, :transparency
21
23
  attr_reader :width, :height, :bits
22
24
  attr_reader :color_type, :compression_method, :filter_method
23
25
  attr_reader :interlace_method, :alpha_channel
24
26
  attr_accessor :scaled_width, :scaled_height
25
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
+
26
32
  # Process a new PNG image
27
33
  #
28
34
  # <tt>data</tt>:: A binary string of PNG data
@@ -88,6 +94,8 @@ module Prawn
88
94
 
89
95
  data.read(4) # Skip the CRC
90
96
  end
97
+
98
+ @img_data = Zlib::Inflate.inflate(@img_data)
91
99
  end
92
100
 
93
101
  # number of color components to each pixel
@@ -101,34 +109,17 @@ module Prawn
101
109
  end
102
110
  end
103
111
 
104
- # number of bits used per pixel
105
- #
106
- def pixel_bitlength
107
- if alpha_channel?
108
- self.bits * (self.colors + 1)
109
- else
110
- self.bits * self.colors
111
- end
112
- end
113
-
114
112
  # split the alpha channel data from the raw image data in images
115
113
  # where it's required.
116
114
  #
117
115
  def split_alpha_channel!
118
- unfilter_image_data if alpha_channel?
116
+ split_image_data if alpha_channel?
119
117
  end
120
118
 
121
119
  def alpha_channel?
122
120
  @color_type == 4 || @color_type == 6
123
121
  end
124
122
 
125
- # Adobe Reader can't handle 16-bit png channels -- chop off the second
126
- # byte (least significant)
127
- #
128
- def alpha_channel_bits
129
- 8
130
- end
131
-
132
123
  # Build a PDF object representing this image in +document+, and return
133
124
  # a Reference to it.
134
125
  #
@@ -168,31 +159,31 @@ module Prawn
168
159
  :Subtype => :Image,
169
160
  :Height => height,
170
161
  :Width => width,
171
- :BitsPerComponent => bits,
172
- :Length => img_data.size,
173
- :Filter => :FlateDecode
162
+ :BitsPerComponent => bits
174
163
  )
175
164
 
176
- unless alpha_channel
177
- obj.data[:DecodeParms] = {:Predictor => 15,
178
- :Colors => colors,
179
- :BitsPerComponent => bits,
180
- :Columns => width}
181
- end
182
-
183
165
  # append the actual image data to the object as a stream
184
166
  obj << img_data
185
-
167
+
168
+ obj.stream.filters << {
169
+ :FlateDecode => {
170
+ :Predictor => 15,
171
+ :Colors => colors,
172
+ :BitsPerComponent => bits,
173
+ :Columns => width
174
+ }
175
+ }
176
+
186
177
  # sort out the colours of the image
187
178
  if palette.empty?
188
179
  obj.data[:ColorSpace] = color
189
180
  else
190
181
  # embed the colour palette in the PDF as a object stream
191
- palette_obj = document.ref!(:Length => palette.size)
182
+ palette_obj = document.ref!({})
192
183
  palette_obj << palette
193
184
 
194
185
  # build the color space array for the image
195
- obj.data[:ColorSpace] = [:Indexed,
186
+ obj.data[:ColorSpace] = [:Indexed,
196
187
  :DeviceRGB,
197
188
  (palette.size / 3) -1,
198
189
  palette_obj]
@@ -233,13 +224,20 @@ module Prawn
233
224
  :Subtype => :Image,
234
225
  :Height => height,
235
226
  :Width => width,
236
- :BitsPerComponent => alpha_channel_bits,
237
- :Length => alpha_channel.size,
238
- :Filter => :FlateDecode,
227
+ :BitsPerComponent => bits,
239
228
  :ColorSpace => :DeviceGray,
240
229
  :Decode => [0, 1]
241
230
  )
242
- smask_obj << alpha_channel
231
+ smask_obj.stream << alpha_channel
232
+
233
+ smask_obj.stream.filters << {
234
+ :FlateDecode => {
235
+ :Predictor => 15,
236
+ :Colors => 1,
237
+ :BitsPerComponent => bits,
238
+ :Columns => width
239
+ }
240
+ }
243
241
  obj.data[:SMask] = smask_obj
244
242
  end
245
243
 
@@ -261,95 +259,40 @@ module Prawn
261
259
 
262
260
  private
263
261
 
264
- def unfilter_image_data
265
- data = Zlib::Inflate.inflate(@img_data).unpack 'C*'
266
- @img_data = ""
267
- @alpha_channel = ""
268
-
269
- pixel_bytes = pixel_bitlength / 8
270
- scanline_length = pixel_bytes * self.width + 1
271
- row = 0
272
- pixels = []
273
- paeth, pa, pb, pc = nil
274
- until data.empty? do
275
- row_data = data.slice! 0, scanline_length
276
- filter = row_data.shift
277
- case filter
278
- when 0 # None
279
- when 1 # Sub
280
- row_data.each_with_index do |byte, index|
281
- left = index < pixel_bytes ? 0 : row_data[index - pixel_bytes]
282
- row_data[index] = (byte + left) % 256
283
- #p [byte, left, row_data[index]]
284
- end
285
- when 2 # Up
286
- row_data.each_with_index do |byte, index|
287
- col = index / pixel_bytes
288
- upper = row == 0 ? 0 : pixels[row-1][col][index % pixel_bytes]
289
- row_data[index] = (upper + byte) % 256
290
- end
291
- when 3 # Average
292
- row_data.each_with_index do |byte, index|
293
- col = index / pixel_bytes
294
- upper = row == 0 ? 0 : pixels[row-1][col][index % pixel_bytes]
295
- left = index < pixel_bytes ? 0 : row_data[index - pixel_bytes]
262
+ def split_image_data
263
+ alpha_bytes = bits / 8
264
+ color_bytes = colors * bits / 8
296
265
 
297
- row_data[index] = (byte + ((left + upper)/2).floor) % 256
298
- end
299
- when 4 # Paeth
300
- left = upper = upper_left = nil
301
- row_data.each_with_index do |byte, index|
302
- col = index / pixel_bytes
303
-
304
- left = index < pixel_bytes ? 0 : row_data[index - pixel_bytes]
305
- if row.zero?
306
- upper = upper_left = 0
307
- else
308
- upper = pixels[row-1][col][index % pixel_bytes]
309
- upper_left = col.zero? ? 0 :
310
- pixels[row-1][col-1][index % pixel_bytes]
311
- end
312
-
313
- p = left + upper - upper_left
314
- pa = (p - left).abs
315
- pb = (p - upper).abs
316
- pc = (p - upper_left).abs
317
-
318
- paeth = if pa <= pb && pa <= pc
319
- left
320
- elsif pb <= pc
321
- upper
322
- else
323
- upper_left
324
- end
325
-
326
- row_data[index] = (byte + paeth) % 256
327
- end
328
- else
329
- raise ArgumentError, "Invalid filter algorithm #{filter}"
330
- end
266
+ scanline_length = (color_bytes + alpha_bytes) * self.width + 1
267
+ scanlines = @img_data.bytesize / scanline_length
268
+ pixels = self.width * self.height
331
269
 
332
- s = []
333
- row_data.each_slice pixel_bytes do |slice|
334
- s << slice
335
- end
336
- pixels << s
337
- row += 1
338
- end
270
+ data = StringIO.new(@img_data)
271
+ data.binmode
272
+
273
+ color_data = [0x00].pack('C') * (pixels * color_bytes + scanlines)
274
+ color = StringIO.new(color_data)
275
+ color.binmode
276
+
277
+ @alpha_channel = [0x00].pack('C') * (pixels * alpha_bytes + scanlines)
278
+ alpha = StringIO.new(@alpha_channel)
279
+ alpha.binmode
280
+
281
+ scanlines.times do |line|
282
+ data.seek(line * scanline_length)
283
+
284
+ filter = data.getbyte
285
+
286
+ color.putc filter
287
+ alpha.putc filter
339
288
 
340
- # convert the pixel data to seperate strings for colours and alpha
341
- color_byte_size = self.colors * self.bits / 8
342
- alpha_byte_size = alpha_channel_bits / 8
343
- pixels.each do |this_row|
344
- this_row.each do |pixel|
345
- @img_data << pixel[0, color_byte_size].pack("C*")
346
- @alpha_channel << pixel[color_byte_size, alpha_byte_size].pack("C*")
289
+ self.width.times do
290
+ color.write data.read(color_bytes)
291
+ alpha.write data.read(alpha_bytes)
347
292
  end
348
293
  end
349
294
 
350
- # compress the data
351
- @img_data = Zlib::Deflate.deflate(@img_data)
352
- @alpha_channel = Zlib::Deflate.deflate(@alpha_channel)
295
+ @img_data = color_data
353
296
  end
354
297
  end
355
298
  end
data/lib/prawn/images.rb CHANGED
@@ -6,18 +6,16 @@
6
6
  # This is free software. Please see the LICENSE and COPYING files for details.
7
7
 
8
8
  require 'digest/sha1'
9
+ require 'pathname'
9
10
 
10
11
  module Prawn
11
12
 
12
13
  module Images
14
+ # @group Stable API
13
15
 
14
16
  # 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
17
+ # JPG and PNG files are supported. (Note that processing PNG
18
+ # images with alpha channels can be processor and memory intensive.)
21
19
  #
22
20
  # Arguments:
23
21
  # <tt>file</tt>:: path to file or an object that responds to #read
@@ -25,22 +23,22 @@ module Prawn
25
23
  # Options:
26
24
  # <tt>:at</tt>:: an array [x,y] with the location of the top left corner of the image.
27
25
  # <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
26
+ # <tt>:vposition</tt>:: One of (:top, :center, :center) or an y-offset
29
27
  # <tt>:height</tt>:: the height of the image [actual height of the image]
30
28
  # <tt>:width</tt>:: the width of the image [actual width of the image]
31
29
  # <tt>:scale</tt>:: scale the dimensions of the image proportionally
32
30
  # <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
31
  #
38
- # dice = "#{Prawn::BASEDIR}/data/images/dice.png"
39
- # image dice, :at => [50, 450], :scale => 0.75
40
- # end
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
41
39
  #
42
40
  # 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
41
+ # proportionally. When both are provided, the image will be stretched to
44
42
  # fit the dimensions without maintaining the aspect ratio.
45
43
  #
46
44
  #
@@ -54,25 +52,32 @@ module Prawn
54
52
  #
55
53
  # require "open-uri"
56
54
  #
57
- # Prawn::Document.generate("remote_images.pdf") do
55
+ # Prawn::Document.generate("remote_images.pdf") do
58
56
  # image open("http://prawn.majesticseacreature.com/media/prawn_logo.png")
59
57
  # end
60
58
  #
61
59
  # This method returns an image info object which can be used to check the
62
- # dimensions of an image object if needed.
60
+ # dimensions of an image object if needed.
63
61
  # (See also: Prawn::Images::PNG , Prawn::Images::JPG)
64
- #
62
+ #
65
63
  def image(file, options={})
66
- Prawn.verify_options [:at, :position, :vposition, :height,
64
+ Prawn.verify_options [:at, :position, :vposition, :height,
67
65
  :width, :scale, :fit], options
68
66
 
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
-
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
76
81
  image_sha1 = Digest::SHA1.hexdigest(image_content)
77
82
 
78
83
  # if this image has already been embedded, just reuse it
@@ -81,11 +86,7 @@ module Prawn
81
86
  image_obj = image_registry[image_sha1][:obj]
82
87
  else
83
88
  # Build the image object
84
- klass = case detect_image_format(image_content)
85
- when :jpg then Prawn::Images::JPG
86
- when :png then Prawn::Images::PNG
87
- end
88
- info = klass.new(image_content)
89
+ info = Prawn.image_handler.find(image_content).new(image_content)
89
90
 
90
91
  # Bump PDF version if the image requires it
91
92
  min_version(info.min_pdf_version) if info.respond_to?(:min_pdf_version)
@@ -95,43 +96,59 @@ module Prawn
95
96
  image_registry[image_sha1] = {:obj => image_obj, :info => info}
96
97
  end
97
98
 
98
- # find where the image will be placed and how big it will be
99
- w,h = calc_image_dimensions(info, options)
99
+ [image_obj, info]
100
+ end
100
101
 
101
- if options[:at]
102
- x,y = map_to_absolute(options[:at])
103
- else
104
- x,y = image_position(w,h,options)
105
- move_text_position h
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
106
117
  end
107
118
 
108
119
  # add a reference to the image object to the current page
109
120
  # resource list and give it a label
110
121
  label = "I#{next_image_id}"
111
- state.page.xobjects.merge!( label => image_obj )
122
+ state.page.xobjects.merge!(label => pdf_obj)
112
123
 
113
124
  # add the image to the current page
114
125
  instruct = "\nq\n%.3f 0 0 %.3f %.3f %.3f cm\n/%s Do\nQ"
115
126
  add_content instruct % [ w, h, x, y - h, label ]
116
-
117
- return info
118
127
  end
119
128
 
120
- private
121
-
129
+ private
130
+
131
+ def verify_and_open_image(io_or_path)
132
+ # File or IO
133
+ if io_or_path.respond_to?(:rewind)
134
+ io = io_or_path
135
+ # Rewind if the object we're passed is an IO, so that multiple embeds of
136
+ # the same IO object will work
137
+ io.rewind
138
+ # read the file as binary so the size is calculated correctly
139
+ # guard binmode because some objects acting io-like don't implement it
140
+ io.binmode if io.respond_to?(:binmode)
141
+ return io
142
+ end
143
+ # String or Pathname
144
+ io_or_path = Pathname.new(io_or_path)
145
+ raise ArgumentError, "#{io_or_path} not found" unless io_or_path.file?
146
+ io = io_or_path.open('rb')
147
+ io
148
+ end
149
+
122
150
  def image_position(w,h,options)
123
151
  options[:position] ||= :left
124
-
125
- x = case options[:position]
126
- when :left
127
- bounds.absolute_left
128
- when :center
129
- bounds.absolute_left + (bounds.width - w) / 2.0
130
- when :right
131
- bounds.absolute_right - w
132
- when Numeric
133
- options[:position] + bounds.absolute_left
134
- end
135
152
 
136
153
  y = case options[:vposition]
137
154
  when :top
@@ -145,66 +162,30 @@ module Prawn
145
162
  else
146
163
  determine_y_with_page_flow(h)
147
164
  end
148
- return [x,y]
149
- end
150
-
151
- def determine_y_with_page_flow(h)
152
- if overruns_page?(h)
153
- start_new_page
154
- bounds.absolute_top
155
- else
156
- self.y
165
+
166
+ x = case options[:position]
167
+ when :left
168
+ bounds.left_side
169
+ when :center
170
+ bounds.left_side + (bounds.width - w) / 2.0
171
+ when :right
172
+ bounds.right_side - w
173
+ when Numeric
174
+ options[:position] + bounds.left_side
157
175
  end
158
- end
159
-
160
- def overruns_page?(h)
161
- (self.y - h) < bounds.absolute_bottom
176
+
177
+ return [x,y]
162
178
  end
163
179
 
164
- def calc_image_dimensions(info, options)
165
- w = options[:width] || info.width
166
- h = options[:height] || info.height
167
-
168
- if options[:width] && !options[:height]
169
- wp = w / info.width.to_f
170
- w = info.width * wp
171
- h = info.height * wp
172
- elsif options[:height] && !options[:width]
173
- hp = h / info.height.to_f
174
- w = info.width * hp
175
- h = info.height * hp
176
- elsif options[:scale]
177
- w = info.width * options[:scale]
178
- h = info.height * options[:scale]
179
- elsif options[:fit]
180
- bw, bh = options[:fit]
181
- bp = bw / bh.to_f
182
- ip = info.width / info.height.to_f
183
- if ip > bp
184
- w = bw
185
- h = bw / ip
186
- else
187
- h = bh
188
- w = bh * ip
189
- end
180
+ def determine_y_with_page_flow(h)
181
+ if overruns_page?(h)
182
+ bounds.move_past_bottom
190
183
  end
191
- info.scaled_width = w
192
- info.scaled_height = h
193
- [w,h]
184
+ self.y
194
185
  end
195
186
 
196
- def detect_image_format(content)
197
- top = content[0,128]
198
-
199
- # Unpack before comparing for JPG header, so as to avoid having to worry
200
- # about the source string encoding. We just want a byte-by-byte compare.
201
- if top[0, 3].unpack("C*") == [255, 216, 255]
202
- return :jpg
203
- elsif top[0, 8].unpack("C*") == [137, 80, 78, 71, 13, 10, 26, 10]
204
- return :png
205
- else
206
- raise Errors::UnsupportedImageType, "image file is an unrecognised format"
207
- end
187
+ def overruns_page?(h)
188
+ (self.y - h) < reference_bounds.absolute_bottom
208
189
  end
209
190
 
210
191
  def image_registry