pdf-labels 1.0.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 (142) hide show
  1. data/History.txt +8 -0
  2. data/LICENCE +38 -0
  3. data/Manifest.txt +141 -0
  4. data/README.txt +72 -0
  5. data/Rakefile +30 -0
  6. data/lib/alias.rb +8 -0
  7. data/lib/glabel_template.rb +36 -0
  8. data/lib/label.rb +52 -0
  9. data/lib/layout.rb +13 -0
  10. data/lib/length_node.rb +47 -0
  11. data/lib/markup.rb +25 -0
  12. data/lib/pdf_label_page.rb +171 -0
  13. data/lib/pdf_labels.rb +6 -0
  14. data/lib/template.rb +37 -0
  15. data/templates/avery-iso-templates.xml +222 -0
  16. data/templates/avery-other-templates.xml +21 -0
  17. data/templates/avery-us-templates.xml +599 -0
  18. data/templates/glabels-2.0.dtd +329 -0
  19. data/templates/misc-iso-templates.xml +434 -0
  20. data/templates/misc-other-templates.xml +21 -0
  21. data/templates/misc-us-templates.xml +183 -0
  22. data/templates/paper-sizes.xml +37 -0
  23. data/templates/zweckform-iso-templates.xml +197 -0
  24. data/test/test_pdf_label_page.rb +91 -0
  25. data/vendor/color.rb +87 -0
  26. data/vendor/color/cmyk.rb +182 -0
  27. data/vendor/color/css.rb +27 -0
  28. data/vendor/color/grayscale.rb +135 -0
  29. data/vendor/color/hsl.rb +130 -0
  30. data/vendor/color/palette.rb +15 -0
  31. data/vendor/color/palette/gimp.rb +107 -0
  32. data/vendor/color/palette/monocontrast.rb +180 -0
  33. data/vendor/color/rgb-colors.rb +189 -0
  34. data/vendor/color/rgb.rb +311 -0
  35. data/vendor/color/rgb/metallic.rb +28 -0
  36. data/vendor/color/yiq.rb +78 -0
  37. data/vendor/pdf/charts.rb +13 -0
  38. data/vendor/pdf/charts/stddev.rb +433 -0
  39. data/vendor/pdf/grid.rb +135 -0
  40. data/vendor/pdf/math.rb +108 -0
  41. data/vendor/pdf/pagenumbers.rb +288 -0
  42. data/vendor/pdf/quickref.rb +331 -0
  43. data/vendor/pdf/simpletable.rb +947 -0
  44. data/vendor/pdf/techbook.rb +901 -0
  45. data/vendor/pdf/writer.rb +2801 -0
  46. data/vendor/pdf/writer/arc4.rb +63 -0
  47. data/vendor/pdf/writer/fontmetrics.rb +202 -0
  48. data/vendor/pdf/writer/fonts/Courier-Bold.afm +342 -0
  49. data/vendor/pdf/writer/fonts/Courier-BoldOblique.afm +342 -0
  50. data/vendor/pdf/writer/fonts/Courier-Oblique.afm +342 -0
  51. data/vendor/pdf/writer/fonts/Courier.afm +342 -0
  52. data/vendor/pdf/writer/fonts/Helvetica-Bold.afm +2827 -0
  53. data/vendor/pdf/writer/fonts/Helvetica-BoldOblique.afm +2827 -0
  54. data/vendor/pdf/writer/fonts/Helvetica-Oblique.afm +3051 -0
  55. data/vendor/pdf/writer/fonts/Helvetica.afm +3051 -0
  56. data/vendor/pdf/writer/fonts/Symbol.afm +213 -0
  57. data/vendor/pdf/writer/fonts/Times-Bold.afm +2588 -0
  58. data/vendor/pdf/writer/fonts/Times-BoldItalic.afm +2384 -0
  59. data/vendor/pdf/writer/fonts/Times-Italic.afm +2667 -0
  60. data/vendor/pdf/writer/fonts/Times-Roman.afm +2419 -0
  61. data/vendor/pdf/writer/fonts/ZapfDingbats.afm +225 -0
  62. data/vendor/pdf/writer/graphics.rb +813 -0
  63. data/vendor/pdf/writer/graphics/imageinfo.rb +365 -0
  64. data/vendor/pdf/writer/lang.rb +44 -0
  65. data/vendor/pdf/writer/lang/en.rb +104 -0
  66. data/vendor/pdf/writer/object.rb +23 -0
  67. data/vendor/pdf/writer/object/action.rb +40 -0
  68. data/vendor/pdf/writer/object/annotation.rb +42 -0
  69. data/vendor/pdf/writer/object/catalog.rb +39 -0
  70. data/vendor/pdf/writer/object/contents.rb +69 -0
  71. data/vendor/pdf/writer/object/destination.rb +40 -0
  72. data/vendor/pdf/writer/object/encryption.rb +53 -0
  73. data/vendor/pdf/writer/object/font.rb +68 -0
  74. data/vendor/pdf/writer/object/fontdescriptor.rb +34 -0
  75. data/vendor/pdf/writer/object/fontencoding.rb +40 -0
  76. data/vendor/pdf/writer/object/image.rb +308 -0
  77. data/vendor/pdf/writer/object/info.rb +79 -0
  78. data/vendor/pdf/writer/object/outline.rb +30 -0
  79. data/vendor/pdf/writer/object/outlines.rb +30 -0
  80. data/vendor/pdf/writer/object/page.rb +195 -0
  81. data/vendor/pdf/writer/object/pages.rb +115 -0
  82. data/vendor/pdf/writer/object/procset.rb +46 -0
  83. data/vendor/pdf/writer/object/viewerpreferences.rb +74 -0
  84. data/vendor/pdf/writer/ohash.rb +58 -0
  85. data/vendor/pdf/writer/oreader.rb +25 -0
  86. data/vendor/pdf/writer/state.rb +48 -0
  87. data/vendor/pdf/writer/strokestyle.rb +140 -0
  88. data/vendor/transaction/simple.rb +693 -0
  89. data/vendor/transaction/simple/group.rb +133 -0
  90. data/vendor/transaction/simple/threadsafe.rb +52 -0
  91. data/vendor/transaction/simple/threadsafe/group.rb +23 -0
  92. data/vendor/xml-mapping/ChangeLog +128 -0
  93. data/vendor/xml-mapping/LICENSE +56 -0
  94. data/vendor/xml-mapping/README +386 -0
  95. data/vendor/xml-mapping/README_XPATH +175 -0
  96. data/vendor/xml-mapping/Rakefile +214 -0
  97. data/vendor/xml-mapping/TODO.txt +32 -0
  98. data/vendor/xml-mapping/doc/xpath_impl_notes.txt +119 -0
  99. data/vendor/xml-mapping/examples/company.rb +34 -0
  100. data/vendor/xml-mapping/examples/company.xml +26 -0
  101. data/vendor/xml-mapping/examples/company_usage.intin.rb +19 -0
  102. data/vendor/xml-mapping/examples/company_usage.intout +39 -0
  103. data/vendor/xml-mapping/examples/order.rb +61 -0
  104. data/vendor/xml-mapping/examples/order.xml +54 -0
  105. data/vendor/xml-mapping/examples/order_signature_enhanced.rb +7 -0
  106. data/vendor/xml-mapping/examples/order_signature_enhanced.xml +9 -0
  107. data/vendor/xml-mapping/examples/order_signature_enhanced_usage.intin.rb +12 -0
  108. data/vendor/xml-mapping/examples/order_signature_enhanced_usage.intout +16 -0
  109. data/vendor/xml-mapping/examples/order_usage.intin.rb +73 -0
  110. data/vendor/xml-mapping/examples/order_usage.intout +147 -0
  111. data/vendor/xml-mapping/examples/time_augm.intin.rb +19 -0
  112. data/vendor/xml-mapping/examples/time_augm.intout +23 -0
  113. data/vendor/xml-mapping/examples/time_node.rb +27 -0
  114. data/vendor/xml-mapping/examples/xpath_create_new.intin.rb +85 -0
  115. data/vendor/xml-mapping/examples/xpath_create_new.intout +181 -0
  116. data/vendor/xml-mapping/examples/xpath_docvsroot.intin.rb +30 -0
  117. data/vendor/xml-mapping/examples/xpath_docvsroot.intout +34 -0
  118. data/vendor/xml-mapping/examples/xpath_ensure_created.intin.rb +62 -0
  119. data/vendor/xml-mapping/examples/xpath_ensure_created.intout +114 -0
  120. data/vendor/xml-mapping/examples/xpath_pathological.intin.rb +42 -0
  121. data/vendor/xml-mapping/examples/xpath_pathological.intout +56 -0
  122. data/vendor/xml-mapping/examples/xpath_usage.intin.rb +51 -0
  123. data/vendor/xml-mapping/examples/xpath_usage.intout +57 -0
  124. data/vendor/xml-mapping/install.rb +40 -0
  125. data/vendor/xml-mapping/lib/xml/mapping.rb +14 -0
  126. data/vendor/xml-mapping/lib/xml/mapping/base.rb +571 -0
  127. data/vendor/xml-mapping/lib/xml/mapping/standard_nodes.rb +343 -0
  128. data/vendor/xml-mapping/lib/xml/mapping/version.rb +8 -0
  129. data/vendor/xml-mapping/lib/xml/xxpath.rb +354 -0
  130. data/vendor/xml-mapping/test/all_tests.rb +6 -0
  131. data/vendor/xml-mapping/test/company.rb +56 -0
  132. data/vendor/xml-mapping/test/documents_folders.rb +33 -0
  133. data/vendor/xml-mapping/test/fixtures/bookmarks1.xml +24 -0
  134. data/vendor/xml-mapping/test/fixtures/company1.xml +85 -0
  135. data/vendor/xml-mapping/test/fixtures/documents_folders.xml +71 -0
  136. data/vendor/xml-mapping/test/fixtures/documents_folders2.xml +30 -0
  137. data/vendor/xml-mapping/test/multiple_mappings.rb +80 -0
  138. data/vendor/xml-mapping/test/tests_init.rb +2 -0
  139. data/vendor/xml-mapping/test/xml_mapping_adv_test.rb +84 -0
  140. data/vendor/xml-mapping/test/xml_mapping_test.rb +201 -0
  141. data/vendor/xml-mapping/test/xpath_test.rb +273 -0
  142. metadata +191 -0
