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