ydim 1.0.0 → 1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ebd5360c95d8427ee4b3a093102866fce2d3e482
4
+ data.tar.gz: c2c5e41885205634f338502a40c044eaa7b090c8
5
+ SHA512:
6
+ metadata.gz: 24534e21dd9a128df999e34922820fa4740eb85faedfb0b3069c16802628929491d89a8786d663ae5b918cc61a02c75c33cc10a75be556f3eddea14d44659a0e
7
+ data.tar.gz: 450bc76a0b030402f9011884cb1392946864764841d8bca4240be27ac7ee03bff80b70d6424710153686ebba07211d9ac21a500599aa55a4583f8ae99005b52f
@@ -0,0 +1 @@
1
+ *.rb diff
@@ -0,0 +1,6 @@
1
+ vendor/
2
+ coverage/
3
+ pkg/
4
+ .bundle
5
+ .ruby-version
6
+ Gemfile.lock
@@ -0,0 +1,26 @@
1
+ language: ruby
2
+
3
+ bundler_args: --without debugger
4
+
5
+ cache: bundler
6
+ sudo: false
7
+
8
+ before_install:
9
+ - gem --version
10
+
11
+ script: bundle exec test/suite.rb
12
+
13
+ rvm:
14
+ - 2.1
15
+ - 2.2
16
+ - 2.3.0
17
+ - ruby-head
18
+
19
+ matrix:
20
+ allow_failures:
21
+ - rvm: ruby-head
22
+
23
+ notifications:
24
+ email:
25
+ recipients:
26
+ - ngiger@ywesee.com
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ group :debugger do
5
+ if RUBY_VERSION.match(/^1/)
6
+ gem 'pry-debugger'
7
+ else
8
+ gem 'pry-byebug'
9
+ gem 'pry-doc'
10
+ end
11
+ end
@@ -1,6 +1,11 @@
1
- === 1.0.0 / 2010-12-20
1
+ === 1.0.1 / 10.05.2016
2
2
 
3
- * 1 major enhancement
3
+ * Works only for Ruby version >= 2.1.0
4
+ * Replaced hoe by bundler/gem_tasks
5
+
6
+ === 1.0.0 / 20.12.2010
7
+
8
+ * Version 1.0
4
9
 
5
10
  * Birthday!
6
11
 
data/Rakefile CHANGED
@@ -1,28 +1,21 @@
1
- # -*- ruby -*-
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'ydim/version'
6
+ require "bundler/gem_tasks"
7
+ require 'rspec/core/rake_task'
2
8
 
3
- require 'rubygems'
4
- require 'hoe'
9
+ RSpec::Core::RakeTask.new(:spec)
5
10
 
6
- # Hoe.plugin :compiler
7
- # Hoe.plugin :cucumberfeatures
8
- # Hoe.plugin :gem_prelude_sucks
9
- # Hoe.plugin :inline
10
- # Hoe.plugin :inline
11
- # Hoe.plugin :manifest
12
- # Hoe.plugin :newgem
13
- # Hoe.plugin :racc
14
- # Hoe.plugin :rubyforge
15
- # Hoe.plugin :rubyforge
16
- # Hoe.plugin :website
11
+ # dependencies are now declared in ydim.gemspec
17
12
 
18
- Hoe.spec 'ydim' do
19
- # HEY! If you fill these out in ~/.hoe_template/Rakefile.erb then
20
- # you'll never have to touch them again!
21
- # (delete this comment too, of course)
22
-
23
- developer('Masaomi Hatakeyama, Zeno R.R. Davatz', 'mhatakeyama@ywesee.com, zdavatz@ywesee.com')
24
-
25
- # self.rubyforge_name = 'ydimx' # if different than 'ydim'
13
+ desc 'Offer a gem task like hoe'
14
+ task :gem => :build do
15
+ Rake::Task[:build].invoke
26
16
  end
27
17
 
