prawn-shadings 0.1.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.
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/BSDL ADDED
@@ -0,0 +1,24 @@
1
+ Copyright (C) 2013 Alexander Mankuta. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions
5
+ are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright
8
+ notice, this list of conditions and the following disclaimer.
9
+
10
+ 2. Redistributions in binary form must reproduce the above copyright
11
+ notice, this list of conditions and the following disclaimer in the
12
+ documentation and/or other materials provided with the distribution.
13
+
14
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
+ SUCH DAMAGE.
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source :rubygems
2
+
3
+ gem 'prawn', github: 'prawnpdf/prawn'
4
+
5
+ group :developemnt do
6
+ gem 'rdoc'
7
+ end
8
+
9
+ group :test do
10
+ gem "pdf-inspector", "~> 1.0.2", :require => "pdf/inspector"
11
+ gem "rspec"
12
+ gem "rake"
13
+ end
data/LICENSE ADDED
@@ -0,0 +1,56 @@
1
+ Prawn-Shadings is copyrighted free software by Alexander Mankuta <alex@pointlessone.org>.
2
+ You can redistribute it and/or modify it under either the terms of the
3
+ 2-clause BSDL (see the file BSDL), or the conditions below:
4
+
5
+ 1. You may make and give away verbatim copies of the source form of the
6
+ software without restriction, provided that you duplicate all of the
7
+ original copyright notices and associated disclaimers.
8
+
9
+ 2. You may modify your copy of the software in any way, provided that
10
+ you do at least ONE of the following:
11
+
12
+ a) place your modifications in the Public Domain or otherwise
13
+ make them Freely Available, such as by posting said
14
+ modifications to Usenet or an equivalent medium, or by allowing
15
+ the author to include your modifications in the software.
16
+
17
+ b) use the modified software only within your corporation or
18
+ organization.
19
+
20
+ c) give non-standard binaries non-standard names, with
21
+ instructions on where to get the original software distribution.
22
+
23
+ d) make other distribution arrangements with the author.
24
+
25
+ 3. You may distribute the software in object code or binary form,
26
+ provided that you do at least ONE of the following:
27
+
28
+ a) distribute the binaries and library files of the software,
29
+ together with instructions (in the manual page or equivalent)
30
+ on where to get the original distribution.
31
+
32
+ b) accompany the distribution with the machine-readable source of
33
+ the software.
34
+
35
+ c) give non-standard binaries non-standard names, with
36
+ instructions on where to get the original software distribution.
37
+
38
+ d) make other distribution arrangements with the author.
39
+
40
+ 4. You may modify and include the part of the software into any other
41
+ software (possibly commercial). But some files in the distribution
42
+ are not written by the author, so that they are not under these terms.
43
+
44
+ For the list of those files and their copying conditions, see the
45
+ file LEGAL.
46
+
47
+ 5. The scripts and library files supplied as input to or produced as
48
+ output from the software do not automatically fall under the
49
+ copyright of the software, but belong to whomever generated them,
50
+ and may be sold commercially, and may be aggregated with this
51
+ software.
52
+
53
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
54
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
55
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
56
+ PURPOSE.
data/README.md ADDED
@@ -0,0 +1,68 @@
1
+
2
+ Prawn Shadings
3
+ ==============
4
+
5
+ [![Build Status](https://travis-ci.org/cheba/prawn-shadings.png)](https://travis-ci.org/cheba/prawn-shadings)
6
+
7
+ This is a Prawn extension that implements advanced PDF shadings.
8
+
9
+ Specifically, it a bit refactors already implemented linear and radial gradients
10
+ and adds a few more advanced ones.
11
+
12
+ Currently implemented shadings:
13
+
14
+ * Axial Shadings (or linear gradients) (PDF Type 2 Shading)
15
+ * Radial Shading (PDF Type 3 Shading)
16
+ * Free-Form Gouraud-Shaded Triangle Meshes (PDF Type 4 Shading)
17
+ * Lattice-Form Gouraud-Shaded Triangle Meshes (PDF Type 5 Shading)
18
+ * Coons Patch Meshes (PDF Type 6 Shading)
19
+ * Tensor-Product Patch Meshes (PDF Type 7 Shading)
20
+
21
+ API is simple but you need to know what data to supply. Please get familiar with
22
+ Section 4.6.3 of PDF Reference.
23
+
24
+
25
+ A Word of Warning
26
+ -----------------
27
+
28
+ Please note that this gem gives you ability use all these shadings in your
29
+ generated PDF but gives no guarantee that PDF will be rendered correctly.
30
+
31
+ Just a few examples:
32
+
33
+ * OS X Preview has problems with flags other than 0 in Type 4, 6 and 7 type
34
+ shadings.
35
+ * OS X Previews renders Type 7 shadings exactly the same as Type 6 shadings
36
+ ignoring all 4 extra control points.
37
+ * PDF.js can not render pretty much any of these.
38
+
39
+ Installation
40
+ ------------
41
+
42
+ gem install prawn-shadings
43
+
44
+
45
+ Usage
46
+ -----
47
+
48
+ require 'prawn/shadings'
49
+
50
+
51
+ Testing
52
+ -------
53
+
54
+ To run the tests:
55
+
56
+ $ rake
57
+
58
+
59
+ Contributing
60
+ ------------
61
+
62
+ 1. Fork it.
63
+ 2. Create a branch (`git checkout -b my_shading`)
64
+ 3. Commit your changes (`git commit -am "Added Shading"`)
65
+ 4. Push to the branch (`git push origin my_shading`)
66
+ 5. Open a [Pull Request][1]
67
+
68
+ [1]: http://github.com/cheba/prawn-shadings/pulls
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ require 'rspec/core/rake_task'
2
+ require 'rdoc/task'
3
+
4
+ desc "genrates documentation"
5
+ RDoc::Task.new do |rdoc|
6
+ rdoc.rdoc_files.include( "README.md",
7
+ "LICENSE", "BSDL",
8
+ "lib/" )
9
+ rdoc.main = "README.md"
10
+ rdoc.rdoc_dir = "doc/html"
11
+ rdoc.title = "Prawn Shadings Documentation"
12
+ end
13
+
14
+ desc "Run all rspec files"
15
+ RSpec::Core::RakeTask.new("spec")
16
+
17
+ desc "Run tests"
18
+ task default: :spec
@@ -0,0 +1 @@
1
+ require 'prawn/shadings'
@@ -0,0 +1,649 @@
1
+ # encoding: utf-8
2
+
3
+ require 'prawn/graphics/patterns'
4
+
5
+ module Prawn
6
+ module Graphics
7
+
8
+ ##
9
+ # This module implements Shading patterns.
10
+ #
11
+ # Both fill_gradient and stroke_gradient accept absolutely identical sets of
12
+ # arguments. The only difference, obviously, is that one sets fill shading
13
+ # and the other sets stroke shading.
14
+ #
15
+ # == Basic gradients
16
+ #
17
+ # Linear and radial gradients are one of the simplest shadings supported by
18
+ # PDF.
19
+ #
20
+ # === Linear gradient
21
+ #
22
+ # fill_gradient(from, to, color1, color2)
23
+ #
24
+ # from::
25
+ # This is the starting point of the gradient. It must be an arrays of
26
+ # two values: <tt>[x, y]</tt>.
27
+ #
28
+ # to::
29
+ # This is the ending point of the gradient. Format is the same:
30
+ # <tt>[x, y]</tt>.
31
+ #
32
+ # color1::
33
+ # Color of the gradient at the starting point.
34
+ #
35
+ # color2::
36
+ # Color of the gradient at the ending point of the gradient.
37
+ #
38
+ # Please note that both colors must be in the same color space.
39
+ #
40
+ # === Radial gradient
41
+ #
42
+ # fill_gradient(from, r1, to, r2, color1, color2)
43
+ #
44
+ # from::
45
+ # This is the starting point of the gradient. It must be an arrays of
46
+ # two values: <tt>[x, y]</tt>.
47
+ #
48
+ # r1::
49
+ # Radius of the starting circle.
50
+ #
51
+ # to::
52
+ # This is the ending point of the gradient. Format is the same:
53
+ # <tt>[x, y]</tt>.
54
+ #
55
+ # r2::
56
+ # Radius of the ending circle.
57
+ #
58
+ # color1::
59
+ # Color of the gradient at the starting point.
60
+ #
61
+ # color2::
62
+ # Color of the gradient at the ending point of the gradient.
63
+ #
64
+ # Please note that both colors must be in the same color space.
65
+ #
66
+ #
67
+ # == More complex gradients
68
+ #
69
+ # PDF is capable of creating very complex shadings. Though, this
70
+ # functionality is rarely used.
71
+ #
72
+ # Because this gradients require variable amounts of data to be properly
73
+ # constructed gradient methods are called with a single parameter stating
74
+ # the type of gradient you want to produce and a block that returns actual
75
+ # data for gradient.
76
+ #
77
+ # [Note]
78
+ # This types of shading are not supported by all renderers.
79
+ # For example PDF.js doesn't support any of this shadings. OS X Preview
80
+ # (10.8) has problems with flags other than 0 and renders Tensor-Product
81
+ # Patch Meshes exactly the same as Coons Patch Meshes.
82
+ #
83
+ # === Free-Form Gouraud-Shaded Triangle Meshes
84
+ #
85
+ # fill_gradient(:ffgstm) {
86
+ # [
87
+ # # vertices
88
+ # ]
89
+ # }
90
+ #
91
+ # The data is an Array of vertices. Each vertex has the following format:
92
+ # <tt>[flag, x, y, color]</tt>.
93
+ #
94
+ # Colors of all vertices must be in the same color space.
95
+ #
96
+ # Please refer to <b>PDF Refernce, Section 4.6.3, Shading Types, Type
97
+ # 4 Shadings</b> for more details on the meaning of the vertex fields.
98
+ #
99
+ #
100
+ # === Lattice-Form Gouraud-Shaded Triangle Meshes
101
+ #
102
+ # fill_gradient(:lfgstm) {
103
+ # [
104
+ # # data
105
+ # ]
106
+ # }
107
+ #
108
+ # The data is a rectangular matrix of vertices. That is an array of rows (arrays)
109
+ # of vertices. It must have at least 2 rows and at least 2 column. Each vertex has
110
+ # the following format: <tt>[x, y, color]</tt>.
111
+ #
112
+ # Colors of all vertices must be in the same color space.
113
+ #
114
+ # Please refer to <b>PDF Refernce, Section 4.6.3, Shading Types, Type
115
+ # 5 Shadings</b> for more details on the meaning of the vertex fields.
116
+ #
117
+ #
118
+ # === Coons Patch Meshes
119
+ #
120
+ # fill_gradient(:cpm) {
121
+ # [
122
+ # # data
123
+ # ]
124
+ # }
125
+ #
126
+ # The data is an array of patches. There are two types of patches. They're
127
+ # distinguished by their flag parameter.
128
+ #
129
+ # First one is a stand alone patches. Their flag = 0. They have a form of <tt>[0 (flag),
130
+ # x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, x6, y6, x7, y7, x8, y8, x9, y9, x10, y10,
131
+ # x11, y11, x12, y12, c1, c2, c3, c4]</tt>.
132
+ #
133
+ # The second is a edge-sharing patches. Their flag is either 1, 2, or 3. And
134
+ # they have 4 less points (because they borrow them from previous patch) and
135
+ # 2 less colors. They have a form of <tt>[flag, x5, y5, x6, y6, x7, y7, x8, y8, x9, y9,
136
+ # x10, y10, x11, y11, x12, y12, c3, c4]</tt>.
137
+ #
138
+ # Please refer to <b>PDF Refernce, Section 4.6.3, Shading Types, Type
139
+ # 6 Shadings</b> for more details on the meaning of the patch fields.
140
+ #
141
+ # === Tensor-Product Patch Meshes
142
+ #
143
+ # fill_gradient(:tppm) {
144
+ # [
145
+ # # data
146
+ # ]
147
+ # }
148
+ #
149
+ # This type of shading is very similar to Coons Patch Mesh except it has
150
+ # 4 extra pairs of coordinates in every patch, right before the colors.
151
+ #
152
+ # Please refer to <b>PDF Refernce, Section 4.6.3, Shading Types, Type
153
+ # 7 Shadings</b> for more details on the meaning of the patch fields.
154
+ #
155
+ module Patterns
156
+
157
+ ##
158
+ # :call-seq:
159
+ # fill_gradient(from, to, color1, color2)
160
+ # fill_gradient(from, r1, to, r2, color1, color2)
161
+ # fill_gradient(complex_shading_type) { }
162
+ #
163
+ # Sets the fill gradient.
164
+ def fill_gradient(*args, &block)
165
+ if args[1].is_a?(Array) || args[2].is_a?(Array) || args[0].is_a?(Symbol)
166
+ set_gradient(:fill, *args, &block)
167
+ else
168
+ warn "[DEPRECATION] 'fill_gradient(point, width, height,...)' is deprecated in favor of 'fill_gradient(from, to,...)'. " +
169
+ "Old arguments will be removed in release 1.1"
170
+ set_gradient :fill, args[0], [args[0].first, args[0].last - args[2]], args[3], args[4]
171
+ end
172
+ end
173
+
174
+ ##
175
+ # :call-seq:
176
+ # stroke_gradient(from, to, color1, color2)
177
+ # stroke_gradient(from, r1, to, r2, color1, color2)
178
+ # stroke_gradient(complex_shading_type) { }
179
+ #
180
+ # Sets the stroke gradient.
181
+ def stroke_gradient(*args, &block)
182
+ if args[1].is_a?(Array) || args[2].is_a?(Array) || args[0].is_a?(Symbol)
183
+ set_gradient(:stroke, *args, &block)
184
+ else
185
+ warn "[DEPRECATION] 'stroke_gradient(point, width, height,...)' is deprecated in favor of 'stroke_gradient(from, to,...)'. " +
186
+ "Old arguments will be removed in release 1.1"
187
+ set_gradient :stroke, args[0], [args[0].first, args[0].last - args[2]], args[3], args[4]
188
+ end
189
+ end
190
+
191
+ private
192
+
193
+ def set_gradient(type, *grad, &block)
194
+ patterns = page.resources[:Pattern] ||= {}
195
+
196
+ registry_key = gradient_registry_key grad, &block
197
+
198
+ if patterns["SP#{registry_key}"]
199
+ shading = patterns["SP#{registry_key}"]
200
+ else
201
+ unless shading = gradient_registry[registry_key]
202
+ shading = gradient(*grad, &block)
203
+ gradient_registry[registry_key] = shading
204
+ end
205
+
206
+ patterns["SP#{registry_key}"] = shading
207
+ end
208
+
209
+ operator = case type
210
+ when :fill
211
+ 'scn'
212
+ when :stroke
213
+ 'SCN'
214
+ else
215
+ raise ArgumentError, "unknown type '#{type}'"
216
+ end
217
+
218
+ set_color_space type, :Pattern
219
+ add_content "/SP#{registry_key} #{operator}"
220
+ end
221
+
222
+ def gradient_registry_key(gradient)
223
+ if gradient[0].is_a?(Symbol) # all kinds of patches
224
+ [
225
+ gradient[0],
226
+ *yield
227
+ ]
228
+ elsif gradient[1].is_a?(Array) # axial
229
+ [
230
+ map_to_absolute(gradient[0]),
231
+ map_to_absolute(gradient[1]),
232
+ gradient[2], gradient[3]
233
+ ]
234
+ else # radial
235
+ [
236
+ map_to_absolute(gradient[0]),
237
+ gradient[1],
238
+ map_to_absolute(gradient[2]),
239
+ gradient[3],
240
+ gradient[4], gradient[5]
241
+ ]
242
+ end.hash
243
+ end
244
+
245
+ def gradient_registry
246
+ @gradient_registry ||= {}
247
+ end
248
+
249
+ def gradient(*args, &block)
250
+ if args.length != 1 && args.length != 4 && args.length != 6
251
+ raise ArgumentError, "Unknown type of gradient: #{args.inspect}"
252
+ end
253
+
254
+ case args.length
255
+ when 1
256
+ case args.first
257
+ when :ffgstm # Free-Form Gouraud-Shaded Triangle Meshes
258
+ shading_type_4(&block)
259
+ when :lfgstm # Lattice-Form Gouraud-Shaded Triangle Meshes
260
+ shading_type_5(&block)
261
+ when :cpm # Coons Patch Meshes
262
+ shading_type_6(&block)
263
+ when :tppm # Tensor-Product Patch Meshes
264
+ shading_type_7(&block)
265
+ else
266
+ raise ArgumentError, "Unknown type of gradient: #{args.inspect}"
267
+ end
268
+ when 4
269
+ shading_type_2(*args)
270
+ when 6
271
+ shading_type_3(*args)
272
+ else
273
+ raise ArgumentError, "Unknown type of gradient: #{args.inspect}"
274
+ end
275
+ end
276
+
277
+ def shading_type_2(from, to, from_color, to_color)
278
+ color1 = normalize_color(from_color).dup.freeze
279
+ color2 = normalize_color(to_color).dup.freeze
280
+
281
+ if color_type(color1) != color_type(color2)
282
+ raise ArgumentError, "Both colors must be of the same color space: #{color1.inspect} and #{color2.inspect}"
283
+ end
284
+
285
+ process_color color1
286
+ process_color color2
287
+
288
+ shader = ref!({
289
+ :FunctionType => 2,
290
+ :Domain => [0.0, 1.0],
291
+ :C0 => color1,
292
+ :C1 => color2,
293
+ :N => 1.0,
294
+ })
295
+
296
+ shading = ref!({
297
+ :ShadingType => 2, # axial shading
298
+ :ColorSpace => color_space(color1),
299
+ :Coords => [0, 0, to.first - from.first, to.last - from.last],
300
+ :Function => shader,
301
+ :Extend => [true, true],
302
+ })
303
+
304
+ ref!({
305
+ :PatternType => 2, # shading pattern
306
+ :Shading => shading,
307
+ :Matrix => [1, 0, 0, 1] + map_to_absolute(from),
308
+ })
309
+ end
310
+
311
+ def shading_type_3(from, r1, to, r2, from_color, to_color)
312
+ color1 = normalize_color(from_color).dup.freeze
313
+ color2 = normalize_color(to_color).dup.freeze
314
+
315
+ if color_type(color1) != color_type(color2)
316
+ raise ArgumentError, "Both colors must be of the same color space: #{color1.inspect} and #{color2.inspect}"
317
+ end
318
+
319
+ process_color color1
320
+ process_color color2
321
+
322
+ shader = ref!({
323
+ :FunctionType => 2,
324
+ :Domain => [0.0, 1.0],
325
+ :C0 => color1,
326
+ :C1 => color2,
327
+ :N => 1.0,
328
+ })
329
+
330
+ shading = ref!({
331
+ :ShadingType => 3, # radial shading
332
+ :ColorSpace => color_space(color1),
333
+ :Coords => [0, 0, r1, to.first - from.first, to.last - from.last, r2],
334
+ :Function => shader,
335
+ :Extend => [true, true],
336
+ })
337
+
338
+ ref!({
339
+ :PatternType => 2, # shading pattern
340
+ :Shading => shading,
341
+ :Matrix => [1, 0, 0, 1] + map_to_absolute(from),
342
+ })
343
+ end
344
+
345
+ def shading_type_4
346
+ data = yield
347
+
348
+ x_min = x_max = data[0][1]
349
+ y_min = y_max = data[0][2]
350
+ data_color_space = color_space(data[0][3])
351
+
352
+ data.each do |(_, x, y, c, extra)|
353
+ x_min = x if x_min > x
354
+ x_max = x if x_max < x
355
+ y_min = y if y_min > y
356
+ y_max = y if y_max < y
357
+ if color_space(c) != data_color_space
358
+ raise ArgumentError, "Colors of all vertices must be in the same color space. Expected #{data_color_space} got #{color_space(c)}"
359
+ end
360
+ if extra
361
+ raise ArgumentError, "Unexpected extra data in vertices stream: #{extra.inspect}"
362
+ end
363
+ end
364
+ x_min = x_min.to_f
365
+ x_max = x_max.to_f
366
+ y_min = y_min.to_f
367
+ y_max = y_max.to_f
368
+ dx = x_max - x_min
369
+ dy = y_max - y_min
370
+
371
+
372
+ shading = ref!({
373
+ :ShadingType => 4, # Free-Form Gouraud-Shaded Triangle Meshes
374
+ :ColorSpace => data_color_space,
375
+ :BitsPerCoordinate => 32,
376
+ :BitsPerComponent => 8,
377
+ :BitsPerFlag => 8,
378
+ :Decode => [x_min, x_max, y_min, y_max, 0, 1, 0, 1, 0, 1]
379
+ })
380
+
381
+ data.each do |(f, x, y, color)|
382
+ unless [0, 1, 2].include? f
383
+ raise ArgumentError, "Flag must be 0, 1, or 2. Got #{f}."
384
+ end
385
+
386
+ shading << [
387
+ f,
388
+ normalize_coord(x, x_min, dx), normalize_coord(y, y_min, dy),
389
+ *(normalize_color(color).map{|c| (c * 255.0).round })
390
+ ].pack('CNNC*')
391
+ end
392
+
393
+
394
+ ref!({
395
+ :PatternType => 2, # shading pattern
396
+ :Shading => shading,
397
+ :Matrix => [1, 0,
398
+ 0, 1] + map_to_absolute([0, 0]),
399
+ })
400
+ end
401
+
402
+ def shading_type_5
403
+ data = yield
404
+
405
+ x_min = x_max = data[0][0][0]
406
+ y_min = y_max = data[0][0][1]
407
+ data_color_space = color_space(data[0][0][2])
408
+
409
+ if data.length < 2
410
+ raise ArgumentError, "Lattice-Form Gouraud-Shaded Triangle Meshes Shading requires at least 2 rows of vertices"
411
+ end
412
+
413
+ vertices_per_row = data[0].length
414
+ if vertices_per_row < 2
415
+ raise ArgumentError, "Lattice-Form Gouraud-Shaded Triangle Meshes Shading requires at least 2 vertices per row"
416
+ end
417
+
418
+ data.each do |row|
419
+ if vertices_per_row != row.length
420
+ raise ArgumentError, "Lattice-Form Gouraud-Shaded Triangle Meshes Shading requires data to be organized in rectangular matrix"
421
+ end
422
+
423
+ row.each do |(x, y, c)|
424
+ x_min = x if x_min > x
425
+ x_max = x if x_max < x
426
+ y_min = y if y_min > y
427
+ y_max = y if y_max < y
428
+
429
+ if color_space(c) != data_color_space
430
+ raise ArgumentError, "Colors of all vertices must be in the same color space. Expected #{data_color_space} got #{color_space(c)}"
431
+ end
432
+ end
433
+ end
434
+ x_min = x_min.to_f
435
+ x_max = x_max.to_f
436
+ y_min = y_min.to_f
437
+ y_max = y_max.to_f
438
+ dx = x_max - x_min
439
+ dy = y_max - y_min
440
+
441
+
442
+ shading = ref!({
443
+ :ShadingType => 5, # Lattice-Form Gouraud-Shaded Triangle Meshes
444
+ :ColorSpace => :DeviceRGB,
445
+ :BitsPerCoordinate => 32,
446
+ :BitsPerComponent => 8,
447
+ :VerticesPerRow => data[0].length,
448
+ :Decode => [x_min, x_max, y_min, y_max, 0, 1, 0, 1, 0, 1]
449
+ })
450
+
451
+
452
+ data.each do |row|
453
+ row.each do |(x, y, color)|
454
+ shading << [
455
+ normalize_coord(x, x_min, dx), normalize_coord(y, y_min, dy),
456
+ *(normalize_color(color).map{|c| (c * 255.0).round })
457
+ ].pack('NNC*')
458
+ end
459
+ end
460
+
461
+
462
+ ref!({
463
+ :PatternType => 2, # shading pattern
464
+ :Shading => shading,
465
+ :Matrix => [1, 0,
466
+ 0, 1] + map_to_absolute([0, 0]),
467
+ })
468
+ end
469
+
470
+ def shading_type_6
471
+ data = yield
472
+
473
+ x_min = x_max = data[0][1]
474
+ y_min = y_max = data[0][2]
475
+ data_color_space = color_space(data[0][25])
476
+
477
+ data.each do |values|
478
+ f = values[0]
479
+ coords = if f == 0
480
+ values[1..24]
481
+ else
482
+ values[1..16]
483
+ end
484
+ coords.each_with_index do |c, i|
485
+ if i.even? # x coordinate
486
+ x_min = c if x_min > c
487
+ x_max = c if x_max < c
488
+ else # y coordinate
489
+ y_min = c if y_min > c
490
+ y_max = c if y_max < c
491
+ end
492
+ end
493
+ end
494
+ x_min = x_min.to_f
495
+ x_max = x_max.to_f
496
+ y_min = y_min.to_f
497
+ y_max = y_max.to_f
498
+ dx = x_max - x_min
499
+ dy = y_max - y_min
500
+
501
+
502
+ shading = ref!({
503
+ :ShadingType => 6, # Coons Patch Meshes
504
+ :ColorSpace => data_color_space,
505
+ :BitsPerCoordinate => 32,
506
+ :BitsPerComponent => 8,
507
+ :BitsPerFlag => 8,
508
+ :Decode => [x_min, x_max, y_min, y_max, 0, 1, 0, 1, 0, 1]
509
+ })
510
+
511
+
512
+ data.each do |values|
513
+ f = values.shift
514
+ unless [0, 1, 2, 3].include? f
515
+ raise ArgumentError, "Flag must be 0, 1, 2, or 3. Got #{f}."
516
+ end
517
+
518
+ if f == 0
519
+ coords = values.shift(24)
520
+ colors = values.shift(4)
521
+ else
522
+ coords = values.shift(16)
523
+ colors = values.shift(2)
524
+ end
525
+
526
+ shading << [f].pack('C')
527
+
528
+ coords.each_with_index do |c, i|
529
+ if i.even? # x coordinate
530
+ shading << [normalize_coord(c, x_min, dx)].pack('N')
531
+ else
532
+ shading << [normalize_coord(c, y_min, dy)].pack('N')
533
+ end
534
+ end
535
+
536
+ colors.each do |color|
537
+ if color_space(color) != data_color_space
538
+ raise ArgumentError, "Colors of all vertices must be in the same color space. Expected #{data_color_space} got #{color_space(color)}"
539
+ end
540
+ shading << normalize_color(color).map{|c| (c * 255.0).round }.pack('C*')
541
+ end
542
+ end
543
+
544
+
545
+ ref!({
546
+ :PatternType => 2, # shading pattern
547
+ :Shading => shading,
548
+ :Matrix => [1, 0,
549
+ 0, 1] + map_to_absolute([0, 0]),
550
+ })
551
+ end
552
+
553
+ def shading_type_7
554
+ data = yield
555
+
556
+ x_min = x_max = data[0][1]
557
+ y_min = y_max = data[0][2]
558
+ data_color_space = color_space(data[0][33])
559
+
560
+ data.each do |values|
561
+ f = values[0]
562
+ coords = if f == 0
563
+ values[1..32]
564
+ else
565
+ values[1..24]
566
+ end
567
+ coords.each_with_index do |c, i|
568
+ if i.even? # x coordinate
569
+ x_min = c if x_min > c
570
+ x_max = c if x_max < c
571
+ else # y coordinate
572
+ y_min = c if y_min > c
573
+ y_max = c if y_max < c
574
+ end
575
+ end
576
+ end
577
+ x_min = x_min.to_f
578
+ x_max = x_max.to_f
579
+ y_min = y_min.to_f
580
+ y_max = y_max.to_f
581
+ dx = x_max - x_min
582
+ dy = y_max - y_min
583
+
584
+
585
+ shading = ref!({
586
+ :ShadingType => 7, # Tensor-Product Patch Meshes
587
+ :ColorSpace => data_color_space,
588
+ :BitsPerCoordinate => 32,
589
+ :BitsPerComponent => 8,
590
+ :BitsPerFlag => 8,
591
+ :Decode => [x_min, x_max, y_min, y_max, 0, 1, 0, 1, 0, 1]
592
+ })
593
+
594
+
595
+ data.each do |values|
596
+ f = values.shift
597
+ unless [0, 1, 2, 3].include? f
598
+ raise ArgumentError, "Flag must be 0, 1, 2, or 3. Got #{f}."
599
+ end
600
+
601
+ if f == 0
602
+ coords = values.shift(32)
603
+ colors = values.shift(4)
604
+ else
605
+ coords = values.shift(24)
606
+ colors = values.shift(2)
607
+ end
608
+
609
+ shading << [f].pack('C')
610
+
611
+ coords.each_with_index do |c, i|
612
+ if i.even? # x coordinate
613
+ shading << [normalize_coord(c, x_min, dx)].pack('N')
614
+ else
615
+ shading << [normalize_coord(c, y_min, dy)].pack('N')
616
+ end
617
+ end
618
+
619
+ colors.each do |color|
620
+ if color_space(color) != data_color_space
621
+ raise ArgumentError, "Colors of all vertices must be in the same color space. Expected #{data_color_space} got #{color_space(color)}"
622
+ end
623
+ shading << normalize_color(color).map{|c| (c * 255.0).round }.pack('C*')
624
+ end
625
+ end
626
+
627
+
628
+ ref!({
629
+ :PatternType => 2, # shading pattern
630
+ :Shading => shading,
631
+ :Matrix => [1, 0,
632
+ 0, 1] + map_to_absolute([0, 0]),
633
+ })
634
+ end
635
+
636
+ def normalize_coord(coord, min, delta)
637
+ diff = (coord.to_f - min) / delta
638
+ if diff > 1
639
+ diff = 1.0
640
+ elsif diff < 0
641
+ diff = 0.0;
642
+ end
643
+
644
+ (diff * 0xffffffff).round
645
+ end
646
+ end
647
+ end
648
+ end
649
+