220: def render_on(pdf)
221: raise TypeError, PDF::Writer::Lang[:charts_stddev_data_empty] if @data.empty?
222: data = @data.dup
223: leftover_data = nil
224:
225: loop do
226:
227: scale = []
228:
229: (@scale.first + @scale.step).step(@scale.last, @scale.step) do |ii|
230: scale << "%01.#{@scale.label.decimal_precision}f" % ii
231: end
232:
233: scales = PDF::Writer::OHash.new
234: scale.each_with_index do |gg, ii|
235: scales[ii] = OpenStruct.new
236: scales[ii].value = gg
237: end
238:
239:
240:
241:
242: scale_count = scale.size + 1
243:
244: label_height_adjuster = 0
245: label_height_adjuster = @label.height if @show_labels
246:
247: chart_area_height = @height - label_height_adjuster
248: scale_height = chart_area_height / scale_count.to_f
249:
250: scales.each_key do |index|
251: this_height = scale_height * (index + 1) + @label.height
252: scales[index].line_height = this_height
253: if @scale.show_labels
254: scales[index].label_height = this_height -
255: (@scale.label.text_size / 3.0)
256: end
257: end
258:
259:
260:
261: chunk_width = @datapoint_width
262: num_chunks = data.size
263: widest_scale_label = 0
264:
265: if @scale.show_labels
266: scales.each_value do |scale|
267: this_width = pdf.text_width(scale.value, @scale.label.text_size)
268: widest_scale_label = this_width if this_width > widest_scale_label
269: end
270: end
271:
272: chart_width = chunk_width * num_chunks
273: total_width = chart_width + widest_scale_label + @scale.label.pad
274:
275:
276:
277: if total_width > @maximum_width
278: max_column_count = 0
279: base_width = widest_scale_label + @scale.label.pad
280: (1..(num_chunks + 1)).each do |ii|
281: if (base_width + (ii * chunk_width)) > @maximum_width
282: break
283: else
284: max_column_count += 1
285: end
286: end
287:
288: leftover_data = data.slice!(max_column_count, -1)
289:
290: num_chunks = data.size
291: chart_width = chunk_width * num_chunks
292: total_width = chart_width + widest_scale_label + @scale.label.pad
293: end
294:
295: chart_y = pdf.y - @height + @leading_gap
296: chart_y += (@outer_borders.style.width * 2.0) if @outer_borders
297:
298: if chart_y < pdf.bottom_margin
299: pdf.start_new_page
300: chart_y = pdf.y - @height
301: chart_y += (@outer_borders.style.width * 2.0) if @outer_borders
302: end
303:
304: chart_x = pdf.absolute_x_middle - (total_width / 2.0) + widest_scale_label
305:
306:
307: if @show_labels
308: pdf.save_state
309: pdf.fill_color! @label.background_color
310:
311: num_chunks.times do |ii|
312: this_x = chart_x + ii * chunk_width
313: pdf.rectangle(this_x, chart_y, chunk_width, @label.height).fill
314: end
315:
316:
317: if @outer_borders
318: pdf.stroke_style! @outer_borders.style
319: pdf.line(chart_x, chart_y + @label.height, chart_x + chart_width, chart_y + @label.height).stroke
320: end
321: pdf.fill_color! @label.text_color
322:
323: data.each_with_index do |datum, ii|
324: label = datum.label.to_s
325: label_width = pdf.text_width(label, @label.text_size)
326: this_x = chart_x + (ii * chunk_width) + (chunk_width / 2.0) - (label_width / 2.0)
327: this_y = chart_y + (@label.height / 2.0) - (@label.text_size / 3.0)
328: pdf.add_text(this_x, this_y, label, @label.text_size)
329: end
330: pdf.restore_state
331: end
332:
333: if @inner_borders
334: pdf.save_state
335: pdf.stroke_color! @inner_borders.color
336: pdf.stroke_style! @inner_borders.style
337: (num_chunks - 1).times do |ii|
338: this_x = chart_x + (ii * chunk_width) + chunk_width
339: pdf.line(this_x, chart_y, this_x, chart_y + @height).stroke
340: end
341: pdf.restore_state
342: end
343:
344: pdf.save_state
345: if @outer_borders
346: pdf.stroke_color! @outer_borders.color
347: pdf.stroke_style! @outer_borders.style
348: pdf.rectangle(chart_x, chart_y, chart_width, @height).stroke
349: end
350:
351: if @scale.style
352: pdf.save_state
353: pdf.stroke_style! @scale.style
354: scales.each_value do |scale|
355: this_y = chart_y + scale.line_height
356: pdf.line(chart_x, this_y, chart_x + chart_width, this_y).stroke
357: end
358: pdf.restore_state
359: end
360:
361: if @scale.show_labels
362: pdf.save_state
363: scales.each_value do |scale|
364: this_y = chart_y + scale.label_height
365: label_width = pdf.text_width(scale.value, @scale.label.text_size)
366: this_x = chart_x - label_width - @scale.label.pad
367: pdf.fill_color! @scale.label.text_color
368: pdf.add_text(this_x, this_y, scale.value, @scale.label.text_size)
369: end
370: pdf.restore_state
371: end
372:
373: data.each_with_index do |datum, ii|
374: avg_height = datum.average * scale_height
375: stddev_height = datum.stddev * scale_height
376: this_y = chart_y + label_height_adjuster + avg_height
377: this_x = chart_x + (ii * chunk_width) + (chunk_width / 2.0)
378: line_top_y = this_y + (stddev_height / 2.0)
379: line_bot_y = this_y - (stddev_height / 2.0)
380:
381:
382: if @dot
383: pdf.stroke_color! @dot.color
384: pdf.stroke_style! @dot.style
385: pdf.circle_at(this_x, this_y, (@dot.style.width / 2.0)).fill
386: end
387:
388:
389: if @bar
390: pdf.stroke_color! @bar.color
391: pdf.stroke_style! @bar.style
392: pdf.line(this_x, line_top_y, this_x, line_bot_y).stroke
393: end
394:
395:
396: if @upper_crossbar
397: if @dot
398: cb_width = @dot.style.width
399: else
400: cb_width = @upper_crossbar.style.width
401: end
402: pdf.stroke_color! @upper_crossbar.color
403: pdf.stroke_style! @upper_crossbar.style
404: pdf.line(this_x - cb_width, line_top_y, this_x + cb_width, line_top_y).stroke
405: end
406: if @lower_crossbar
407: if @dot
408: cb_width = @dot.style.width
409: else
410: cb_width = @lower_crossbar.style.width
411: end
412: pdf.stroke_color! @lower_crossbar.color
413: pdf.stroke_style! @lower_crossbar.style
414:
415: pdf.line(this_x - cb_width, line_bot_y, this_x + cb_width, line_bot_y).stroke
416: end
417: end
418:
419: pdf.restore_state
420:
421: pdf.y = chart_y
422:
423: break if leftover_data.nil?
424:
425: data = leftover_data
426: leftover_data = nil
427: end
428:
429: pdf.y
430: end