28
- # vim: syntax=ruby
18
+ task :spec => :clean
19
+
20
+ require 'rake/clean'
21
+ CLEAN.include FileList['pkg/*.gem']
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+ puts "#{Time.now}: Loading #{__FILE__}"
4
+ STDOUT.sync = true
5
+
6
+ begin
7
+ require 'pry'
8
+ rescue LoadError
9
+ end
10
+
11
+ $: << File.expand_path(File.join(File.dirname(__FILE__), 'lib'))
12
+ require 'logger'
13
+ require 'needle'
14
+ require 'odba/id_server'
15
+ require 'rrba/server'
16
+ require 'ydim/autoinvoicer'
17
+ require 'ydim/client'
18
+ require 'ydim/currency_converter'
19
+ require 'ydim/currency_updater'
20
+ require 'ydim/factory'
21
+ require 'ydim/root_user'
22
+ require 'ydim/server'
23
+ require 'ydim/util'
24
+ require 'odba/18_19_loading_compatibility'
25
+
26
+ require 'odba/connection_pool'
27
+ require 'odba/drbwrapper'
28
+ require 'ydim/odba'
29
+ require 'ydim/root_session'
30
+ require 'ydim/root_user'
31
+
32
+ module YDIM
33
+ class Server
34
+ # http://stackoverflow.com/questions/2982677/ruby-1-9-invalid-byte-sequence-in-utf-8
35
+ # https://robots.thoughtbot.com/fight-back-utf-8-invalid-byte-sequences
36
+ def sanitize_utf8(string)
37
+ return nil if string.nil?
38
+ # return string.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
39
+ return string if string.valid_encoding?
40
+ if string.force_encoding(Encoding::ISO_8859_1).valid_encoding?
41
+ string.force_encoding(Encoding::ISO_8859_1).clone.encode(Encoding::UTF_8)
42
+ else
43
+ string.chars.select { |c| c.valid_encoding? }.join
44
+ end
45
+ end
46
+ def _migrate_to_utf8 queue, table, opts={}
47
+ while obj = queue.shift do
48
+ if obj.is_a?(Numeric)
49
+ begin
50
+ obj = ODBA.cache.fetch obj
51
+ rescue ODBA::OdbaError
52
+ return
53
+ end
54
+ else
55
+ obj = obj.odba_instance
56
+ end
57
+ puts " #{__LINE__}: Migrating #{obj.class} #{obj.to_s}" if $VERBOSE
58
+ return unless obj
59
+ _migrate_obj_to_utf8 obj, queue, table, opts
60
+ obj.odba_store unless obj.odba_unsaved?
61
+ end
62
+ end
63
+ def _migrate_obj_to_utf8 obj, queue, table, opts={}
64
+ obj.instance_variables.each do |name|
65
+ child = obj.instance_variable_get name
66
+ if child.respond_to?(:odba_unsaved?) && !child.odba_unsaved? \
67
+ && obj.respond_to?(:odba_serializables) \
68
+ && obj.odba_serializables.include?(name)
69
+ child.instance_variable_set '@odba_persistent', nil
70
+ end
71
+ child = _migrate_child_to_utf8 child, queue, table, opts
72
+ obj.instance_variable_set name, child
73
+ end
74
+ if obj.is_a?(Array)
75
+ obj.collect! do |child|
76
+ _migrate_child_to_utf8 child, queue, table, opts
77
+ end
78
+ end
79
+ if obj.is_a?(Hash)
80
+ obj.dup.each do |key, child|
81
+ obj.store key, _migrate_child_to_utf8(child, queue, table, opts)
82
+ end
83
+ end
84
+ obj
85
+ end
86
+ def _migrate_child_to_utf8 child, queue, table, opts={}
87
+ @serialized ||= {}
88
+ case child
89
+ when ODBA::Persistable, ODBA::Stub
90
+ if child = child.odba_instance
91
+ if child.odba_unsaved?
92
+ _migrate_to_utf8 [child], table, opts
93
+ elsif opts[:all]
94
+ odba_id = child.odba_id
95
+ unless table[odba_id]
96
+ table.store odba_id, true
97
+ queue.push odba_id
98
+ end
99
+ end
100
+ end
101
+ when String
102
+ old = child.encoding
103
+ if ( child.encoding != Encoding::UTF_8 && child.force_encoding(Encoding::ISO_8859_1).valid_encoding? ) ||
104
+ ( child.encoding == Encoding::UTF_8 && !child.valid_encoding? )
105
+ child = child.force_encoding(Encoding::ISO_8859_1).clone.encode(Encoding::UTF_8)
106
+ puts "force_encoding from ISO_8859_1 #{old}. is now #{child}"
107
+ end
108
+ binding.pry unless child.valid_encoding?
109
+ case child.encoding.to_s
110
+ when /ASCII-8BIT|US-ASCII/
111
+ # nothing todo
112
+ when /UTF-8/
113
+ puts "UTF-8: for #{child.to_s}" if $VERBOSE
114
+ child = sanitize_utf8(child)
115
+ when /ISO-8859-1/i
116
+ child = sanitize_utf8(child)
117
+ # child = child.force_encoding('UTF-8')
118
+ puts "force_encoding from #{old}. is now #{child}"
119
+ else
120
+ puts "Unhandeled encoding #{child.encoding}"
121
+ # require 'pry'; binding.pry
122
+ # child = child.force_encoding
123
+ end
124
+ when
125
+ YDIM::AutoInvoice,
126
+ YDIM::Debitor,
127
+ YDIM::Debitor,
128
+ YDIM::Invoice,
129
+ YDIM::Invoice::Info,
130
+ YDIM::Item
131
+ child = _migrate_obj_to_utf8 child, queue, table, opts
132
+ when Float, Fixnum, TrueClass, FalseClass, NilClass,
133
+ Symbol, Time, Date, DateTime,
134
+ YDIM::Factory,
135
+ YDIM::CurrencyConverter,
136
+ YDIM::MobileCurrencyConverter
137
+ # do nothing
138
+ else
139
+ @ignored ||= {}
140
+ unless @ignored[child.class]
141
+ @ignored.store child.class, true
142
+ warn "ignoring #{child.class}"
143
+ end
144
+ end
145
+ child
146
+ rescue SystemStackError
147
+ puts child.class
148
+ raise
149
+ end
150
+ end
151
+ end
152
+
153
+ puts "#{Time.now}: Calling migrate_to_utf8"
154
+ @logger = Logger.new('migrate_to_utf8.log')
155
+ @logger.level = Logger::DEBUG
156
+ config = YDIM::Server.config
157
+
158
+ ODBA.storage.dbi = ODBA::ConnectionPool.new(config.db_driver_url,
159
+ config.db_user, config.db_auth, :client_encoding => 'LATIN1')
160
+ ODBA.cache.setup
161
+
162
+ DRb.install_id_conv ODBA::DRbIdConv.new
163
+
164
+ if false
165
+ server = YDIM::Server.new(config, logger)
166
+ server.extend(DRbUndumped)
167
+
168
+ puts config.inspect
169
+ if(config.detach)
170
+ pidfile = '/var/run/ydimd.pid'
171
+ File.open(pidfile, 'w') { |fh| fh.puts $$ }
172
+ at_exit { File.unlink(pidfile) }
173
+ Process.fork and exit!(0)
174
+ end
175
+
176
+ begin
177
+ url = config.server_url
178
+ url.untaint
179
+ DRb.start_service(url, server)
180
+ $SAFE = 1
181
+ logger.info('start') {
182
+ sprintf("starting ydim-server on %s", config.server_url) }
183
+ DRb.thread.join
184
+ rescue Exception => error
185
+ logger.error('fatal') { error }
186
+ raise
187
+ end
188
+ end
189
+
190
+ @server = YDIM::Server.new(config, @logger)
191
+ @server.extend(DRbUndumped)
192
+ session = YDIM::RootSession.new(YDIM::RootUser.new(1))
193
+ session.serv = @server
194
+ { :autoinvoices => session.autoinvoices,
195
+ :invoices => session.invoices,
196
+ :debitors => session.debitors,
197
+ }.each do |name, to_migrate|
198
+ puts "#{Time.now}: Start migrating #{name}"
199
+ @server._migrate_to_utf8(to_migrate, {})
200
+ end
201
+ puts "#{Time.now}: Finished migrate_to_utf8"
@@ -1,6 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
2
  # ypdmd -- ydim -- 09.11.2005 -- hwyss@ywesee.com
