pdf-writer 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 (74) hide show
  1. data/ChangeLog +44 -0
  2. data/LICENCE +118 -0
  3. data/README +32 -0
  4. data/bin/loader +54 -0
  5. data/bin/manual +22 -0
  6. data/bin/manual.bat +2 -0
  7. data/demo/chunkybacon.rb +28 -0
  8. data/demo/code.rb +63 -0
  9. data/demo/colornames.rb +843 -0
  10. data/demo/demo.rb +65 -0
  11. data/demo/gettysburg.rb +58 -0
  12. data/demo/hello.rb +18 -0
  13. data/demo/individual-i.rb +81 -0
  14. data/demo/pac.rb +62 -0
  15. data/demo/pagenumber.rb +67 -0
  16. data/demo/qr-language.rb +573 -0
  17. data/demo/qr-library.rb +371 -0
  18. data/images/chunkybacon.jpg +0 -0
  19. data/images/chunkybacon.png +0 -0
  20. data/images/pdfwriter-icon.jpg +0 -0
  21. data/images/pdfwriter-small.jpg +0 -0
  22. data/lib/pdf/charts.rb +13 -0
  23. data/lib/pdf/charts/stddev.rb +431 -0
  24. data/lib/pdf/grid.rb +135 -0
  25. data/lib/pdf/math.rb +108 -0
  26. data/lib/pdf/quickref.rb +330 -0
  27. data/lib/pdf/simpletable.rb +946 -0
  28. data/lib/pdf/techbook.rb +890 -0
  29. data/lib/pdf/writer.rb +2661 -0
  30. data/lib/pdf/writer/arc4.rb +63 -0
  31. data/lib/pdf/writer/fontmetrics.rb +201 -0
  32. data/lib/pdf/writer/fonts/Courier-Bold.afm +342 -0
  33. data/lib/pdf/writer/fonts/Courier-BoldOblique.afm +342 -0
  34. data/lib/pdf/writer/fonts/Courier-Oblique.afm +342 -0
  35. data/lib/pdf/writer/fonts/Courier.afm +342 -0
  36. data/lib/pdf/writer/fonts/Helvetica-Bold.afm +2827 -0
  37. data/lib/pdf/writer/fonts/Helvetica-BoldOblique.afm +2827 -0
  38. data/lib/pdf/writer/fonts/Helvetica-Oblique.afm +3051 -0
  39. data/lib/pdf/writer/fonts/Helvetica.afm +3051 -0
  40. data/lib/pdf/writer/fonts/MustRead.html +1 -0
  41. data/lib/pdf/writer/fonts/Symbol.afm +213 -0
  42. data/lib/pdf/writer/fonts/Times-Bold.afm +2588 -0
  43. data/lib/pdf/writer/fonts/Times-BoldItalic.afm +2384 -0
  44. data/lib/pdf/writer/fonts/Times-Italic.afm +2667 -0
  45. data/lib/pdf/writer/fonts/Times-Roman.afm +2419 -0
  46. data/lib/pdf/writer/fonts/ZapfDingbats.afm +225 -0
  47. data/lib/pdf/writer/graphics.rb +727 -0
  48. data/lib/pdf/writer/graphics/imageinfo.rb +365 -0
  49. data/lib/pdf/writer/lang.rb +43 -0
  50. data/lib/pdf/writer/lang/en.rb +77 -0
  51. data/lib/pdf/writer/object.rb +23 -0
  52. data/lib/pdf/writer/object/action.rb +40 -0
  53. data/lib/pdf/writer/object/annotation.rb +42 -0
  54. data/lib/pdf/writer/object/catalog.rb +39 -0
  55. data/lib/pdf/writer/object/contents.rb +68 -0
  56. data/lib/pdf/writer/object/destination.rb +40 -0
  57. data/lib/pdf/writer/object/encryption.rb +53 -0
  58. data/lib/pdf/writer/object/font.rb +76 -0
  59. data/lib/pdf/writer/object/fontdescriptor.rb +34 -0
  60. data/lib/pdf/writer/object/fontencoding.rb +39 -0
  61. data/lib/pdf/writer/object/image.rb +168 -0
  62. data/lib/pdf/writer/object/info.rb +55 -0
  63. data/lib/pdf/writer/object/outline.rb +30 -0
  64. data/lib/pdf/writer/object/outlines.rb +30 -0
  65. data/lib/pdf/writer/object/page.rb +195 -0
  66. data/lib/pdf/writer/object/pages.rb +115 -0
  67. data/lib/pdf/writer/object/procset.rb +46 -0
  68. data/lib/pdf/writer/object/viewerpreferences.rb +74 -0
  69. data/lib/pdf/writer/ohash.rb +58 -0
  70. data/lib/pdf/writer/oreader.rb +25 -0
  71. data/lib/pdf/writer/state.rb +48 -0
  72. data/lib/pdf/writer/strokestyle.rb +138 -0
  73. data/manual.pwd +5151 -0
  74. metadata +147 -0
