prawn_calendar 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ explicit.pdf
19
+ hugo.pdf
20
+ implicit.pdf
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in prawn_calendar.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Bernhard Weichel
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # PrawnCalendar
2
+
3
+ This gem provides a class to generate calendars with schedules using prawn.
4
+
5
+ * Weekly overview
6
+ * Specifiy hours of a day to show
7
+ * Handle schedules out of the limits of a day
8
+ * Indicate recurring schedules
9
+
10
+ see [Sample output](spec/output/testcalendar.pdf)
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ gem 'prawn_calendar'
17
+
18
+ And then execute:
19
+
20
+ $ bundle
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install prawn_calendar
25
+
26
+ ## Usage
27
+
28
+ PrawnCalendar is a ruby library. See the spec
29
+ how to use it.
30
+
31
+ For the impatient ...
32
+
33
+ ~~~~ruby
34
+ it "implicit creates a calendar" do
35
+ Prawn::Document.generate("implicit.pdf") do
36
+ calendar=PrawnCalendar::WeeklyCalendar.new(self)
37
+ calendar.mk_calendar([20,700], width:500,height:250) do
38
+ cal_entry("2013-05-04T08:00:00+01:00", "2013-05-04T19:00:00", "cc 7as ist ein test, der laufen muss")
39
+
40
+ end
41
+ end
42
+ end
43
+
44
+ ~~~~
45
+
46
+ ## Limitations
47
+
48
+ * It does not handle schedules over multiple days
49
+ * Only generates week calendars
50
+ * cannot colorize calendars
51
+ * no exception handling
52
+
53
+ ## Contributing
54
+
55
+ 1. Fork it
56
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
57
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
58
+ 4. Push to the branch (`git push origin my-new-feature`)
59
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+ require 'rake/clean'
4
+
5
+ desc "Run specs"
6
+ RSpec::Core::RakeTask.new do |t|
7
+ t.pattern = "./**/*_spec.rb" # don't need this, it's default.
8
+ t.rspec_opts = ['-fd -fd --out ./testresults/test_results.log -fh --out ./testresults/test_results.html']
9
+ # Put spec opts in a file named .rspec in root
10
+ end
11
+
12
+
13
+ desc "document (yard) all AUTOSAR ruby helpers defined here"
14
+ task :doc do
15
+ sh "yard --markup markdown doc . "
16
+ end
@@ -0,0 +1,3 @@
1
+ module PrawnCalendar
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,399 @@
1
+ require "prawn_calendar/version"
2
+ require 'prawn'
3
+ require 'time'
4
+ require 'date'
5
+
6
+
7
+ module Prawn
8
+ class Document
9
+
10
+ # Defines the grid system for a particular document. Takes the number of
11
+ # rows and columns and the width to use for the gutter as the
12
+ # keys :rows, :columns, :gutter, :row_gutter, :column_gutter
13
+ #
14
+ def define_grid(options = {})
15
+ @grid = Grid.new(self, options)
16
+ @boxes = nil # see
17
+ end
18
+ end
19
+ end
20
+
21
+ module PrawnCalendar
22
+
23
+ #
24
+ # [ class description]
25
+ #
26
+ # @author [author]
27
+ #
28
+ class WeeklyCalendar
29
+ # the Prawn instance xxx
30
+ attr_accessor :pdf
31
+
32
+ # the start of the interval
33
+ attr_accessor :c_date_start
34
+
35
+ # the gutter of calendar annotations
36
+ attr_accessor :c_annotation_gutter
37
+
38
+ # the number of divisions in calender columns
39
+ # this is to adjust the proportion of the first and
40
+ # the subsequent columns
41
+ attr_accessor :c_col_division
42
+
43
+ # the end of the interval
44
+ attr_accessor :c_date_end
45
+
46
+ # An array with labels of the day. e.g. ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]
47
+ attr_accessor :c_days
48
+
49
+ # The fontsize of calendar entries
50
+ attr_accessor :c_entry_fontsize
51
+
52
+ # The gutter of calendar entries
53
+ attr_accessor :c_entry_gutter
54
+
55
+ # The radius of the calendar entries
56
+ attr_accessor :c_entry_radius
57
+
58
+ # The right margin of calendar entries
59
+ attr_accessor :c_entry_right_gutter
60
+
61
+ # The number of Rows shown in the calendar without time
62
+ # These rows are intended for additional comments
63
+ attr_accessor :c_extra_rows
64
+
65
+ # the number of divisions in the first column
66
+ # the one showing the time
67
+ attr_accessor :c_firstcol_division
68
+
69
+ # The font size of the calendar annotations
70
+ attr_accessor :c_fontsize
71
+
72
+ # the number of divisions of the calenar rows.
73
+ # this finally determines the resolution of
74
+ # time shown in the calendar.
75
+ # Defaults to 4 which is (15 minutes)
76
+ #
77
+ attr_accessor :c_row_division
78
+
79
+ # the time where calendar display ends (defaults to 22)
80
+ attr_accessor :c_time_end
81
+
82
+ # the time where calendar display starts (defaults to 8)
83
+ attr_accessor :c_time_start
84
+
85
+
86
+ #
87
+ # This is the write accessor to the attributre c_cate_start.
88
+ # Note that this adjusts the start date such that it
89
+ # comes to a monday and sets the end of the interval
90
+ # to the subseqent sunday
91
+ #
92
+ # @param day [String] Iso 8601 form of the start date.
93
+ #
94
+ # @return [type] [description]
95
+ def c_date_start=(day)
96
+ # round to the beginning of the day.
97
+ d = Date.iso8601(day)
98
+ # note that we start the week on monday, therefore d -(d-1).wday
99
+ # note that Date adds days, while Time adds seconds
100
+ #
101
+ # compute the beginning of the week
102
+ @c_date_start = (d -(d-1).wday).to_time
103
+ # set end to the last second of end date
104
+ @c_date_end = (@c_date_start.to_date + 7).to_time-1
105
+ end
106
+
107
+ #
108
+ # This is the constructor
109
+ # @param pdf [Prawn] The handle to prawn which renders the calendar
110
+ # @param &block [Proc] Code to change the initial configruation of the calendar.
111
+ #
112
+ # @return [type] [description]
113
+ def initialize(pdf, &block)
114
+ @pdf=pdf
115
+
116
+ @c_annotation_gutter = 2
117
+ @c_col_division = 5
118
+ self.c_date_start=(Date.today.iso8601) # default to today.
119
+ @c_days = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]
120
+ @c_entry_fontsize = 7
121
+ @c_entry_gutter = 0
122
+ @c_entry_radius = 2
123
+ @c_entry_right_gutter = 2
124
+ @c_extra_rows = 1
125
+ @c_firstcol_division = 2
126
+ @c_fontsize = 8
127
+ @c_row_division = 4
128
+ @c_time_end = 22
129
+ @c_time_start = 8
130
+ yield(self) if block_given?
131
+ end
132
+
133
+
134
+ #
135
+ # This generates an entry in the calendar gird.
136
+ # Basically it is used for annotations.
137
+ #
138
+ # Note that the coordinates are counter intuitive related
139
+ # to the usual positioning in pdf/prawn. While bounding_box
140
+ # is [horizontal, vertical] this stuff is [vertial, horizontal]
141
+ #
142
+ # @param ll [Array] left point [row, column] in grid coordinates
143
+ # [0,0] is the upper left corner
144
+ # [0,1] is the second field in the first row.
145
+ # @param rr [Array] end point [row, column] in grid coordinates
146
+ #
147
+ # @param text [String] The string to be placed. It allows to use
148
+ # html formatting as far as supported by prawn.
149
+ #
150
+ # @return nil
151
+ def mk_entry(ll, rr, text)
152
+ @pdf.grid(ll, rr).bounding_box do
153
+
154
+ @pdf.stroke_bounds
155
+ excess_text = @pdf.text_box text,
156
+ :at => [@c_annotation_gutter, @pdf.bounds.height - @c_annotation_gutter],
157
+ :width => @pdf.bounds.width - (2 * @c_annotation_gutter),
158
+ :height => @pdf.bounds.height - (2 * @c_annotation_gutter),
159
+ :overflow => :truncate,
160
+ :kerning => true,
161
+ :inline_format => true,
162
+ :size => @c_fontsize
163
+ end
164
+ nil
165
+ end
166
+
167
+
168
+ #
169
+ # This makes a calendar entry based on time.
170
+ #
171
+ # Please note
172
+ #
173
+ # * it is not very robust yet
174
+ # * it only supports single day entries
175
+ # * entries are truncated if they do not fit into the range of hours
176
+ # in this case, the end time is added to the text
177
+ #
178
+ # @param starttime [String] Starttime in iso8601 format
179
+ # @param endtime [String] Endtime in iso8601 format
180
+ # @param text [String] The text of calender entry
181
+ # @param extraargs [Hash] additional arguments
182
+ # :recurring true/false
183
+ #
184
+ # @return nil
185
+ #
186
+ def cal_entry(starttime, endtime, text, extraargs={})
187
+ time_to_start = Time.iso8601(starttime).localtime
188
+ time_to_end = Time.iso8601(endtime).localtime
189
+
190
+ a= ((@c_date_start.to_time .. @c_date_end.to_time).cover?(time_to_start))
191
+ puts "datum ausserhalb des Kalenders #{@c_date_start} < #{time_to_start} < #{@c_date_end} #{text}" if a==false
192
+
193
+ hour_to_start = time_to_start.hour
194
+ hour_to_end = time_to_end.hour
195
+ min_to_start = time_to_start.min / (60/@c_row_division)
196
+ min_to_end = time_to_end.min / (60/@c_row_division)
197
+
198
+ finaltext="<b>#{text}</b>"
199
+
200
+ # handle the case that the entry is out of bounds
201
+ # in this case it is drawn in the extra lines
202
+ # an start/endtime is shown as extra tet
203
+ extratext=nil
204
+
205
+
206
+
207
+ # entry starts too early
208
+ if (hour_to_start == 0) || (hour_to_start) < @c_time_start then
209
+ extratext = true
210
+ hour_to_start = @c_time_start-1
211
+ min_to_start = 0
212
+ end
213
+
214
+ # entry ends too early
215
+ if (hour_to_end == 0) || (hour_to_end) < @c_time_start then
216
+ extratext=true
217
+ hour_to_end= @c_time_start
218
+ min_to_end = 0
219
+ end
220
+
221
+ # entry starts too late
222
+
223
+ if (hour_to_start == 0) || (hour_to_start >= @c_time_end+1) then
224
+ extratext = true
225
+ hour_to_start = @c_time_end + 1
226
+ min_to_start = 0
227
+ end
228
+
229
+ # entry ends too late
230
+ if (hour_to_end == 0) || (hour_to_end >= @c_time_end+1) then
231
+ extratext = true
232
+ hour_to_end = @c_time_end + 2
233
+ min_to_end = 0
234
+ end
235
+
236
+ if extratext then
237
+ finaltext = finaltext + "<br><font size='#{@c_fontsize-2}'>#{time_to_start.strftime('%k.%M')} - #{time_to_end.strftime('%k.%M')}</font>"
238
+ end
239
+
240
+
241
+
242
+
243
+ starttime_s = time_to_start.strftime("%k.%M")
244
+ endtime_s = time_to_end.strftime("%k.%M")
245
+
246
+ text_to_show = "#{starttime_s} - #{endtime_s}<br/>#{text}"
247
+ text_to_show = "#{finaltext}"
248
+
249
+ day = (time_to_start.wday + 6) % 7 # since we start on monday shift it one left
250
+ column = @c_firstcol_division + day * @c_col_division
251
+
252
+ srow = 2 * @c_row_division + (hour_to_start - @c_time_start) * @c_row_division + min_to_start
253
+ erow = 2 * @c_row_division + (hour_to_end - @c_time_start) * @c_row_division -1 + min_to_end
254
+
255
+ cal_entry_raw([srow, column], erow - srow, text_to_show, extraargs[:recurring])
256
+ end
257
+
258
+
259
+ #
260
+ # This creates a raw calendar entry based on grid coordinates
261
+ #
262
+ # @param ll [Array] Left corner, see mk_entry for details.
263
+ # @param length [Integer] The number of grid rows covered by the entry
264
+ # @param text [String] The text of the calendar entry
265
+ #
266
+ # @return nil
267
+ def cal_entry_raw(ll, length, text, recurring=false)
268
+ width=4
269
+ ur=[ll[0]+length, ll[1]+width]
270
+ @pdf.grid(ll,ur).bounding_box do
271
+
272
+ @pdf.fill_color "f0f0f0"
273
+
274
+ @pdf.line_width 0.1
275
+
276
+
277
+ # add one pixel to the borders to keep the entry away from the lines
278
+ @pdf.rounded_rectangle([@c_entry_gutter+1, @pdf.bounds.height - @c_entry_gutter], # startpoint
279
+ @pdf.bounds.width - 2 * @c_entry_gutter - @c_entry_right_gutter -2, # width
280
+ @pdf.bounds.height - 2 * @c_entry_gutter, # height
281
+ @c_entry_radius # radius
282
+ )
283
+ @pdf.fill_and_stroke
284
+
285
+ @pdf.fill_color "000000"
286
+
287
+ if recurring==true then
288
+ @pdf.text_box "(w)",
289
+ :at => [@pdf.bounds.width - 13,8],
290
+ :width => 10,
291
+ :size => 6
292
+ end
293
+
294
+ if recurring==true then
295
+ @pdf.rounded_rectangle([@pdf.bounds.width - 2, @c_entry_gutter], # startpoint
296
+ 2 , # width
297
+ 2 , # height
298
+ @c_entry_radius # radius
299
+ )
300
+ @pdf.fill_and_stroke
301
+ end
302
+
303
+
304
+
305
+ # text is limited to gutter. Therefore gutter needs to be doubled
306
+ # no limit at right and bottom
307
+ excess_text = @pdf.text_box text,
308
+ :at => [@c_entry_gutter +2, @pdf.bounds.height- 2*@c_entry_gutter-1], # need 1 pixel more a the top
309
+ :width => @pdf.bounds.width-4*@c_entry_gutter -2*@c_entry_right_gutter,
310
+ :height => @pdf.bounds.height-4*@c_entry_gutter,
311
+ :overflow => :truncate,
312
+ :kerning => true,
313
+ :inline_format => true,
314
+ :size => @c_entry_fontsize
315
+ end
316
+ nil
317
+ end
318
+
319
+
320
+
321
+ #
322
+ # Creates an empty calendar
323
+ #
324
+ # @param ll [Array] the start point of the calendar
325
+ # values are points, 0,0 is the lower left corner of the calender
326
+ # see Prawn::Document.bounding_box for details
327
+ # @param opts [Hash] [the options, keys: :width, :height]
328
+ # @param &block [Proc] The statements to fill the calendar.
329
+ #
330
+ # @return [type] [description]
331
+ def mk_calendar(ll, opts, &block)
332
+ @pdf.bounding_box(ll, width: opts[:width], height: opts[:height]) do
333
+ @pdf.stroke_bounds
334
+ rows = (2 + @c_time_end - @c_time_start +1 + @c_extra_rows) * @c_row_division
335
+ columns = @c_firstcol_division + @c_days.count * @c_col_division
336
+
337
+ @pdf.define_grid(:columns => columns, :rows => rows, :gutter => 0)
338
+
339
+ # the frames
340
+ @pdf.line_width 1
341
+ mk_entry([0,0],
342
+ [rows - 1, columns-1], "") # outer frame
343
+
344
+ mk_entry([2 * @c_row_division, @c_firstcol_division],
345
+ [rows - 1 , columns-1], "") # inner frame
346
+
347
+ mk_entry([2 * @c_row_division, 0],
348
+ [rows -1, @c_firstcol_division - 1], "") # left frame
349
+
350
+ # the day - line
351
+ row=0;col=@c_firstcol_division
352
+ curday=@c_date_start.to_date
353
+
354
+ @c_days.each do |i|
355
+
356
+ columnhead="#{i} #{curday.strftime('%d.%m.')}"
357
+ mk_entry([row,col], [row+@c_row_division-1, col+@c_col_division-1], columnhead)
358
+ #require 'pry';binding.pry
359
+ curday=curday+1
360
+ col += @c_col_division
361
+ end
362
+
363
+ # the day after line
364
+ row=@c_row_division; col=@c_firstcol_division
365
+ @pdf.line_width 0.75
366
+ @c_days.each do |i|
367
+ mk_entry([row, col], [rows -1, col + @c_col_division - 1], "")
368
+ col+=@c_col_division
369
+ end
370
+
371
+ #the calendar fields
372
+ @pdf.line_width "0.1"
373
+ col=0;row=@c_row_division
374
+
375
+ # the rows
376
+ rowlabels=Array(@c_time_start .. @c_time_end).map{|i| "0#{i}.00"[-5..5]}
377
+
378
+ a=[" ", rowlabels, [].fill(" ", 0, @c_extra_rows)]
379
+ a.flatten.each do |i|
380
+
381
+ # the first column
382
+ # -1 bcause it denotes tha last filled cell, not the next unfilled one
383
+ mk_entry([row,col],[row + @c_row_division - 1, col+@c_firstcol_division - 1], "#{i}")
384
+
385
+ # the other columns
386
+ icol=@c_firstcol_division
387
+ (1..7).each do |i|
388
+ mk_entry([row, icol], [row + @c_row_division -1, icol + @c_col_division - 1], " ")
389
+ icol=icol + @c_col_division
390
+ end
391
+ row += @c_row_division
392
+ end
393
+
394
+ #yield the block
395
+ instance_eval(&block)
396
+ end
397
+ end
398
+ end
399
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'prawn_calendar/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "prawn_calendar"
8
+ spec.version = PrawnCalendar::VERSION
9
+ spec.authors = ["Bernhard Weichel"]
10
+ spec.email = ["github.com@nospam.weichel21.de"]
11
+ spec.description = %q{This gem provides a function to generate calendars and calendar entries.}
12
+ spec.summary = %q{generate calendars with prawn}
13
+ spec.homepage = "https://github.com/bwl21/prawn_calendar"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "prawn" # "~> 1.0.0.rc2"
22
+ spec.add_development_dependency "bundler", "~> 1.3"
23
+ spec.add_development_dependency "rake"
24
+ spec.add_development_dependency "rspec"
25
+ spec.add_development_dependency "yard"
26
+ spec.add_development_dependency "redcarpet"
27
+ spec.add_development_dependency "pry"
28
+ end