3
3
 
4
+ require 'yaml'
5
+ require 'syck'
6
+ require 'pg'
7
+ require 'dbi'
8
+ puts "YAML is now #{YAML} in #{__FILE__} at #{__LINE__}"
9
+
4
10
  require 'ydim/server'
5
11
  require 'ydim/odba'
6
12
  require 'rclconf'
@@ -39,7 +45,7 @@ defaults = {
39
45
  'root_key' => 'root_dsa',
40
46
  'smtp_from' => '',
41
47
  'smtp_server' => 'localhost',
42
- 'vat_rate' => 7.6,
48
+ 'vat_rate' => 8.0,
43
49
  }
44
50
  config = RCLConf::RCLConf.new(ARGV, defaults)
45
51
  config.load(config.config)
@@ -7,7 +7,8 @@ input = if(File.exist?(ARGV.first.to_s))
7
7
  end
8
8
 
9
9
  require 'yaml'
10
-
10
+ require 'syck'
11
+ puts "YAML is now #{YAML} in #{__FILE__} at #{__LINE__}"
11
12
  data = YAML.load(input) || exit(-1)
12
13
 
13
14
  require 'openssl'
data/bin/ydimd CHANGED
@@ -1,7 +1,9 @@
1
- #!/usr/bin/env ruby
1
+ #!/usr/bin/env ruby231
2
2
  # ypdmd -- ydim -- 12.01.2005 -- hwyss@ywesee.com
