giga-fast-kit 0.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 (75) hide show
  1. checksums.yaml +7 -0
  2. data/giga-fast-kit.gemspec +12 -0
  3. data/prawn-2.5.0/COPYING +2 -0
  4. data/prawn-2.5.0/GPLv2 +339 -0
  5. data/prawn-2.5.0/GPLv3 +674 -0
  6. data/prawn-2.5.0/LICENSE +56 -0
  7. data/prawn-2.5.0/data/fonts/Courier-Bold.afm +342 -0
  8. data/prawn-2.5.0/data/fonts/Courier-BoldOblique.afm +342 -0
  9. data/prawn-2.5.0/data/fonts/Courier-Oblique.afm +342 -0
  10. data/prawn-2.5.0/data/fonts/Courier.afm +342 -0
  11. data/prawn-2.5.0/data/fonts/Helvetica-Bold.afm +2827 -0
  12. data/prawn-2.5.0/data/fonts/Helvetica-BoldOblique.afm +2827 -0
  13. data/prawn-2.5.0/data/fonts/Helvetica-Oblique.afm +3051 -0
  14. data/prawn-2.5.0/data/fonts/Helvetica.afm +3051 -0
  15. data/prawn-2.5.0/data/fonts/MustRead.html +19 -0
  16. data/prawn-2.5.0/data/fonts/Symbol.afm +213 -0
  17. data/prawn-2.5.0/data/fonts/Times-Bold.afm +2588 -0
  18. data/prawn-2.5.0/data/fonts/Times-BoldItalic.afm +2384 -0
  19. data/prawn-2.5.0/data/fonts/Times-Italic.afm +2667 -0
  20. data/prawn-2.5.0/data/fonts/Times-Roman.afm +2419 -0
  21. data/prawn-2.5.0/data/fonts/ZapfDingbats.afm +225 -0
  22. data/prawn-2.5.0/lib/prawn/document/bounding_box.rb +625 -0
  23. data/prawn-2.5.0/lib/prawn/document/column_box.rb +177 -0
  24. data/prawn-2.5.0/lib/prawn/document/internals.rb +76 -0
  25. data/prawn-2.5.0/lib/prawn/document/span.rb +65 -0
  26. data/prawn-2.5.0/lib/prawn/document.rb +846 -0
  27. data/prawn-2.5.0/lib/prawn/encoding.rb +85 -0
  28. data/prawn-2.5.0/lib/prawn/errors.rb +73 -0
  29. data/prawn-2.5.0/lib/prawn/font.rb +567 -0
  30. data/prawn-2.5.0/lib/prawn/font_metric_cache.rb +50 -0
  31. data/prawn-2.5.0/lib/prawn/fonts/afm.rb +332 -0
  32. data/prawn-2.5.0/lib/prawn/fonts/dfont.rb +42 -0
  33. data/prawn-2.5.0/lib/prawn/fonts/otf.rb +14 -0
  34. data/prawn-2.5.0/lib/prawn/fonts/to_unicode_cmap.rb +151 -0
  35. data/prawn-2.5.0/lib/prawn/fonts/ttc.rb +41 -0
  36. data/prawn-2.5.0/lib/prawn/fonts/ttf.rb +615 -0
  37. data/prawn-2.5.0/lib/prawn/fonts.rb +14 -0
  38. data/prawn-2.5.0/lib/prawn/graphics/blend_mode.rb +62 -0
  39. data/prawn-2.5.0/lib/prawn/graphics/cap_style.rb +44 -0
  40. data/prawn-2.5.0/lib/prawn/graphics/color.rb +257 -0
  41. data/prawn-2.5.0/lib/prawn/graphics/dash.rb +123 -0
  42. data/prawn-2.5.0/lib/prawn/graphics/join_style.rb +60 -0
  43. data/prawn-2.5.0/lib/prawn/graphics/patterns.rb +389 -0
  44. data/prawn-2.5.0/lib/prawn/graphics/transformation.rb +172 -0
  45. data/prawn-2.5.0/lib/prawn/graphics/transparency.rb +79 -0
  46. data/prawn-2.5.0/lib/prawn/graphics.rb +791 -0
  47. data/prawn-2.5.0/lib/prawn/grid.rb +449 -0
  48. data/prawn-2.5.0/lib/prawn/image_handler.rb +61 -0
  49. data/prawn-2.5.0/lib/prawn/images/image.rb +48 -0
  50. data/prawn-2.5.0/lib/prawn/images/jpg.rb +121 -0
  51. data/prawn-2.5.0/lib/prawn/images/png.rb +383 -0
  52. data/prawn-2.5.0/lib/prawn/images.rb +195 -0
  53. data/prawn-2.5.0/lib/prawn/measurement_extensions.rb +82 -0
  54. data/prawn-2.5.0/lib/prawn/measurements.rb +129 -0
  55. data/prawn-2.5.0/lib/prawn/outline.rb +316 -0
  56. data/prawn-2.5.0/lib/prawn/repeater.rb +140 -0
  57. data/prawn-2.5.0/lib/prawn/security/arcfour.rb +54 -0
  58. data/prawn-2.5.0/lib/prawn/security.rb +308 -0
  59. data/prawn-2.5.0/lib/prawn/soft_mask.rb +105 -0
  60. data/prawn-2.5.0/lib/prawn/stamp.rb +147 -0
  61. data/prawn-2.5.0/lib/prawn/text/box.rb +192 -0
  62. data/prawn-2.5.0/lib/prawn/text/formatted/arranger.rb +396 -0
  63. data/prawn-2.5.0/lib/prawn/text/formatted/box.rb +725 -0
  64. data/prawn-2.5.0/lib/prawn/text/formatted/fragment.rb +373 -0
  65. data/prawn-2.5.0/lib/prawn/text/formatted/line_wrap.rb +335 -0
  66. data/prawn-2.5.0/lib/prawn/text/formatted/parser.rb +284 -0
  67. data/prawn-2.5.0/lib/prawn/text/formatted/wrap.rb +166 -0
  68. data/prawn-2.5.0/lib/prawn/text/formatted.rb +82 -0
  69. data/prawn-2.5.0/lib/prawn/text.rb +701 -0
  70. data/prawn-2.5.0/lib/prawn/transformation_stack.rb +63 -0
  71. data/prawn-2.5.0/lib/prawn/utilities.rb +34 -0
  72. data/prawn-2.5.0/lib/prawn/version.rb +6 -0
  73. data/prawn-2.5.0/lib/prawn/view.rb +116 -0
  74. data/prawn-2.5.0/lib/prawn.rb +83 -0
  75. metadata +114 -0
