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