3
3
 
4
- require 'fastthread'
4
+ require 'yaml'
5
+ require 'syck'
6
+
5
7
  require 'ydim/server'
6
8
  require 'ydim/odba'
7
9
  require 'logger'
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env ruby
2
+ # Config -- pdfinvoice -- 28.07.2005 -- hwyss@ywesee.com
3
+
4
+ require 'rclconf'
5
+
6
+ module PdfInvoice
7
+ def PdfInvoice.config(argv=[])
8
+ default_dir = File.join(Dir.home, '.pdfinvoice')
9
+ default_config_files = [
10
+ File.join(default_dir, 'config.yml'),
11
+ '/etc/pdfinvoice/config.yml',
12
+ ]
13
+ defaults = {
14
+ 'colors' => {
15
+ 'items' => [0xFA, 0xFA, 0xFA],
16
+ 'total' => [0xF0, 0xF0, 0xF0],
17
+ },
18
+ 'config' => default_config_files,
19
+ 'creditor_address' => "Please set creditor_address etc in: #{default_config_files}",
20
+ 'creditor_email' => '',
21
+ 'creditor_bank' => '',
22
+ 'due_days' => '',
23
+ 'font' => 'Helvetica',
24
+ 'font_b' => 'Helvetica-Bold',
25
+ 'formats' => {
26
+ 'currency' => "%1.2f",
27
+ 'total' => "%1.2f",
28
+ 'date' => "%d.%m.%Y",
29
+ 'invoice_number' => "<b>#%06i</b>",
30
+ 'quantity' => '%1.1f',
31
+ },
32
+ 'logo_path' => nil,
33
+ 'logo_link' => nil,
34
+ 'tax' => 0,
35
+ 'texts' => {
36
+ 'date' => 'Date',
37
+ 'description' => 'Description',
38
+ 'unit' => 'Unit',
39
+ 'quantity' => 'Quantity',
40
+ 'price' => 'Price',
41
+ 'item_total' => 'Item Total',
42
+ 'subtotal' => 'Subtotal',
43
+ 'tax' => 'Tax',
44
+ 'thanks' => nil,
45
+ 'total' => 'Total',
46
+ },
47
+ 'text_options' => {:spacing => 1.25},
48
+ }
49
+ config = RCLConf::RCLConf.new(ARGV, defaults)
50
+ config.load(config.config)
51
+ config
52
+ end
53
+ end
@@ -0,0 +1,203 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+ # Invoice -- pdfinvoice -- 25.07.2005 -- hwyss@ywesee.com
4
+
5
+ begin
6
+ require 'rubygems'
7
+ rescue LoadError
8
+ warn "could not load 'rubygems'"
9
+ end
10
+ require 'pdf/writer'
11
+ require 'pdf/simpletable'
12
+
13
+ module PdfInvoice
14
+ class Invoice
15
+ attr_accessor :invoice_number, :debitor_address, :items, :date, :description
16
+ def initialize(config)
17
+ @config = config
18
+ @date = Date.today
19
+ @items = []
20
+ end
21
+ def to_pdf
22
+ pdf = PDF::Writer.new
23
+ pdf.margins_pt(pdf.mm2pts(15), pdf.mm2pts(25),
24
+ pdf.mm2pts(15), pdf.mm2pts(15))
25
+ pdf.select_font(@config.font)
26
+ pdf_header(pdf)
27
+ pdf_items(pdf)
28
+ pdf_footer(pdf)
29
+ pdf.render
30
+ end
31
+ private
32
+ def calculate_column_widths(pdf, widths, data)
33
+ data.each { |key, text|
34
+ widths[key] = [widths[key], text_width(pdf, text)].max
35
+ }
36
+ widths
37
+ end
38
+ def currency_format(amount, fmt='currency')
39
+ sprintf(@config.formats[fmt], amount)
40
+ end
41
+ def number_format(string)
42
+ string.reverse.gsub(/\d{3}(?=\d)(?!\d*\.)/) do |match|
43
+ match << "'"
44
+ end.reverse
45
+ end
46
+ def pdf_footer(pdf)
47
+ if(txt = @config.texts['thanks'])
48
+ pdf.move_pointer(pdf.font_height)
49
+ pdf.text(txt, @config.text_options)
50
+ end
51
+ end
52
+ def pdf_header(pdf)
53
+ if(path = @config.logo_path)
54
+ args = {:pad => 0}
55
+ if(url = @config.logo_link)
56
+ args.store(:link, url)
57
+ end
58
+ pdf.image(path, args)
59
+ end
60
+ pdf.start_columns(2)
61
+ pdf.text(sprintf(@config.formats['invoice_number'],
62
+ @invoice_number), @config.text_options)
63
+ pdf.text(@description.to_s, @config.text_options)
64
+ pdf.start_new_page
65
+ pdf_lines(pdf, @config.creditor_address)
66
+ pdf.stop_columns
67
+ pdf.start_columns(2)
68
+ pdf_lines(pdf, @debitor_address)
69
+ pdf.start_new_page
70
+ pdf.text(@config.creditor_email, @config.text_options)
71
+ pdf_lines(pdf, @config.creditor_bank)
72
+ pdf.stop_columns
73
+ pdf.move_pointer(pdf.font_height)
74
+ pdf.start_columns(2)
75
+ pdf.text(@config.due_days, @config.text_options)
76
+ pdf.start_new_page
77
+ pdf.text(@date.strftime(@config.formats['date']), @config.text_options)
78
+ pdf.stop_columns
79
+ end
80
+ def pdf_items(pdf)
81
+ sstyle = PDF::Writer::StrokeStyle::DEFAULT.dup
82
+ sstyle.width = 0.5
83
+ sstyle.dash = { :pattern => [1] }
84
+ pdf.start_columns(1)
85
+ pdf.move_pointer(pdf.font_height)
86
+ total = 0.0
87
+ vat = 0.0
88
+ left = 0
89
+ width = 0
90
+ row_gap = 4
91
+ columns = ['date', 'description', 'unit', 'quantity', 'price',
92
+ 'item_total']
93
+ column_widths = {}
94
+ pdf.select_font(@config.font_b)
95
+ headings = columns.collect { |col|
96
+ heading = @config.texts[col]
97
+ column_widths.store(col, text_width(pdf, heading))
98
+ heading
99
+ }
100
+ ['Zwischensumme', 'MwSt 7.5%', 'Fälliger Betrag'].each { |title|
101
+ calculate_column_widths(pdf, column_widths, {'date' => title})
102
+ }
103
+ pdf.select_font(@config.font)
104
+ PDF::SimpleTable.new { |table|
105
+ table.column_order = columns
106
+ columns.each_with_index { |col, idx|
107
+ table.columns[col] = PDF::SimpleTable::Column.new('date') {
108
+ |column|
109
+ column.heading = PDF::SimpleTable::Column::Heading.new(col)
110
+ column.heading.title = headings.at(idx)
111
+ if(['quantity', 'price', 'item_total'].include?(col))
112
+ column.justification = :right
113
+ column.heading.justification = :right
114
+ end
115
+ }
116
+ }
117
+ table.data = @items.collect { |line|
118
+ item_total = line.at(3).to_f * line.at(4).to_f
119
+ vat += line[5] || @config.tax.to_f * item_total
120
+ total += item_total
121
+ date = line.at(0).strftime(@config.formats['date'])
122
+ cw = pdf.text_width(date) * PDF::SimpleTable::WIDTH_FACTOR
123
+ data = {
124
+ 'date' => date,
125
+ 'description' => number_format(line.at(1)),
126
+ 'unit' => line.at(2),
127
+ 'quantity' => number_format(sprintf(@config.formats['quantity'],
128
+ line.at(3))),
129
+ 'price' => number_format(currency_format(line.at(4))),
130
+ 'item_total' => number_format(currency_format(item_total)),
131
+ }
132
+ calculate_column_widths(pdf, column_widths, data)
133
+ data
134
+ }
135
+ column_widths.each { |key, col_width|
136
+ unless(key == 'description')
137
+ table.columns[key].width = col_width + 2 * table.column_gap
138
+ end
139
+ }
140
+ table.position = left = pdf.left_margin + table.column_gap
141
+ table.orientation = :right
142
+ table.width = width = pdf.margin_width - 2 * table.column_gap
143
+ cl = Color::RGB.new(*@config.colors['items'])
144
+ table.shade_color2 = table.shade_color = cl
145
+ table.shade_rows = :striped
146
+ table.show_lines = :all
147
+ table.inner_line_style = sstyle
148
+ table.outer_line_style = sstyle
149
+ table.row_gap = row_gap
150
+ table.bold_headings = true
151
+ table.heading_font_size = 10
152
+ table.render_on(pdf)
153
+ }
154
+ pdf.select_font(@config.font_b)
155
+ PDF::SimpleTable.new { |table|
156
+ table.column_order = ['date', 'total']
157
+ table.data = [
158
+ { 'date' => @config.texts['subtotal'],
159
+ 'total' => number_format(currency_format(total, 'total'))},
160
+ { 'date' => @config.texts['tax'],
161
+ 'total' => number_format(currency_format(vat, 'total')) },
162
+ { 'date' => @config.texts['total'],
163
+ 'total' => number_format(currency_format(total + vat, 'total')) },
164
+ ]
165
+ table.show_headings = false
166
+ table.position = left
167
+ table.orientation = :right
168
+ table.width = width
169
+ table.columns['date'] = PDF::SimpleTable::Column.new('date') {
170
+ |column|
171
+ column.width = column_widths['date'] + 2 * table.column_gap
172
+ }
173
+ table.columns['total'] = PDF::SimpleTable::Column.new('date') {
174
+ |column|
175
+ column.justification = :right
176
+ }
177
+ cl = Color::RGB.new(*@config.colors['total'])
178
+ table.shade_color2 = table.shade_color = cl
179
+ table.shade_rows = :striped
180
+ table.show_lines = :all
181
+ table.inner_line_style = sstyle
182
+ table.outer_line_style = sstyle
183
+ table.row_gap = row_gap
184
+ table.render_on(pdf)
185
+ }
186
+ pdf.select_font(@config.font)
187
+ end
188
+ def pdf_lines(pdf, lines)
189
+ if (lines && lines.is_a?(Array))
190
+ lines.each { |line|
191
+ pdf.text(line.strip, @config.text_options)
192
+ }
193
+ elsif (lines && lines.is_a?(String))
194
+ lines.each_line { |line|
195
+ pdf.text(line.strip, @config.text_options)
196
+ }
197
+ end
198
+ end
199
+ def text_width(pdf, text)
200
+ pdf.text_width(text) * PDF::SimpleTable::WIDTH_FACTOR
201
+ end
202
+ end
203
+ end