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,288 @@
1
+ # encoding: utf-8
2
+ #
3
+ # encryption.rb : Implements encrypted PDF and access permissions.
4
+ #
5
+ # Copyright August 2008, Brad Ediger. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+
9
+ require 'digest/md5'
10
+
11
+ require 'pdf/core/byte_string'
12
+
13
+ require_relative 'security/arcfour'
14
+
15
+ module Prawn
16
+ class Document
17
+
18
+ # Implements PDF encryption (password protection and permissions) as
19
+ # specified in the PDF Reference, version 1.3, section 3.5 "Encryption".
20
+ module Security
21
+
22
+ # @group Experimental API
23
+
24
+ # Encrypts the document, to protect confidential data or control
25
+ # modifications to the document. The encryption algorithm used is
26
+ # detailed in the PDF Reference 1.3, section 3.5 "Encryption", and it is
27
+ # implemented by all major PDF readers.
28
+ #
29
+ # +options+ can contain the following:
30
+ #
31
+ # <tt>:user_password</tt>:: Password required to open the document. If
32
+ # this is omitted or empty, no password will be
33
+ # required. The document will still be
34
+ # encrypted, but anyone can read it.
35
+ #
36
+ # <tt>:owner_password</tt>:: Password required to make modifications to
37
+ # the document or change or override its
38
+ # permissions. If this is set to
39
+ # <tt>:random</tt>, a random password will be
40
+ # used; this can be useful if you never want
41
+ # users to be able to override the document
42
+ # permissions.
43
+ #
44
+ # <tt>:permissions</tt>:: A hash mapping permission symbols (see below) to
45
+ # <tt>true</tt> or <tt>false</tt>. True means
46
+ # "permitted", and false means "not permitted".
47
+ # All permissions default to <tt>true</tt>.
48
+ #
49
+ # The following permissions can be specified:
50
+ #
51
+ # <tt>:print_document</tt>:: Print document.
52
+ #
53
+ # <tt>:modify_contents</tt>:: Modify contents of document (other than text
54
+ # annotations and interactive form fields).
55
+ #
56
+ # <tt>:copy_contents</tt>:: Copy text and graphics from document.
57
+ #
58
+ # <tt>:modify_annotations</tt>:: Add or modify text annotations and
59
+ # interactive form fields.
60
+ #
61
+ # == Examples
62
+ #
63
+ # Deny printing to everyone, but allow anyone to open without a password:
64
+ #
65
+ # encrypt_document :permissions => { :print_document => false },
66
+ # :owner_password => :random
67
+ #
68
+ # Set a user and owner password on the document, with full permissions for
69
+ # both the user and the owner:
70
+ #
71
+ # encrypt_document :user_password => 'foo', :owner_password => 'bar'
72
+ #
73
+ # Set no passwords, grant all permissions (This is useful because the
74
+ # default in some readers, if no permissions are specified, is "deny"):
75
+ #
76
+ # encrypt_document
77
+ #
78
+ # == Caveats
79
+ #
80
+ # * The encryption used is weak; the key is password-derived and is
81
+ # limited to 40 bits, due to US export controls in effect at the time
82
+ # the PDF standard was written.
83
+ #
84
+ # * There is nothing technologically requiring PDF readers to respect the
85
+ # permissions embedded in a document. Many PDF readers do not.
86
+ #
87
+ # * In short, you have <b>no security at all</b> against a moderately
88
+ # motivated person. Don't use this for anything super-serious. This is
89
+ # not a limitation of Prawn, but is rather a built-in limitation of the
90
+ # PDF format.
91
+ #
92
+ def encrypt_document(options={})
93
+ Prawn.verify_options [:user_password, :owner_password, :permissions],
94
+ options
95
+ @user_password = options.delete(:user_password) || ""
96
+
97
+ @owner_password = options.delete(:owner_password) || @user_password
98
+ if @owner_password == :random
99
+ # Generate a completely ridiculous password
100
+ @owner_password = (1..32).map{ rand(256) }.pack("c*")
101
+ end
102
+
103
+ self.permissions = options.delete(:permissions) || {}
104
+
105
+ # Shove the necessary entries in the trailer and enable encryption.
106
+ state.trailer[:Encrypt] = encryption_dictionary
107
+ state.encrypt = true
108
+ state.encryption_key = user_encryption_key
109
+ end
110
+
111
+ # Encrypts the given string under the given key, also requiring the
112
+ # object ID and generation number of the reference.
113
+ # See Algorithm 3.1.
114
+ def self.encrypt_string(str, key, id, gen)
115
+ # Convert ID and Gen number into little-endian truncated byte strings
116
+ id = [id].pack('V')[0,3]
117
+ gen = [gen].pack('V')[0,2]
118
+ extended_key = "#{key}#{id}#{gen}"
119
+
120
+ # Compute the RC4 key from the extended key and perform the encryption
121
+ rc4_key = Digest::MD5.digest(extended_key)[0, 10]
122
+ Arcfour.new(rc4_key).encrypt(str)
123
+ end
124
+
125
+ private
126
+
127
+ # Provides the values for the trailer encryption dictionary.
128
+ def encryption_dictionary
129
+ { :Filter => :Standard, # default PDF security handler
130
+ :V => 1, # "Algorithm 3.1", PDF reference 1.3
131
+ :R => 2, # Revision 2 of the algorithm
132
+ :O => PDF::Core::ByteString.new(owner_password_hash),
133
+ :U => PDF::Core::ByteString.new(user_password_hash),
134
+ :P => permissions_value }
135
+ end
136
+
137
+ # Flags in the permissions word, numbered as LSB = 1
138
+ PermissionsBits = { :print_document => 3,
139
+ :modify_contents => 4,
140
+ :copy_contents => 5,
141
+ :modify_annotations => 6 }
142
+
143
+ FullPermissions = 0b1111_1111_1111_1111_1111_1111_1111_1111
144
+
145
+ def permissions=(perms={})
146
+ @permissions ||= FullPermissions
147
+ perms.each do |key, value|
148
+ unless PermissionsBits[key]
149
+ raise ArgumentError, "Unknown permission :#{key}. Valid options: " +
150
+ PermissionsBits.keys.map { |k| k.inspect }.join(", ")
151
+ end
152
+
153
+ # 0-based bit number, from LSB
154
+ bit_position = PermissionsBits[key] - 1
155
+
156
+ if value # set bit
157
+ @permissions |= (1 << bit_position)
158
+ else # clear bit
159
+ @permissions &= ~(1 << bit_position)
160
+ end
161
+ end
162
+ end
163
+
164
+ def permissions_value
165
+ @permissions || FullPermissions
166
+ end
167
+
168
+ PasswordPadding =
169
+ "28BF4E5E4E758A4164004E56FFFA01082E2E00B6D0683E802F0CA9FE6453697A".
170
+ scan(/../).map{|x| x.to_i(16)}.pack("c*")
171
+
172
+ # Pads or truncates a password to 32 bytes as per Alg 3.2.
173
+ def pad_password(password)
174
+ password = password[0, 32]
175
+ password + PasswordPadding[0, 32 - password.length]
176
+ end
177
+
178
+ def user_encryption_key
179
+ @user_encryption_key ||= begin
180
+ md5 = Digest::MD5.new
181
+ md5 << pad_password(@user_password)
182
+ md5 << owner_password_hash
183
+ md5 << [permissions_value].pack("V")
184
+ md5.digest[0, 5]
185
+ end
186
+ end
187
+
188
+ # The O (owner) value in the encryption dictionary. Algorithm 3.3.
189
+ def owner_password_hash
190
+ @owner_password_hash ||= begin
191
+ key = Digest::MD5.digest(pad_password(@owner_password))[0, 5]
192
+ Arcfour.new(key).encrypt(pad_password(@user_password))
193
+ end
194
+ end
195
+
196
+ # The U (user) value in the encryption dictionary. Algorithm 3.4.
197
+ def user_password_hash
198
+ Arcfour.new(user_encryption_key).encrypt(PasswordPadding)
199
+ end
200
+
201
+ end
202
+
203
+ end
204
+ end
205
+
206
+ # @private
207
+ module PDF
208
+ module Core
209
+ module_function
210
+
211
+ # Like PdfObject, but returns an encrypted result if required.
212
+ # For direct objects, requires the object identifier and generation number
213
+ # from the indirect object referencing obj.
214
+ #
215
+ # @private
216
+ def EncryptedPdfObject(obj, key, id, gen, in_content_stream=false)
217
+ case obj
218
+ when Array
219
+ "[" << obj.map { |e|
220
+ EncryptedPdfObject(e, key, id, gen, in_content_stream)
221
+ }.join(' ') << "]"
222
+ when LiteralString
223
+ obj = ByteString.new(Prawn::Document::Security.encrypt_string(obj, key, id, gen)).gsub(/[\\\n\(\)]/) { |m| "\\#{m}" }
224
+ "(#{obj})"
225
+ when Time
226
+ obj = obj.strftime("D:%Y%m%d%H%M%S%z").chop.chop + "'00'"
227
+ obj = ByteString.new(Prawn::Document::Security.encrypt_string(obj, key, id, gen)).gsub(/[\\\n\(\)]/) { |m| "\\#{m}" }
228
+ "(#{obj})"
229
+ when String
230
+ PdfObject(
231
+ ByteString.new(
232
+ Prawn::Document::Security.encrypt_string(obj, key, id, gen)),
233
+ in_content_stream)
234
+ when ::Hash
235
+ output = "<< "
236
+ obj.each do |k,v|
237
+ unless String === k || Symbol === k
238
+ raise PDF::Core::Errors::FailedObjectConversion,
239
+ "A PDF Dictionary must be keyed by names"
240
+ end
241
+ output << PdfObject(k.to_sym, in_content_stream) << " " <<
242
+ EncryptedPdfObject(v, key, id, gen, in_content_stream) << "\n"
243
+ end
244
+ output << ">>"
245
+ when NameTree::Value
246
+ PdfObject(obj.name) + " " +
247
+ EncryptedPdfObject(obj.value, key, id, gen, in_content_stream)
248
+ when PDF::Core::OutlineRoot, PDF::Core::OutlineItem
249
+ EncryptedPdfObject(obj.to_hash, key, id, gen, in_content_stream)
250
+ else # delegate back to PdfObject
251
+ PdfObject(obj, in_content_stream)
252
+ end
253
+ end
254
+
255
+
256
+ # @private
257
+ class Stream
258
+ def encrypted_object(key, id, gen)
259
+ if filtered_stream
260
+ "stream\n#{Prawn::Document::Security.encrypt_string filtered_stream, key, id, gen}\nendstream\n"
261
+ else
262
+ ''
263
+ end
264
+ end
265
+ end
266
+
267
+ # @private
268
+ class Reference
269
+
270
+ # Returns the object definition for the object this references, keyed from
271
+ # +key+.
272
+ def encrypted_object(key)
273
+ @on_encode.call(self) if @on_encode
274
+
275
+ output = "#{@identifier} #{gen} obj\n"
276
+ unless @stream.empty?
277
+ output << PDF::Core::EncryptedPdfObject(data.merge(@stream.data), key, @identifier, gen) << "\n" <<
278
+ @stream.encrypted_object(key, @identifier, gen)
279
+ else
280
+ output << PDF::Core::EncryptedPdfObject(data, key, @identifier, gen) << "\n"
281
+ end
282
+
283
+ output << "endobj\n"
284
+ end
285
+
286
+ end
287
+ end
288
+ end
@@ -0,0 +1,54 @@
1
+ # encoding: utf-8
2
+
3
+ # Implementation of the "ARCFOUR" algorithm ("alleged RC4 (tm)"). Implemented
4
+ # as described at:
5
+ # http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt
6
+ #
7
+ # "RC4" is a trademark of RSA Data Security, Inc.
8
+ #
9
+ # Copyright August 2009, Brad Ediger. All Rights Reserved.
10
+ #
11
+ # This is free software. Please see the LICENSE and COPYING files for details.
12
+
13
+ # @private
14
+ class Arcfour
15
+ def initialize(key)
16
+ # Convert string key to Array of integers
17
+ key = key.unpack('c*') if key.is_a?(String)
18
+
19
+ # 1. Allocate an 256 element array of 8 bit bytes to be used as an S-box
20
+ # 2. Initialize the S-box. Fill each entry first with it's index
21
+ @sbox = (0..255).to_a
22
+
23
+ # 3. Fill another array of the same size (256) with the key, repeating
24
+ # bytes as necessary.
25
+ s2 = []
26
+ while s2.length < 256
27
+ s2 += key
28
+ end
29
+ s2 = s2[0, 256]
30
+
31
+ # 4. Set j to zero and initialize the S-box
32
+ j = 0
33
+ (0..255).each do |i|
34
+ j = (j + @sbox[i] + s2[i]) % 256
35
+ @sbox[i], @sbox[j] = @sbox[j], @sbox[i]
36
+ end
37
+
38
+ @i = @j = 0
39
+ end
40
+
41
+ def encrypt(string)
42
+ string.unpack('c*').map{|byte| byte ^ key_byte}.pack('c*')
43
+ end
44
+
45
+ private
46
+
47
+ # Produces the next byte of key material in the stream (3.2 Stream Generation)
48
+ def key_byte
49
+ @i = (@i + 1) % 256
50
+ @j = (@j + @sbox[@i]) % 256
51
+ @sbox[@i], @sbox[@j] = @sbox[@j], @sbox[@i]
52
+ @sbox[(@sbox[@i] + @sbox[@j]) % 256]
53
+ end
54
+ end
@@ -0,0 +1,94 @@
1
+ # encoding: utf-8
2
+ #
3
+ # soft_mask.rb : Implements soft-masking
4
+ #
5
+ # Copyright September 2012, Alexander Mankuta. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ #
9
+
10
+ module Prawn
11
+
12
+ # The Prawn::SoftMask module is used to create arbitrary transparency in
13
+ # document. Using a soft mask allows creating more visually rich documents.
14
+ #
15
+ # You must group soft mask and graphics it's applied to under
16
+ # save_graphics_state because soft mask is a part of graphic state in PDF.
17
+ #
18
+ # Example:
19
+ # pdf.save_graphics_state do
20
+ # pdf.soft_mask do
21
+ # pdf.fill_color "444444"
22
+ # pdf.fill_polygon [0, 40], [60, 10], [120, 40], [60, 68]
23
+ # end
24
+ # pdf.fill_color '000000'
25
+ # pdf.fill_rectangle [0, 50], 120, 68
26
+ # end
27
+ #
28
+ module SoftMask
29
+ # @group Stable API
30
+
31
+ def soft_mask(&block)
32
+ renderer.min_version(1.4)
33
+
34
+ group_attrs = ref!({
35
+ :Type => :Group,
36
+ :S => :Transparency,
37
+ :CS => :DeviceRGB,
38
+ :I => false,
39
+ :K => false
40
+ })
41
+
42
+ group = ref!({
43
+ :Type => :XObject,
44
+ :Subtype => :Form,
45
+ :BBox => state.page.dimensions,
46
+ :Group => group_attrs,
47
+ })
48
+
49
+ state.page.stamp_stream(group, &block)
50
+
51
+ mask = ref!({
52
+ :Type => :Mask,
53
+ :S => :Luminosity,
54
+ :G => group
55
+ })
56
+
57
+ g_state = ref!({
58
+ :Type => :ExtGState,
59
+ :SMask => mask,
60
+
61
+ :AIS => false,
62
+ :BM => :Normal,
63
+ :OP => false,
64
+ :op => false,
65
+ :OPM => 1,
66
+ :SA => true,
67
+ })
68
+
69
+ registry_key = {
70
+ :bbox => state.page.dimensions,
71
+ :mask => [group.stream.filters.normalized, group.stream.filtered_stream],
72
+ :page => state.page_count,
73
+ }.hash
74
+
75
+ if soft_mask_registry[registry_key]
76
+ renderer.add_content "/#{soft_mask_registry[registry_key]} gs"
77
+ else
78
+ masks = page.resources[:ExtGState] ||= {}
79
+ id = masks.empty? ? 'GS1' : masks.keys.sort.last.succ
80
+ masks[id] = g_state
81
+
82
+ soft_mask_registry[registry_key] = id
83
+
84
+ renderer.add_content "/#{id} gs"
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ def soft_mask_registry
91
+ @soft_mask_registry ||= {}
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,136 @@
1
+ # encoding: utf-8
2
+ #
3
+ # stamp.rb : Implements a repeatable stamp
4
+ #
5
+ # Copyright October 2009, Daniel Nelson. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ #
9
+
10
+ module Prawn
11
+
12
+ # The Prawn::Stamp module is used to create content that will be
13
+ # included multiple times in a document. Using a stamp has three
14
+ # advantages over creating content anew each time it is placed on
15
+ # the page:
16
+ # i. faster document creation
17
+ # ii. smaller final document
18
+ # iii. faster display on subsequent displays of the repeated
19
+ # element because the viewer application can cache the rendered
20
+ # results
21
+ #
22
+ # Example:
23
+ # pdf.create_stamp("my_stamp") {
24
+ # pdf.fill_circle([10, 15], 5)
25
+ # pdf.draw_text("hello world", :at => [20, 10])
26
+ # }
27
+ # pdf.stamp("my_stamp")
28
+ #
29
+ module Stamp
30
+
31
+ # @group Stable API
32
+
33
+ # Renders the stamp named <tt>name</tt> to the page
34
+ # raises <tt>Prawn::Errors::InvalidName</tt> if name.empty?
35
+ # raises <tt>Prawn::Errors::UndefinedObjectName</tt> if no stamp
36
+ # has been created with this name
37
+ #
38
+ # Example:
39
+ # pdf.create_stamp("my_stamp") {
40
+ # pdf.fill_circle([10, 15], 5)
41
+ # pdf.text("hello world", :at => [20, 10])
42
+ # }
43
+ # pdf.stamp("my_stamp")
44
+ #
45
+ def stamp(name)
46
+ dictionary_name, dictionary = stamp_dictionary(name)
47
+ renderer.add_content "/#{dictionary_name} Do"
48
+ state.page.xobjects.merge!(dictionary_name => dictionary)
49
+ end
50
+
51
+ # Renders the stamp named <tt>name</tt> at a position offset from
52
+ # the initial coords at which the elements of the stamp was
53
+ # created
54
+ #
55
+ # Example:
56
+ # pdf.create_stamp("circle") do
57
+ # pdf.fill_circle([0, 0], 25)
58
+ # end
59
+ # # draws a circle at 100, 100
60
+ # pdf.stamp_at("circle", [100, 100])
61
+ #
62
+ # See stamp() for exceptions that might be raised
63
+ #
64
+ def stamp_at(name, point)
65
+ translate(point[0], point[1]) { stamp(name) }
66
+ end
67
+
68
+ # Creates a re-usable stamp named <tt>name</tt>
69
+ #
70
+ # raises <tt>Prawn::Errors::NameTaken</tt> if a stamp already
71
+ # exists in this document with this name
72
+ # raises <tt>Prawn::Errors::InvalidName</tt> if name.empty?
73
+ #
74
+ # Example:
75
+ # pdf.create_stamp("my_stamp") {
76
+ # pdf.fill_circle([10, 15], 5)
77
+ # pdf.draw_text("hello world", :at => [20, 10])
78
+ # }
79
+ #
80
+ def create_stamp(name, &block)
81
+ dictionary = create_stamp_dictionary(name)
82
+
83
+ state.page.stamp_stream(dictionary, &block)
84
+ end
85
+
86
+ private
87
+
88
+ def stamp_dictionary_registry
89
+ @stamp_dictionary_registry ||= {}
90
+ end
91
+
92
+ def next_stamp_dictionary_id
93
+ stamp_dictionary_registry.length + 1
94
+ end
95
+
96
+ def stamp_dictionary(name)
97
+ raise Prawn::Errors::InvalidName if name.empty?
98
+ if stamp_dictionary_registry[name].nil?
99
+ raise Prawn::Errors::UndefinedObjectName
100
+ end
101
+
102
+ dict = stamp_dictionary_registry[name]
103
+
104
+ dictionary_name = dict[:stamp_dictionary_name]
105
+ dictionary = dict[:stamp_dictionary]
106
+ [dictionary_name, dictionary]
107
+ end
108
+
109
+ def create_stamp_dictionary(name)
110
+ raise Prawn::Errors::InvalidName if name.empty?
111
+ raise Prawn::Errors::NameTaken unless stamp_dictionary_registry[name].nil?
112
+ # BBox origin is the lower left margin of the page, so we need
113
+ # it to be the full dimension of the page, or else things that
114
+ # should appear near the top or right margin are invisible
115
+ dictionary = ref!(:Type => :XObject,
116
+ :Subtype => :Form,
117
+ :BBox => [0, 0,
118
+ state.page.dimensions[2], state.page.dimensions[3]])
119
+
120
+ dictionary_name = "Stamp#{next_stamp_dictionary_id}"
121
+
122
+ stamp_dictionary_registry[name] = { :stamp_dictionary_name => dictionary_name,
123
+ :stamp_dictionary => dictionary }
124
+ dictionary
125
+ end
126
+
127
+ def freeze_stamp_graphics
128
+ update_colors
129
+ write_line_width
130
+ write_stroke_cap_style
131
+ write_stroke_join_style
132
+ write_stroke_dash
133
+ end
134
+
135
+ end
136
+ end