@@ -0,0 +1,365 @@
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
+ # This file is also licensed under standard Ruby licensing provisions: the
10
+ # Ruby licence and the GNU General Public Licence, version 2 or later.
11
+ #
12
+ # $Id: imageinfo.rb,v 1.2 2005/05/16 03:59:21 austin Exp $
13
+ #++
14
+ require 'pdf/writer/oreader'
15
+
16
+ # This is based on ImageSize, by Keisuke Minami <keisuke@rccn.com>. It can
17
+ # be found at http://www.rubycgi.org/tools/index.en.htm
18
+ #
19
+ # This has been integrated into PDF::Writer because as yet there has been
20
+ # no response to emails asking for my extensions to be integrated and a
21
+ # RubyGem package to be made available.
22
+ class PDF::Writer::Graphics::ImageInfo
23
+ # Image Format Constants
24
+ module Formats
25
+ OTHER = "OTHER"
26
+ GIF = "GIF" # CompuServe GIF87a and GIF89a images.
27
+ PNG = "PNG" # Portable Network Graphics, PNG
28
+ JPEG = "JPEG" # JPEG
29
+ BMP = "BMP" # Windows or OS/2 Bitmaps
30
+ PPM = "PPM" # PPM is like PBM, PGM, & XV
31
+ PBM = "PBM"
32
+ PGM = "PGM"
33
+ # XV = "XV" # Not supported
34
+ TIFF = "TIFF" # TIFF formats
35
+ XBM = "XBM" # X Bitmap
36
+ XPM = "XPM" # X Pixmap
37
+ PSD = "PSD" # PhotoShop
38
+ PCX = "PCX" # PCX Bitmap
39
+ SWF = "SWF" # Flash
40
+ end
41
+ Type = Formats
42
+
43
+ class << self
44
+ def formats
45
+ Formats.constants
46
+ end
47
+ alias :type_list :formats
48
+ end
49
+
50
+ JPEG_SOF_BLOCKS = %W(\xc0 \xc1 \xc2 \xc3 \xc5 \xc6 \xc7 \xc9 \xca \xcb \xcd \xce \xcf)
51
+ JPEG_APP_BLOCKS = %W(\xe0 \xe1 \xe2 \xe3 \xe4 \xe5 \xe6 \xe7 \xe8 \xe9 \xea \xeb \xec \xed \xee \xef)
52
+
53
+ # Receive image & make size. argument is image String or IO
54
+ def initialize(data, format = nil)
55
+ @data = data.dup rescue data
56
+ @info = {}
57
+
58
+ if @data.kind_of?(IO)
59
+ @top = @data.read(128)
60
+ @data.seek(0, 0)
61
+ # Define Singleton-method definition to IO (byte, offset)
62
+ def @data.read_o(length = 1, offset = nil)
63
+ self.seek(offset, 0) if offset
64
+ ret = self.read(length)
65
+ raise "cannot read!!" unless ret
66
+ ret
67
+ end
68
+ elsif @data.is_a?(String)
69
+ @top = @data[0, 128]
70
+ # Define Singleton-method definition to String (byte, offset)
71
+ @data.extend(PDF::Writer::OffsetReader)
72
+ else
73
+ raise "argument class error!! #{data.type}"
74
+ end
75
+
76
+ if format.nil?
77
+ @format = discover_format
78
+ else
79
+ match = false
80
+ Formats.constants.each { |t| match = true if format == t }
81
+ raise("format is failed. #{format}\n") unless match
82
+ @format = format
83
+ end
84
+
85
+ __send__("measure_#@format".intern) unless @format == Formats::OTHER
86
+
87
+ @data = data.dup
88
+ end
89
+
90
+ attr_reader :format
91
+ alias :get_type :format
92
+ attr_reader :height
93
+ alias :get_height :height
94
+ attr_reader :width
95
+ alias :get_width width
96
+
97
+ attr_reader :bits
98
+ attr_reader :channels
99
+
100
+ attr_reader :info
101
+
102
+ def discover_format
103
+ if @top =~ %r{^GIF8[79]a}
104
+ Formats::GIF
105
+ elsif @top[0, 3] == "\xff\xd8\xff"
106
+ Formats::JPEG
107
+ elsif @top[0, 8] == "\x89PNG\x0d\x0a\x1a\x0a"
108
+ Formats::PNG
109
+ elsif @top[0, 3] == "FWS"
110
+ Formats::SWF
111
+ elsif @top[0, 4] == "8BPS"
112
+ Formats::PSD
113
+ elsif @top[0, 2] == 'BM'
114
+ Formats::BMP
115
+ elsif @top[0, 4] == "MM\x00\x2a"
116
+ Formats::TIFF
117
+ elsif @top[0, 4] == "II\x2a\x00"
118
+ Formats::TIFF
119
+ elsif @top[0, 12] == "\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a"
120
+ Formats::JP2
121
+ elsif @top =~ %r{^P[1-7]}
122
+ Formats::PPM
123
+ elsif @top =~ %r{\#define\s+\S+\s+\d+}
124
+ Formats::XBM
125
+ elsif @top =~ %r{/\* XPM \*/}
126
+ Formats::XPM
127
+ elsif @top[0] == 10
128
+ Formats::PCX
129
+ else
130
+ Formats::OTHER # might be WBMP
131
+ end
132
+ end
133
+ private :discover_format
134
+
135
+ def measure_GIF
136
+ @data.read_o(6) # Skip GIF8.a
137
+ @width, @height, @bits = @data.read_o(5).unpack('vvC')
138
+ if @bits & 0x80 == 0x80
139
+ @bits = (@bits & 0x07) + 1
140
+ else
141
+ @bits = 0
142
+ end
143
+ @channels = 3
144
+ end
145
+ private :measure_GIF
146
+
147
+ def measure_PNG
148
+ @data.read_o(12)
149
+ raise "This file is not PNG." unless @data.read_o(4) == "IHDR"
150
+ # The file information is in the IHDR section.
151
+ # Offset Bytes Meaning
152
+ # 0 4 Width
153
+ # 5 4 Height
154
+ # 9 1 Bit Depth
155
+ # 10 1 Compression Method
156
+ # 11 1 Filter Method
157
+ # 12 1 Interlace Method
158
+ ihdr = @data.read_o(13).unpack("NNCCCCC")
159
+ @width = ihdr[0]
160
+ @height = ihdr[1]
161
+ @bits = ihdr[2]
162
+ @info[:color_type] = ihdr[3]
163
+ @info[:compression_method] = ihdr[4]
164
+ @info[:filter_method] = ihdr[5]
165
+ @info[:interlace_method] = ihdr[6]
166
+
167
+
168
+ end
169
+ private :measure_PNG
170
+
171
+ def measure_JPEG
172
+ c_marker = "\xff" # Section marker.
173
+ @data.read_o(2) # Skip the first two bytes of JPEG identifier.
174
+ loop do
175
+ marker, code, length = @data.read_o(4).unpack('aan')
176
+ raise "JPEG marker not found!" if marker != c_marker
177
+
178
+ if JPEG_SOF_BLOCKS.include?(code)
179
+ @bits, @height, @width, @channels = @data.read_o(6).unpack("CnnC")
180
+ break
181
+ end
182
+
183
+ buffer = @data.read_o(length - 2)
184
+
185
+ if JPEG_APP_BLOCKS.include?(code)
186
+ @info["APP#{code.to_i - 0xe0}"] = buffer
187
+ end
188
+ end
189
+ end
190
+ private :measure_JPEG
191
+
192
+ def measure_BMP
193
+ # Skip the first 14 bytes of the image.
194
+ @data.read_o(14)
195
+ # Up to the next 16 bytes will be used.
196
+ dim = @data.read_o(16)
197
+
198
+ # Get the "size" of the image from the next four bytes.
199
+ size = dim.unpack("V")
200
+
201
+ if size == 12
202
+ @width, @height, @bits = dim.unpack("x4vvx3C")
203
+ elsif size > 12 and (size <= 64 or size == 108)
204
+ @width, @height, @bits = dim.unpack("x4VVv")
205
+ end
206
+ end
207
+ private :measure_BMP
208
+
209
+ def measure_PPM
210
+ header = @data.read_o(1024)
211
+ header.gsub!(/^\#[^\n\r]*/m, "")
212
+ md = %r{^(P[1-6])\s+?(\d+)\s+?(\d+)}mo.match(header)
213
+
214
+ @width = md.captures[1]
215
+ @height = md.captures[2]
216
+
217
+ case md.captures[0]
218
+ when "P1", "P4"
219
+ @format = "PBM"
220
+ when "P2", "P5"
221
+ @format = "PGM"
222
+ when "P3", "P6"
223
+ @format = "PPM"
224
+ # when "P7"
225
+ # @format = "XV"
226
+ # header =~ /IMGINFO:(\d+)x(\d+)/m
227
+ # width = $1.to_i; height = $2.to_i
228
+ end
229
+ end
230
+ private :measure_PPM
231
+
232
+ alias :measure_PGM :measure_PPM
233
+ private :measure_PGM
234
+ alias :measure_PBM :measure_PPM
235
+ private :measure_PBM
236
+
237
+ XBM_DIMENSIONS_RE = %r{^\#define\s*\S*\s*(\d+)\s*\n\#define\s*\S*\s*(\d+)}mi
238
+ def measure_XBM
239
+ md = XBM_DIMENSIONS_RE.match(@data.read_o(1024))
240
+
241
+ @width = md.captures[0].to_i
242
+ @height = md.captures[1].to_i
243
+ end
244
+ private :measure_XBM
245
+
246
+ XPM_DIMENSIONS_RE = %r<"\s*(\d+)\s+(\d+)(\s+\d+\s+\d+){1,2}\s*">m
247
+ def measure_XPM
248
+ while line = @data.read_o(1024)
249
+ md = XPM_DIMENSIONS_RE.match(line)
250
+ if md
251
+ @width = md.captures[0].to_i
252
+ @height = md.captures[1].to_i
253
+ break
254
+ end
255
+ end
256
+ end
257
+ private :measure_XPM
258
+
259
+ def measure_PSD
260
+ @width, @height = @data.read_o(26).unpack("x14NN")
261
+ end
262
+ private :measure_PSD
263
+
264
+ def measure_PCX
265
+ header = @data.read_o(128)
266
+ head_part = header.unpack('C4S4')
267
+ @width = head_part[6] - head_part[4] + 1
268
+ @height = head_part[7] - head_part[5] + 1
269
+ end
270
+ private :measure_PCX
271
+
272
+ def measure_SWF
273
+ header = @data.read_o(9)
274
+ raise "This file is not SWF." unless header.unpack('a3')[0] == 'FWS'
275
+
276
+ bits = Integer("0b#{header.unpack('@8B5')}")
277
+ header << @data.read_o(bits * 4 / 8 + 1)
278
+
279
+ str = *(header.unpack("@8B#{5 + bits * 4}"))
280
+ last = 5
281
+ x_min = Integer("0b#{str[last, bits]}")
282
+ x_max = Integer("0b#{str[(last + bits), bits]}")
283
+ y_min = Integer("0b#{str[(last + (2 * bits)), bits]}")
284
+ y_max = Integer("0b#{str[(last + (3 * bits)), bits]}")
285
+ @width = (x_max - x_min) / 20
286
+ @height = (y_max - y_min) / 20
287
+ end
288
+ private :measure_SWF
289
+
290
+ # The same as SWF, except that the original data is compressed with
291
+ # Zlib. Disabled for now.
292
+ def measure_SWC
293
+ end
294
+ private :measure_SWC
295
+
296
+ def measure_TIFF
297
+ # 'v' little-endian
298
+ # 'n' default to big-endian
299
+ endian = (@data.read_o(4) =~ /II\x2a\x00/o) ? 'v' : 'n'
300
+
301
+ packspec = [
302
+ nil, # nothing (shouldn't happen)
303
+ 'C', # BYTE (8-bit unsigned integer)
304
+ nil, # ASCII
305
+ endian, # SHORT (16-bit unsigned integer)
306
+ endian.upcase, # LONG (32-bit unsigned integer)
307
+ nil, # RATIONAL
308
+ 'c', # SBYTE (8-bit signed integer)
309
+ nil, # UNDEFINED
310
+ endian, # SSHORT (16-bit unsigned integer)
311
+ endian.upcase, # SLONG (32-bit unsigned integer)
312
+ ]
313
+
314
+ # Find the IFD location.
315
+ ifd_addr = *(@data.read_o(4).unpack(endian.upcase))
316
+ # Get the number of entries in the IFD.
317
+ ifd = @data.read_o(2, ifd_addr)
318
+ num_dirent = *(ifd.unpack(endian)) # Make it useful
319
+ ifd_addr += 2
320
+ num_dirent = ifd_addr + (num_dirent * 12) # Calc. maximum offset of IFD
321
+
322
+ loop do
323
+ break if @width and @height
324
+
325
+ ifd = @data.read_o(12, ifd_addr) # Get directory entry.
326
+ break if ifd.nil? or ifd_addr > num_dirent
327
+ ifd_addr += 12
328
+
329
+ tag = *(ifd.unpack(endian)) # ...decode its tag
330
+ type = *(ifd[2, 2].unpack(endian)) # ... and data type
331
+
332
+ # Check the type for sanity.
333
+ next if type > packspec.size or packspec[type].nil?
334
+
335
+ case tag
336
+ when 0x0100, 0xa002 # width
337
+ @width = *(ifd[8, 4].unpack(packspec[type]))
338
+ when 0x0101, 0xa003 # height
339
+ @height = *(ifd[8, 4].unpack(packspec[type]))
340
+ end
341
+ end
342
+ end
343
+ private :measure_TIFF
344
+ end
345
+
346
+ if __FILE__ == $0
347
+ require 'pdf/writer'
348
+ require 'pdf/writer/graphics'
349
+
350
+ print "Image Formats: #{PDF::Writer::Graphics::ImageInfo.formats.inspect}\n"
351
+
352
+ Dir.glob("*").each do |file|
353
+ print "#{file} (string)\n"
354
+ open(file, "rb") do |fh|
355
+ image = PDF::Writer::Graphics::ImageInfo.new(fh.read)
356
+ print <<-EOF
357
+ Format : #{image.format}
358
+ Width : #{image.width.inspect}
359
+ Height : #{image.height.inspect}
360
+ Bits : #{image.bits.inspect}
361
+ Channels: #{image.channels.inspect}
362
+ EOF
363
+ end
364
+ end
365
+ end
@@ -0,0 +1,43 @@
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: lang.rb,v 1.1 2005/05/18 11:57:58 austin Exp $
10
+ #++
11
+
12
+ module PDF::Writer::Lang
13
+ @message = {}
14
+
15
+ class << self
16
+ # PDF::Writer is fully internationalized. This module method sets the
17
+ # error messages to the specified language Module. The language Module
18
+ # must have a constant Hash called +Message+ containing a set of
19
+ # symbols and localized versions of the messages associated with them.
20
+ #
21
+ # If the file 'pdf/writer/lang/es' contains the module
22
+ # <tt>PDF::Writer::Lang::ES</tt>, the error messages for PDF could be
23
+ # localized to Espa�ol thus:
24
+ #
25
+ # require 'pdf/writer'
26
+ # require 'pdf/writer/lang/es'
27
+ #
28
+ # Localization is module-global; in a multithreaded program, all
29
+ # threads will use the current language's messages.
30
+ #
31
+ # See PDF::Writer::Lang::EN for more information.
32
+ attr_accessor :language
33
+ def language=(ll) #:nodoc:
34
+ @language = ll
35
+ @message.replace ll.instance_variable_get('@message')
36
+ end
37
+
38
+ # Looks up the mesasge
39
+ def [](message_id)
40
+ @message[message_id]
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,77 @@
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: en.rb,v 1.7 2005/06/08 12:16:11 austin Exp $
10
+ #++
11
+ # PDF::Writer::Lang::EN is the English-language output module. It contains a
12
+ # hash, @message, that contains the messages that may be reported by any
13
+ # method in the PDF library. The messages are identified by a Symbol.
14
+ #
15
+ # <b>Symbol</b>:: <b>Meaning</b>
16
+ # <b>:uri_09x</b>:: URIs must be HTTP or FTP URIs in this version of RSS.
17
+ module PDF::Writer::Lang::EN
18
+ @message = {
19
+ :invalid_pos => ":pos must be either :before or :after.",
20
+ :req_FPXO => "Pages#<< requires a PDF::Writer::Page, PDF::Writer::Font, or PDF::Writer::ExternalObject.",
21
+ :req_FPXOH => "Pages#add requires a PDF::Writer::Page, PDF::Writer::Font, PDF::Writer::ExternalObject, or Hash.",
22
+ :png_invalid_header => "Invalid PNG header.",
23
+ :png_unsupp_compres => "Unsupported PNG compression method.",
24
+ :png_unsupp_filter => "Unsupported PNG filter method.",
25
+ :png_header_missing => "PNG information header is missing.",
26
+ :png_8bit_colour => "Only PNG colour depths of 8 bits or less are supported.",
27
+ :png_alpha_trans => "PNG alpha channel transparency is not supported; only palette transparency is supported.",
28
+ :data_must_be_array => "The table data is not an Array. (Temporary limitation.)",
29
+ :columns_unspecified => "Columns are unspecified. They must be data[0] and be an array.",
30
+ :no_zlib_no_compress => "Could not load Zlib. PDF compression is disabled.",
31
+ :simpletable_columns_undefined => "Columns are undefined for table.",
32
+ :simpletable_data_empty => "Table data is empty.",
33
+ :techbook_eval_exception => <<-EOS ,
34
+
35
+ Error in document around line %d:
36
+ %s
37
+ Backtrace:
38
+ %s
39
+ EOS
40
+ :techbook_bad_columns_directive => "Invalid argument to directive .columns: %s",
41
+ :techbook_cannot_find_document => "Error: cannot find a document.",
42
+ :techbook_using_default_doc => "Using default document '%s'.",
43
+ :techbook_using_cached_doc => "Using cached document '%s'...",
44
+ :techbook_regenerating => "Cached document is older than source document. Regenerating.",
45
+ :techbook_ignoring_cache => "Ignoring cached document.",
46
+ :techbook_unknown_xref => "Unknown cross-reference %s.",
47
+ :techbook_code_not_empty => "Code is not empty:",
48
+ :techbook_usage_banner => "Usage: %s [options] [INPUT FILE]",
49
+ :techbook_usage_banner_1 => [
50
+ "INPUT FILE, if not specified, will be 'manual.pwd', either in the",
51
+ "current directory or relative to this file.",
52
+ ""
53
+ ],
54
+ :techbook_help_force_regen => [
55
+ "Forces the regeneration of the document,",
56
+ "ignoring the cached document version."
57
+ ],
58
+ :techbook_help_no_cache => [
59
+ "Disables generated document caching.",
60
+ ],
61
+ :techbook_help_compress => [
62
+ "Compresses the resulting PDF.",
63
+ ],
64
+ :techbook_help_help => [
65
+ "Shows this text.",
66
+ ],
67
+ :techbook_exception => "Exception %1s around line %2d.",
68
+ :C_callback_form_error => "Stand-alone callbacks must be of the form <C:callback />.",
69
+ :c_callback_form_error => "Paired callbacks must be of the form <c:callback>...</c:callback>.",
70
+ :callback_warning => "Unknown %1s callback: '%2s'. Ignoring.",
71
+ :charts_stddev_data_empty => "Charts::StdDev data is empty.",
72
+ :charts_stddev_scale_norange => "Charts::StdDev::Scale has no range.",
73
+ :charts_stddev_scale_nostep => "Charts::StdDev::Scale has no step.",
74
+ }
75
+
76
+ PDF::Writer::Lang.language = self
77
+ end