# File lib/pdf/charts/stddev.rb, line 220
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:       # Set up the scale information.
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:       # Add information about the scales' locations to the scales
240:       # hash. Note that the count is one smaller than it should be, so we're
241:       # increasing it. The first scale is the bottom of the chart.
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:       # How many sections do we need in this chart, and how wide will it
260:       # need to be?
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:         # What happens if the projected width of the chart is too big?
276:         # Figure out how to break the chart in pieces.
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:         # Add labels, if needed.
307:       if @show_labels
308:         pdf.save_state
309:         pdf.fill_color! @label.background_color
310:         # Draw a rectangle for each label
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:           # Add a border above the label rectangle.
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:           # Plot the dot
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:           # Plot the bar
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:           # Plot the crossbars
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