@@ -0,0 +1,311 @@
1
+ #--
2
+ # Colour management with Ruby.
3
+ #
4
+ # Copyright 2005 Austin Ziegler
5
+ # http://rubyforge.org/ruby-pdf/
6
+ #
7
+ # Licensed under a MIT-style licence.
8
+ #
9
+ # $Id: rgb.rb,v 1.6 2005/08/08 02:44:17 austin Exp $
10
+ #++
11
+
12
+ # An RGB colour object.
13
+ class Color::RGB
14
+ # The format of a DeviceRGB colour for PDF. In color-tools 2.0 this will
15
+ # be removed from this package and added back as a modification by the
16
+ # PDF::Writer package.
17
+ PDF_FORMAT_STR = "%.3f %.3f %.3f %s"
18
+
19
+ class << self
20
+ # Creates an RGB colour object from percentages 0..100.
21
+ #
22
+ # Color::RGB.from_percentage(10, 20 30)
23
+ def from_percentage(r = 0, g = 0, b = 0)
24
+ from_fraction(r / 100.0, g / 100.0, b / 100.0)
25
+ end
26
+
27
+ # Creates an RGB colour object from fractional values 0..1.
28
+ #
29
+ # Color::RGB.from_fraction(.3, .2, .1)
30
+ def from_fraction(r = 0.0, g = 0.0, b = 0.0)
31
+ colour = Color::RGB.new
32
+ colour.r = r
33
+ colour.g = g
34
+ colour.b = b
35
+ colour
36
+ end
37
+
38
+ # Creates an RGB colour object from an HTML colour descriptor (e.g.,
39
+ # <tt>"fed"</tt> or <tt>"#cabbed;"</tt>.
40
+ #
41
+ # Color::RGB.from_html("fed")
42
+ # Color::RGB.from_html("#fed")
43
+ # Color::RGB.from_html("#cabbed")
44
+ # Color::RGB.from_html("cabbed")
45
+ def from_html(html_colour)
46
+ html_colour = html_colour.gsub(%r{[#;]}, '')
47
+ case html_colour.size
48
+ when 3
49
+ colours = html_colour.scan(%r{[0-9A-Fa-f]}).map { |el| (el * 2).to_i(16) }
50
+ when 6
51
+ colours = html_colour.scan(%r<[0-9A-Fa-f]{2}>).map { |el| el.to_i(16) }
52
+ else
53
+ raise ArgumentError
54
+ end
55
+
56
+ Color::RGB.new(*colours)
57
+ end
58
+ end
59
+
60
+ # Compares the other colour to this one. The other colour will be
61
+ # converted to RGB before comparison, so the comparison between a RGB
62
+ # colour and a non-RGB colour will be approximate and based on the other
63
+ # colour's default #to_rgb conversion. If there is no #to_rgb
64
+ # conversion, this will raise an exception. This will report that two
65
+ # RGB colours are equivalent if all component values are within 1e-4
66
+ # (0.0001) of each other.
67
+ def ==(other)
68
+ other = other.to_rgb
69
+ other.kind_of?(Color::RGB) and
70
+ ((@r - other.r).abs <= 1e-4) and
71
+ ((@g - other.g).abs <= 1e-4) and
72
+ ((@b - other.b).abs <= 1e-4)
73
+ end
74
+
75
+ # Creates an RGB colour object from the standard range 0..255.
76
+ #
77
+ # Color::RGB.new(32, 64, 128)
78
+ # Color::RGB.new(0x20, 0x40, 0x80)
79
+ def initialize(r = 0, g = 0, b = 0)
80
+ @r = r / 255.0
81
+ @g = g / 255.0
82
+ @b = b / 255.0
83
+ end
84
+
85
+ # Present the colour as a DeviceRGB fill colour string for PDF. This
86
+ # will be removed from the default package in color-tools 2.0.
87
+ def pdf_fill
88
+ PDF_FORMAT_STR % [ @r, @g, @b, "rg" ]
89
+ end
90
+
91
+ # Present the colour as a DeviceRGB stroke colour string for PDF. This
92
+ # will be removed from the default package in color-tools 2.0.
93
+ def pdf_stroke
94
+ PDF_FORMAT_STR % [ @r, @g, @b, "RG" ]
95
+ end
96
+
97
+ # Present the colour as an HTML/CSS colour string.
98
+ def html
99
+ r = (@r * 255).round
100
+ r = 255 if r > 255
101
+
102
+ g = (@g * 255).round
103
+ g = 255 if g > 255
104
+
105
+ b = (@b * 255).round
106
+ b = 255 if b > 255
107
+
108
+ "#%02x%02x%02x" % [ r, g, b ]
109
+ end
110
+
111
+ # Converts the RGB colour to CMYK. Most colour experts strongly suggest
112
+ # that this is not a good idea (some even suggesting that it's a very
113
+ # bad idea). CMYK represents additive percentages of inks on white
114
+ # paper, whereas RGB represents mixed colour intensities on a black
115
+ # screen.
116
+ #
117
+ # However, the colour conversion can be done. The basic method is
118
+ # multi-step:
119
+ #
120
+ # 1. Convert the R, G, and B components to C, M, and Y components.
121
+ # c = 1.0 � r
122
+ # m = 1.0 � g
123
+ # y = 1.0 � b
124
+ # 2. Compute the minimum amount of black (K) required to smooth the
125
+ # colour in inks.
126
+ # k = min(c, m, y)
127
+ # 3. Perform undercolour removal on the C, M, and Y components of the
128
+ # colours because less of each colour is needed for each bit of
129
+ # black. Also, regenerate the black (K) based on the undercolour
130
+ # removal so that the colour is more accurately represented in ink.
131
+ # c = min(1.0, max(0.0, c � UCR(k)))
132
+ # m = min(1.0, max(0.0, m � UCR(k)))
133
+ # y = min(1.0, max(0.0, y � UCR(k)))
134
+ # k = min(1.0, max(0.0, BG(k)))
135
+ #
136
+ # The undercolour removal function and the black generation functions
137
+ # return a value based on the brightness of the RGB colour.
138
+ def to_cmyk
139
+ c = 1.0 - @r.to_f
140
+ m = 1.0 - @g.to_f
141
+ y = 1.0 - @b.to_f
142
+
143
+ k = [c, m, y].min
144
+ k = k - (k * brightness)
145
+
146
+ c = [1.0, [0.0, c - k].max].min
147
+ m = [1.0, [0.0, m - k].max].min
148
+ y = [1.0, [0.0, y - k].max].min
149
+ k = [1.0, [0.0, k].max].min
150
+
151
+ Color::CMYK.from_fraction(c, m, y, k)
152
+ end
153
+
154
+ def to_rgb(ignored = nil)
155
+ self
156
+ end
157
+
158
+ # Returns the YIQ (NTSC) colour encoding of the RGB value.
159
+ def to_yiq
160
+ y = (@r * 0.299) + (@g * 0.587) + (@b * 0.114)
161
+ i = (@r * 0.596) + (@g * -0.275) + (@b * -0.321)
162
+ q = (@r * 0.212) + (@g * -0.523) + (@b * 0.311)
163
+ Color::YIQ.from_fraction(y, i, q)
164
+ end
165
+
166
+ # Returns the HSL colour encoding of the RGB value.
167
+ def to_hsl
168
+ min = [ @r, @g, @b ].min
169
+ max = [ @r, @g, @b ].max
170
+ delta = (max - min).to_f
171
+
172
+ lum = (max + min) / 2.0
173
+
174
+ if delta <= 1e-5 # close to 0.0, so it's a grey
175
+ hue = 0
176
+ sat = 0
177
+ else
178
+ if (lum - 0.5) <= 1e-5
179
+ sat = delta / (max + min).to_f
180
+ else
181
+ sat = delta / (2 - max - min).to_f
182
+ end
183
+
184
+ if @r == max
185
+ hue = (@g - @b) / delta.to_f
186
+ elsif @g == max
187
+ hue = (2.0 + @b - @r) / delta.to_f
188
+ elsif (@b - max) <= 1e-5
189
+ hue = (4.0 + @r - @g) / delta.to_f
190
+ end
191
+ hue /= 6.0
192
+
193
+ hue += 1 if hue < 0
194
+ hue -= 1 if hue > 1
195
+ end
196
+ Color::HSL.from_fraction(hue, sat, lum)
197
+ end
198
+
199
+ # Mix the RGB hue with White so that the RGB hue is the specified
200
+ # percentage of the resulting colour. Strictly speaking, this isn't a
201
+ # darken_by operation.
202
+ def lighten_by(percent)
203
+ mix_with(White, percent)
204
+ end
205
+
206
+ # Mix the RGB hue with Black so that the RGB hue is the specified
207
+ # percentage of the resulting colour. Strictly speaking, this isn't a
208
+ # darken_by operation.
209
+ def darken_by(percent)
210
+ mix_with(Black, percent)
211
+ end
212
+
213
+ # Mix the mask colour (which must be an RGB object) with the current
214
+ # colour at the stated opacity percentage (0..100).
215
+ def mix_with(mask, opacity)
216
+ opacity /= 100.0
217
+ rgb = self.dup
218
+
219
+ rgb.r = (@r * opacity) + (mask.r * (1 - opacity))
220
+ rgb.g = (@g * opacity) + (mask.g * (1 - opacity))
221
+ rgb.b = (@b * opacity) + (mask.b * (1 - opacity))
222
+
223
+ rgb
224
+ end
225
+
226
+ # Returns the brightness value for a colour, a number between 0..1.
227
+ # Based on the Y value of YIQ encoding, representing luminosity, or
228
+ # perceived brightness.
229
+ #
230
+ # This may be modified in a future version of color-tools to use the
231
+ # luminosity value of HSL.
232
+ def brightness
233
+ to_yiq.y
234
+ end
235
+ def to_grayscale
236
+ Color::GrayScale.from_fraction(to_hsl.l)
237
+ end
238
+
239
+ alias to_greyscale to_grayscale
240
+
241
+ # Returns a new colour with the brightness adjusted by the specified
242
+ # percentage. Negative percentages will darken the colour; positive
243
+ # percentages will brighten the colour.
244
+ #
245
+ # Color::RGB::DarkBlue.adjust_brightness(10)
246
+ # Color::RGB::DarkBlue.adjust_brightness(-10)
247
+ def adjust_brightness(percent)
248
+ percent /= 100.0
249
+ percent += 1.0
250
+ percent = [ percent, 2.0 ].min
251
+ percent = [ 0.0, percent ].max
252
+
253
+ hsl = to_hsl
254
+ hsl.l *= percent
255
+ hsl.to_rgb
256
+ end
257
+
258
+ # Returns a new colour with the saturation adjusted by the specified
259
+ # percentage. Negative percentages will reduce the saturation; positive
260
+ # percentages will increase the saturation.
261
+ #
262
+ # Color::RGB::DarkBlue.adjust_saturation(10)
263
+ # Color::RGB::DarkBlue.adjust_saturation(-10)
264
+ def adjust_saturation(percent)
265
+ percent /= 100.0
266
+ percent += 1.0
267
+ percent = [ percent, 2.0 ].min
268
+ percent = [ 0.0, percent ].max
269
+
270
+ hsl = to_hsl
271
+ hsl.s *= percent
272
+ hsl.to_rgb
273
+ end
274
+
275
+ # Returns a new colour with the hue adjusted by the specified
276
+ # percentage. Negative percentages will reduce the hue; positive
277
+ # percentages will increase the hue.
278
+ #
279
+ # Color::RGB::DarkBlue.adjust_hue(10)
280
+ # Color::RGB::DarkBlue.adjust_hue(-10)
281
+ def adjust_hue(percent)
282
+ percent /= 100.0
283
+ percent += 1.0
284
+ percent = [ percent, 2.0 ].min
285
+ percent = [ 0.0, percent ].max
286
+
287
+ hsl = to_hsl
288
+ hsl.h *= percent
289
+ hsl.to_rgb
290
+ end
291
+
292
+ attr_accessor :r, :g, :b
293
+ remove_method :r=, :g=, :b= ;
294
+ def r=(rr) #:nodoc:
295
+ rr = 1.0 if rr > 1
296
+ rr = 0.0 if rr < 0
297
+ @r = rr
298
+ end
299
+ def g=(gg) #:nodoc:
300
+ gg = 1.0 if gg > 1
301
+ gg = 0.0 if gg < 0
302
+ @g = gg
303
+ end
304
+ def b=(bb) #:nodoc:
305
+ bb = 1.0 if bb > 1
306
+ bb = 0.0 if bb < 0
307
+ @b = bb
308
+ end
309
+ end
310
+
311
+ require 'color/rgb-colors'
@@ -0,0 +1,28 @@
1
+ #--
2
+ # Colour management with Ruby.
3
+ #
4
+ # Copyright 2005 Austin Ziegler
5
+ # http://rubyforge.org/ruby-pdf/
6
+ #
7
+ # Licensed under a MIT-style licence.
8
+ #
9
+ # $Id: metallic.rb,v 1.1 2005/08/05 23:07:20 austin Exp $
10
+ #++
11
+
12
+ # This namespace contains some RGB metallic colours suggested by Jim Freeze.
13
+ module Color::RGB::Metallic
14
+ Aluminum = Color::RGB.new(0x99, 0x99, 0x99)
15
+ CoolCopper = Color::RGB.new(0xd9, 0x87, 0x19)
16
+ Copper = Color::RGB.new(0xb8, 0x73, 0x33)
17
+ Iron = Color::RGB.new(0x4c, 0x4c, 0x4c)
18
+ Lead = Color::RGB.new(0x19, 0x19, 0x19)
19
+ Magnesium = Color::RGB.new(0xb3, 0xb3, 0xb3)
20
+ Mercury = Color::RGB.new(0xe6, 0xe6, 0xe6)
21
+ Nickel = Color::RGB.new(0x80, 0x80, 0x80)
22
+ PolySilicon = Color::RGB.new(0x60, 0x00, 0x00)
23
+ Poly = PolySilicon
24
+ Silver = Color::RGB.new(0xcc, 0xcc, 0xcc)
25
+ Steel = Color::RGB.new(0x66, 0x66, 0x66)
26
+ Tin = Color::RGB.new(0x7f, 0x7f, 0x7f)
27
+ Tungsten = Color::RGB.new(0x33, 0x33, 0x33)
28
+ end
@@ -0,0 +1,78 @@
1
+ #--
2
+ # Colour management with Ruby.
3
+ #
4
+ # Copyright 2005 Austin Ziegler
5
+ # http://rubyforge.org/ruby-pdf/
6
+ #
7
+ # Licensed under a MIT-style licence.
8
+ #
9
+ # $Id: yiq.rb,v 1.3 2005/08/08 02:44:17 austin Exp $
10
+ #++
11
+
12
+ # A colour object representing YIQ (NTSC) colour encoding.
13
+ class Color::YIQ
14
+ # Creates a YIQ colour object from fractional values 0 .. 1.
15
+ #
16
+ # Color::YIQ.new(0.3, 0.2, 0.1)
17
+ def self.from_fraction(y = 0, i = 0, q = 0)
18
+ color = Color::YIQ.new
19
+ color.y = y
20
+ color.i = i
21
+ color.q = q
22
+ color
23
+ end
24
+
25
+ # Creates a YIQ colour object from percentages 0 .. 100.
26
+ #
27
+ # Color::YIQ.new(10, 20, 30)
28
+ def initialize(y = 0, i = 0, q = 0)
29
+ @y = y / 100.0
30
+ @i = i / 100.0
31
+ @q = q / 100.0
32
+ end
33
+
34
+ # Compares the other colour to this one. The other colour will be
35
+ # converted to YIQ before comparison, so the comparison between a YIQ
36
+ # colour and a non-YIQ colour will be approximate and based on the other
37
+ # colour's #to_yiq conversion. If there is no #to_yiq conversion, this
38
+ # will raise an exception. This will report that two YIQ values are
39
+ # equivalent if all component colours are within 1e-4 (0.0001) of each
40
+ # other.
41
+ def ==(other)
42
+ other = other.to_yiq
43
+ other.kind_of?(Color::YIQ) and
44
+ ((@y - other.y).abs <= 1e-4) and
45
+ ((@i - other.i).abs <= 1e-4) and
46
+ ((@q - other.q).abs <= 1e-4)
47
+ end
48
+
49
+ def to_yiq
50
+ self
51
+ end
52
+
53
+ def brightness
54
+ @y
55
+ end
56
+ def to_grayscale
57
+ Color::GrayScale.new(@y)
58
+ end
59
+ alias to_greyscale to_grayscale
60
+
61
+ attr_accessor :y, :i, :q
62
+ remove_method :y=, :i=, :q=
63
+ def y=(yy) #:nodoc:
64
+ yy = 1.0 if yy > 1
65
+ yy = 0.0 if yy < 0
66
+ @y = yy
67
+ end
68
+ def i=(ii) #:nodoc:
69
+ ii = 1.0 if ii > 1
70
+ ii = 0.0 if ii < 0
71
+ @i = ii
72
+ end
73
+ def q=(qq) #:nodoc:
74
+ qq = 1.0 if qq > 1
75
+ qq = 0.0 if qq < 0
76
+ @q = qq
77
+ end
78
+ end
@@ -0,0 +1,13 @@
1
+ #--
2
+ # PDF::Writer for Ruby.
3
+ # http://rubyforge.org/projects/ruby-pdf/
4
+ # Copyright 2003 - 2005 Austin Ziegler.
5
+ #
6
+ # Licensed under a MIT-style licence. See LICENCE in the main distribution
7
+ # for full licensing information.
8
+ #
9
+ # $Id: charts.rb,v 1.2 2005/05/16 03:59:21 austin Exp $
10
+ #++
11
+ # A namespace for charts that can be drawn on PDF::Writer canvases.
12
+ module PDF::Charts
13
+ end
@@ -0,0 +1,433 @@
1
+ #--
2
+ # PDF::Writer for Ruby.
3
+ # http://rubyforge.org/projects/ruby-pdf/
4
+ # Copyright 2003 - 2005 Austin Ziegler.
5
+ #
6
+ # Licensed under a MIT-style licence. See LICENCE in the main distribution
7
+ # for full licensing information.
8
+ #
9
+ # $Id: stddev.rb,v 1.12 2005/10/12 14:41:40 austin Exp $
10
+ #++
11
+ require 'pdf/writer'
12
+ require 'pdf/charts'
13
+ require 'ostruct'
14
+
15
+ # Creates a standard deviation chart. This is a type of chart that is
16
+ # effective for the display of survey results or other data that can
17
+ # easily be measured in terms of the average and the standard deviation
18
+ # from that average.
19
+ #
20
+ # The scale of responses is the vertical scale; the average data points
21
+ # and standard deviation values are the horizontal scale.
22
+ class PDF::Charts::StdDev
23
+ VERSION = '1.1.4'
24
+
25
+ # A data element.
26
+ DataPoint = Struct.new(:label, :average, :stddev)
27
+
28
+ # A label for displaying the scale (vertical) of data in the dataset or
29
+ # the data set identifiers.
30
+ class Label
31
+ def initialize
32
+ yield self if block_given?
33
+ end
34
+
35
+ # The height of the label, in PDF user units. Ignored for scale
36
+ # labels.
37
+ attr_accessor :height
38
+ # The background color of the label. Ignored for scale labels.
39
+ attr_accessor :background_color
40
+ # The text color of the label.
41
+ attr_accessor :text_color
42
+ # The text size, in points, of the label.
43
+ attr_accessor :text_size
44
+ # The padding of the label. Only used for scale labels.
45
+ attr_accessor :pad
46
+ # The decimal precision of the label. Only used for scale labels.
47
+ attr_accessor :decimal_precision
48
+ end
49
+
50
+ # The scale of the dataset.
51
+ class Scale
52
+ def initialize(args = { })
53
+ @range = args[:range]
54
+ @step = args[:step]
55
+ @style = args[:style]
56
+ @show_labels = false
57
+
58
+ yield self if block_given?
59
+
60
+ raise TypeError, PDF::Lange[:charts_stddev_scale_norange] if @range.nil?
61
+ raise TypeError, PDF::Lange[:charts_stddev_scale_nostep] if @step.nil?
62
+ end
63
+
64
+ # Range of the scale. This should be a Range object.
65
+ attr_accessor :range
66
+ # The lower end of the range of the scale. The scale range may be
67
+ # modified by changing this value.
68
+ attr_accessor :first
69
+ remove_method :first, :first= ;
70
+ def first #:nodoc:
71
+ @range.first
72
+ end
73
+ def first=(ff) #:nodoc:
74
+ @range = (ff..@range.last)
75
+ end
76
+ # The upper end of the range of the scale. The scale range may be
77
+ # modified by changing this value.
78
+ attr_accessor :last
79
+ remove_method :last, :last= ;
80
+ def last #:nodoc:
81
+ @range.last
82
+ end
83
+ def last=(ll) #:nodoc:
84
+ @range = (@range.first..ll)
85
+ end
86
+ # Defines the step of the scale. Each step represents a vertical
87
+ # position on the chart.
88
+ attr_accessor :step
89
+ # Defines the line style for the scale on the chart. If this is unset
90
+ # (+nil+), there will be no horizontal marks across the chart for the
91
+ # steps of the scale.
92
+ attr_accessor :style
93
+ # Shows the scale labels if +true+.
94
+ attr_accessor :show_labels
95
+ # Defines the label options.
96
+ attr_accessor :label
97
+ end
98
+
99
+ # This is any line that will be drawn; this is a combination of the line
100
+ # style (which must be a PDF::Writer::StrokeStyle object) and a color.
101
+ class Marker
102
+ def initialize
103
+ yield self if block_given?
104
+ end
105
+
106
+ # The stroke style of the marker.
107
+ attr_accessor :style
108
+ # The stroke color of the marker.
109
+ attr_accessor :color
110
+ end
111
+
112
+ def initialize
113
+ @data = []
114
+
115
+ @scale = Scale.new do |scale|
116
+ scale.range = 0..6
117
+ scale.step = 1
118
+ scale.style = PDF::Writer::StrokeStyle.new(0.25)
119
+ scale.show_labels = false
120
+ scale.label = Label.new do |label|
121
+ label.text_size = 8
122
+ label.text_color = Color::RGB::Black
123
+ label.pad = 2
124
+ label.decimal_precision = 1
125
+ end
126
+ end
127
+ @leading_gap = 10
128
+ @show_labels = true
129
+ @label = Label.new do |label|
130
+ label.height = 25
131
+ label.background_color = Color::RGB::Black
132
+ label.text_color = Color::RGB::White
133
+ label.text_size = 12
134
+ end
135
+
136
+ @outer_borders = Marker.new do |marker|
137
+ marker.style = PDF::Writer::StrokeStyle.new(1.5)
138
+ marker.color = Color::RGB::Black
139
+ end
140
+ @inner_borders = nil
141
+
142
+ @dot = Marker.new do |marker|
143
+ marker.style = PDF::Writer::StrokeStyle.new(5)
144
+ marker.color = Color::RGB::Black
145
+ end
146
+ @bar = Marker.new do |marker|
147
+ marker.style = PDF::Writer::StrokeStyle.new(0.5)
148
+ marker.color = Color::RGB::Black
149
+ end
150
+ @upper_crossbar = Marker.new do |marker|
151
+ marker.style = PDF::Writer::StrokeStyle.new(1)
152
+ marker.color = Color::RGB::Black
153
+ end
154
+ @lower_crossbar = Marker.new do |marker|
155
+ marker.style = PDF::Writer::StrokeStyle.new(1)
156
+ marker.color = Color::RGB::Black
157
+ end
158
+
159
+ @height = 200
160
+ @maximum_width = 500
161
+ @datapoint_width = 35
162
+
163
+ yield self if block_given?
164
+ end
165
+
166
+ # The data used to generate the standard deviation chart. This is an
167
+ # array of DataPoint objects, each containing a +label+, an +average+,
168
+ # and the +stddev+ (standard deviation) from that average.
169
+ attr_reader :data
170
+ # The scale of the chart. All values must be within this range. This
171
+ # will be a Scale object. It defaults to a scale of 0..6 with a step of
172
+ # 1.
173
+ attr_accessor :scale
174
+
175
+ # The minimum gap between the chart and the bottom of the page, in
176
+ # PDF user units.
177
+ attr_accessor :leading_gap
178
+
179
+ # This will be +true+ if labels are to be displayed.
180
+ attr_accessor :show_labels
181
+ # The label style of the labels if they are displayed. This must be a
182
+ # PDF::Charts::StdDev::Label object.
183
+ attr_accessor :label
184
+
185
+ # The inner border style. If +nil+, no inner borders are drawn. This is
186
+ # a PDF::Charts::StdDev::Marker object.
187
+ attr_accessor :inner_borders
188
+ # The outer border style. If +nil+, no inner borders are drawn. This is
189
+ # a PDF::Charts::StdDev::Marker object.
190
+ attr_accessor :outer_borders
191
+
192
+ # The dot marker. A filled circle will be drawn with this information.
193
+ # If +nil+, the dot will not be drawn. This is a
194
+ # PDF::Charts::StdDev::Marker object.
195
+ attr_accessor :dot
196
+ # The standard deviation bar. A line will be drawn through the dot
197
+ # marker (if drawn) from the upper to lower standard deviation.
198
+ # If +nil+, the line will not be drawn. This is a
199
+ # PDF::Charts::StdDev::Marker object.
200
+ attr_accessor :bar
201
+ # The upper crossbar. A line will be drawn across the top of the
202
+ # standard deviation bar to the width of the dot marker. If #dot is
203
+ # +nil+, then the line will be twice as wide as it is thick. If +nil+,
204
+ # the upper crossbar will not be drawn. This is a
205
+ # PDF::Charts::StdDev::Marker object.
206
+ attr_accessor :upper_crossbar
207
+ # The lower crossbar. A line will be drawn across the bottom of the
208
+ # standard deviation bar to the width of the dot marker. If #dot is
209
+ # +nil+, then the line will be twice as wide as it is thick. If +nil+,
210
+ # the lower crossbar will not be drawn. This is a
211
+ # PDF::Charts::StdDev::Marker object.
212
+ attr_accessor :lower_crossbar
213
+
214
+ # The height of the chart in PDF user units. Default 200 units.
215
+ attr_accessor :height
216
+ # The maximum width of the chart in PDF user units. Default 500 units.
217
+ attr_accessor :maximum_width
218
+ # The width of a single datapoint.
219
+ attr_accessor :datapoint_width
220
+
221
+ # Draw the standard deviation chart on the supplied PDF document.
222
+ def render_on(pdf)
223
+ raise TypeError, PDF::Writer::Lang[:charts_stddev_data_empty] if @data.empty?
224
+ data = @data.dup
225
+ leftover_data = nil
226
+
227
+ loop do
228
+ # Set up the scale information.
229
+ scale = []
230
+
231
+ (@scale.first + @scale.step).step(@scale.last, @scale.step) do |ii|
232
+ scale << "%01.#{@scale.label.decimal_precision}f" % ii
233
+ end
234
+
235
+ scales = PDF::Writer::OHash.new
236
+ scale.each_with_index do |gg, ii|
237
+ scales[ii] = OpenStruct.new
238
+ scales[ii].value = gg
239
+ end
240
+
241
+ # Add information about the scales' locations to the scales
242
+ # hash. Note that the count is one smaller than it should be, so we're
243
+ # increasing it. The first scale is the bottom of the chart.
244
+ scale_count = scale.size + 1
245
+
246
+ label_height_adjuster = 0
247
+ label_height_adjuster = @label.height if @show_labels
248
+
249
+ chart_area_height = @height - label_height_adjuster
250
+ scale_height = chart_area_height / scale_count.to_f
251
+
252
+ scales.each_key do |index|
253
+ this_height = scale_height * (index + 1) + @label.height
254
+ scales[index].line_height = this_height
255
+ if @scale.show_labels
256
+ scales[index].label_height = this_height -
257
+ (@scale.label.text_size / 3.0)
258
+ end
259
+ end
260
+
261
+ # How many sections do we need in this chart, and how wide will it
262
+ # need to be?
263
+ chunk_width = @datapoint_width
264
+ num_chunks = data.size
265
+ widest_scale_label = 0
266
+
267
+ if @scale.show_labels
268
+ scales.each_value do |scale|
269
+ this_width = pdf.text_width(scale.value, @scale.label.text_size)
270
+ widest_scale_label = this_width if this_width > widest_scale_label
271
+ end
272
+ end
273
+
274
+ chart_width = chunk_width * num_chunks
275
+ total_width = chart_width + widest_scale_label + @scale.label.pad
276
+
277
+ # What happens if the projected width of the chart is too big?
278
+ # Figure out how to break the chart in pieces.
279
+ if total_width > @maximum_width
280
+ max_column_count = 0
281
+ base_width = widest_scale_label + @scale.label.pad
282
+ (1..(num_chunks + 1)).each do |ii|
283
+ if (base_width + (ii * chunk_width)) > @maximum_width
284
+ break
285
+ else
286
+ max_column_count += 1
287
+ end
288
+ end
289
+
290
+ leftover_data = data.slice!(max_column_count, -1)
291
+
292
+ num_chunks = data.size
293
+ chart_width = chunk_width * num_chunks
294
+ total_width = chart_width + widest_scale_label + @scale.label.pad
295
+ end
296
+
297
+ chart_y = pdf.y - @height + @leading_gap
298
+ chart_y += (@outer_borders.style.width * 2.0) if @outer_borders
299
+
300
+ if chart_y < pdf.bottom_margin
301
+ pdf.start_new_page
302
+ chart_y = pdf.y - @height
303
+ chart_y += (@outer_borders.style.width * 2.0) if @outer_borders
304
+ end
305
+
306
+ chart_x = pdf.absolute_x_middle - (total_width / 2.0) + widest_scale_label
307
+
308
+ # Add labels, if needed.
309
+ if @show_labels
310
+ pdf.save_state
311
+ pdf.fill_color! @label.background_color
312
+ # Draw a rectangle for each label
313
+ num_chunks.times do |ii|
314
+ this_x = chart_x + ii * chunk_width
315
+ pdf.rectangle(this_x, chart_y, chunk_width, @label.height).fill
316
+ end
317
+
318
+ # Add a border above the label rectangle.
319
+ if @outer_borders
320
+ pdf.stroke_style! @outer_borders.style
321
+ pdf.line(chart_x, chart_y + @label.height, chart_x + chart_width, chart_y + @label.height).stroke
322
+ end
323
+ pdf.fill_color! @label.text_color
324
+
325
+ data.each_with_index do |datum, ii|
326
+ label = datum.label.to_s
327
+ label_width = pdf.text_width(label, @label.text_size)
328
+ this_x = chart_x + (ii * chunk_width) + (chunk_width / 2.0) - (label_width / 2.0)
329
+ this_y = chart_y + (@label.height / 2.0) - (@label.text_size / 3.0)
330
+ pdf.add_text(this_x, this_y, label, @label.text_size)
331
+ end
332
+ pdf.restore_state
333
+ end
334
+
335
+ if @inner_borders
336
+ pdf.save_state
337
+ pdf.stroke_color! @inner_borders.color
338
+ pdf.stroke_style! @inner_borders.style
339
+ (num_chunks - 1).times do |ii|
340
+ this_x = chart_x + (ii * chunk_width) + chunk_width
341
+ pdf.line(this_x, chart_y, this_x, chart_y + @height).stroke
342
+ end
343
+ pdf.restore_state
344
+ end
345
+
346
+ pdf.save_state
347
+ if @outer_borders
348
+ pdf.stroke_color! @outer_borders.color
349
+ pdf.stroke_style! @outer_borders.style
350
+ pdf.rectangle(chart_x, chart_y, chart_width, @height).stroke
351
+ end
352
+
353
+ if @scale.style
354
+ pdf.save_state
355
+ pdf.stroke_style! @scale.style
356
+ scales.each_value do |scale|
357
+ this_y = chart_y + scale.line_height
358
+ pdf.line(chart_x, this_y, chart_x + chart_width, this_y).stroke
359
+ end
360
+ pdf.restore_state
361
+ end
362
+
363
+ if @scale.show_labels
364
+ pdf.save_state
365
+ scales.each_value do |scale|
366
+ this_y = chart_y + scale.label_height
367
+ label_width = pdf.text_width(scale.value, @scale.label.text_size)
368
+ this_x = chart_x - label_width - @scale.label.pad
369
+ pdf.fill_color! @scale.label.text_color
370
+ pdf.add_text(this_x, this_y, scale.value, @scale.label.text_size)
371
+ end
372
+ pdf.restore_state
373
+ end
374
+
375
+ data.each_with_index do |datum, ii|
376
+ avg_height = datum.average * scale_height
377
+ stddev_height = datum.stddev * scale_height
378
+ this_y = chart_y + label_height_adjuster + avg_height
379
+ this_x = chart_x + (ii * chunk_width) + (chunk_width / 2.0)
380
+ line_top_y = this_y + (stddev_height / 2.0)
381
+ line_bot_y = this_y - (stddev_height / 2.0)
382
+
383
+ # Plot the dot
384
+ if @dot
385
+ pdf.stroke_color! @dot.color
386
+ pdf.stroke_style! @dot.style
387
+ pdf.circle_at(this_x, this_y, (@dot.style.width / 2.0)).fill
388
+ end
389
+
390
+ # Plot the bar
391
+ if @bar
392
+ pdf.stroke_color! @bar.color
393
+ pdf.stroke_style! @bar.style
394
+ pdf.line(this_x, line_top_y, this_x, line_bot_y).stroke
395
+ end
396
+
397
+ # Plot the crossbars
398
+ if @upper_crossbar
399
+ if @dot
400
+ cb_width = @dot.style.width
401
+ else
402
+ cb_width = @upper_crossbar.style.width
403
+ end
404
+ pdf.stroke_color! @upper_crossbar.color
405
+ pdf.stroke_style! @upper_crossbar.style
406
+ pdf.line(this_x - cb_width, line_top_y, this_x + cb_width, line_top_y).stroke
407
+ end
408
+ if @lower_crossbar
409
+ if @dot
410
+ cb_width = @dot.style.width
411
+ else
412
+ cb_width = @lower_crossbar.style.width
413
+ end
414
+ pdf.stroke_color! @lower_crossbar.color
415
+ pdf.stroke_style! @lower_crossbar.style
416
+
417
+ pdf.line(this_x - cb_width, line_bot_y, this_x + cb_width, line_bot_y).stroke
418
+ end
419
+ end
420
+
421
+ pdf.restore_state
422
+
423
+ pdf.y = chart_y
424
+
425
+ break if leftover_data.nil?
426
+
427
+ data = leftover_data
428
+ leftover_data = nil
429
+ end
430
+
431
+ pdf.y
432
+ end
433
+ end