alphasights-prawn 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. data/COPYING +340 -0
  2. data/HACKING +50 -0
  3. data/LICENSE +56 -0
  4. data/README +141 -0
  5. data/Rakefile +52 -0
  6. data/data/encodings/win_ansi.txt +29 -0
  7. data/data/fonts/Action Man.dfont +0 -0
  8. data/data/fonts/Activa.ttf +0 -0
  9. data/data/fonts/Chalkboard.ttf +0 -0
  10. data/data/fonts/Courier-Bold.afm +342 -0
  11. data/data/fonts/Courier-BoldOblique.afm +342 -0
  12. data/data/fonts/Courier-Oblique.afm +342 -0
  13. data/data/fonts/Courier.afm +342 -0
  14. data/data/fonts/DejaVuSans.ttf +0 -0
  15. data/data/fonts/Dustismo_Roman.ttf +0 -0
  16. data/data/fonts/Helvetica-Bold.afm +2827 -0
  17. data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
  18. data/data/fonts/Helvetica-Oblique.afm +3051 -0
  19. data/data/fonts/Helvetica.afm +3051 -0
  20. data/data/fonts/MustRead.html +19 -0
  21. data/data/fonts/Symbol.afm +213 -0
  22. data/data/fonts/Times-Bold.afm +2588 -0
  23. data/data/fonts/Times-BoldItalic.afm +2384 -0
  24. data/data/fonts/Times-Italic.afm +2667 -0
  25. data/data/fonts/Times-Roman.afm +2419 -0
  26. data/data/fonts/ZapfDingbats.afm +225 -0
  27. data/data/fonts/comicsans.ttf +0 -0
  28. data/data/fonts/gkai00mp.ttf +0 -0
  29. data/data/images/16bit.alpha +0 -0
  30. data/data/images/16bit.dat +0 -0
  31. data/data/images/16bit.png +0 -0
  32. data/data/images/arrow.png +0 -0
  33. data/data/images/arrow2.png +0 -0
  34. data/data/images/barcode_issue.png +0 -0
  35. data/data/images/dice.alpha +0 -0
  36. data/data/images/dice.dat +0 -0
  37. data/data/images/dice.png +0 -0
  38. data/data/images/dice_interlaced.png +0 -0
  39. data/data/images/fractal.jpg +0 -0
  40. data/data/images/letterhead.jpg +0 -0
  41. data/data/images/page_white_text.alpha +0 -0
  42. data/data/images/page_white_text.dat +0 -0
  43. data/data/images/page_white_text.png +0 -0
  44. data/data/images/pigs.jpg +0 -0
  45. data/data/images/rails.dat +0 -0
  46. data/data/images/rails.png +0 -0
  47. data/data/images/ruport.png +0 -0
  48. data/data/images/ruport_data.dat +0 -0
  49. data/data/images/ruport_transparent.png +0 -0
  50. data/data/images/ruport_type0.png +0 -0
  51. data/data/images/stef.jpg +0 -0
  52. data/data/images/tru256.bmp +0 -0
  53. data/data/images/web-links.dat +1 -0
  54. data/data/images/web-links.png +0 -0
  55. data/data/pdfs/complex_template.pdf +0 -0
  56. data/data/pdfs/contains_ttf_font.pdf +0 -0
  57. data/data/pdfs/encrypted.pdf +0 -0
  58. data/data/pdfs/hexagon.pdf +61 -0
  59. data/data/pdfs/indirect_reference.pdf +86 -0
  60. data/data/pdfs/nested_pages.pdf +118 -0
  61. data/data/pdfs/resources_as_indirect_object.pdf +83 -0
  62. data/data/pdfs/two_hexagons.pdf +90 -0
  63. data/data/pdfs/version_1_6.pdf +61 -0
  64. data/data/shift_jis_text.txt +1 -0
  65. data/examples/bounding_box/bounding_boxes.rb +43 -0
  66. data/examples/bounding_box/indentation.rb +34 -0
  67. data/examples/bounding_box/russian_boxes.rb +36 -0
  68. data/examples/bounding_box/stretched_nesting.rb +67 -0
  69. data/examples/builder/simple.rb +28 -0
  70. data/examples/example_helper.rb +4 -0
  71. data/examples/general/background.rb +23 -0
  72. data/examples/general/canvas.rb +15 -0
  73. data/examples/general/context_sensitive_headers.rb +37 -0
  74. data/examples/general/float.rb +11 -0
  75. data/examples/general/margin.rb +36 -0
  76. data/examples/general/measurement_units.rb +51 -0
  77. data/examples/general/metadata-info.rb +16 -0
  78. data/examples/general/multi_page_layout.rb +18 -0
  79. data/examples/general/outlines.rb +50 -0
  80. data/examples/general/page_geometry.rb +31 -0
  81. data/examples/general/page_numbering.rb +15 -0
  82. data/examples/general/repeaters.rb +47 -0
  83. data/examples/general/stamp.rb +41 -0
  84. data/examples/general/templates.rb +13 -0
  85. data/examples/graphics/basic_images.rb +23 -0
  86. data/examples/graphics/chunkable.rb +38 -0
  87. data/examples/graphics/cmyk.rb +12 -0
  88. data/examples/graphics/curves.rb +11 -0
  89. data/examples/graphics/hexagon.rb +13 -0
  90. data/examples/graphics/image_fit.rb +15 -0
  91. data/examples/graphics/image_flow.rb +37 -0
  92. data/examples/graphics/image_position.rb +17 -0
  93. data/examples/graphics/line.rb +32 -0
  94. data/examples/graphics/png_types.rb +22 -0
  95. data/examples/graphics/polygons.rb +16 -0
  96. data/examples/graphics/remote_images.rb +12 -0
  97. data/examples/graphics/rounded_polygons.rb +19 -0
  98. data/examples/graphics/rounded_rectangle.rb +20 -0
  99. data/examples/graphics/ruport_style_helpers.rb +19 -0
  100. data/examples/graphics/stroke_bounds.rb +20 -0
  101. data/examples/graphics/stroke_cap_and_join.rb +45 -0
  102. data/examples/graphics/stroke_dash.rb +42 -0
  103. data/examples/graphics/transformations.rb +52 -0
  104. data/examples/graphics/transparency.rb +26 -0
  105. data/examples/m17n/chinese_text_wrapping.rb +17 -0
  106. data/examples/m17n/euro.rb +15 -0
  107. data/examples/m17n/sjis.rb +28 -0
  108. data/examples/m17n/utf8.rb +13 -0
  109. data/examples/m17n/win_ansi_charset.rb +54 -0
  110. data/examples/security/hello_foo.rb +8 -0
  111. data/examples/table/bill.rb +53 -0
  112. data/examples/table/cell.rb +12 -0
  113. data/examples/table/checkerboard.rb +22 -0
  114. data/examples/table/header.rb +14 -0
  115. data/examples/table/inline_format_table.rb +12 -0
  116. data/examples/table/multi_page_table.rb +9 -0
  117. data/examples/table/simple_table.rb +24 -0
  118. data/examples/table/subtable.rb +12 -0
  119. data/examples/table/widths.rb +20 -0
  120. data/examples/text/alignment.rb +18 -0
  121. data/examples/text/character_spacing.rb +12 -0
  122. data/examples/text/dfont.rb +48 -0
  123. data/examples/text/family_based_styling.rb +24 -0
  124. data/examples/text/font_calculations.rb +91 -0
  125. data/examples/text/font_size.rb +33 -0
  126. data/examples/text/hyphenation.rb +45 -0
  127. data/examples/text/indent_paragraphs.rb +22 -0
  128. data/examples/text/inline_format.rb +103 -0
  129. data/examples/text/kerning.rb +30 -0
  130. data/examples/text/rotated.rb +98 -0
  131. data/examples/text/shaped_text_box.rb +31 -0
  132. data/examples/text/simple_text.rb +17 -0
  133. data/examples/text/simple_text_ttf.rb +17 -0
  134. data/examples/text/text_box.rb +88 -0
  135. data/examples/text/text_box_returning_excess.rb +51 -0
  136. data/examples/text/text_flow.rb +67 -0
  137. data/lib/prawn.rb +27 -0
  138. data/lib/prawn/canvas.rb +119 -0
  139. data/lib/prawn/chunkable.rb +37 -0
  140. data/lib/prawn/compatibility.rb +51 -0
  141. data/lib/prawn/core.rb +85 -0
  142. data/lib/prawn/core/annotations.rb +61 -0
  143. data/lib/prawn/core/byte_string.rb +9 -0
  144. data/lib/prawn/core/chunk.rb +36 -0
  145. data/lib/prawn/core/destinations.rb +90 -0
  146. data/lib/prawn/core/document_state.rb +78 -0
  147. data/lib/prawn/core/literal_string.rb +16 -0
  148. data/lib/prawn/core/name_tree.rb +165 -0
  149. data/lib/prawn/core/object_store.rb +236 -0
  150. data/lib/prawn/core/page.rb +179 -0
  151. data/lib/prawn/core/pdf_object.rb +108 -0
  152. data/lib/prawn/core/reference.rb +112 -0
  153. data/lib/prawn/core/text.rb +140 -0
  154. data/lib/prawn/core/text/formatted/arranger.rb +266 -0
  155. data/lib/prawn/core/text/formatted/line_wrap.rb +127 -0
  156. data/lib/prawn/core/text/formatted/wrap.rb +112 -0
  157. data/lib/prawn/core/text/line_wrap.rb +209 -0
  158. data/lib/prawn/core/text/wrap.rb +80 -0
  159. data/lib/prawn/document.rb +573 -0
  160. data/lib/prawn/document/bounding_box.rb +425 -0
  161. data/lib/prawn/document/graphics_state.rb +48 -0
  162. data/lib/prawn/document/internals.rb +170 -0
  163. data/lib/prawn/document/page_geometry.rb +136 -0
  164. data/lib/prawn/document/snapshot.rb +87 -0
  165. data/lib/prawn/document_builder.rb +51 -0
  166. data/lib/prawn/document_builder/command.rb +38 -0
  167. data/lib/prawn/document_builder/constructs.rb +2 -0
  168. data/lib/prawn/document_builder/constructs/flowing_text_construct.rb +18 -0
  169. data/lib/prawn/document_builder/constructs/path_construct.rb +9 -0
  170. data/lib/prawn/document_builder/layout.rb +25 -0
  171. data/lib/prawn/document_builder/modifications.rb +2 -0
  172. data/lib/prawn/document_builder/modifications/layout_modification.rb +9 -0
  173. data/lib/prawn/document_builder/modifications/path_modification.rb +9 -0
  174. data/lib/prawn/encoding.rb +121 -0
  175. data/lib/prawn/errors.rb +94 -0
  176. data/lib/prawn/font.rb +341 -0
  177. data/lib/prawn/font/afm.rb +225 -0
  178. data/lib/prawn/font/dfont.rb +42 -0
  179. data/lib/prawn/font/ttf.rb +350 -0
  180. data/lib/prawn/graphics.rb +325 -0
  181. data/lib/prawn/graphics/cap_style.rb +38 -0
  182. data/lib/prawn/graphics/color.rb +205 -0
  183. data/lib/prawn/graphics/dash.rb +71 -0
  184. data/lib/prawn/graphics/join_style.rb +38 -0
  185. data/lib/prawn/graphics/transformation.rb +156 -0
  186. data/lib/prawn/graphics/transparency.rb +99 -0
  187. data/lib/prawn/images.rb +348 -0
  188. data/lib/prawn/images/jpg.rb +46 -0
  189. data/lib/prawn/images/png.rb +226 -0
  190. data/lib/prawn/measurement_extensions.rb +46 -0
  191. data/lib/prawn/measurements.rb +71 -0
  192. data/lib/prawn/outline.rb +278 -0
  193. data/lib/prawn/repeater.rb +129 -0
  194. data/lib/prawn/security.rb +262 -0
  195. data/lib/prawn/security/arcfour.rb +51 -0
  196. data/lib/prawn/stamp.rb +126 -0
  197. data/lib/prawn/table.rb +421 -0
  198. data/lib/prawn/table/accessors.rb +180 -0
  199. data/lib/prawn/table/cell.rb +350 -0
  200. data/lib/prawn/table/cell/in_table.rb +27 -0
  201. data/lib/prawn/table/cell/subtable.rb +65 -0
  202. data/lib/prawn/table/cell/text.rb +125 -0
  203. data/lib/prawn/text.rb +449 -0
  204. data/lib/prawn/text/box.rb +392 -0
  205. data/lib/prawn/text/formatted.rb +4 -0
  206. data/lib/prawn/text/formatted/box.rb +228 -0
  207. data/lib/prawn/text/formatted/fragment.rb +181 -0
  208. data/lib/prawn/text/formatted/parser.rb +213 -0
  209. data/spec/annotations_spec.rb +90 -0
  210. data/spec/bounding_box_spec.rb +190 -0
  211. data/spec/cell_spec.rb +348 -0
  212. data/spec/destinations_spec.rb +15 -0
  213. data/spec/document_spec.rb +473 -0
  214. data/spec/font_spec.rb +324 -0
  215. data/spec/formatted_text_arranger_spec.rb +426 -0
  216. data/spec/formatted_text_box_spec.rb +756 -0
  217. data/spec/formatted_text_fragment_spec.rb +211 -0
  218. data/spec/graphics_spec.rb +446 -0
  219. data/spec/images_spec.rb +96 -0
  220. data/spec/inline_formatted_text_parser_spec.rb +502 -0
  221. data/spec/jpg_spec.rb +25 -0
  222. data/spec/line_wrap_spec.rb +341 -0
  223. data/spec/measurement_units_spec.rb +23 -0
  224. data/spec/name_tree_spec.rb +112 -0
  225. data/spec/object_store_spec.rb +160 -0
  226. data/spec/outline_spec.rb +269 -0
  227. data/spec/pdf_object_spec.rb +170 -0
  228. data/spec/png_spec.rb +237 -0
  229. data/spec/reference_spec.rb +82 -0
  230. data/spec/repeater_spec.rb +96 -0
  231. data/spec/security_spec.rb +120 -0
  232. data/spec/snapshot_spec.rb +138 -0
  233. data/spec/spec_helper.rb +26 -0
  234. data/spec/stamp_spec.rb +108 -0
  235. data/spec/stroke_styles_spec.rb +163 -0
  236. data/spec/table_spec.rb +598 -0
  237. data/spec/template_spec.rb +158 -0
  238. data/spec/text_at_spec.rb +119 -0
  239. data/spec/text_box_spec.rb +742 -0
  240. data/spec/text_spacing_spec.rb +75 -0
  241. data/spec/text_spec.rb +333 -0
  242. data/spec/text_with_inline_formatting_spec.rb +193 -0
  243. data/spec/transparency_spec.rb +89 -0
  244. metadata +331 -0