@@ -0,0 +1,449 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Prawn
4
+ class Document # rubocop: disable Style/Documentation
5
+ # @group Experimental API
6
+
7
+ # Defines the grid system for a particular document. Takes the number of
8
+ # rows and columns and the width to use for the gutter as the
9
+ # keys :rows, :columns, :gutter, :row_gutter, :column_gutter
10
+ #
11
+ # @note A completely new grid object is built each time `define_grid`
12
+ # is called. This means that all subsequent calls to grid() will use
13
+ # the newly defined Grid object -- grids are not nestable like
14
+ # bounding boxes are.
15
+ #
16
+ # @param options [Hash{Symbol => any}]
17
+ # @option options :columns [Integer] Number of columns in the grid.
18
+ # @option options :rows [Integer] Number of rows in the grid.
19
+ # @option options :gutter [Number] Gutter size. `:row_gutter` and
20
+ # `:column_gutter` are ignored if specified.
21
+ # @option options :row_gutter [Number] Row gutter size.
22
+ # @option options :column_gutter [Number] Column gutter size.
23
+ # @return [Grid]
24
+ def define_grid(options = {})
25
+ @boxes = nil
26
+ @grid = Grid.new(self, options)
27
+ end
28
+
29
+ # A method that can either be used to access a particular grid on the page
30
+ # or work with the grid system directly.
31
+ #
32
+ # @overload grid
33
+ # Get current grid.
34
+ #
35
+ # @return [Grid]
36
+ #
37
+ # @overload grid(row, column)
38
+ # Get a grid box.
39
+ #
40
+ # @param row [Integer]
41
+ # @param column [Integer]
42
+ # @return [GridBox]
43
+ #
44
+ # @overload grid(box1, box2)
45
+ # Get a grid multi-box.
46
+ #
47
+ # @param box1 [Array(Integer, Integer)] Start box coordinates.
48
+ # @param box2 [Array(Integer, Integer)] End box coordinates.
49
+ # @return [MultiBox]
50
+ def grid(*args)
51
+ @boxes ||= {}
52
+ @boxes[args] ||=
53
+ if args.empty?
54
+ @grid
55
+ else
56
+ g1, g2 = args
57
+
58
+ if g1.is_a?(Array) && g2.is_a?(Array) &&
59
+ g1.length == 2 && g2.length == 2
60
+ multi_box(single_box(*g1), single_box(*g2))
61
+ else
62
+ single_box(g1, g2)
63
+ end
64
+ end
65
+ end
66
+
67
+ # A Grid represents the entire grid system of a Page and calculates
68
+ # the column width and row height of the base box.
69
+ #
70
+ # @group Experimental API
71
+ class Grid
72
+ # @private
73
+ # @return [Prawn::Document]
74
+ attr_reader :pdf
75
+
76
+ # Number of columns in the grid.
77
+ # @return [Integer]
78
+ attr_reader :columns
79
+
80
+ # Number of rows in the grid.
81
+ # @return [Integer]
82
+ attr_reader :rows
83
+
84
+ # Gutter size.
85
+ # @return [Number]
86
+ attr_reader :gutter
87
+
88
+ # Row gutter size.
89
+ # @return [Number]
90
+ attr_reader :row_gutter
91
+
92
+ # Column gutter size.
93
+ # @return [Number]
94
+ attr_reader :column_gutter
95
+
96
+ # @param pdf [Prawn::Document]
97
+ # @param options [Hash{Symbol => any}]
98
+ # @option options :columns [Integer] Number of columns in the grid.
99
+ # @option options :rows [Integer] Number of rows in the grid.
100
+ # @option options :gutter [Number] Gutter size. `:row_gutter` and
101
+ # `:column_gutter` are ignored if specified.
102
+ # @option options :row_gutter [Number] Row gutter size.
103
+ # @option options :column_gutter [Number] Column gutter size.
104
+ def initialize(pdf, options = {})
105
+ valid_options = %i[columns rows gutter row_gutter column_gutter]
106
+ Prawn.verify_options(valid_options, options)
107
+
108
+ @pdf = pdf
109
+ @columns = options[:columns]
110
+ @rows = options[:rows]
111
+ apply_gutter(options)
112
+ end
113
+
114
+ # Calculates the base width of boxes.
115
+ #
116
+ # @return [Float]
117
+ def column_width
118
+ @column_width ||= subdivide(pdf.bounds.width, columns, column_gutter)
119
+ end
120
+
121
+ # Calculates the base height of boxes.
122
+ #
123
+ # @return [Float]
124
+ def row_height
125
+ @row_height ||= subdivide(pdf.bounds.height, rows, row_gutter)
126
+ end
127
+
128
+ # Diagnostic tool to show all of the grid boxes.
129
+ #
130
+ # @param color [Color]
131
+ # @return [void]
132
+ def show_all(color = 'CCCCCC')
133
+ rows.times do |row|
134
+ columns.times do |column|
135
+ pdf.grid(row, column).show(color)
136
+ end
137
+ end
138
+ end
139
+
140
+ private
141
+
142
+ def subdivide(total, num, gutter)
143
+ (Float(total) - (gutter * Float((num - 1)))) / Float(num)
144
+ end
145
+
146
+ def apply_gutter(options)
147
+ if options.key?(:gutter)
148
+ @gutter = Float(options[:gutter])
149
+ @row_gutter = @gutter
150
+ @column_gutter = @gutter
151
+ else
152
+ @row_gutter = Float(options[:row_gutter])
153
+ @column_gutter = Float(options[:column_gutter])
154
+ @gutter = 0
155
+ end
156
+ end
157
+ end
158
+
159
+ # A Box is a class that represents a bounded area of a page.
160
+ # A Grid object has methods that allow easy access to the coordinates of
161
+ # its corners, which can be plugged into most existing Prawn methods.
162
+ #
163
+ # @group Experimental API
164
+ class GridBox
165
+ # @private
166
+ attr_reader :pdf
167
+
168
+ def initialize(pdf, rows, columns)
169
+ @pdf = pdf
170
+ @rows = rows
171
+ @columns = columns
172
+ end
173
+
174
+ # Mostly diagnostic method that outputs the name of a box as
175
+ # col_num, row_num
176
+ #
177
+ # @return [String]
178
+ def name
179
+ "#{@rows},#{@columns}"
180
+ end
181
+
182
+ # @private
183
+ def total_height
184
+ Float(pdf.bounds.height)
185
+ end
186
+
187
+ # Width of a box.
188
+ #
189
+ # @return [Float]
190
+ def width
191
+ Float(grid.column_width)
192
+ end
193
+
194
+ # Height of a box.
195
+ #
196
+ # @return [Float]
197
+ def height
198
+ Float(grid.row_height)
199
+ end
200
+
201
+ # Width of the gutter.
202
+ #
203
+ # @return [Float]
204
+ def gutter
205
+ Float(grid.gutter)
206
+ end
207
+
208
+ # x-coordinate of left side.
209
+ #
210
+ # @return [Float]
211
+ def left
212
+ @left ||= (width + grid.column_gutter) * Float(@columns)
213
+ end
214
+
215
+ # x-coordinate of right side.
216
+ #
217
+ # @return [Float]
218
+ def right
219
+ @right ||= left + width
220
+ end
221
+
222
+ # y-coordinate of the top.
223
+ #
224
+ # @return [Float]
225
+ def top
226
+ @top ||= total_height - ((height + grid.row_gutter) * Float(@rows))
227
+ end
228
+
229
+ # y-coordinate of the bottom.
230
+ #
231
+ # @return [Float]
232
+ def bottom
233
+ @bottom ||= top - height
234
+ end
235
+
236
+ # x,y coordinates of top left corner.
237
+ #
238
+ # @return [Array(Float, Float)]
239
+ def top_left
240
+ [left, top]
241
+ end
242
+
243
+ # x,y coordinates of top right corner.
244
+ #
245
+ # @return [Array(Float, Float)]
246
+ def top_right
247
+ [right, top]
248
+ end
249
+
250
+ # x,y coordinates of bottom left corner.
251
+ #
252
+ # @return [Array(Float, Float)]
253
+ def bottom_left
254
+ [left, bottom]
255
+ end
256
+
257
+ # x,y coordinates of bottom right corner.
258
+ #
259
+ # @return [Array(Float, Float)]
260
+ def bottom_right
261
+ [right, bottom]
262
+ end
263
+
264
+ # Creates a standard bounding box based on the grid box.
265
+ #
266
+ # @yield
267
+ # @return [void]
268
+ def bounding_box(&blk)
269
+ pdf.bounding_box(top_left, width: width, height: height, &blk)
270
+ end
271
+
272
+ # Drawn the box. Diagnostic method.
273
+ #
274
+ # @param grid_color [Color]
275
+ # @return [void]
276
+ def show(grid_color = 'CCCCCC')
277
+ bounding_box do
278
+ original_stroke_color = pdf.stroke_color
279
+
280
+ pdf.stroke_color = grid_color
281
+ pdf.text(name)
282
+ pdf.stroke_bounds
283
+
284
+ pdf.stroke_color = original_stroke_color
285
+ end
286
+ end
287
+
288
+ private
289
+
290
+ def grid
291
+ pdf.grid
292
+ end
293
+ end
294
+
295
+ # A MultiBox is specified by 2 Boxes and spans the areas between.
296
+ #
297
+ # @group Experimental API
298
+ class MultiBox
299
+ def initialize(pdf, box1, box2)
300
+ @pdf = pdf
301
+ @boxes = [box1, box2]
302
+ end
303
+
304
+ # @private
305
+ attr_reader :pdf
306
+
307
+ # Mostly diagnostic method that outputs the name of a box.
308
+ #
309
+ # @return [String]
310
+ def name
311
+ @boxes.map(&:name).join(':')
312
+ end
313
+
314
+ # @private
315
+ def total_height
316
+ @boxes[0].total_height
317
+ end
318
+
319
+ # Width of a box.
320
+ #
321
+ # @return [Float]
322
+ def width
323
+ right_box.right - left_box.left
324
+ end
325
+
326
+ # Height of a box.
327
+ #
328
+ # @return [Float]
329
+ def height
330
+ top_box.top - bottom_box.bottom
331
+ end
332
+
333
+ # Width of the gutter.
334
+ #
335
+ # @return [Float]
336
+ def gutter
337
+ @boxes[0].gutter
338
+ end
339
+
340
+ # x-coordinate of left side.
341
+ #
342
+ # @return [Float]
343
+ def left
344
+ left_box.left
345
+ end
346
+
347
+ # x-coordinate of right side.
348
+ #
349
+ # @return [Float]
350
+ def right
351
+ right_box.right
352
+ end
353
+
354
+ # y-coordinate of the top.
355
+ #
356
+ # @return [Float]
357
+ def top
358
+ top_box.top
359
+ end
360
+
361
+ # y-coordinate of the bottom.
362
+ #
363
+ # @return [Float]
364
+ def bottom
365
+ bottom_box.bottom
366
+ end
367
+
368
+ # x,y coordinates of top left corner.
369
+ #
370
+ # @return [Array(Float, Float)]
371
+ def top_left
372
+ [left, top]
373
+ end
374
+
375
+ # x,y coordinates of top right corner.
376
+ #
377
+ # @return [Array(Float, Float)]
378
+ def top_right
379
+ [right, top]
380
+ end
381
+
382
+ # x,y coordinates of bottom left corner.
383
+ #
384
+ # @return [Array(Float, Float)]
385
+ def bottom_left
386
+ [left, bottom]
387
+ end
388
+
389
+ # x,y coordinates of bottom right corner.
390
+ #
391
+ # @return [Array(Float, Float)]
392
+ def bottom_right
393
+ [right, bottom]
394
+ end
395
+
396
+ # Creates a standard bounding box based on the grid box.
397
+ #
398
+ # @yield
399
+ # @return [void]
400
+ def bounding_box(&blk)
401
+ pdf.bounding_box(top_left, width: width, height: height, &blk)
402
+ end
403
+
404
+ # Drawn the box. Diagnostic method.
405
+ #
406
+ # @param grid_color [Color]
407
+ # @return [void]
408
+ def show(grid_color = 'CCCCCC')
409
+ bounding_box do
410
+ original_stroke_color = pdf.stroke_color
411
+
412
+ pdf.stroke_color = grid_color
413
+ pdf.text(name)
414
+ pdf.stroke_bounds
415
+
416
+ pdf.stroke_color = original_stroke_color
417
+ end
418
+ end
419
+
420
+ private
421
+
422
+ def left_box
423
+ @left_box ||= @boxes.min_by(&:left)
424
+ end
425
+
426
+ def right_box
427
+ @right_box ||= @boxes.max_by(&:right)
428
+ end
429
+
430
+ def top_box
431
+ @top_box ||= @boxes.max_by(&:top)
432
+ end
433
+
434
+ def bottom_box
435
+ @bottom_box ||= @boxes.min_by(&:bottom)
436
+ end
437
+ end
438
+
439
+ private
440
+
441
+ def single_box(rows, columns)
442
+ GridBox.new(self, rows, columns)
443
+ end
444
+
445
+ def multi_box(box1, box2)
446
+ MultiBox.new(self, box1, box2)
447
+ end
448
+ end
449
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Prawn # rubocop: disable Style/Documentation
4
+ # @group Extension API
5
+
6
+ # Image handler.
7
+ #
8
+ # @return [ImageHandler]
9
+ def self.image_handler
10
+ @image_handler ||= ImageHandler.new
11
+ end
12
+
13
+ # ImageHandler provides a way to register image processors with Prawn.
14
+ class ImageHandler
15
+ # @private
16
+ def initialize
17
+ @handlers = []
18
+ end
19
+
20
+ # Register an image handler.
21
+ #
22
+ # @param handler [Object]
23
+ # @return [void]
24
+ def register(handler)
25
+ @handlers.delete(handler)
26
+ @handlers.push(handler)
27
+ end
28
+
29
+ # Register an image handler with the highest priority.
30
+ #
31
+ # @param handler [Object]
32
+ # @return [void]
33
+ def register!(handler)
34
+ @handlers.delete(handler)
35
+ @handlers.unshift(handler)
36
+ end
37
+
38
+ # Unregister an image handler.
39
+ #
40
+ # @param handler [Object]
41
+ # @return [void]
42
+ def unregister(handler)
43
+ @handlers.reject! { |h| h == handler }
44
+ end
45
+
46
+ # Find an image handler for an image.
47
+ #
48
+ # @param image_blob [String]
49
+ # @return [Object]
50
+ # @raise [Prawn::Errors::UnsupportedImageType] If no image handler were
51
+ # found for the image.
52
+ def find(image_blob)
53
+ handler = @handlers.find { |h| h.can_render?(image_blob) }
54
+
55
+ return handler if handler
56
+
57
+ raise Prawn::Errors::UnsupportedImageType,
58
+ 'image file is an unrecognised format'
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest/sha1'
4
+
5
+ module Prawn
6
+ module Images
7
+ # Base class for image info objects
8
+ # @abstract
9
+ class Image
10
+ # @group Extension API
11
+
12
+ # Calculate the final image dimensions from provided options.
13
+ # @private
14
+ def calc_image_dimensions(options)
15
+ w = options[:width] || width
16
+ h = options[:height] || height
17
+
18
+ if options[:width] && !options[:height]
19
+ wp = w / Float(width)
20
+ w = width * wp
21
+ h = height * wp
22
+ elsif options[:height] && !options[:width]
23
+ hp = h / Float(height)
24
+ w = width * hp
25
+ h = height * hp
26
+ elsif options[:scale]
27
+ w = width * options[:scale]
28
+ h = height * options[:scale]
29
+ elsif options[:fit]
30
+ bw, bh = options[:fit]
31
+ bp = bw / Float(bh)
32
+ ip = width / Float(height)
33
+ if ip > bp
34
+ w = bw
35
+ h = bw / ip
36
+ else
37
+ h = bh
38
+ w = bh * ip
39
+ end
40
+ end
41
+ self.scaled_width = w
42
+ self.scaled_height = h
43
+
44
+ [w, h]
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,121 @@
1
+ # encoding: ASCII-8BIT
2
+ # frozen_string_literal: true
3
+
4
+ require 'stringio'
5
+
6
+ module Prawn
7
+ module Images # rubocop: disable Style/Documentation
8
+ # A convenience class that wraps the logic for extracting the parts of a JPG
9
+ # image that we need to embed them in a PDF.
10
+ class JPG < Image
11
+ # Signals an issue with the image format. The image is probably corrupted
12
+ # if you're getting this.
13
+ class FormatError < StandardError; end
14
+
15
+ # @group Extension API
16
+
17
+ # Image width in pixels.
18
+ # @return [Integer]
19
+ attr_reader :width
20
+
21
+ # Image height in pixels.
22
+ # @return [Integer]
23
+ attr_reader :height
24
+
25
+ # Sample Precision in bits.
26
+ # @return [Integer]
27
+ attr_reader :bits
28
+
29
+ # Number of image components (channels).
30
+ # @return [Integer]
31
+ attr_reader :channels
32
+
33
+ # Scaled width of the image in PDF points.
34
+ # @return [Number]
35
+ attr_accessor :scaled_width
36
+
37
+ # Scaled height of the image in PDF points.
38
+ # @return [Number]
39
+ attr_accessor :scaled_height
40
+
41
+ # @private
42
+ JPEG_SOF_BLOCKS = [
43
+ 0xC0, 0xC1, 0xC2, 0xC3, 0xC5, 0xC6, 0xC7, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE,
44
+ 0xCF,
45
+ ].freeze
46
+
47
+ # Can this image handler process this image?
48
+ #
49
+ # @param image_blob [String]
50
+ # @return [Boolean]
51
+ def self.can_render?(image_blob)
52
+ image_blob[0, 3].unpack('C*') == [255, 216, 255]
53
+ end
54
+
55
+ # Process a new JPG image.
56
+ #
57
+ # @param data [String] A binary string of JPEG data.
58
+ def initialize(data)
59
+ super()
60
+ @data = data
61
+ d = StringIO.new(@data)
62
+ d.binmode
63
+
64
+ c_marker = 0xff # Section marker.
65
+ d.seek(2) # Skip the first two bytes of JPEG identifier.
66
+ loop do
67
+ marker, code, length = d.read(4).unpack('CCn')
68
+ raise FormatError, 'JPEG marker not found!' if marker != c_marker
69
+
70
+ if JPEG_SOF_BLOCKS.include?(code)
71
+ @bits, @height, @width, @channels = d.read(6).unpack('CnnC')
72
+ break
73
+ end
74
+
75
+ d.seek(length - 2, IO::SEEK_CUR)
76
+ end
77
+ end
78
+
79
+ # Build a PDF object representing this image in `document`, and return
80
+ # a Reference to it.
81
+ #
82
+ # @param document [Prawn::Document]
83
+ # @return [PDF::Core::Reference]
84
+ def build_pdf_object(document)
85
+ color_space =
86
+ case channels
87
+ when 1
88
+ :DeviceGray
89
+ when 3
90
+ :DeviceRGB
91
+ when 4
92
+ :DeviceCMYK
93
+ else
94
+ raise ArgumentError, 'JPG uses an unsupported number of channels'
95
+ end
96
+
97
+ obj = document.ref!(
98
+ Type: :XObject,
99
+ Subtype: :Image,
100
+ ColorSpace: color_space,
101
+ BitsPerComponent: bits,
102
+ Width: width,
103
+ Height: height,
104
+ )
105
+
106
+ # add extra decode params for CMYK images. By swapping the
107
+ # min and max values from the default, we invert the colours. See
108
+ # section 4.8.4 of the spec.
109
+ if color_space == :DeviceCMYK
110
+ obj.data[:Decode] = [1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0]
111
+ end
112
+
113
+ obj.stream << @data
114
+ obj.stream.filters << :DCTDecode
115
+ obj
116
+ end
117
+ end
118
+
119
+ Prawn.image_handler.register(Prawn::Images::JPG)
120
+ end
121
+ end