@@ -0,0 +1,129 @@
1
+ # encoding: utf-8
2
+ #
3
+ # repeater.rb : Implements repeated page elements.
4
+ # Heavy inspired by repeating_element() in PDF::Wrapper
5
+ # http://pdf-wrapper.rubyforge.org/
6
+ #
7
+ # Copyright November 2009, Gregory Brown. All Rights Reserved.
8
+ #
9
+ # This is free software. Please see the LICENSE and COPYING files for details.
10
+
11
+ module Prawn
12
+
13
+ class Document
14
+
15
+ # A list of all repeaters in the document.
16
+ # See Document#repeat for details
17
+ #
18
+ def repeaters
19
+ @repeaters ||= []
20
+ end
21
+
22
+ # Provides a way to execute a block of code repeatedly based on a
23
+ # page_filter. Since Stamp is used under the hood, this method is very space
24
+ # efficient.
25
+ #
26
+ # Available page filters are:
27
+ # :all -- repeats on every page
28
+ # :odd -- repeats on odd pages
29
+ # :even -- repeats on even pages
30
+ # some_array -- repeats on every page listed in the array
31
+ # some_range -- repeats on every page included in the range
32
+ # some_lambda -- yields page number and repeats for true return values
33
+ #
34
+ # Also accepts an optional second argument for dynamic content which executes the code
35
+ # in the context of the filtered pages without using a Stamp.
36
+ #
37
+ # Example:
38
+ #
39
+ # Prawn::Document.generate("repeat.pdf", :skip_page_creation => true) do
40
+ #
41
+ # repeat :all do
42
+ # draw_text "ALLLLLL", :at => bounds.top_left
43
+ # end
44
+ #
45
+ # repeat :odd do
46
+ # draw_text "ODD", :at => [0,0]
47
+ # end
48
+ #
49
+ # repeat :even do
50
+ # draw_text "EVEN", :at => [0,0]
51
+ # end
52
+ #
53
+ # repeat [1,2] do
54
+ # draw_text "[1,2]", :at => [100,0]
55
+ # end
56
+ #
57
+ # repeat 2..4 do
58
+ # draw_text "2..4", :at => [200,0]
59
+ # end
60
+ #
61
+ # repeat(lambda { |pg| pg % 3 == 0 }) do
62
+ # draw_text "Every third", :at => [250, 20]
63
+ # end
64
+ #
65
+ # 10.times do
66
+ # start_new_page
67
+ # draw_text "A wonderful page", :at => [400,400]
68
+ # end
69
+ #
70
+ # repeat(:all, :dynamic => true) do
71
+ # text page_number, :at => [500, 0]
72
+ # end
73
+ #
74
+ # end
75
+ #
76
+ def repeat(page_filter, options={}, &block)
77
+ repeaters << Prawn::Repeater.new(self, page_filter, !!options[:dynamic], &block)
78
+ end
79
+ end
80
+
81
+ class Repeater #:nodoc:
82
+ class << self
83
+ attr_writer :count
84
+
85
+ def count
86
+ @count ||= 0
87
+ end
88
+ end
89
+
90
+ attr_reader :name
91
+
92
+ def initialize(document, page_filter, dynamic = false, &block)
93
+ @document = document
94
+ @page_filter = page_filter
95
+ @dynamic = dynamic
96
+ @stamp_name = "prawn_repeater(#{Repeater.count})"
97
+ @document.create_stamp(@stamp_name, &block) unless dynamic
98
+ @block = block if dynamic
99
+
100
+ Repeater.count += 1
101
+ end
102
+
103
+ def match?(page_number)
104
+ case @page_filter
105
+ when :all
106
+ true
107
+ when :odd
108
+ page_number % 2 == 1
109
+ when :even
110
+ page_number % 2 == 0
111
+ when Range, Array
112
+ @page_filter.include?(page_number)
113
+ when Proc
114
+ @page_filter.call(page_number)
115
+ end
116
+ end
117
+
118
+ def run(page_number)
119
+ if !@dynamic
120
+ @document.stamp(@stamp_name) if match?(page_number)
121
+ elsif @block
122
+ @block.arity < 1 ? @document.instance_eval(&@block) : @block[@document]
123
+ end
124
+ end
125
+
126
+ end
127
+ end
128
+
129
+
@@ -0,0 +1,262 @@
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
+ require 'prawn/security/arcfour'
11
+ require 'prawn/core/byte_string'
12
+
13
+ module Prawn
14
+ class Document
15
+
16
+ # Implements PDF encryption (password protection and permissions) as
17
+ # specified in the PDF Reference, version 1.3, section 3.5 "Encryption".
18
+ module Security
19
+ include Prawn::Core
20
+
21
+ # Encrypts the document, to protect confidential data or control
22
+ # modifications to the document. The encryption algorithm used is
23
+ # detailed in the PDF Reference 1.3, section 3.5 "Encryption", and it is
24
+ # implemented by all major PDF readers.
25
+ #
26
+ # +options+ can contain the following:
27
+ #
28
+ # <tt>:user_password</tt>:: Password required to open the document. If
29
+ # this is omitted or empty, no password will be
30
+ # required. The document will still be
31
+ # encrypted, but anyone can read it.
32
+ #
33
+ # <tt>:owner_password</tt>:: Password required to make modifications to
34
+ # the document or change or override its
35
+ # permissions. If this is set to
36
+ # <tt>:random</tt>, a random password will be
37
+ # used; this can be useful if you never want
38
+ # users to be able to override the document
39
+ # permissions.
40
+ #
41
+ # <tt>:permissions</tt>:: A hash mapping permission symbols (see below) to
42
+ # <tt>true</tt> or <tt>false</tt>. True means
43
+ # "permitted", and false means "not permitted".
44
+ # All permissions default to <tt>true</tt>.
45
+ #
46
+ # The following permissions can be specified:
47
+ #
48
+ # <tt>:print_document</tt>:: Print document.
49
+ #
50
+ # <tt>:modify_document</tt>:: Modify contents of document (other than text
51
+ # annotations and interactive form fields).
52
+ #
53
+ # <tt>:copy_contents</tt>:: Copy text and graphics from document.
54
+ #
55
+ # <tt>:modify_annotations</tt>:: Add or modify text annotations and
56
+ # interactive form fields.
57
+ #
58
+ # == Examples
59
+ #
60
+ # Deny printing to everyone, but allow anyone to open without a password:
61
+ #
62
+ # encrypt_document :permissions => { :print_document => false },
63
+ # :owner_password => :random
64
+ #
65
+ # Set a user and owner password on the document, with full permissions for
66
+ # both the user and the owner:
67
+ #
68
+ # encrypt_document :user_password => 'foo', :owner_password => 'bar'
69
+ #
70
+ # Set no passwords, grant all permissions (This is useful because the
71
+ # default in some readers, if no permissions are specified, is "deny"):
72
+ #
73
+ # encrypt_document
74
+ #
75
+ # == Caveats
76
+ #
77
+ # * The encryption used is weak; the key is password-derived and is
78
+ # limited to 40 bits, due to US export controls in effect at the time
79
+ # the PDF standard was written.
80
+ #
81
+ # * There is nothing technologically requiring PDF readers to respect the
82
+ # permissions embedded in a document. Many PDF readers do not.
83
+ #
84
+ # * In short, you have <b>no security at all</b> against a moderately
85
+ # motivated person. Don't use this for anything super-serious. This is
86
+ # not a limitation of Prawn, but is rather a built-in limitation of the
87
+ # PDF format.
88
+ #
89
+ def encrypt_document(options={})
90
+ Prawn.verify_options [:user_password, :owner_password, :permissions],
91
+ options
92
+ @user_password = options.delete(:user_password) || ""
93
+
94
+ @owner_password = options.delete(:owner_password) || @user_password
95
+ if @owner_password == :random
96
+ # Generate a completely ridiculous password
97
+ @owner_password = (1..32).map{ rand(256) }.pack("c*")
98
+ end
99
+
100
+ self.permissions = options.delete(:permissions) || {}
101
+
102
+ # Shove the necessary entries in the trailer and enable encryption.
103
+ state.trailer[:Encrypt] = encryption_dictionary
104
+ state.encrypt = true
105
+ state.encryption_key = user_encryption_key
106
+ end
107
+
108
+ # Encrypts the given string under the given key, also requiring the
109
+ # object ID and generation number of the reference.
110
+ # See Algorithm 3.1.
111
+ def self.encrypt_string(str, key, id, gen)
112
+ # Convert ID and Gen number into little-endian truncated byte strings
113
+ id = [id].pack('V')[0,3]
114
+ gen = [gen].pack('V')[0,2]
115
+ extended_key = "#{key}#{id}#{gen}"
116
+
117
+ # Compute the RC4 key from the extended key and perform the encryption
118
+ rc4_key = Digest::MD5.digest(extended_key)[0, 10]
119
+ Arcfour.new(rc4_key).encrypt(str)
120
+ end
121
+
122
+ private
123
+
124
+ # Provides the values for the trailer encryption dictionary.
125
+ def encryption_dictionary
126
+ { :Filter => :Standard, # default PDF security handler
127
+ :V => 1, # "Algorithm 3.1", PDF reference 1.3
128
+ :R => 2, # Revision 2 of the algorithm
129
+ :O => ByteString.new(owner_password_hash),
130
+ :U => ByteString.new(user_password_hash),
131
+ :P => permissions_value }
132
+ end
133
+
134
+ # Flags in the permissions word, numbered as LSB = 1
135
+ PermissionsBits = { :print_document => 3,
136
+ :modify_contents => 4,
137
+ :copy_contents => 5,
138
+ :modify_annotations => 6 }
139
+
140
+ FullPermissions = 0b1111_1111_1111_1111_1111_1111_1111_1111
141
+
142
+ def permissions=(perms={})
143
+ @permissions ||= FullPermissions
144
+ perms.each do |key, value|
145
+ # 0-based bit number, from LSB
146
+ bit_position = PermissionsBits[key] - 1
147
+
148
+ if value # set bit
149
+ @permissions |= (1 << bit_position)
150
+ else # clear bit
151
+ @permissions &= ~(1 << bit_position)
152
+ end
153
+ end
154
+ end
155
+
156
+ def permissions_value
157
+ @permissions || FullPermissions
158
+ end
159
+
160
+ PasswordPadding =
161
+ "28BF4E5E4E758A4164004E56FFFA01082E2E00B6D0683E802F0CA9FE6453697A".
162
+ scan(/../).map{|x| x.to_i(16)}.pack("c*")
163
+
164
+ # Pads or truncates a password to 32 bytes as per Alg 3.2.
165
+ def pad_password(password)
166
+ password = password[0, 32]
167
+ password + PasswordPadding[0, 32 - password.length]
168
+ end
169
+
170
+ def user_encryption_key
171
+ @user_encryption_key ||= begin
172
+ md5 = Digest::MD5.new
173
+ md5 << pad_password(@user_password)
174
+ md5 << owner_password_hash
175
+ md5 << [permissions_value].pack("V")
176
+ md5.digest[0, 5]
177
+ end
178
+ end
179
+
180
+ # The O (owner) value in the encryption dictionary. Algorithm 3.3.
181
+ def owner_password_hash
182
+ @owner_password_hash ||= begin
183
+ key = Digest::MD5.digest(pad_password(@owner_password))[0, 5]
184
+ Arcfour.new(key).encrypt(pad_password(@user_password))
185
+ end
186
+ end
187
+
188
+ # The U (user) value in the encryption dictionary. Algorithm 3.4.
189
+ def user_password_hash
190
+ Arcfour.new(user_encryption_key).encrypt(PasswordPadding)
191
+ end
192
+
193
+ end
194
+
195
+ end
196
+
197
+ module Core #:nodoc:
198
+ module_function
199
+
200
+ # Like PdfObject, but returns an encrypted result if required.
201
+ # For direct objects, requires the object identifier and generation number
202
+ # from the indirect object referencing obj.
203
+ def EncryptedPdfObject(obj, key, id, gen, in_content_stream=false)
204
+ case obj
205
+ when Array
206
+ "[" << obj.map { |e|
207
+ EncryptedPdfObject(e, key, id, gen, in_content_stream)
208
+ }.join(' ') << "]"
209
+ when LiteralString
210
+ # FIXME: encrypted?
211
+ obj = obj.gsub(/[\\\n\(\)]/) { |m| "\\#{m}" }
212
+ "(#{obj})"
213
+ when Time
214
+ # FIXME: encrypted?
215
+ obj = obj.strftime("D:%Y%m%d%H%M%S%z").chop.chop + "'00'"
216
+ obj = obj.gsub(/[\\\n\(\)]/) { |m| "\\#{m}" }
217
+ "(#{obj})"
218
+ when String
219
+ PdfObject(
220
+ ByteString.new(
221
+ Document::Security.encrypt_string(obj, key, id, gen)),
222
+ in_content_stream)
223
+ when Hash
224
+ output = "<< "
225
+ obj.each do |k,v|
226
+ unless String === k || Symbol === k
227
+ raise Prawn::Errors::FailedObjectConversion,
228
+ "A PDF Dictionary must be keyed by names"
229
+ end
230
+ output << PdfObject(k.to_sym, in_content_stream) << " " <<
231
+ EncryptedPdfObject(v, key, id, gen, in_content_stream) << "\n"
232
+ end
233
+ output << ">>"
234
+ when NameTree::Value
235
+ PdfObject(obj.name) + " " +
236
+ EncryptedPdfObject(obj.value, key, id, gen, in_content_stream)
237
+ else # delegate back to PdfObject
238
+ PdfObject(obj, in_content_stream)
239
+ end
240
+ end
241
+
242
+ class Reference
243
+
244
+ # Returns the object definition for the object this references, keyed from
245
+ # +key+.
246
+ def encrypted_object(key)
247
+ @on_encode.call(self) if @on_encode
248
+ output = "#{@identifier} #{gen} obj\n" <<
249
+ Prawn::Core::EncryptedPdfObject(data, key, @identifier, gen) << "\n"
250
+ if @stream
251
+ output << "stream\n" <<
252
+ Document::Security.encrypt_string(@stream, key, @identifier, gen) <<
253
+ "\nendstream\n"
254
+ end
255
+ output << "endobj\n"
256
+ end
257
+
258
+ end
259
+ end
260
+
261
+ end
262
+
@@ -0,0 +1,51 @@
1
+ # Implementation of the "ARCFOUR" algorithm ("alleged RC4 (tm)"). Implemented
2
+ # as described at:
3
+ # http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt
4
+ #
5
+ # "RC4" is a trademark of RSA Data Security, Inc.
6
+ #
7
+ # Copyright August 2009, Brad Ediger. All Rights Reserved.
8
+ #
9
+ # This is free software. Please see the LICENSE and COPYING files for details.
10
+
11
+ class Arcfour
12
+ def initialize(key)
13
+ # Convert string key to Array of integers
14
+ key = key.unpack('c*') if key.is_a?(String)
15
+
16
+ # 1. Allocate an 256 element array of 8 bit bytes to be used as an S-box
17
+ # 2. Initialize the S-box. Fill each entry first with it's index
18
+ @sbox = (0..255).to_a
19
+
20
+ # 3. Fill another array of the same size (256) with the key, repeating
21
+ # bytes as necessary.
22
+ s2 = []
23
+ while s2.length < 256
24
+ s2 += key
25
+ end
26
+ s2 = s2[0, 256]
27
+
28
+ # 4. Set j to zero and initialize the S-box
29
+ j = 0
30
+ (0..255).each do |i|
31
+ j = (j + @sbox[i] + s2[i]) % 256
32
+ @sbox[i], @sbox[j] = @sbox[j], @sbox[i]
33
+ end
34
+
35
+ @i = @j = 0
36
+ end
37
+
38
+ def encrypt(string)
39
+ string.unpack('c*').map{|byte| byte ^ key_byte}.pack('c*')
40
+ end
41
+
42
+ private
43
+
44
+ # Produces the next byte of key material in the stream (3.2 Stream Generation)
45
+ def key_byte
46
+ @i = (@i + 1) % 256
47
+ @j = (@j + @sbox[@i]) % 256
48
+ @sbox[@i], @sbox[@j] = @sbox[@j], @sbox[@i]
49
+ @sbox[(@sbox[@i] + @sbox[@j]) % 256]
50
+ end
51
+ end
@@ -0,0 +1,126 @@
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_at([10, 15], :radius => 5)
25
+ # pdf.draw_text("hello world", :at => [20, 10])
26
+ # }
27
+ # pdf.stamp("my_stamp")
28
+ #
29
+ module Stamp
30
+
31
+ # Renders the stamp named <tt>name</tt> to the page
32
+ # raises <tt>Prawn::Errors::InvalidName</tt> if name.empty?
33
+ # raises <tt>Prawn::Errors::UndefinedObjectName</tt> if no stamp
34
+ # has been created with this name
35
+ #
36
+ # Example:
37
+ # pdf.create_stamp("my_stamp") {
38
+ # pdf.fill_circle_at([10, 15], :radius => 5)
39
+ # pdf.text("hello world", :at => [20, 10])
40
+ # }
41
+ # pdf.stamp("my_stamp")
42
+ #
43
+ def stamp(name)
44
+ dictionary_name, dictionary = stamp_dictionary(name)
45
+ add_content "/#{dictionary_name} Do"
46
+ state.page.xobjects.merge!(dictionary_name => dictionary)
47
+ end
48
+
49
+ # Renders the stamp named <tt>name</tt> at a position offset from
50
+ # the initial coords at which the elements of the stamp was
51
+ # created
52
+ #
53
+ # Example:
54
+ # pdf.create_stamp("circle") do
55
+ # pdf.fill_circle_at([0, 0], :radius => 25)
56
+ # end
57
+ # # draws a circle at 100, 100
58
+ # pdf.stamp_at("circle", [100, 100])
59
+ #
60
+ # See stamp() for exceptions that might be raised
61
+ #
62
+ def stamp_at(name, point)
63
+ translate(point[0], point[1]) { stamp(name) }
64
+ end
65
+
66
+ # Creates a re-usable stamp named <tt>name</tt>
67
+ #
68
+ # raises <tt>Prawn::Errors::NameTaken</tt> if a stamp already
69
+ # exists in this document with this name
70
+ # raises <tt>Prawn::Errors::InvalidName</tt> if name.empty?
71
+ #
72
+ # Example:
73
+ # pdf.create_stamp("my_stamp") {
74
+ # pdf.fill_circle_at([10, 15], :radius => 5)
75
+ # pdf.draw_text("hello world", :at => [20, 10])
76
+ # }
77
+ #
78
+ def create_stamp(name, &block)
79
+ dictionary = create_stamp_dictionary(name)
80
+
81
+ state.page.stamp_stream(dictionary, &block)
82
+ end
83
+
84
+ private
85
+
86
+ def stamp_dictionary_registry
87
+ @stamp_dictionary_registry ||= {}
88
+ end
89
+
90
+ def next_stamp_dictionary_id
91
+ stamp_dictionary_registry.length + 1
92
+ end
93
+
94
+ def stamp_dictionary(name)
95
+ raise Prawn::Errors::InvalidName if name.empty?
96
+ if stamp_dictionary_registry[name].nil?
97
+ raise Prawn::Errors::UndefinedObjectName
98
+ end
99
+
100
+ dict = stamp_dictionary_registry[name]
101
+
102
+ dictionary_name = dict[:stamp_dictionary_name]
103
+ dictionary = dict[:stamp_dictionary]
104
+ [dictionary_name, dictionary]
105
+ end
106
+
107
+ def create_stamp_dictionary(name)
108
+ raise Prawn::Errors::InvalidName if name.empty?
109
+ raise Prawn::Errors::NameTaken unless stamp_dictionary_registry[name].nil?
110
+ # BBox origin is the lower left margin of the page, so we need
111
+ # it to be the full dimension of the page, or else things that
112
+ # should appear near the top or right margin are invisible
113
+ dictionary = ref!(:Type => :XObject,
114
+ :Subtype => :Form,
115
+ :BBox => [0, 0,
116
+ state.page.dimensions[2], state.page.dimensions[3]])
117
+
118
+ dictionary_name = "Stamp#{next_stamp_dictionary_id}"
119
+
120
+ stamp_dictionary_registry[name] = { :stamp_dictionary_name => dictionary_name,
121
+ :stamp_dictionary => dictionary }
122
+ dictionary
123
+ end
124
+
125
+ end
126
+ end