├── .gitignore ├── data ├── images │ ├── dice.dat │ ├── dice.png │ ├── pigs.jpg │ ├── stef.jpg │ ├── 16bit.dat │ ├── 16bit.png │ ├── arrow.png │ ├── arrow2.png │ ├── dice.alpha │ ├── rails.dat │ ├── rails.png │ ├── ruport.png │ ├── tru256.bmp │ ├── 16bit.alpha │ ├── fractal.jpg │ ├── web-links.dat │ ├── web-links.png │ ├── letterhead.jpg │ ├── ruport_data.dat │ ├── barcode_issue.png │ ├── ruport_type0.png │ ├── dice_interlaced.png │ ├── page_white_text.dat │ ├── page_white_text.png │ ├── page_white_text.alpha │ └── ruport_transparent.png ├── fonts │ ├── Activa.ttf │ ├── Chalkboard.ttf │ ├── DejaVuSans.ttf │ ├── comicsans.ttf │ ├── gkai00mp.ttf │ ├── Action Man.dfont │ ├── Dustismo_Roman.ttf │ └── MustRead.html ├── pdfs │ ├── hexagon.pdf │ ├── encrypted.pdf │ ├── nested_pages.pdf │ ├── two_hexagons.pdf │ ├── complex_template.pdf │ ├── contains_ttf_font.pdf │ ├── indirect_reference.pdf │ ├── resources_as_indirect_object.pdf │ └── version_1_6.pdf ├── shift_jis_text.txt └── encodings │ └── win_ansi.txt ├── www ├── media │ ├── image.pdf │ ├── images.png │ ├── tables.png │ ├── utf8.pdf │ ├── utf8.png │ ├── fancy_table.pdf │ ├── prawn_logo.png │ ├── bounding_boxes.pdf │ ├── bounding_boxes.png │ └── bounding_boxes_out.png └── prawn.css ├── reference_pdfs ├── bill.pdf ├── canvas.pdf ├── cell.pdf ├── cmyk.pdf ├── curves.pdf ├── dfont.pdf ├── euro.pdf ├── float.pdf ├── flow.pdf ├── header.pdf ├── lines.pdf ├── margin.pdf ├── repeat.pdf ├── ruport.pdf ├── sjis.pdf ├── span.pdf ├── stamp.pdf ├── utf8.pdf ├── widths.pdf ├── hexagon.pdf ├── kerning.pdf ├── outlines.pdf ├── subtable.pdf ├── text_box.pdf ├── win-ansi.pdf ├── alignment.pdf ├── background.pdf ├── column_box.pdf ├── font_size.pdf ├── image-flow.pdf ├── image_fit.pdf ├── indentation.pdf ├── multi_boxes.pdf ├── padded_box.pdf ├── png_types.pdf ├── portrait_a4.pdf ├── show_grid.pdf ├── simple_grid.pdf ├── simple_text.pdf ├── stroke_dash.pdf ├── basic_images.pdf ├── checkerboard.pdf ├── chinese_flow.pdf ├── family_style.pdf ├── landscape_a4.pdf ├── metadata-info.pdf ├── multi-layout.pdf ├── remote_images.pdf ├── rotated_text.pdf ├── russian_boxes.pdf ├── simple_table.pdf ├── stroke_bounds.pdf ├── transparency.pdf ├── bounding_boxes.pdf ├── image_position.pdf ├── landscape_legal.pdf ├── landscape_letter.pdf ├── multi_page_table.pdf ├── portrait_legal.pdf ├── portrait_letter.pdf ├── pretty_polygons.pdf ├── rounded_polygon.pdf ├── simple_text_ttf.pdf ├── transformations.pdf ├── bounding_box_grid.pdf ├── column_gutter_grid.pdf ├── font_calculations.pdf ├── indent_paragraphs.pdf ├── measurement_units.pdf ├── rounded_rectangle.pdf ├── security_hello_foo.pdf ├── stretched_nesting.pdf ├── inline_format_table.pdf ├── lazy_bounding_boxes.pdf ├── page_with_numbering.pdf ├── stroke_cap_and_join.pdf ├── context_sensitive_headers.pdf └── text_box_returning_excess.pdf ├── Gemfile ├── lib ├── prawn │ ├── text │ │ └── formatted.rb │ ├── core │ │ ├── byte_string.rb │ │ ├── literal_string.rb │ │ ├── annotations.rb │ │ ├── document_state.rb │ │ ├── destinations.rb │ │ ├── reference.rb │ │ └── text │ │ │ └── wrap.rb │ ├── layout.rb │ ├── table │ │ └── cell │ │ │ ├── in_table.rb │ │ │ └── subtable.rb │ ├── measurement_extensions.rb │ ├── graphics │ │ ├── cap_style.rb │ │ ├── join_style.rb │ │ ├── dash.rb │ │ └── gradient.rb │ ├── compatibility.rb │ ├── font │ │ └── dfont.rb │ ├── document │ │ ├── graphics_state.rb │ │ ├── span.rb │ │ └── snapshot.rb │ ├── measurements.rb │ ├── security │ │ └── arcfour.rb │ ├── core.rb │ ├── images │ │ └── jpg.rb │ └── errors.rb └── prawn.rb ├── examples ├── example_helper.rb ├── table │ ├── multi_page_table.rb │ ├── subtable.rb │ ├── inline_format_table.rb │ ├── header.rb │ ├── cell.rb │ ├── widths.rb │ ├── checkerboard.rb │ ├── simple_table.rb │ └── bill.rb ├── security │ └── hello_foo.rb ├── general │ ├── float.rb │ ├── page_numbering.rb │ ├── canvas.rb │ ├── templates.rb │ ├── metadata-info.rb │ ├── background.rb │ ├── multi_page_layout.rb │ ├── page_geometry.rb │ ├── repeaters.rb │ ├── context_sensitive_headers.rb │ ├── stamp.rb │ ├── margin.rb │ ├── measurement_units.rb │ └── outlines.rb ├── text │ ├── character_spacing.rb │ ├── rendering_mode.rb │ ├── simple_text_ttf.rb │ ├── simple_text.rb │ ├── indent_paragraphs.rb │ ├── alignment.rb │ ├── family_based_styling.rb │ ├── font_size.rb │ ├── shaped_text_box.rb │ ├── kerning.rb │ ├── span.rb │ ├── hyphenation.rb │ ├── dfont.rb │ ├── text_box_returning_excess.rb │ ├── text_box.rb │ ├── font_calculations.rb │ ├── text_flow.rb │ └── rotated.rb ├── grid │ ├── show_grid.rb │ ├── simple_grid.rb │ ├── bounding_boxes.rb │ ├── column_gutter_grid.rb │ └── multi_boxes.rb ├── graphics │ ├── curves.rb │ ├── remote_images.rb │ ├── cmyk.rb │ ├── hexagon.rb │ ├── polygons.rb │ ├── image_fit.rb │ ├── ruport_style_helpers.rb │ ├── stroke_bounds.rb │ ├── image_position.rb │ ├── rounded_polygons.rb │ ├── gradient.rb │ ├── transparency.rb │ ├── line.rb │ ├── png_types.rb │ ├── rounded_rectangle.rb │ ├── basic_images.rb │ ├── stroke_cap_and_join.rb │ ├── image_flow.rb │ ├── stroke_dash.rb │ └── transformations.rb ├── m17n │ ├── utf8.rb │ ├── euro.rb │ ├── chinese_text_wrapping.rb │ ├── sjis.rb │ └── win_ansi_charset.rb └── bounding_box │ ├── russian_boxes.rb │ ├── indentation.rb │ ├── bounding_boxes.rb │ └── stretched_nesting.rb ├── .gitmodules ├── bugs ├── resolved │ ├── png_barcode_issue.rb │ ├── layout │ │ ├── fill_color.rb │ │ ├── table_suppress_newline.rb │ │ ├── cell_width_miscalculation.rb │ │ ├── table_in_bounding_box_without_height.rb │ │ ├── table_ignores_align_headers.rb │ │ ├── table_header_overrun.rb │ │ └── table_row_background_color_issue.rb │ ├── transaction_page_number_issue_79.rb │ ├── canvas_sets_y_to_0.rb │ └── ttf_fails_in_transactions.rb └── indentation_across_pagebreaks.rb ├── bench ├── png_type_6.rb ├── afm_text_bench.rb └── ttf_text_bench.rb ├── spec ├── destinations_spec.rb ├── measurement_units_spec.rb ├── jpg_spec.rb ├── spec_helper.rb ├── extensions │ └── mocha.rb ├── span_spec.rb ├── text_rendering_mode_spec.rb ├── text_spacing_spec.rb ├── annotations_spec.rb ├── reference_spec.rb ├── repeater_spec.rb ├── transparency_spec.rb └── grid_spec.rb ├── prawn.gemspec ├── HACKING ├── Rakefile └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .*.sw? 2 | nbproject 3 | pkg 4 | .rvmrc 5 | .bundle 6 | Gemfile.lock 7 | -------------------------------------------------------------------------------- /data/images/dice.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/dice.dat -------------------------------------------------------------------------------- /data/images/dice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/dice.png -------------------------------------------------------------------------------- /data/images/pigs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/pigs.jpg -------------------------------------------------------------------------------- /data/images/stef.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/stef.jpg -------------------------------------------------------------------------------- /www/media/image.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/www/media/image.pdf -------------------------------------------------------------------------------- /www/media/images.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/www/media/images.png -------------------------------------------------------------------------------- /www/media/tables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/www/media/tables.png -------------------------------------------------------------------------------- /www/media/utf8.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/www/media/utf8.pdf -------------------------------------------------------------------------------- /www/media/utf8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/www/media/utf8.png -------------------------------------------------------------------------------- /data/fonts/Activa.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/fonts/Activa.ttf -------------------------------------------------------------------------------- /data/images/16bit.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/16bit.dat -------------------------------------------------------------------------------- /data/images/16bit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/16bit.png -------------------------------------------------------------------------------- /data/images/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/arrow.png -------------------------------------------------------------------------------- /data/images/arrow2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/arrow2.png -------------------------------------------------------------------------------- /data/images/dice.alpha: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/dice.alpha -------------------------------------------------------------------------------- /data/images/rails.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/rails.dat -------------------------------------------------------------------------------- /data/images/rails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/rails.png -------------------------------------------------------------------------------- /data/images/ruport.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/ruport.png -------------------------------------------------------------------------------- /data/images/tru256.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/tru256.bmp -------------------------------------------------------------------------------- /data/pdfs/hexagon.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/pdfs/hexagon.pdf -------------------------------------------------------------------------------- /data/fonts/Chalkboard.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/fonts/Chalkboard.ttf -------------------------------------------------------------------------------- /data/fonts/DejaVuSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/fonts/DejaVuSans.ttf -------------------------------------------------------------------------------- /data/fonts/comicsans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/fonts/comicsans.ttf -------------------------------------------------------------------------------- /data/fonts/gkai00mp.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/fonts/gkai00mp.ttf -------------------------------------------------------------------------------- /data/images/16bit.alpha: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/16bit.alpha -------------------------------------------------------------------------------- /data/images/fractal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/fractal.jpg -------------------------------------------------------------------------------- /data/images/web-links.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/web-links.dat -------------------------------------------------------------------------------- /data/images/web-links.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/web-links.png -------------------------------------------------------------------------------- /data/pdfs/encrypted.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/pdfs/encrypted.pdf -------------------------------------------------------------------------------- /data/shift_jis_text.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/shift_jis_text.txt -------------------------------------------------------------------------------- /reference_pdfs/bill.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/bill.pdf -------------------------------------------------------------------------------- /reference_pdfs/canvas.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/canvas.pdf -------------------------------------------------------------------------------- /reference_pdfs/cell.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/cell.pdf -------------------------------------------------------------------------------- /reference_pdfs/cmyk.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/cmyk.pdf -------------------------------------------------------------------------------- /reference_pdfs/curves.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/curves.pdf -------------------------------------------------------------------------------- /reference_pdfs/dfont.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/dfont.pdf -------------------------------------------------------------------------------- /reference_pdfs/euro.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/euro.pdf -------------------------------------------------------------------------------- /reference_pdfs/float.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/float.pdf -------------------------------------------------------------------------------- /reference_pdfs/flow.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/flow.pdf -------------------------------------------------------------------------------- /reference_pdfs/header.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/header.pdf -------------------------------------------------------------------------------- /reference_pdfs/lines.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/lines.pdf -------------------------------------------------------------------------------- /reference_pdfs/margin.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/margin.pdf -------------------------------------------------------------------------------- /reference_pdfs/repeat.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/repeat.pdf -------------------------------------------------------------------------------- /reference_pdfs/ruport.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/ruport.pdf -------------------------------------------------------------------------------- /reference_pdfs/sjis.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/sjis.pdf -------------------------------------------------------------------------------- /reference_pdfs/span.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/span.pdf -------------------------------------------------------------------------------- /reference_pdfs/stamp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/stamp.pdf -------------------------------------------------------------------------------- /reference_pdfs/utf8.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/utf8.pdf -------------------------------------------------------------------------------- /reference_pdfs/widths.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/widths.pdf -------------------------------------------------------------------------------- /www/media/fancy_table.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/www/media/fancy_table.pdf -------------------------------------------------------------------------------- /www/media/prawn_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/www/media/prawn_logo.png -------------------------------------------------------------------------------- /data/fonts/Action Man.dfont: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/fonts/Action Man.dfont -------------------------------------------------------------------------------- /data/images/letterhead.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/letterhead.jpg -------------------------------------------------------------------------------- /data/images/ruport_data.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/ruport_data.dat -------------------------------------------------------------------------------- /data/pdfs/nested_pages.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/pdfs/nested_pages.pdf -------------------------------------------------------------------------------- /data/pdfs/two_hexagons.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/pdfs/two_hexagons.pdf -------------------------------------------------------------------------------- /reference_pdfs/hexagon.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/hexagon.pdf -------------------------------------------------------------------------------- /reference_pdfs/kerning.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/kerning.pdf -------------------------------------------------------------------------------- /reference_pdfs/outlines.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/outlines.pdf -------------------------------------------------------------------------------- /reference_pdfs/subtable.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/subtable.pdf -------------------------------------------------------------------------------- /reference_pdfs/text_box.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/text_box.pdf -------------------------------------------------------------------------------- /reference_pdfs/win-ansi.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/win-ansi.pdf -------------------------------------------------------------------------------- /data/fonts/Dustismo_Roman.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/fonts/Dustismo_Roman.ttf -------------------------------------------------------------------------------- /data/images/barcode_issue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/barcode_issue.png -------------------------------------------------------------------------------- /data/images/ruport_type0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/ruport_type0.png -------------------------------------------------------------------------------- /data/pdfs/complex_template.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/pdfs/complex_template.pdf -------------------------------------------------------------------------------- /reference_pdfs/alignment.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/alignment.pdf -------------------------------------------------------------------------------- /reference_pdfs/background.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/background.pdf -------------------------------------------------------------------------------- /reference_pdfs/column_box.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/column_box.pdf -------------------------------------------------------------------------------- /reference_pdfs/font_size.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/font_size.pdf -------------------------------------------------------------------------------- /reference_pdfs/image-flow.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/image-flow.pdf -------------------------------------------------------------------------------- /reference_pdfs/image_fit.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/image_fit.pdf -------------------------------------------------------------------------------- /reference_pdfs/indentation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/indentation.pdf -------------------------------------------------------------------------------- /reference_pdfs/multi_boxes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/multi_boxes.pdf -------------------------------------------------------------------------------- /reference_pdfs/padded_box.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/padded_box.pdf -------------------------------------------------------------------------------- /reference_pdfs/png_types.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/png_types.pdf -------------------------------------------------------------------------------- /reference_pdfs/portrait_a4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/portrait_a4.pdf -------------------------------------------------------------------------------- /reference_pdfs/show_grid.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/show_grid.pdf -------------------------------------------------------------------------------- /reference_pdfs/simple_grid.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/simple_grid.pdf -------------------------------------------------------------------------------- /reference_pdfs/simple_text.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/simple_text.pdf -------------------------------------------------------------------------------- /reference_pdfs/stroke_dash.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/stroke_dash.pdf -------------------------------------------------------------------------------- /www/media/bounding_boxes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/www/media/bounding_boxes.pdf -------------------------------------------------------------------------------- /www/media/bounding_boxes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/www/media/bounding_boxes.png -------------------------------------------------------------------------------- /data/images/dice_interlaced.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/dice_interlaced.png -------------------------------------------------------------------------------- /data/images/page_white_text.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/page_white_text.dat -------------------------------------------------------------------------------- /data/images/page_white_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/page_white_text.png -------------------------------------------------------------------------------- /data/pdfs/contains_ttf_font.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/pdfs/contains_ttf_font.pdf -------------------------------------------------------------------------------- /data/pdfs/indirect_reference.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/pdfs/indirect_reference.pdf -------------------------------------------------------------------------------- /reference_pdfs/basic_images.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/basic_images.pdf -------------------------------------------------------------------------------- /reference_pdfs/checkerboard.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/checkerboard.pdf -------------------------------------------------------------------------------- /reference_pdfs/chinese_flow.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/chinese_flow.pdf -------------------------------------------------------------------------------- /reference_pdfs/family_style.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/family_style.pdf -------------------------------------------------------------------------------- /reference_pdfs/landscape_a4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/landscape_a4.pdf -------------------------------------------------------------------------------- /reference_pdfs/metadata-info.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/metadata-info.pdf -------------------------------------------------------------------------------- /reference_pdfs/multi-layout.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/multi-layout.pdf -------------------------------------------------------------------------------- /reference_pdfs/remote_images.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/remote_images.pdf -------------------------------------------------------------------------------- /reference_pdfs/rotated_text.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/rotated_text.pdf -------------------------------------------------------------------------------- /reference_pdfs/russian_boxes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/russian_boxes.pdf -------------------------------------------------------------------------------- /reference_pdfs/simple_table.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/simple_table.pdf -------------------------------------------------------------------------------- /reference_pdfs/stroke_bounds.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/stroke_bounds.pdf -------------------------------------------------------------------------------- /reference_pdfs/transparency.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/transparency.pdf -------------------------------------------------------------------------------- /www/media/bounding_boxes_out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/www/media/bounding_boxes_out.png -------------------------------------------------------------------------------- /data/images/page_white_text.alpha: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/page_white_text.alpha -------------------------------------------------------------------------------- /data/images/ruport_transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/images/ruport_transparent.png -------------------------------------------------------------------------------- /reference_pdfs/bounding_boxes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/bounding_boxes.pdf -------------------------------------------------------------------------------- /reference_pdfs/image_position.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/image_position.pdf -------------------------------------------------------------------------------- /reference_pdfs/landscape_legal.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/landscape_legal.pdf -------------------------------------------------------------------------------- /reference_pdfs/landscape_letter.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/landscape_letter.pdf -------------------------------------------------------------------------------- /reference_pdfs/multi_page_table.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/multi_page_table.pdf -------------------------------------------------------------------------------- /reference_pdfs/portrait_legal.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/portrait_legal.pdf -------------------------------------------------------------------------------- /reference_pdfs/portrait_letter.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/portrait_letter.pdf -------------------------------------------------------------------------------- /reference_pdfs/pretty_polygons.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/pretty_polygons.pdf -------------------------------------------------------------------------------- /reference_pdfs/rounded_polygon.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/rounded_polygon.pdf -------------------------------------------------------------------------------- /reference_pdfs/simple_text_ttf.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/simple_text_ttf.pdf -------------------------------------------------------------------------------- /reference_pdfs/transformations.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/transformations.pdf -------------------------------------------------------------------------------- /reference_pdfs/bounding_box_grid.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/bounding_box_grid.pdf -------------------------------------------------------------------------------- /reference_pdfs/column_gutter_grid.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/column_gutter_grid.pdf -------------------------------------------------------------------------------- /reference_pdfs/font_calculations.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/font_calculations.pdf -------------------------------------------------------------------------------- /reference_pdfs/indent_paragraphs.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/indent_paragraphs.pdf -------------------------------------------------------------------------------- /reference_pdfs/measurement_units.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/measurement_units.pdf -------------------------------------------------------------------------------- /reference_pdfs/rounded_rectangle.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/rounded_rectangle.pdf -------------------------------------------------------------------------------- /reference_pdfs/security_hello_foo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/security_hello_foo.pdf -------------------------------------------------------------------------------- /reference_pdfs/stretched_nesting.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/stretched_nesting.pdf -------------------------------------------------------------------------------- /reference_pdfs/inline_format_table.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/inline_format_table.pdf -------------------------------------------------------------------------------- /reference_pdfs/lazy_bounding_boxes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/lazy_bounding_boxes.pdf -------------------------------------------------------------------------------- /reference_pdfs/page_with_numbering.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/page_with_numbering.pdf -------------------------------------------------------------------------------- /reference_pdfs/stroke_cap_and_join.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/stroke_cap_and_join.pdf -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | 3 | gem "pdf-reader", "~>0.9.0" 4 | 5 | group :test do 6 | gem "test-spec" 7 | gem "mocha" 8 | end 9 | -------------------------------------------------------------------------------- /data/pdfs/resources_as_indirect_object.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/data/pdfs/resources_as_indirect_object.pdf -------------------------------------------------------------------------------- /reference_pdfs/context_sensitive_headers.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/context_sensitive_headers.pdf -------------------------------------------------------------------------------- /reference_pdfs/text_box_returning_excess.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexyoung/prawn/master/reference_pdfs/text_box_returning_excess.pdf -------------------------------------------------------------------------------- /lib/prawn/text/formatted.rb: -------------------------------------------------------------------------------- 1 | require "prawn/core/text/formatted/wrap" 2 | require "prawn/text/formatted/box" 3 | require "prawn/text/formatted/parser" 4 | require "prawn/text/formatted/fragment" 5 | -------------------------------------------------------------------------------- /examples/example_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 2 | require 'rubygems' 3 | require 'prawn' 4 | require 'prawn/security' 5 | require "prawn/layout" 6 | 7 | 8 | Prawn.debug = true 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/pdf-inspector"] 2 | path = vendor/pdf-inspector 3 | url = git://github.com/sandal/pdf-inspector.git 4 | [submodule "vendor/ttfunk"] 5 | path = vendor/ttfunk 6 | url = git://github.com/sandal/ttfunk 7 | -------------------------------------------------------------------------------- /lib/prawn/core/byte_string.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | module Prawn 3 | module Core 4 | # This is used to differentiate strings that must be encoded as 5 | # a byte string, such as binary data from encrypted strings. 6 | class ByteString < String #:nodoc: 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /examples/table/multi_page_table.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | require File.expand_path(File.join(File.dirname(__FILE__), 4 | %w[.. example_helper])) 5 | 6 | Prawn::Document.generate("multi_page_table.pdf") do 7 | 8 | table([%w[Some data in a table]] * 50) 9 | 10 | end 11 | -------------------------------------------------------------------------------- /examples/security/hello_foo.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.join(File.dirname(__FILE__), 2 | %w[.. example_helper])) 3 | 4 | Prawn::Document.generate("security_hello_foo.pdf") do 5 | text "Hello, world!" 6 | encrypt_document :user_password => 'foo', :owner_password => 'bar', 7 | :permissions => { :print_document => false } 8 | end 9 | 10 | -------------------------------------------------------------------------------- /examples/general/float.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.join(File.dirname(__FILE__), 2 | %w[.. example_helper])) 3 | 4 | Prawn::Document.generate('float.pdf') do 5 | float do 6 | bounding_box [bounds.width / 2.0, bounds.top], :width => 100 do 7 | text "Hello world. " * 50 8 | end 9 | end 10 | 11 | text "Hello world again" 12 | end 13 | -------------------------------------------------------------------------------- /examples/table/subtable.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | require File.expand_path(File.join(File.dirname(__FILE__), 4 | %w[.. example_helper])) 5 | 6 | Prawn::Document.generate("subtable.pdf") do |pdf| 7 | 8 | subtable = Prawn::Table.new([%w[one two], %w[three four]], pdf) 9 | 10 | pdf.table([["Subtable ->", subtable, "<-"]]) 11 | 12 | end 13 | 14 | -------------------------------------------------------------------------------- /bugs/resolved/png_barcode_issue.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # As of 200fc36455fa3bee0e1e3bb25d1b5bf73dbf3b52, 4 | # the following code does not correctly render a PNG image 5 | # 6 | $LOAD_PATH << File.join(File.dirname(__FILE__), '..', '..','lib') 7 | require "prawn/core" 8 | 9 | Prawn::Document.generate('png_barcode_issue.pdf') do 10 | image "#{Prawn::BASEDIR}/data/images/barcode_issue.png" 11 | end 12 | -------------------------------------------------------------------------------- /bench/png_type_6.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 2 | require "prawn" 3 | require "benchmark" 4 | 5 | N=5 6 | 7 | Benchmark.bmbm do |x| 8 | x.report("PNG Type 6") do 9 | N.times do 10 | Prawn::Document.new do 11 | image "#{Prawn::BASEDIR}/data/images/dice.png" 12 | end.render 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /examples/text/character_spacing.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Example of character spacing 4 | # 5 | # 6 | require File.expand_path(File.join(File.dirname(__FILE__), 7 | %w[.. example_helper])) 8 | 9 | Prawn::Document.generate "character_spacing.pdf" do |pdf| 10 | string = "hello world " * 50 11 | pdf.text(string, :character_spacing => 2.5) 12 | pdf.text(string) 13 | end 14 | -------------------------------------------------------------------------------- /examples/grid/show_grid.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require File.expand_path(File.join(File.dirname(__FILE__), 4 | %w[.. example_helper])) 5 | 6 | Prawn::Document.generate('show_grid.pdf') do |p| 7 | p.define_grid(:columns => 5, :rows => 8, :gutter => 10) 8 | 9 | p.grid.show_all 10 | 11 | p.grid(2,4).show("FF0000") 12 | p.grid([3,0], [5,3]).show("0000FF") 13 | end 14 | 15 | -------------------------------------------------------------------------------- /bugs/indentation_across_pagebreaks.rb: -------------------------------------------------------------------------------- 1 | # As of 2009.02.13, indentation does not work across page breaks. [#86] 2 | 3 | $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib') 4 | require "prawn/core" 5 | 6 | Prawn::Document.generate("indent_page_breaks.pdf") do 7 | text "heading", :size => 14, :style => :bold 8 | 9 | indent(20) do 10 | 100.times do 11 | text "test" 12 | end 13 | end 14 | end 15 | 16 | -------------------------------------------------------------------------------- /spec/destinations_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper") 4 | 5 | describe "When creating destinations" do 6 | 7 | before(:each) { create_pdf } 8 | 9 | it "should add entry to Dests name tree" do 10 | @pdf.dests.data.empty?.should == true 11 | @pdf.add_dest "candy", "chocolate" 12 | @pdf.dests.data.size.should == 1 13 | end 14 | 15 | end 16 | -------------------------------------------------------------------------------- /examples/graphics/curves.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Demonstrates simple curve and circle usage 4 | # 5 | require File.expand_path(File.join(File.dirname(__FILE__), 6 | %w[.. example_helper])) 7 | 8 | pdf = Prawn::Document.new 9 | pdf.move_to [100,100] 10 | pdf.stroke_curve_to [50,50], :bounds => [[60,90], [60, 90]] 11 | pdf.fill_circle_at [200,200], :radius => 10 12 | pdf.render_file "curves.pdf" 13 | -------------------------------------------------------------------------------- /lib/prawn/layout.rb: -------------------------------------------------------------------------------- 1 | require "prawn/table" 2 | require 'prawn/layout/grid' 3 | 4 | module Prawn 5 | 6 | module Errors 7 | 8 | # This error is raised when table data is malformed 9 | # 10 | InvalidTableData = Class.new(StandardError) 11 | 12 | # This error is raised when an empty or nil table is rendered 13 | # 14 | EmptyTable = Class.new(StandardError) 15 | end 16 | 17 | module Layout 18 | 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /bugs/resolved/layout/fill_color.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # As of 9e48a6 (2009.01.03), this code fails to recognize fill_color in tables. 4 | # Resolved in 664760 (2009.01.05) 5 | # 6 | $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', '..','lib') 7 | require "prawn/core" 8 | require "prawn/layout" 9 | 10 | Prawn::Document.generate("fill_color.pdf") do 11 | fill_color "ff0000" 12 | table [%w[1 2 3],%w[4 5 6],%w[7 8 9]], 13 | end 14 | 15 | -------------------------------------------------------------------------------- /examples/graphics/remote_images.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Demonstrates how to use open-uri and Document#image to embed remote image 4 | # files. 5 | # 6 | require File.expand_path(File.join(File.dirname(__FILE__), 7 | %w[.. example_helper])) 8 | 9 | require "open-uri" 10 | 11 | Prawn::Document.generate("remote_images.pdf") do 12 | image open("http://prawn.majesticseacreature.com/images/prawn.png") 13 | end 14 | -------------------------------------------------------------------------------- /examples/table/inline_format_table.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | require File.expand_path(File.join(File.dirname(__FILE__), 4 | %w[.. example_helper])) 5 | 6 | Prawn::Document.generate("inline_format_table.pdf") do 7 | 8 | table([%w[foo bar bazbaz], %w[baz bar foofoo]], 9 | :cell_style => { :padding => 12, :inline_format => true }, 10 | :width => bounds.width) 11 | 12 | end 13 | 14 | -------------------------------------------------------------------------------- /data/encodings/win_ansi.txt: -------------------------------------------------------------------------------- 1 | # A mapping of WinAnsi (win-1252) characters to unicode. Anything 2 | # not specified is left unchanged 3 | 80;20AC 4 | 82;201A 5 | 83;0192 6 | 84;201E 7 | 85;2026 8 | 86;2020 9 | 87;2021 10 | 88;02C6 11 | 89;2030 12 | 8A;0160 13 | 8B;2039 14 | 8C;0152 15 | 8E;017D 16 | 91;2018 17 | 92;2019 18 | 93;201C 19 | 94;201D 20 | 95;2022 21 | 96;2013 22 | 97;2014 23 | 98;02DC 24 | 99;2122 25 | 9A;0161 26 | 9B;203A 27 | 9C;0152 28 | 9E;017E 29 | 9F;0178 30 | -------------------------------------------------------------------------------- /examples/table/header.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | require File.expand_path(File.join(File.dirname(__FILE__), 4 | %w[.. example_helper])) 5 | 6 | Prawn::Document.generate("header.pdf") do 7 | 8 | header = %w[Name Occupation] 9 | data = ["Bender Bending Rodriguez", "Bender"] 10 | 11 | table([header] + [data] * 50, :header => true) do 12 | row(0).style(:font_style => :bold, :background_color => 'cccccc') 13 | end 14 | 15 | end 16 | -------------------------------------------------------------------------------- /examples/graphics/cmyk.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Demonstrates Prawn's support for CMYK images and colors. 4 | # 5 | require File.expand_path(File.join(File.dirname(__FILE__), 6 | %w[.. example_helper])) 7 | 8 | Prawn::Document.generate("cmyk.pdf", :page_layout => :landscape) do 9 | fill_color 50, 100, 0, 0 10 | text "Prawn is CYMK Friendly" 11 | fractal = "#{Prawn::BASEDIR}/data/images/fractal.jpg" 12 | image fractal, :at => [50,450] 13 | end 14 | -------------------------------------------------------------------------------- /examples/graphics/hexagon.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Draws and fills a Hexagon using Document#polygon 4 | # 5 | require File.expand_path(File.join(File.dirname(__FILE__), 6 | %w[.. example_helper])) 7 | 8 | pdf = Prawn::Document.new 9 | 10 | pdf.fill_color "ff0000" 11 | pdf.fill_polygon [100, 250], [200, 300], [300, 250], 12 | [300, 150], [200, 100], [100, 150] 13 | 14 | pdf.render_file "hexagon.pdf" 15 | -------------------------------------------------------------------------------- /examples/m17n/utf8.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Shows that Prawn works out of the box with UTF-8 text, so long as you use 4 | # a TTF file with the necessary glyphs for your content. 5 | # 6 | require File.expand_path(File.join(File.dirname(__FILE__), 7 | %w[.. example_helper])) 8 | 9 | Prawn::Document.generate("utf8.pdf") do 10 | font "#{Prawn::BASEDIR}/data/fonts/DejaVuSans.ttf" 11 | text "ὕαλον ϕαγεῖν δύναμαι· τοῦτο οὔ με βλάπτει." * 20 12 | end 13 | 14 | 15 | -------------------------------------------------------------------------------- /bench/afm_text_bench.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 2 | require "prawn" 3 | require "benchmark" 4 | 5 | N=2000 6 | 7 | Benchmark.bmbm do |x| 8 | x.report("AFM text") do 9 | Prawn::Document.new { 10 | N.times do 11 | (1..5).each do |i| 12 | draw_text "Hello Prawn", :at => [200, i * 100] 13 | end 14 | start_new_page 15 | end 16 | }.render 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /examples/general/page_numbering.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # This example demonstrates how to add a "page k of n" 4 | # template to your documents. 5 | # 6 | require File.expand_path(File.join(File.dirname(__FILE__), 7 | %w[.. example_helper])) 8 | 9 | Prawn::Document.generate("page_with_numbering.pdf") do 10 | text "Hai" 11 | start_new_page 12 | text "bai" 13 | start_new_page 14 | text "-- Hai again" 15 | number_pages " in a total of ", [bounds.right - 50, 0] 16 | end 17 | -------------------------------------------------------------------------------- /examples/graphics/polygons.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Basic polygon drawing example. See also: hexagon.rb 4 | # 5 | require File.expand_path(File.join(File.dirname(__FILE__), 6 | %w[.. example_helper])) 7 | 8 | pdf = Prawn::Document.new 9 | 10 | 10.times do |i| 11 | pdf.stroke_polygon [ 50 + i*25, 50 + i*25], 12 | [100 + i*25, 50 + i*25], 13 | [100 + i*25, 100 + i*25] 14 | pdf.stroke_rectangle [0,600], 5*i, 10*i 15 | end 16 | 17 | pdf.render_file "pretty_polygons.pdf" 18 | -------------------------------------------------------------------------------- /examples/table/cell.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Low level cell and row implementation, which form the basic building 4 | # blocks for Prawn tables. Only necessary to know about if you plan on 5 | # building your own table implementation from scratch or heavily modify 6 | # the existing table system. 7 | # 8 | require File.expand_path(File.join(File.dirname(__FILE__), 9 | %w[.. example_helper])) 10 | 11 | Prawn::Document.generate("cell.pdf") do 12 | cell :content => "test", :padding => 10, :font_style => :bold, :size => 7 13 | end 14 | -------------------------------------------------------------------------------- /bugs/resolved/transaction_page_number_issue_79.rb: -------------------------------------------------------------------------------- 1 | # As of 2010.01.12, we have confirmed that page_number is not properly set on 2 | # transaction rollback, resulting in an error from the code sample below. 3 | # 4 | # Resolved in 7c62bbf. 5 | # 6 | 7 | $LOAD_PATH << File.join(File.dirname(__FILE__), '..','lib') 8 | require "prawn/core" 9 | 10 | Prawn::Document.generate("transaction_rollback_pagenumber.pdf") do 11 | text "Hello world" 12 | 13 | transaction do 14 | text "hello " * 1000 15 | rollback 16 | end 17 | 18 | start_new_page 19 | text "hi there" 20 | end 21 | 22 | -------------------------------------------------------------------------------- /bugs/resolved/canvas_sets_y_to_0.rb: -------------------------------------------------------------------------------- 1 | # As of 7e94d25828021732f7872934cb91430ef798cd86, Document#canvas 2 | # sets pdf.y to 0 after executing a block, which is probably not useful for 3 | # anyone. It should retain the y position present at the end of the block. 4 | # 5 | # This was resolved in 998a5c3fad40c9e0a79e1468e3a83815ed948a74 [#88] 6 | # 7 | $LOAD_PATH << File.join(File.dirname(__FILE__), '..', '..','lib') 8 | require "prawn/core" 9 | 10 | Prawn::Document.generate("canvas_sets_y_to_0.pdf") do 11 | 12 | canvas { text "blah" } 13 | 14 | text "Here's my sentence. by satoko" 15 | end 16 | -------------------------------------------------------------------------------- /bench/ttf_text_bench.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 2 | require "prawn" 3 | require "benchmark" 4 | 5 | N=2000 6 | 7 | Benchmark.bmbm do |x| 8 | x.report("TTF text") do 9 | Prawn::Document.new { 10 | font "#{Prawn::BASEDIR}/data/fonts/DejaVuSans.ttf" 11 | N.times do 12 | (1..5).each do |i| 13 | draw_text "Hello Prawn", :at => [200, i * 100] 14 | end 15 | start_new_page 16 | end 17 | }.render 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /examples/graphics/image_fit.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Demonstrates fitting an image within a rectangular width and height. 4 | # The image will be scaled down to fit within the box, while preserving 5 | # the aspect ratio. 6 | # 7 | require File.expand_path(File.join(File.dirname(__FILE__), 8 | %w[.. example_helper])) 9 | 10 | Prawn::Document.generate("image_fit.pdf", :page_layout => :landscape) do 11 | 12 | pigs = "#{Prawn::BASEDIR}/data/images/pigs.jpg" 13 | stroke_rectangle [50,450], 200, 200 14 | image pigs, :at => [50,450], :fit => [200,200] 15 | 16 | end 17 | -------------------------------------------------------------------------------- /examples/text/rendering_mode.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Example of character spacing 4 | # 5 | # 6 | require File.expand_path(File.join(File.dirname(__FILE__), 7 | %w[.. example_helper])) 8 | 9 | Prawn::Document.generate "rendering_mode.pdf" do |pdf| 10 | pdf.fill_color "00ff00" 11 | pdf.stroke_color "0000ff" 12 | 13 | # inline rendering mode 14 | pdf.text("Inline mode", :mode => :stroke, :size => 40) 15 | 16 | # block rendering mode 17 | pdf.text_rendering_mode(:stroke) do 18 | pdf.text("Block", :size => 30) 19 | pdf.text("Mode", :size => 30) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /examples/general/canvas.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Demonstrates how to enable absolute positioning in Prawn by temporarily 4 | # removing the margin_box via Document#canvas() 5 | # 6 | require File.expand_path(File.join(File.dirname(__FILE__), 7 | %w[.. example_helper])) 8 | 9 | Prawn::Document.generate("canvas.pdf") do 10 | canvas do 11 | text "This text should appear at the absolute top left" 12 | 13 | # stroke a line to show that the relative coordinates are the same as absolute 14 | stroke_line [bounds.left,bounds.bottom], [bounds.right,bounds.top] 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /examples/text/simple_text_ttf.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # An early example of TTF font embedding. Mostly kept for nostalgia's sake. 4 | # 5 | require File.expand_path(File.join(File.dirname(__FILE__), 6 | %w[.. example_helper])) 7 | 8 | Prawn::Document.generate "simple_text_ttf.pdf" do 9 | fill_color "0000ff" 10 | font "#{Prawn::BASEDIR}/data/fonts/comicsans.ttf" 11 | draw_text "Hello World", :at => [200,720], :size => 32 12 | 13 | font "#{Prawn::BASEDIR}/data/fonts/Chalkboard.ttf" 14 | 15 | pad(20) do 16 | text "This is chalkboard wrapping " * 20 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /bugs/resolved/layout/table_suppress_newline.rb: -------------------------------------------------------------------------------- 1 | # As of bbe1df6530455dff41768bcc329bdc7cfdfaded1 (and earlier), 2 | # Prawn does not properly display cells with newlines in tables. 3 | # 4 | # Fixed in e28cf53b5d05e6cb343e8dd5265c57d5f24ef4da [#76] 5 | # 6 | $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', '..', 'lib') 7 | require "rubygems" 8 | require "prawn" 9 | require "prawn/layout" 10 | 11 | Prawn::Document.generate("table_supresses_newlines.pdf") do 12 | table [["test\n\naaaa","test\n\nbbbb"], 13 | ["test\n\ncccc", "test\n\ndddd"]], :border_style => :grid 14 | 15 | cell [100,100], :text => "test\n\naaaa" 16 | end 17 | -------------------------------------------------------------------------------- /examples/general/templates.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # This sample demonstrates the use of the :template option when generating 4 | # a new Document. The template PDF file is imported into a new document. 5 | 6 | require File.expand_path(File.join(File.dirname(__FILE__), 7 | %w[.. example_helper])) 8 | 9 | filename = "#{Prawn::BASEDIR}/reference_pdfs/curves.pdf" 10 | 11 | Prawn::Document.generate("template.pdf", :template => filename) do 12 | text "Previous pages and content imported", :size => 18, :align => :center 13 | text "This page and content is brand new", :size => 18, :align => :center 14 | end 15 | -------------------------------------------------------------------------------- /lib/prawn/core/literal_string.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | module Prawn 3 | module Core 4 | # This is used to differentiate strings that must be encoded as 5 | # a *literal* string, versus those that can be encoded in 6 | # the PDF hexadecimal format. 7 | # 8 | # Some features of the PDF format appear to require that literal 9 | # strings be used. One such feature is the /Dest key of a link 10 | # annotation; if a hex encoded string is used there, the links 11 | # do not work (as tested in Mac OS X Preview, and Adobe Acrobat 12 | # Reader). 13 | class LiteralString < String #:nodoc: 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/prawn/table/cell/in_table.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | # Accessors for using a Cell inside a Table. 4 | 5 | module Prawn 6 | class Table 7 | class Cell 8 | 9 | # This module extends Cell objects when they are used in a table (as 10 | # opposed to standalone). Its properties apply to cells-in-tables but not 11 | # cells themselves. 12 | # 13 | module InTable 14 | 15 | # Row number (0-based). 16 | # 17 | attr_accessor :row 18 | 19 | # Column number (0-based). 20 | # 21 | attr_accessor :column 22 | 23 | end 24 | 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /examples/table/widths.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | require File.expand_path(File.join(File.dirname(__FILE__), 4 | %w[.. example_helper])) 5 | 6 | Prawn::Document.generate("widths.pdf") do 7 | 8 | text "Some 300-pt tables:" 9 | 10 | table([%w[A B C]], :width => 300) 11 | move_down 12 12 | 13 | table([%w[A B C], %w[D Everything\ under\ the\ sun F]], :width => 300) 14 | move_down 12 15 | 16 | # TODO: what should this be doing? Like the current prawn-layout, it does 17 | # not attempt to reflow the second column. 18 | # table([["A", "Blah " * 20, "C"]], :width => 300) 19 | # move_down 12 20 | 21 | end 22 | -------------------------------------------------------------------------------- /examples/grid/simple_grid.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require File.expand_path(File.join(File.dirname(__FILE__), 4 | %w[.. example_helper])) 5 | 6 | Prawn::Document.generate('simple_grid.pdf') do |p| 7 | p.define_grid(:columns => 5, :rows => 8, :gutter => 10) 8 | 9 | p.grid.rows.times do |i| 10 | p.grid.columns.times do |j| 11 | b = p.grid(i,j) 12 | p.bounding_box b.top_left, :width => b.width, :height => b.height do 13 | p.text b.name 14 | p.stroke do 15 | p.rectangle(p.bounds.top_left, b.width, b.height) 16 | end 17 | end 18 | end 19 | end 20 | end 21 | 22 | -------------------------------------------------------------------------------- /examples/grid/bounding_boxes.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require File.expand_path(File.join(File.dirname(__FILE__), 4 | %w[.. example_helper])) 5 | 6 | Prawn::Document.generate('bounding_box_grid.pdf') do |p| 7 | p.define_grid(:columns => 5, :rows => 8, :gutter => 10) 8 | 9 | p.stroke_color = "ff0000" 10 | 11 | p.grid.rows.times do |i| 12 | p.grid.columns.times do |j| 13 | p.grid(i,j).bounding_box do 14 | p.text p.grid(i,j).name 15 | p.stroke do 16 | p.rectangle(p.bounds.top_left, p.bounds.width, p.bounds.height) 17 | end 18 | end 19 | end 20 | end 21 | end 22 | 23 | -------------------------------------------------------------------------------- /examples/m17n/euro.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # As of Prawn 0.3, it is possible to generate a Euro using the built-in 4 | # AFM files. However, you need to be sure to manually add spacing around it, 5 | # as its calculated width in the AFM files seem to be wrong. 6 | # 7 | # We are investigating this issue, but it does not seem to be Prawn specific. 8 | # If you need precision spacing, use a TTF file instead and the issue will 9 | # go away. 10 | # 11 | require File.expand_path(File.join(File.dirname(__FILE__), 12 | %w[.. example_helper])) 13 | 14 | Prawn::Document.generate "euro.pdf" do 15 | text "A Euro! € ©", :size => 32 16 | end 17 | -------------------------------------------------------------------------------- /examples/table/checkerboard.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | # Shows how to use the style() method with a block to style each cell with 4 | # custom code. 5 | 6 | require File.expand_path(File.join(File.dirname(__FILE__), 7 | %w[.. example_helper])) 8 | 9 | Prawn::Document.generate("checkerboard.pdf") do 10 | 11 | text "Here is a checkerboard:" 12 | 13 | table [[""] * 8] * 8 do |t| 14 | t.cells.style :width => 24, :height => 24 15 | t.cells.style do |c| 16 | c.background_color = ((c.row + c.column) % 2).zero? ? '000000' : 'ffffff' 17 | end 18 | end 19 | 20 | move_down 12 21 | text "Hope you enjoyed it!" 22 | 23 | end 24 | -------------------------------------------------------------------------------- /examples/text/simple_text.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # An early example of basic text generation at absolute positions. 4 | # Mostly kept for nostalgia. 5 | # 6 | require File.expand_path(File.join(File.dirname(__FILE__), 7 | %w[.. example_helper])) 8 | 9 | Prawn::Document.generate "simple_text.pdf" do 10 | fill_color "0000ff" 11 | draw_text "Hello World", :at => [200,420], :size => 32, :rotate => 45 12 | font "Times-Roman" 13 | fill_color "ff0000" 14 | draw_text "Using Another Font", :at => [5,5] 15 | start_new_page 16 | font "Courier" 17 | draw_text "Goodbye World", :at => [288,50] 18 | end 19 | -------------------------------------------------------------------------------- /examples/grid/column_gutter_grid.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require File.expand_path(File.join(File.dirname(__FILE__), 4 | %w[.. example_helper])) 5 | 6 | Prawn::Document.generate('column_gutter_grid.pdf') do |p| 7 | p.define_grid(:columns => 3, :rows => 10, :column_gutter => 10) 8 | 9 | p.grid.rows.times do |i| 10 | p.grid.columns.times do |j| 11 | b = p.grid(i,j) 12 | p.bounding_box b.top_left, :width => b.width, :height => b.height do 13 | p.text b.name 14 | p.stroke do 15 | p.rectangle(p.bounds.top_left, b.width, b.height) 16 | end 17 | end 18 | end 19 | end 20 | end 21 | 22 | -------------------------------------------------------------------------------- /spec/measurement_units_spec.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper") 2 | require "prawn/measurement_extensions" 3 | 4 | describe "Measurement units" do 5 | 6 | it "should convert units to PostScriptPoints" do 7 | 1.mm.should.be.close(2.834645669, 0.000000001) 8 | 1.mm.should == (72 / 25.4) 9 | 2.mm.should == (2 * 72 / 25.4) 10 | 3.mm.should == 3 * 72 / 25.4 11 | -3.mm.should == -3 * 72/25.4 12 | 1.cm.should == 10 * 72 / 25.4 13 | 1.dm.should == 100 * 72 / 25.4 14 | 1.m.should == 1000 * 72 / 25.4 15 | 16 | 1.in.should == 72 17 | 1.ft.should == 72 * 12 18 | 1.yd.should == 72 * 12 * 3 19 | 1.pt.should == 1 20 | end 21 | 22 | end 23 | -------------------------------------------------------------------------------- /lib/prawn.rb: -------------------------------------------------------------------------------- 1 | # Welcome to Prawn, the best PDF Generation library ever. 2 | # This documentation covers user level functionality. 3 | # 4 | # Those looking to contribute code or write extensions should look 5 | # into the lib/prawn/core/* source tree. 6 | # 7 | module Prawn #:nodoc: 8 | VERSION = "0.11.1.pre" 9 | end 10 | 11 | require "prawn/core" 12 | require "prawn/text" 13 | require "prawn/graphics" 14 | require "prawn/images" 15 | require "prawn/images/jpg" 16 | require "prawn/images/png" 17 | require "prawn/stamp" 18 | require "prawn/security" 19 | require "prawn/document" 20 | require "prawn/font" 21 | require "prawn/encoding" 22 | require "prawn/measurements" 23 | require "prawn/repeater" 24 | require "prawn/outline" 25 | require "prawn/layout" 26 | 27 | -------------------------------------------------------------------------------- /examples/graphics/ruport_style_helpers.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # These helpers will be familiar to Ruport users, and now are supported 4 | # directly in Prawn. Run the example to see how they work. 5 | # 6 | require File.expand_path(File.join(File.dirname(__FILE__), 7 | %w[.. example_helper])) 8 | 9 | # Demonstrates some features stolen from Ruport::Formatter::PDF 10 | Prawn::Document.generate("ruport.pdf") do 11 | move_down 50 12 | # TODO: Figure out where to set the y cursor to. 13 | stroke_horizontal_rule 14 | text "Hi there" 15 | pad(50) { text "I'm Padded" } 16 | text "I'm far away" 17 | stroke_horizontal_line 50, 100 18 | stroke_horizontal_line 50, 100, :at => 300 19 | stroke_vertical_line 300, 50, :at => 250 20 | end 21 | -------------------------------------------------------------------------------- /examples/graphics/stroke_bounds.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Demonstrates Document#stroke_bounds, which will stroke a rectange outlining 4 | # the boundaries of the current bounding box. This is useful for debugging 5 | # and can also be used as a light-weight and lower level alternative to 6 | # Cells. 7 | # 8 | # Feature borrowed from Josh Knowle's pt at: 9 | # http://github.com/joshknowles/pt/tree/master 10 | # 11 | require File.expand_path(File.join(File.dirname(__FILE__), 12 | %w[.. example_helper])) 13 | 14 | Prawn::Document.generate("stroke_bounds.pdf") do 15 | stroke_bounds 16 | 17 | bounding_box [100,500], :width => 200, :height => 300 do 18 | text "Hey there, here's some text. " * 10 19 | stroke_bounds 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /examples/graphics/image_position.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Demonstrates vertical and horizontal positioning of images. 4 | # 5 | require File.expand_path(File.join(File.dirname(__FILE__), 6 | %w[.. example_helper])) 7 | 8 | Prawn::Document.generate("image_position.pdf", :page_layout => :landscape) do 9 | 10 | dice = "#{Prawn::BASEDIR}/data/images/dice.png" 11 | 12 | image dice, :scale => 0.2, :position => :left, :vposition => :top 13 | image dice, :scale => 0.2, :position => :right, :vposition => :top 14 | image dice, :scale => 0.2, :position => :center, :vposition => :top 15 | image dice, :scale => 0.2, :position => :center, :vposition => :center 16 | image dice, :scale => 0.2, :position => :center, :vposition => :bottom 17 | 18 | end 19 | -------------------------------------------------------------------------------- /examples/general/metadata-info.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Demonstrates how to set metadata properties via the info option 4 | # It allows one to specify no standard properties 5 | # 6 | require File.expand_path(File.join(File.dirname(__FILE__), 7 | %w[.. example_helper])) 8 | 9 | Prawn::Document.generate "metadata-info.pdf", 10 | :info => { 11 | :Title => "My title", :Author => "John Doe", :Subject => "My Subject", 12 | :Keywords => "test metadata ruby pdf dry", :Creator => "ACME Soft App", 13 | :Producer => "Prawn", :CreationDate => Time.now, :Grok => "Test Property" 14 | } do 15 | text "This is a test of setting metadata properties via the info option" 16 | text "It allows one to specify no standard properties like 'Grok'" 17 | end 18 | -------------------------------------------------------------------------------- /examples/graphics/rounded_polygons.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.join(File.dirname(__FILE__), 2 | %w[.. example_helper])) 3 | 4 | def radian(degree) 5 | Math::PI/180*degree 6 | end 7 | 8 | def point_on_circle(center, radius, degrees) 9 | [center[0] + radius*(Math.cos(radian(degrees))), center[1] - radius*(Math.sin(radian(degrees)))] 10 | end 11 | 12 | pdf = Prawn::Document.new 13 | 14 | pentagon_points = (0..4).map{|i| point_on_circle([200, 400], 100, i * 72)} 15 | pentagram_points = [0, 2, 4, 1, 3].map{|i| pentagon_points[i]} 16 | pdf.stroke_rounded_polygon(20, *pentagram_points) 17 | pdf.fill_and_stroke_rounded_polygon(10, [100, 250], [200, 300], [300, 250], 18 | [300, 150], [200, 100], [100, 150]) 19 | 20 | pdf.render_file "rounded_polygon.pdf" 21 | -------------------------------------------------------------------------------- /spec/jpg_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | # Spec'ing the PNG class. Not complete yet - still needs to check the 4 | # contents of palette and transparency to ensure they're correct. 5 | # Need to find files that have these sections first. 6 | 7 | require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper") 8 | 9 | describe "When reading a JPEG file" do 10 | 11 | before(:each) do 12 | @filename = "#{Prawn::BASEDIR}/data/images/pigs.jpg" 13 | @img_data = File.open(@filename, "rb") { |f| f.read } 14 | end 15 | 16 | it "should read the basic attributes correctly" do 17 | jpg = Prawn::Images::JPG.new(@img_data) 18 | 19 | jpg.width.should == 604 20 | jpg.height.should == 453 21 | jpg.bits.should == 8 22 | jpg.channels.should == 3 23 | end 24 | end 25 | 26 | -------------------------------------------------------------------------------- /examples/text/indent_paragraphs.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Example of two ways of indenting paragraphs 4 | # 5 | # 6 | require File.expand_path(File.join(File.dirname(__FILE__), 7 | %w[.. example_helper])) 8 | 9 | Prawn::Document.generate "indent_paragraphs.pdf" do |pdf| 10 | hello = "hello " * 50 11 | world = "world " * 50 12 | pdf.text(hello + "\n" + world, :indent_paragraphs => 60) 13 | 14 | pdf.move_cursor_to(pdf.font.height) 15 | pdf.text(hello + "\n" + world, :indent_paragraphs => 60) 16 | 17 | pdf.move_cursor_to(pdf.font.height * 3) 18 | pdf.text(hello + "\n" + world, :indent_paragraphs => 60) 19 | 20 | # can also indent using a non-breaking space 21 | nbsp = Prawn::Text::NBSP 22 | pdf.text("\n" * 10 + hello + "\n#{nbsp * 10}" + world) 23 | end 24 | -------------------------------------------------------------------------------- /examples/text/alignment.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # This example demonstrates usage of Document#text with the :align option. 4 | # Available options are :left, :right, and :center, with :left as default. 5 | # 6 | require File.expand_path(File.join(File.dirname(__FILE__), 7 | %w[.. example_helper])) 8 | 9 | Prawn::Document.generate("alignment.pdf") do 10 | text "This text should be left aligned" 11 | text "This text should be centered", :align => :center 12 | text "This text should be right aligned", :align => :right 13 | 14 | pad(20) { text "This is Flowing from the left. " * 20 } 15 | 16 | pad(20) { text "This is Flowing from the center. " * 20, :align => :center } 17 | 18 | pad(20) { text "This is Flowing from the right. " * 20, :align => :right } 19 | end 20 | -------------------------------------------------------------------------------- /examples/graphics/gradient.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Demonstrate use of gradients 4 | # 5 | require File.expand_path(File.join(File.dirname(__FILE__), 6 | %w[.. example_helper])) 7 | 8 | pdf = Prawn::Document.new(:margin => 0) 9 | 10 | pdf.stroke_gradient [0, pdf.bounds.height], pdf.bounds.width, pdf.bounds.height/2, '69CD31', '0000FF' 11 | pdf.fill_gradient [0, pdf.bounds.height], pdf.bounds.width, pdf.bounds.height/2, 'FF0000', '00FF00' 12 | 13 | pdf.rectangle [10, pdf.bounds.height-10], 100, pdf.bounds.height-20 14 | pdf.line_width 20 15 | pdf.fill_and_stroke 16 | 17 | pdf.fill_gradient [150, 250], 400, 70, 'F0FF00', '0000FF' 18 | pdf.bounding_box [150, 250], :width => 450, :height => 150 do 19 | pdf.text "Gradient!", :size => 80 20 | end 21 | 22 | pdf.render_file 'gradient.pdf' 23 | 24 | -------------------------------------------------------------------------------- /examples/graphics/transparency.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Demonstrate use of transparency 4 | # 5 | require File.expand_path(File.join(File.dirname(__FILE__), 6 | %w[.. example_helper])) 7 | 8 | Prawn::Document.generate("transparency.pdf") do 9 | fill_color("ff0000") 10 | fill_circle_at([200, 200], :radius => 200) 11 | transparent(0.5, 1) do 12 | fill_color("000000") 13 | stroke_color("ffffff") 14 | fill_and_stroke_circle_at([300, 300], :radius => 200) 15 | fill_color("ffffff") 16 | text "transparency " * 150, :size => 18 17 | end 18 | 19 | start_new_page 20 | 21 | fill_color("000000") 22 | fill_rectangle([0, bounds.top], 200, 100) 23 | transparent(0.5) do 24 | fill_color("ff0000") 25 | fill_rectangle([100, bounds.top - 50], 200, 100) 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /bugs/resolved/layout/cell_width_miscalculation.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # As of 40c7bde9690e5174b6a958a5df6b2aabc6b8b041 this code produces an extra 4 | # empty line of text in row 2. 5 | # 6 | # Simple rounding of string_width floats seems to fix this issue, see the patch 7 | # in 09c837466c31bb715f1276118c606e20477577df. 8 | # 9 | $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', '..', 'lib') 10 | require "rubygems" 11 | require "prawn" 12 | require "prawn/layout" 13 | 14 | Prawn::Document.generate("broken_table.pdf") do 15 | font "#{Prawn::BASEDIR}/data/fonts/comicsans.ttf" 16 | table [["foo", "baaar", "1" ], 17 | ["This is","a sample", "2" ], 18 | ["Table", "dont\ncha\nknow?", "3" ]], 19 | :font_size => 30, 20 | :padding => 10, 21 | :border => 2, 22 | :position => :center 23 | end 24 | 25 | -------------------------------------------------------------------------------- /examples/table/simple_table.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | require File.expand_path(File.join(File.dirname(__FILE__), 4 | %w[.. example_helper])) 5 | 6 | Prawn::Document.generate("simple_table.pdf") do 7 | 8 | table([["foo", "bar " * 15, "baz"], 9 | ["baz", "bar", "foo " * 15]], :cell_style => { :padding => 12 }) do 10 | cells.borders = [] 11 | 12 | # Use the row() and style() methods to select and style a row. 13 | style row(0), :border_width => 2, :borders => [:bottom] 14 | 15 | # The style method can take a block, allowing you to customize properties 16 | # per-cell. 17 | style(columns(0..1)) { |cell| cell.borders |= [:right] } 18 | end 19 | 20 | move_down 12 21 | 22 | table([%w[foo bar bazbaz], %w[baz bar foofoo]], 23 | :cell_style => { :padding => 12 }, :width => bounds.width) 24 | 25 | end 26 | -------------------------------------------------------------------------------- /examples/general/background.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # This example demonstrates the use of the new :background option when 4 | # generating a new Document. Image is assumed to be pre-fit for your page 5 | # size, and will not be rescaled. 6 | # 7 | require File.expand_path(File.join(File.dirname(__FILE__), 8 | %w[.. example_helper])) 9 | 10 | img = "#{Prawn::BASEDIR}/data/images/letterhead.jpg" 11 | 12 | Prawn::Document.generate("background.pdf", :background => img, :margin => 100) do 13 | text "My report caption", :size => 18, :align => :right 14 | 15 | move_down font.height * 2 16 | 17 | text "Here is my text explaning this report. " * 20, 18 | :size => 12, :align => :left, :leading => 2 19 | 20 | move_down font.height 21 | 22 | text "I'm using a soft background. " * 40, 23 | :size => 12, :align => :left, :leading => 2 24 | end 25 | -------------------------------------------------------------------------------- /examples/text/family_based_styling.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # This example demonstrates using the :style option for Document#text. 4 | # If you are working with TTF fonts, you'll want to check out the 5 | # documentation for Document#font_families and register your fonts with it. 6 | # 7 | require File.expand_path(File.join(File.dirname(__FILE__), 8 | %w[.. example_helper])) 9 | 10 | Prawn::Document.generate("family_style.pdf") do 11 | ["Courier","Helvetica","Times-Roman"].each do |f| 12 | [:bold,:bold_italic,:italic,:normal].each do |s| 13 | font f, :style => s 14 | text "I'm writing in #{f} (#{s})" 15 | end 16 | end 17 | 18 | font "Helvetica" 19 | 20 | text "Normal" 21 | text "Bold", :style => :bold 22 | text "Bold Italic", :style => :bold_italic 23 | text "Italic", :style => :italic 24 | text "Normal" 25 | end 26 | -------------------------------------------------------------------------------- /bugs/resolved/layout/table_in_bounding_box_without_height.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Issue with tables within stretchy bounding boxes. Changes to the way 4 | # bounding boxes work caused tables to not properly render within stretchy 5 | # bounding boxes. 6 | # 7 | # A fix in 200fc36455fa3bee0e1e3bb25d1b5bf73dbf3b52 makes it so the bottom 8 | # of the margin_box will be used as the page boundary in stretchy bounding 9 | # boxes. Ideally, this would instead use the nesting bounding box dimensions 10 | # [#80] , but this works for now. 11 | # 12 | $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', '..', 'lib') 13 | require "rubygems" 14 | require "prawn" 15 | require "prawn/layout" 16 | 17 | Prawn::Document.generate("table_in_bounding_box_without_height.pdf") do 18 | bounding_box bounds.top_left, :width => 200 do 19 | table [%w(These should all be), %w(on the same page)] 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /examples/graphics/line.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # The very first Prawn example. Here for nostalgia's sake. 4 | # 5 | require File.expand_path(File.join(File.dirname(__FILE__), 6 | %w[.. example_helper])) 7 | 8 | pdf = Prawn::Document.new 9 | pdf.line_width = 10 10 | 11 | [[100,741,100,641], 12 | [100,691,150,691], 13 | [150,741,150,641], 14 | [200,741,200,641], 15 | [100,600,100,500], 16 | [100,600,150,550], 17 | [100,550,150,550], 18 | [175,600,175,500], 19 | [175,600,225,550], 20 | [175,550,225,550], 21 | [175,550,225,500], 22 | [275,600,250,500], 23 | [275,600,300,500], 24 | [250,550,300,550], 25 | [315,600,350,500], 26 | [350,500,365,550], 27 | [365,550,380,500], 28 | [380,500,415,600], 29 | [430,600,430,500], 30 | [430,600,465,500], 31 | [465,600,465,500]].each { |points| pdf.stroke_line(*points) } 32 | 33 | pdf.render_file "lines.pdf" 34 | -------------------------------------------------------------------------------- /examples/graphics/png_types.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # PNG files come in different flavours - 5 of them. This example embeds 4 | # one of each type as proof that they all work. 5 | # 6 | require File.expand_path(File.join(File.dirname(__FILE__), 7 | %w[.. example_helper])) 8 | 9 | images = [ 10 | ["Type 0", "#{Prawn::BASEDIR}/data/images/web-links.png"], 11 | ["Type 2", "#{Prawn::BASEDIR}/data/images/ruport.png"], 12 | ["Type 3", "#{Prawn::BASEDIR}/data/images/rails.png"], 13 | ["Type 4", "#{Prawn::BASEDIR}/data/images/page_white_text.png"], 14 | ["Type 6", "#{Prawn::BASEDIR}/data/images/dice.png"], 15 | ] 16 | 17 | Prawn::Document.generate("png_types.pdf", :page_size => "A5") do 18 | images.each do |header, file| 19 | start_new_page unless header.include?("0") 20 | text header 21 | image file, :at => [50,450] 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | puts "Prawn specs: Running on Ruby Version: #{RUBY_VERSION}" 4 | 5 | require "rubygems" 6 | $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib') 7 | $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'vendor', 8 | 'pdf-inspector','lib') 9 | require "prawn" 10 | 11 | Prawn.debug = true 12 | 13 | ruby_19 do 14 | gem "test-unit", "=1.2.3" 15 | end 16 | require "test/spec" 17 | require "mocha" 18 | 19 | gem 'pdf-reader', ">=0.9.0" 20 | require "pdf/reader" 21 | require "pdf/inspector" 22 | 23 | def create_pdf(klass=Prawn::Document) 24 | @pdf = klass.new(:margin => 0) 25 | end 26 | 27 | # Make some methods public to assist in testing 28 | module Prawn::Graphics 29 | public :map_to_absolute 30 | end 31 | 32 | require File.expand_path(File.join(File.dirname(__FILE__), 33 | %w[extensions mocha])) 34 | 35 | -------------------------------------------------------------------------------- /lib/prawn/measurement_extensions.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # measurement_extensions.rb: Core extensions for Prawn::Measurements 3 | # 4 | # Copyright December 2008, Florian Witteler. All Rights Reserved. 5 | # 6 | # This is free software. Please see the LICENSE and COPYING files for details. 7 | 8 | require 'prawn/measurements' 9 | 10 | class Numeric 11 | include Prawn::Measurements 12 | # prawns' basic unit is PostScript-Point 13 | # 72 points per inch 14 | 15 | def mm 16 | return mm2pt(self) 17 | end 18 | 19 | def cm 20 | return cm2pt(self) 21 | end 22 | 23 | def dm 24 | return dm2pt(self) 25 | end 26 | 27 | def m 28 | return m2pt(self) 29 | end 30 | 31 | def in 32 | return in2pt(self) 33 | end 34 | 35 | def yd 36 | return yd2pt(self) 37 | end 38 | 39 | def ft 40 | return ft2pt(self) 41 | end 42 | 43 | def pt 44 | return self 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /examples/graphics/rounded_rectangle.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Rounded rectangle example demonstrating both stroke and stroke and fill. 4 | # A rectangle with rounded join_style is added just for comparison. 5 | # 6 | require File.expand_path(File.join(File.dirname(__FILE__), 7 | %w[.. example_helper])) 8 | 9 | pdf = Prawn::Document.new 10 | pdf.font_size 8 11 | pdf.draw_text "a stroked rounded rectangle:", :at => [30, 575] 12 | pdf.stroke_rounded_rectangle([50, 550], 50, 100, 10) 13 | pdf.draw_text "a stroked and filled rounded rectangle:", :at => [180, 575] 14 | pdf.fill_and_stroke_rounded_rectangle([200, 550], 50, 100, 10) 15 | pdf.draw_text "a regular rectangle with rounded join style;", :at => [330, 575] 16 | pdf.draw_text "needs thick line width for similar result:", :at => [330, 565] 17 | pdf.join_style :round 18 | pdf.line_width 10 19 | pdf.stroke_rectangle([350, 550], 50, 100) 20 | 21 | pdf.render_file "rounded_rectangle.pdf" 22 | -------------------------------------------------------------------------------- /examples/graphics/basic_images.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Demonstrates basic image embedding and positioning functionality. 4 | # For positioning images alongside flowing text, see the image_flow.rb 5 | # example. 6 | # 7 | require File.expand_path(File.join(File.dirname(__FILE__), 8 | %w[.. example_helper])) 9 | 10 | Prawn::Document.generate("basic_images.pdf", :page_layout => :landscape) do 11 | stef = "#{Prawn::BASEDIR}/data/images/stef.jpg" 12 | image stef, :at => [75, 75] 13 | 14 | stef = "#{Prawn::BASEDIR}/data/images/stef.jpg" 15 | image stef, :at => [500, 400], :width => 200, :height => 200 16 | 17 | draw_text "Please enjoy the pigs", :size => 36, :at => [200,15] 18 | 19 | ruport = "#{Prawn::BASEDIR}/data/images/ruport.png" 20 | image ruport, :at => [400,200], :width => 150 21 | 22 | ruport = "#{Prawn::BASEDIR}/data/images/ruport_transparent.png" 23 | image ruport, :at => [50,525] 24 | end 25 | -------------------------------------------------------------------------------- /examples/general/multi_page_layout.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # This demonstrates that Prawn can modify page size, margins and layout for 4 | # each individual page, via Document#start_new_page() 5 | # 6 | require File.expand_path(File.join(File.dirname(__FILE__), 7 | %w[.. example_helper])) 8 | 9 | Prawn::Document.generate("multi-layout.pdf", :page_layout => :landscape) do |pdf| 10 | pdf.text "This is on a landscaped page" 11 | pdf.start_new_page(:layout => :portrait) 12 | pdf.text "This is on a portrait page" 13 | pdf.start_new_page(:size => "LEGAL") 14 | pdf.text "This is on legal paper size" 15 | pdf.start_new_page(:left_margin => 150, :right_margin => 150) 16 | pdf.text "This page has very wide left and right margins, causing a squeeze" 17 | pdf.start_new_page(:margin => 300, :right_margin => 0) 18 | pdf.text "This page has even wider margins on all sides except for the right side, where it's 0" 19 | end 20 | -------------------------------------------------------------------------------- /examples/m17n/chinese_text_wrapping.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # 4 | require File.expand_path(File.join(File.dirname(__FILE__), 5 | %w[.. example_helper])) 6 | 7 | start = Time.now 8 | Prawn::Document.generate("chinese_flow.pdf") do 9 | font "#{Prawn::BASEDIR}/data/fonts/gkai00mp.ttf" 10 | font_size 16 11 | 12 | long_text = "更可怕的是,同质化竞争对手可以按照URL中后面这个ID来遍历您的DB中的内容,写个小爬虫把你的页面上的关键信息顺次爬下来也不是什么难事,这样的话,你就非常被动了。写个小爬虫把你的页面上的关键信息顺次爬下来也不是什么难事写个小爬虫把你的页面上的关键信息顺次爬下来也不是什么难事写个小爬虫把你的页面上的关键信息顺次爬下来也不是什么难事写个小爬虫把你的页面上的关键信息顺次爬下来也不是什么难事写个小爬虫把你的页面上的关键信息顺次爬下来也不是什么难事写个小爬虫把你的页面上的关键信息顺次爬下来也不是什么难事写个小爬虫把你的页面上的关键信息顺次爬下来也不是什么难事写个小爬虫把你的页面上的关键信息顺次爬下来也不是什么难事写个小爬虫把你的页面上的关键信息顺次爬下来也不是什么难事写个小爬虫把你的页面上的关键信息顺次爬下来也不是什么难事写个小爬虫把你的页面上的关键信息顺次爬下来也不是什么难事" 13 | text long_text 14 | 15 | # be sure to restore space based wrapping when dealing with latin scripts 16 | long_text = "Text with some spaces " * 25 17 | text long_text 18 | end 19 | -------------------------------------------------------------------------------- /data/pdfs/version_1_6.pdf: -------------------------------------------------------------------------------- 1 | %PDF-1.6 2 | %???? 3 | 1 0 obj 4 | << /Creator (Prawn) 5 | /Producer (Prawn) 6 | >> 7 | endobj 8 | 2 0 obj 9 | << /Kids [5 0 R] 10 | /Type /Pages 11 | /Count 1 12 | >> 13 | endobj 14 | 3 0 obj 15 | << /Type /Catalog 16 | /Pages 2 0 R 17 | >> 18 | endobj 19 | 4 0 obj 20 | << /Length 195 21 | >> 22 | stream 23 | 0.000 0.000 0.000 rg 24 | 0.000 0.000 0.000 RG 25 | q 26 | 1.000 0.000 0.000 rg 27 | 136.000 286.000 m 28 | 236.000 336.000 l 29 | 336.000 286.000 l 30 | 336.000 186.000 l 31 | 236.000 136.000 l 32 | 136.000 186.000 l 33 | 136.000 286.000 l 34 | f 35 | Q 36 | 37 | endstream 38 | endobj 39 | 5 0 obj 40 | << /Contents 4 0 R 41 | /Type /Page 42 | /MediaBox [0 0 612.0 792.0] 43 | /Parent 2 0 R 44 | >> 45 | endobj 46 | xref 47 | 0 6 48 | 0000000000 65535 f 49 | 0000000015 00000 n 50 | 0000000071 00000 n 51 | 0000000128 00000 n 52 | 0000000177 00000 n 53 | 0000000423 00000 n 54 | trailer 55 | << /Info 1 0 R 56 | /Size 6 57 | /Root 3 0 R 58 | >> 59 | startxref 60 | 514 61 | %%EOF 62 | -------------------------------------------------------------------------------- /spec/extensions/mocha.rb: -------------------------------------------------------------------------------- 1 | # Allow speccing things when an expectation matcher runs. Similar to #with, but 2 | # always succeeds. 3 | # 4 | # @pdf.expects(:stroke_line).checking do |from, to| 5 | # @pdf.map_to_absolute(from).should == [0, 0] 6 | # end 7 | # 8 | # Note that the outer expectation does *not* fail only because the inner one 9 | # does; in the above example, the outer expectation would only fail if 10 | # stroke_line were not called. 11 | 12 | class ParameterChecker < Mocha::ParametersMatcher 13 | def initialize(&matching_block) 14 | @matching_block = matching_block 15 | @run_matching_block = false 16 | end 17 | 18 | def match?(actual_parameters = []) 19 | @matching_block.call(*actual_parameters) unless @run_matching_block 20 | @run_matching_block = true 21 | 22 | true # always succeed 23 | end 24 | end 25 | 26 | class Mocha::Expectation 27 | def checking(&block) 28 | @parameters_matcher = ParameterChecker.new(&block) 29 | self 30 | end 31 | end 32 | 33 | -------------------------------------------------------------------------------- /bugs/resolved/ttf_fails_in_transactions.rb: -------------------------------------------------------------------------------- 1 | # http://github.com/sandal/prawn/issues#issue/56 2 | # 3 | # As of f952055d03f9b21b78ec2844bd873cf62005d00a 4 | # Transactions fail when using TTF fonts. 5 | # 6 | # This is because we use an on_encode Proc that gets included in the 7 | # @current_page object, which breaks snapshots. We can surely write 8 | # around this to either split out the Proc into non-marshalled data 9 | # or set up some sort of callback that is indicated by something that 10 | # can be safely marshalled. 11 | # 12 | # But whoever tackles this patch should take care to ensure we 13 | # don't break TTF subsetting support, adding specs if necessary. 14 | # 15 | # Resolved in 36ef89c2bc21e504df623f61d918c5bfdc1fdab1. 16 | 17 | $LOAD_PATH << File.join(File.dirname(__FILE__), '..', '..','lib') 18 | require 'prawn/core' 19 | 20 | Prawn::Document.generate("err.pdf") do 21 | font "#{Prawn::BASEDIR}/data/fonts/DejaVuSans.ttf" 22 | text "Hi there" 23 | transaction { text "Nice, thank you" } 24 | end 25 | -------------------------------------------------------------------------------- /data/fonts/MustRead.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Core 14 AFM Files - ReadMe 7 | 8 | 9 | 10 | or 11 | 12 | 13 | 14 | 15 | 16 |
This file and the 14 PostScript(R) AFM files it accompanies may be used, copied, and distributed for any purpose and without charge, with or without modification, provided that all copyright notices are retained; that the AFM files are not distributed without this file; that all modifications to this file or any of the AFM files are prominently noted in the modified file(s); and that this paragraph is not modified. Adobe Systems has no responsibility or obligation to support the use of the AFM files. Col
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /examples/text/font_size.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # This example shows the many ways of setting font sizes in Prawn 4 | # 5 | require File.expand_path(File.join(File.dirname(__FILE__), 6 | %w[.. example_helper])) 7 | 8 | Prawn::Document.generate "font_size.pdf", :page_size => "A4" do 9 | 10 | # Explicit global changes 11 | font 'Helvetica' 12 | self.font_size = 16 13 | 14 | text 'Font at 16 point' 15 | 16 | # Transactional changes rolled back after block exit 17 | font_size 9 do 18 | text 'Font at 9 point' 19 | # single line changes, not persisted. 20 | text 'Font at manual override 20 point', :size => 20 21 | text 'Font at 9 point' 22 | end 23 | 24 | # Transactional changes rolled back after block exit on full fonts. 25 | font("Times-Roman", :style => :italic, :size => 12) do 26 | text "Font in times at 12" 27 | font_size(16) { text "Font in Times at 16" } 28 | end 29 | 30 | text 'Font at 16 point' 31 | 32 | font "Courier", :size => 40 33 | text "40 pt!" 34 | end 35 | -------------------------------------------------------------------------------- /lib/prawn/graphics/cap_style.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | # cap_style.rb : Implements stroke cap styling 4 | # 5 | # Contributed by Daniel Nelson. October, 2009 6 | # 7 | # This is free software. Please see the LICENSE and COPYING files for details. 8 | # 9 | module Prawn 10 | module Graphics 11 | module CapStyle 12 | 13 | CAP_STYLES = { :butt => 0, :round => 1, :projecting_square => 2 } 14 | 15 | # Sets the cap style for stroked lines and curves 16 | # 17 | # style is one of :butt, :round, or :projecting_square 18 | # 19 | # NOTE: If this method is never called, :butt will be used by default. 20 | # 21 | def cap_style(style=nil) 22 | return @cap_style || :butt if style.nil? 23 | 24 | @cap_style = style 25 | 26 | write_stroke_cap_style 27 | end 28 | 29 | alias_method :cap_style=, :cap_style 30 | 31 | private 32 | 33 | def write_stroke_cap_style 34 | add_content "#{CAP_STYLES[@cap_style]} J" 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /examples/m17n/sjis.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # FIXME: Introducing TTFunk into Prawn broke this example and a cooresponding 4 | # test. Ticket: #139 5 | # 6 | # Tests passing non utf-8 data into Prawns text function. Should 7 | # be transparently converted to utf-8 and rendered as usual. 8 | # 9 | # NOTE: only works on ruby1.9 compatible VMs, and requires the current 10 | # font to include japanese glyphs. On 1.8.x comaptible VMs, an exception 11 | # will be raised. 12 | 13 | require File.expand_path(File.join(File.dirname(__FILE__), 14 | %w[.. example_helper])) 15 | 16 | begin 17 | ruby_19 do 18 | datafile = File.join(File.dirname(__FILE__), "..", "..", "data", 19 | "shift_jis_text.txt") 20 | sjis_str = File.open(datafile, "r:shift_jis") { |f| f.gets } 21 | 22 | Prawn::Document.generate("sjis.pdf") do 23 | font "#{Prawn::BASEDIR}/data/fonts/gkai00mp.ttf" 24 | text sjis_str 25 | end 26 | end 27 | rescue 28 | puts "\n FIXME: SJIS Broken due to TTFunk integration." 29 | end 30 | -------------------------------------------------------------------------------- /lib/prawn/compatibility.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # 3 | # Why would we ever use Ruby 1.8.7 when we can backport with something 4 | # as simple as this? 5 | # 6 | class String #:nodoc: 7 | def first_line 8 | self.each_line { |line| return line } 9 | end 10 | unless "".respond_to?(:lines) 11 | alias_method :lines, :to_a 12 | end 13 | unless "".respond_to?(:each_char) 14 | def each_char #:nodoc: 15 | # copied from jcode 16 | if block_given? 17 | scan(/./m) { |x| yield x } 18 | else 19 | scan(/./m) 20 | end 21 | end 22 | end 23 | end 24 | 25 | unless File.respond_to?(:binread) 26 | def File.binread(file) #:nodoc: 27 | File.open(file,"rb") { |f| f.read } 28 | end 29 | end 30 | 31 | if RUBY_VERSION < "1.9" 32 | 33 | def ruby_18 #:nodoc: 34 | yield 35 | end 36 | 37 | def ruby_19 #:nodoc: 38 | false 39 | end 40 | 41 | else 42 | 43 | def ruby_18 #:nodoc: 44 | false 45 | end 46 | 47 | def ruby_19 #:nodoc: 48 | yield 49 | end 50 | 51 | end 52 | -------------------------------------------------------------------------------- /lib/prawn/graphics/join_style.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | # join_style.rb : Implements stroke join styling 4 | # 5 | # Contributed by Daniel Nelson. October, 2009 6 | # 7 | # This is free software. Please see the LICENSE and COPYING files for details. 8 | # 9 | module Prawn 10 | module Graphics 11 | module JoinStyle 12 | JOIN_STYLES = { :miter => 0, :round => 1, :bevel => 2 } 13 | 14 | # Sets the join style for stroked lines and curves 15 | # 16 | # style is one of :miter, :round, or :bevel 17 | # 18 | # NOTE: if this method is never called, :miter will be used for join style 19 | # throughout the document 20 | # 21 | def join_style(style=nil) 22 | return @join_style || :miter if style.nil? 23 | 24 | @join_style = style 25 | 26 | write_stroke_join_style 27 | end 28 | 29 | alias_method :join_style=, :join_style 30 | 31 | private 32 | 33 | def write_stroke_join_style 34 | add_content "#{JOIN_STYLES[@join_style]} j" 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /examples/graphics/stroke_cap_and_join.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Stroke dashing can be applied to any line or curve 4 | 5 | require File.expand_path(File.join(File.dirname(__FILE__), 6 | %w[.. example_helper])) 7 | 8 | Prawn::Document.generate("stroke_cap_and_join.pdf") do 9 | self.line_width = 25 10 | x0 = bounds.left + 100 11 | x1 = bounds.left + 200 12 | x2 = bounds.left + 300 13 | 14 | y = bounds.top - 125 15 | 16 | 3.times do |i| 17 | case i 18 | when 0 19 | self.join_style = :miter 20 | when 1 21 | self.join_style = :round 22 | when 2 23 | self.join_style = :bevel 24 | end 25 | stroke do 26 | move_to(x0, y) 27 | line_to(x1, y + 100) 28 | line_to(x2, y) 29 | end 30 | y -= 100 31 | end 32 | 33 | 34 | 3.times do |i| 35 | case i 36 | when 0 37 | self.cap_style = :butt 38 | when 1 39 | self.cap_style = :round 40 | when 2 41 | self.cap_style = :projecting_square 42 | end 43 | stroke_line([x0, y, x2, y]) 44 | y -= 30 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /bugs/resolved/layout/table_ignores_align_headers.rb: -------------------------------------------------------------------------------- 1 | # As of fadb65c303ff129d0b25a929d3b9d1f915b2f98d, 2 | # Prawn ignores :align_headers property in tables 3 | # when :border_style => :grid is present (Lighthouse issue #119). 4 | # 5 | # NOTES: 6 | # 7 | # * This issue can only be reproduced when :border_style => :grid is used 8 | # 9 | # Resolved as of 47297900dcf3f16c4765ca817f17c53fb0a5a079 10 | # I think a bad merge created issues in edge, and this code fixes previous 11 | # problems that are present in stable. 12 | # 13 | $DEBUG = true 14 | 15 | $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', '..', 'lib') 16 | require "rubygems" 17 | require "prawn" 18 | require "prawn/layout" 19 | 20 | Prawn::Document.generate("table_ignores_align_headers.pdf") do 21 | left = "Left justified" 22 | left2 = "left" 23 | center = "centered" 24 | table [[left, left], [left2, left2]], :headers => [center, center], 25 | :align => :left, 26 | :align_headers => :center, 27 | :border_style => :grid 28 | end 29 | -------------------------------------------------------------------------------- /examples/text/shaped_text_box.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Demonstrates extending Text::Box 4 | # 5 | require File.expand_path(File.join(File.dirname(__FILE__), 6 | %w[.. example_helper])) 7 | 8 | Prawn::Document.generate "shaped_text_box.pdf" do |pdf| 9 | module ShapedBox 10 | def available_width 11 | height + 25 12 | end 13 | end 14 | 15 | Prawn::Text::Box.extensions << ShapedBox 16 | pdf.stroke_rectangle([10, pdf.bounds.top - 10], 300, 300) 17 | pdf.text_box("A" * 500, 18 | :at => [10, pdf.bounds.top - 10], 19 | :width => 300, 20 | :height => 300, 21 | :align => :center) 22 | 23 | Prawn::Text::Formatted::Box.extensions << ShapedBox 24 | pdf.stroke_rectangle([10, pdf.bounds.top - 330], 300, 300) 25 | pdf.formatted_text_box([:text => "A" * 500, 26 | :color => "009900"], 27 | :at => [10, pdf.bounds.top - 330], 28 | :width => 300, 29 | :height => 300, 30 | :align => :center) 31 | end 32 | 33 | -------------------------------------------------------------------------------- /examples/bounding_box/russian_boxes.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # This example is mostly just for fun, and shows how nested bounding boxes 4 | # can simplify calculations. See the other files in examples/bounding_box 5 | # for more basic uses. 6 | 7 | require File.expand_path(File.join(File.dirname(__FILE__), 8 | %w[.. example_helper])) 9 | 10 | class Array 11 | def combine(arr) 12 | output = [] 13 | self.each do |i1| 14 | arr.each do |i2| 15 | output += [[i1,i2]] 16 | end 17 | end 18 | output 19 | end 20 | end 21 | 22 | def recurse_bounding_box(pdf, max_depth=5, depth=1) 23 | box = pdf.bounds 24 | width = (box.width-15)/2 25 | height = (box.height-15)/2 26 | left_top_corners = [5, box.right-width-5].combine [box.top-5, height+5] 27 | left_top_corners.each do |lt| 28 | pdf.bounding_box(lt, :width=>width, :height=>height) do 29 | pdf.stroke_bounds 30 | recurse_bounding_box(pdf, max_depth, depth+1) if depth [200,720], :size => 24, :kerning => true 10 | draw_text "To not kern?", :at => [200,690], :size => 24, :kerning => false 11 | 12 | move_down 100 13 | 14 | pad(30) do 15 | text "To kern and wrap. " * 5, :size => 24, :kerning => true 16 | end 17 | 18 | text "To not kern and wrap. " * 5, :size => 24, :kerning => false 19 | 20 | font "#{Prawn::BASEDIR}/data/fonts/DejaVuSans.ttf" 21 | 22 | draw_text "To kern?", :at => [200,660], :size => 24, :kerning => true 23 | draw_text "To not kern?", :at => [200,630], :size => 24, :kerning => false 24 | 25 | pad(30) do 26 | text "To kern and wrap. " * 5, :size => 24, :kerning => true 27 | end 28 | 29 | text "To not kern and wrap. " * 5, :size => 24, :kerning => false 30 | 31 | end 32 | -------------------------------------------------------------------------------- /bugs/resolved/layout/table_header_overrun.rb: -------------------------------------------------------------------------------- 1 | # Text was overflowing into following cells because of some issues with 2 | # floating point numbers in naive wrap. 3 | # 4 | # Resolved in: 9c357bc488d26e7bbc2e442606106106d349e232 5 | # 6 | $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', '..', 'lib') 7 | require "rubygems" 8 | require "prawn" 9 | require "prawn/layout" 10 | 11 | @prawn_document_options = { 12 | :page_layout => :landscape, 13 | :left_margin => 36, 14 | :right_margin => 36, 15 | :top_margin => 36, 16 | :bottom_margin => 36} 17 | 18 | Prawn::Document.generate("table_header_overrun.pdf", @prawn_document_options) do 19 | 20 | headers = [ "Customer", "Grand\nHijynx", "Kh", "Red\nCorvette", "Rushmore", "bPnr", "lGh", "retail\nPantaloons", "sRsm", "Total\nBoxes"] 21 | data = [[1,0,1,0,1,0,1,0,1,0], [0,1,0,1,0,1,0,1,0,1]] 22 | 23 | table(data, 24 | :headers => headers, 25 | :font_size => 16, 26 | :horizontal_padding => 5, 27 | :vertical_padding => 3, 28 | :border => 2, 29 | :position => :center) 30 | 31 | start_new_page 32 | 33 | table [['MyString']], :headers=>['Field1'] 34 | 35 | end 36 | -------------------------------------------------------------------------------- /examples/general/page_geometry.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # This demonstrates basic page layout and landscape options for Prawn 4 | # documents. The style used here is a bit out of date, see 5 | # multi_page_layout.rb for a more modern example. 6 | # 7 | require File.expand_path(File.join(File.dirname(__FILE__), 8 | %w[.. example_helper])) 9 | 10 | def pdf(*options) 11 | Prawn::Document.new(*options) 12 | end 13 | 14 | # defaults to portrait and US letter 15 | portrait_letter = pdf 16 | portrait_letter.render_file "portrait_letter.pdf" 17 | 18 | landscape_letter = pdf(:page_layout => :landscape) 19 | landscape_letter.render_file "landscape_letter.pdf" 20 | 21 | portrait_legal = pdf(:page_size => "LEGAL") 22 | portrait_legal.render_file "portrait_legal.pdf" 23 | 24 | landscape_legal = pdf(:page_size => "LEGAL", :page_layout => :landscape) 25 | landscape_legal.render_file "landscape_legal.pdf" 26 | 27 | portrait_a4 = pdf(:page_size => "A4") 28 | portrait_a4.render_file "portrait_a4.pdf" 29 | 30 | landscape_a4 = pdf(:page_size => "A4", :page_layout => :landscape) 31 | landscape_a4.render_file("landscape_a4.pdf") 32 | 33 | -------------------------------------------------------------------------------- /prawn.gemspec: -------------------------------------------------------------------------------- 1 | # Version numbering: http://wiki.github.com/sandal/prawn/development-roadmap 2 | PRAWN_VERSION = "0.11.1.pre" 3 | 4 | Gem::Specification.new do |spec| 5 | spec.name = "prawn" 6 | spec.version = PRAWN_VERSION 7 | spec.platform = Gem::Platform::RUBY 8 | spec.summary = "A fast and nimble PDF generator for Ruby" 9 | spec.files = Dir.glob("{examples,lib,spec,vendor,data}/**/**/*") + 10 | ["Rakefile", "prawn.gemspec"] 11 | spec.require_path = "lib" 12 | spec.required_ruby_version = '>= 1.8.7' 13 | spec.required_rubygems_version = ">= 1.3.6" 14 | 15 | spec.test_files = Dir[ "test/*_test.rb" ] 16 | spec.has_rdoc = true 17 | spec.extra_rdoc_files = %w{HACKING README LICENSE COPYING} 18 | spec.rdoc_options << '--title' << 'Prawn Documentation' << 19 | '--main' << 'README' << '-q' 20 | spec.author = "Gregory Brown" 21 | spec.email = " gregory.t.brown@gmail.com" 22 | spec.rubyforge_project = "prawn" 23 | spec.add_dependency('pdf-reader', '>=0.9.0') 24 | spec.homepage = "http://prawn.majesticseacreature.com" 25 | spec.description = < true) do 12 | 13 | repeat :all do 14 | draw_text "ALLLLLL", :at => bounds.top_left 15 | end 16 | 17 | repeat :odd do 18 | draw_text "ODD", :at => [0,0] 19 | end 20 | 21 | repeat :even do 22 | draw_text "EVEN", :at => [0,0] 23 | end 24 | 25 | repeat [1,2] do 26 | draw_text "[1,2]", :at => [100,0] 27 | end 28 | 29 | repeat 2..4 do 30 | draw_text "2..4", :at => [200,0] 31 | end 32 | 33 | repeat(lambda { |pg| pg % 3 == 0 }) do 34 | draw_text "Every third", :at => [250, 20] 35 | end 36 | 37 | repeat(:all, :dynamic => true) do 38 | draw_text page_number, :at => [500, 0] 39 | end 40 | 41 | 10.times do 42 | start_new_page 43 | draw_text "A wonderful page", :at => [400,400] 44 | end 45 | 46 | end 47 | 48 | 49 | -------------------------------------------------------------------------------- /examples/text/span.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Demonstration of Document#span, which is used for generating flowing 4 | # columns of text. 5 | # 6 | require File.expand_path(File.join(File.dirname(__FILE__), 7 | %w[.. example_helper])) 8 | 9 | Prawn::Document.generate("span.pdf") do 10 | 11 | # Spans will typically be used outside of bounding boxes as a way to build 12 | # single columns of flowing text that span across pages. 13 | # 14 | span(350, :position => :center) do 15 | text "Here's some centered text in a 350 point column. " * 100 16 | end 17 | 18 | # Spans are not really compatible with bounding boxes because they break 19 | # the nesting chain and also may position text outside of the bounding box 20 | # boundaries, but sometimes you may wish to use them anyway for convenience 21 | # Here's an example of how to do that dynamically. 22 | # 23 | bounding_box([50,300], :width => 400) do 24 | text "Here's some default bounding box text. " * 10 25 | span(bounds.width, 26 | :position => bounds.absolute_left - margin_box.absolute_left) do 27 | text "The rain in spain falls mainly on the plains. " * 300 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /examples/bounding_box/indentation.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # This example demonstrates the basic functionality of Prawn's bounding boxes. 4 | # Note that top level bounding boxes are positioned relative to the margin_box. 5 | # 6 | require File.expand_path(File.join(File.dirname(__FILE__), 7 | %w[.. example_helper])) 8 | 9 | Prawn::Document.generate("indentation.pdf") do 10 | 11 | text "No indentation" 12 | indent(20) do 13 | text "Some indentation" 14 | # Generates a box with a top-left of [100,600] and a top-right of [300,600] 15 | # The box automatically expands as the cursor moves down the page. Notice 16 | # that the final coordinates are outlined by a top and bottom line drawn 17 | # relatively using calculations from +bounds+. 18 | # 19 | bounding_box [100,600], :width => 200 do 20 | text "A little more indentation" 21 | indent(20) do 22 | text "And some more indentation" 23 | indent(20) do 24 | text "And some deeper indentation" 25 | end 26 | end 27 | end 28 | text "Some indentation" 29 | end 30 | indent(10) do 31 | text "A bit of indentation" 32 | end 33 | 34 | text "No indentation" 35 | end 36 | -------------------------------------------------------------------------------- /examples/graphics/image_flow.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Demonstrates automated flowing and positioning of images, as well as 4 | # aligining images along the x-axis via the :position argument. This is 5 | # useful when used in combination with flowing text, where the exact final 6 | # position of the image is not known ahead of time. 7 | # 8 | require File.expand_path(File.join(File.dirname(__FILE__), 9 | %w[.. example_helper])) 10 | 11 | Prawn::Document.generate("image-flow.pdf", :page_layout => :landscape) do 12 | self.font_size = 8 13 | stef = "#{Prawn::BASEDIR}/data/images/stef.jpg" 14 | 15 | text "Image at default position with no arguments" 16 | 17 | move_down 10 18 | 19 | image stef 20 | 21 | text "Centered image flowing" 22 | 23 | image stef, :position => :center 24 | 25 | text "Right aligned image flowing" 26 | 27 | image stef, :position => :right 28 | 29 | text "Explicitly left aligned image flowing" 30 | 31 | move_down 10 32 | 33 | image stef, :position => :left 34 | 35 | text "Flowing image at x=50" 36 | 37 | image stef, :position => 50 38 | end 39 | -------------------------------------------------------------------------------- /examples/general/context_sensitive_headers.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.join(File.dirname(__FILE__), 2 | %w[.. example_helper])) 3 | 4 | # Ex. Generate a roster of meeting attendees given a set of meetings. 5 | # Attendees for a meeting may overflow to accross page boundaries but 6 | # each meeting starts on a separate page. Each page for any given 7 | # meeting will have the heading for that meeting. 8 | 9 | #dummying up some meetings 10 | meetings = [] 11 | 5.times do |i| 12 | meetings << "Meeting number #{i}" 13 | end 14 | 15 | Prawn::Document.generate('context_sensitive_headers.pdf', :margin => [100, 100], :skip_page_creation => true) do 16 | meetings.each_with_index do |meeting,i| 17 | 18 | create_stamp(meeting.to_s) do 19 | canvas do 20 | text_box("header for #{meeting}", 21 | :at => [bounds.left + 50, bounds.top - 20], 22 | :height => 50, 23 | :width => margin_box.width) 24 | end 25 | end 26 | on_page_create { stamp(meeting.to_s) } 27 | 28 | start_new_page 29 | 30 | #simulate some meetings with content over multiple pages 31 | (15 + 20*i).times do |i| 32 | text "#{meeting} attendee #{i}" 33 | end 34 | 35 | end 36 | 37 | end 38 | 39 | -------------------------------------------------------------------------------- /examples/graphics/stroke_dash.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Stroke dashing can be applied to any line or curve 4 | 5 | require File.expand_path(File.join(File.dirname(__FILE__), 6 | %w[.. example_helper])) 7 | 8 | Prawn::Document.generate("stroke_dash.pdf") do 9 | self.line_width = 1 10 | base_y = bounds.top 11 | 12 | 100.times do |i| 13 | length = i / 4 + 1 14 | # space between dashes same length as dash 15 | space = length 16 | # start with dash 17 | phase = 0 18 | case i % 4 19 | when 0 20 | base_y -= 10 21 | when 1 22 | # start with space between dashes 23 | phase = length 24 | when 2 25 | base_y -= 10 26 | # space between dashes half as long as dash 27 | space = length * 0.5 28 | when 3 29 | # space between dashes half as long as dash 30 | space = length * 0.5 31 | # start with space between dashes 32 | phase = length 33 | end 34 | dash(length, :space => space, :phase => phase) 35 | points = [bounds.left, base_y - 2 * i, bounds.right, base_y - 2 * i] 36 | stroke_line(points) 37 | end 38 | i = 100 39 | base_y -= 10 40 | undash 41 | points = [bounds.left, base_y - 2 * i, bounds.right, base_y - 2 * i] 42 | stroke_line(points) 43 | end 44 | -------------------------------------------------------------------------------- /examples/general/stamp.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Demonstrate use of stamps 4 | # 5 | require File.expand_path(File.join(File.dirname(__FILE__), 6 | %w[.. example_helper])) 7 | 8 | Prawn::Document.generate("stamp.pdf", :skip_page_creation => true) do 9 | 10 | create_stamp("odd_page_template") do 11 | draw_text "This is the odd page template", 12 | :at => [0, bounds.top - font.height] 13 | draw_text "This is also in the odd page template", :at => [0, 0] 14 | end 15 | 16 | create_stamp("even_page_template") do 17 | draw_text "This is the even page template", 18 | :at => [0, bounds.top - font.height] 19 | draw_text "This is also in the even page template", :at => [0, 0] 20 | end 21 | 22 | start_new_page 23 | stamp("odd_page_template") 24 | 25 | create_stamp("MyStamp") do 26 | fill_color("ff0000") 27 | fill_circle_at([0, 0], :radius => 20) 28 | fill_color("000000") 29 | fill_circle_at([10, 10], :radius => 20) 30 | end 31 | 32 | 10.times do |i| 33 | 10.times do |j| 34 | stamp_at("MyStamp", [100 + j * 50, bounds.top - 100 - i * 50]) 35 | end 36 | end 37 | 38 | 4.times do |i| 39 | start_new_page 40 | stamp("#{i % 2 == 0 ? 'even' : 'odd'}_page_template") 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/prawn/font/dfont.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # font.rb : The Prawn font class 4 | # 5 | # Copyright November 2008, Jamis Buck. All Rights Reserved. 6 | # 7 | # This is free software. Please see the LICENSE and COPYING files for details. 8 | # 9 | require 'prawn/font/ttf' 10 | 11 | module Prawn 12 | class Font 13 | class DFont < TTF 14 | 15 | # Returns a list of the names of all named fonts in the given dfont file. 16 | # Note that fonts are not required to be named in a dfont file, so the 17 | # list may be empty even if the file does contain fonts. Also, note that 18 | # the list is returned in no particular order, so the first font in the 19 | # list is not necessarily the font at index 0 in the file. 20 | # 21 | def self.named_fonts(file) 22 | TTFunk::ResourceFile.open(file) do |f| 23 | return f.resources_for("sfnt") 24 | end 25 | end 26 | 27 | # Returns the number of fonts contained in the dfont file. 28 | # 29 | def self.font_count(file) 30 | TTFunk::ResourceFile.open(file) do |f| 31 | return f.map["sfnt"][:list].length 32 | end 33 | end 34 | 35 | private 36 | 37 | def read_ttf_file 38 | TTFunk::File.from_dfont(@name, @options[:font] || 0) 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/span_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper") 4 | 5 | describe "drawing span" do 6 | 7 | def setup 8 | Prawn.debug = false 9 | create_pdf 10 | end 11 | 12 | def teardown 13 | Prawn.debug = true 14 | end 15 | 16 | it "should only accept :position as option in debug mode" do 17 | Prawn.debug = true 18 | lambda { @pdf.span(350, {:x => 3}) {} }.should.raise(Prawn::Errors::UnknownOption) 19 | end 20 | 21 | it "should have raise an error if :position is invalid" do 22 | lambda { @pdf.span(350, :position => :x) {} }.should.raise(ArgumentError) 23 | end 24 | 25 | it "should restore the margin box when bounding box exits" do 26 | margin_box = @pdf.bounds 27 | 28 | @pdf.span(350, :position => :center) do 29 | @pdf.text "Here's some centered text in a 350 point column. " * 100 30 | end 31 | 32 | @pdf.bounds.should == margin_box 33 | 34 | end 35 | 36 | it "should do create a margin box" do 37 | y = @pdf.y 38 | margin_box = @pdf.span(350, :position => :center) do 39 | @pdf.text "Here's some centered text in a 350 point column. " * 100 40 | end 41 | 42 | margin_box.top.should == 792.0 43 | margin_box.bottom.should == 0 44 | 45 | end 46 | 47 | end 48 | 49 | 50 | -------------------------------------------------------------------------------- /examples/text/hyphenation.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | 4 | require File.expand_path(File.join(File.dirname(__FILE__), 5 | %w[.. example_helper])) 6 | 7 | Prawn::Document.generate("hyphenation.pdf") do 8 | def get_string(i) 9 | case i 10 | when 0 11 | text = "this­is­soft­hyphenated­text­" * 30 12 | when 1 13 | text = "this-is-hard-hyphenated-text-" * 30 14 | when 2 15 | text = "this­-is­-soft­-hard­-hyphenated­-text­-" * 30 16 | end 17 | end 18 | 19 | options = { 20 | :width => bounds.width * 0.3, 21 | :height => bounds.width * 0.3, 22 | :overflow => :ellipses, 23 | :at => [0, 0], 24 | :align => :left, 25 | :document => self 26 | } 27 | 28 | stroke_color("555555") 29 | 3.times do |i| 30 | options[:at][0] = (bounds.width - options[:width]) * 0.5 * i 31 | options[:at][1] = bounds.height * 0.5 + options[:height] + 50 32 | box = Prawn::Text::Box.new(get_string(i), options) 33 | box.render 34 | end 35 | 36 | 37 | font("#{Prawn::BASEDIR}/data/fonts/DejaVuSans.ttf") 38 | 39 | stroke_color("555555") 40 | 3.times do |i| 41 | options[:at][0] = (bounds.width - options[:width]) * 0.5 * i 42 | options[:at][1] = bounds.height * 0.5 - 50 43 | box = Prawn::Text::Box.new(get_string(i), options) 44 | box.render 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /examples/grid/multi_boxes.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require File.expand_path(File.join(File.dirname(__FILE__), 4 | %w[.. example_helper])) 5 | 6 | Prawn::Document.generate('multi_boxes.pdf') do |p| 7 | p.define_grid(:columns => 5, :rows => 8, :gutter => 10) 8 | 9 | p.grid.rows.times do |i| 10 | p.grid.columns.times do |j| 11 | p.grid(i,j).bounding_box do 12 | p.text p.grid(i,j).name 13 | p.stroke_color = "cccccc" 14 | p.stroke do 15 | p.rectangle(p.bounds.top_left, p.bounds.width, p.bounds.height) 16 | end 17 | end 18 | end 19 | end 20 | 21 | g = p.grid([0,0], [1,1]) 22 | g.bounding_box do 23 | p.move_down 12 24 | p.text g.name 25 | p.stroke_color = "333333" 26 | p.stroke do 27 | p.rectangle(p.bounds.top_left, p.bounds.width, p.bounds.height) 28 | end 29 | end 30 | 31 | g = p.grid([3,0], [3,3]) 32 | g.bounding_box do 33 | p.move_down 12 34 | p.text g.name 35 | p.stroke_color = "333333" 36 | p.stroke do 37 | p.rectangle(p.bounds.top_left, p.bounds.width, p.bounds.height) 38 | end 39 | end 40 | 41 | g = p.grid([4,0], [5,1]) 42 | g.bounding_box do 43 | p.move_down 12 44 | p.text g.name 45 | p.stroke_color = "333333" 46 | p.stroke do 47 | p.rectangle(p.bounds.top_left, p.bounds.width, p.bounds.height) 48 | end 49 | end 50 | 51 | end 52 | 53 | -------------------------------------------------------------------------------- /lib/prawn/document/graphics_state.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # graphics_state.rb: Implements graphics state saving and restoring 4 | # 5 | # Copyright January 2010, Michael Witrant. All Rights Reserved. 6 | # 7 | # This is free software. Please see the LICENSE and COPYING files for details. 8 | # 9 | 10 | module Prawn 11 | class Document 12 | module GraphicsState 13 | 14 | # Pushes the current graphics state on to the graphics state stack so we 15 | # can restore it when finished with a change we want to isolate (such as 16 | # modifying the transformation matrix). Used in pairs with 17 | # restore_graphics_state or passed a block 18 | # 19 | # Example without a block: 20 | # 21 | # save_graphics_state 22 | # rotate 30 23 | # text "rotated text" 24 | # restore_graphics_state 25 | # 26 | # Example with a block: 27 | # 28 | # save_graphics_state do 29 | # rotate 30 30 | # text "rotated text" 31 | # end 32 | # 33 | def save_graphics_state 34 | add_content "q" 35 | if block_given? 36 | yield 37 | restore_graphics_state 38 | end 39 | end 40 | 41 | # Pops the last saved graphics state off the graphics state stack and 42 | # restores the state to those values 43 | def restore_graphics_state 44 | add_content "Q" 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /examples/general/margin.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # This demonstrates the Prawn options for document and page margin, similar to CSS shorthand. 4 | # 5 | require File.expand_path(File.join(File.dirname(__FILE__), 6 | %w[.. example_helper])) 7 | 8 | LOREM = ("Lorem ipsum dolor sit amet, consectetur adipisicing elit, "+ 9 | "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "+ 10 | "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris "+ 11 | "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in "+ 12 | "reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla "+ 13 | "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in " + 14 | "culpa qui officia deserunt mollit anim id est laborum. ") * 20 15 | 16 | Prawn::Document.generate("margin.pdf", :margin => 100) do |pdf| 17 | 18 | pdf.text "100 on all sides", :style => :bold 19 | pdf.text LOREM 20 | 21 | pdf.start_new_page(:margin => 100, :left_margin => 0) 22 | pdf.text "100 on all sides but 0 on the left", :style => :bold 23 | pdf.text LOREM 24 | 25 | pdf.start_new_page(:margin => [100, 0]) 26 | pdf.text "100 top and bottom, 0 left and right.", :style => :bold 27 | pdf.text LOREM 28 | 29 | pdf.start_new_page(:margin => [100, 0, 50]) 30 | pdf.text "100 top, 0 left and right, 50 bottom.", :style => :bold 31 | pdf.text LOREM 32 | 33 | pdf.start_new_page(:margin => [0, 50, 100, 150]) 34 | pdf.text "0 top, 50 right, 100 bottom, 150 left.", :style => :bold 35 | pdf.text LOREM 36 | 37 | end 38 | -------------------------------------------------------------------------------- /lib/prawn/table/cell/subtable.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | # subtable.rb: Yo dawg. 4 | # 5 | # Copyright January 2010, Brad Ediger. All Rights Reserved. 6 | # 7 | # This is free software. Please see the LICENSE and COPYING files for details. 8 | module Prawn 9 | class Table 10 | class Cell 11 | 12 | # A Cell that contains another table. 13 | # 14 | class Subtable < Cell 15 | 16 | attr_reader :subtable 17 | 18 | def initialize(pdf, point, options={}) 19 | super 20 | @subtable = options[:content] 21 | 22 | # Subtable padding defaults to zero 23 | @padding = [0, 0, 0, 0] 24 | end 25 | 26 | # Sets the text color of the entire subtable. 27 | # 28 | def text_color=(color) 29 | @subtable.cells.text_color = color 30 | end 31 | 32 | # Proxied to subtable. 33 | # 34 | def natural_content_width 35 | @subtable.cells.width 36 | end 37 | 38 | # Proxied to subtable. 39 | # 40 | def min_width 41 | @subtable.cells.min_width 42 | end 43 | 44 | # Proxied to subtable. 45 | # 46 | def max_width 47 | @subtable.cells.max_width 48 | end 49 | 50 | # Proxied to subtable. 51 | # 52 | def natural_content_height 53 | @subtable.cells.height 54 | end 55 | 56 | # Draws the subtable. 57 | # 58 | def draw_content 59 | @subtable.draw 60 | end 61 | 62 | end 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /www/prawn.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | body { 7 | font: Verdana, "Lucida Grande", "Lucida Sans", sans-serif; 8 | background-color: #000000; 9 | color: #ffffff; 10 | margin-bottom: 1em; 11 | } 12 | 13 | h2, h3, h4, h5, h6, p, pre, blockquote, form, fieldset, table, ul { 14 | margin: 1em 0; 15 | } 16 | h2 { 17 | font-size: 1.75em; 18 | } 19 | 20 | h2, h3 { 21 | color: #00bb77; 22 | } 23 | 24 | h1 { 25 | color: #bb5566; 26 | font-size: 1.1em; 27 | margin-top: 0.5em; 28 | } 29 | 30 | a { 31 | color: #ffffff; 32 | text-decoration: none; 33 | outline: none; 34 | } 35 | 36 | a:visited { 37 | color: #ffffff; 38 | } 39 | 40 | a:hover, a:active { 41 | color: #2299ff; 42 | } 43 | 44 | a img { 45 | border:none; 46 | } 47 | 48 | #contents { 49 | background-color: #101010; 50 | width: 740px; 51 | margin: 0 auto; 52 | border: 1px solid #fff; 53 | margin-top: 1em; 54 | padding: 1em 1em 1em 3em; 55 | } 56 | 57 | #sidebar { 58 | width: 180px; 59 | float: right; 60 | padding: 2em 2em 2em 0; 61 | font-size: 1.2em; 62 | } 63 | 64 | 65 | #screenshots { 66 | margin-top: 2em; 67 | float: right; 68 | } 69 | 70 | ul { 71 | text-align: center; 72 | float: right; 73 | } 74 | 75 | li { 76 | display: inline; 77 | border-top: 1px solid #fff; 78 | border-right: 1px solid #fff; 79 | border-bottom: 1px solid #666; 80 | border-left: 1px solid #666; 81 | margin-right: 5px; 82 | padding-left: 10px; 83 | padding-right: 10px; 84 | color: #fff; 85 | font-weight: bold; 86 | } 87 | -------------------------------------------------------------------------------- /HACKING: -------------------------------------------------------------------------------- 1 | = Hacking on Prawn 2 | 3 | While we hope to have more extensive documentation for contributors in time, for 4 | now, here is the bare minimum to get you up and running 5 | 6 | == NOTES 7 | 8 | Install test-spec, pdf-reader, and mocha from RubyGems 9 | 10 | Be sure to load in the necessary git submodules as well: 11 | 12 | git submodule init 13 | git submodule update 14 | 15 | If you are running on Ruby 1.9, you will need Test::Unit 1.2.3: 16 | 17 | gem install test-unit -v 1.2.3 18 | 19 | == Patch process 20 | 21 | 1. File a ticket in the bug tracker describing a defect or feature 22 | 23 | http://github.com/sandal/prawn/issues 24 | 25 | 2. Fork us on Github, make your changes. Bug fixes or tiny enhancements can 26 | be done on your master branch, everything else should be done on its own topic 27 | branch. 28 | 29 | 3. Post a comment to the ticket telling us where your fork is, and what 30 | patches we should be looking at. 31 | 32 | If you are working on a ticket that has already been created, skip step 1. 33 | 34 | All feature enhancements should come with an example in the examples/ dir, 35 | and preferably, some specs. 36 | 37 | All bug reports should have a reproducible example in bugs/ and preferably, 38 | some specs. 39 | 40 | == Support 41 | 42 | Find us in #prawn on irc.freenode.net 43 | - Gregory Brown 44 | - James Healy 45 | - Daniel Nelson 46 | - Brad Ediger 47 | - Jonathan Greenberg 48 | 49 | Otherwise, use the mailing list: 50 | http://groups.google.com/group/prawn-ruby 51 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'rake' 3 | require 'rake/testtask' 4 | require "rake/rdoctask" 5 | require "rake/gempackagetask" 6 | 7 | task :default => [:test] 8 | 9 | desc "Run all tests, test-spec, mocha, and pdf-reader required" 10 | Rake::TestTask.new do |test| 11 | # test.ruby_opts << "-w" # .should == true triggers a lot of warnings 12 | test.libs << "spec" 13 | test.test_files = Dir[ "spec/*_spec.rb" ] 14 | test.verbose = true 15 | end 16 | 17 | desc "Show library's code statistics" 18 | task :stats do 19 | require 'code_statistics' 20 | CodeStatistics::TEST_TYPES << "Specs" 21 | CodeStatistics.new( ["Prawn", "lib"], 22 | ["Specs", "spec"] ).to_s 23 | end 24 | 25 | desc "genrates documentation" 26 | Rake::RDocTask.new do |rdoc| 27 | rdoc.rdoc_files.include( "README", 28 | "COPYING", 29 | "LICENSE", 30 | "HACKING", "lib/" ) 31 | rdoc.main = "README" 32 | rdoc.rdoc_dir = "doc/html" 33 | rdoc.title = "Prawn Documentation" 34 | end 35 | 36 | desc "run all examples, and then diff them against reference PDFs" 37 | task :examples do 38 | mkdir_p "output" 39 | examples = Dir["examples/**/*.rb"] 40 | t = Time.now 41 | puts "Running Examples" 42 | examples.each { |file| `ruby -Ilib #{file}` } 43 | puts "Ran in #{Time.now - t} s" 44 | `mv *.pdf output` 45 | end 46 | 47 | spec = Gem::Specification.load "prawn.gemspec" 48 | Rake::GemPackageTask.new(spec) do |pkg| 49 | pkg.need_zip = true 50 | pkg.need_tar = true 51 | end 52 | 53 | -------------------------------------------------------------------------------- /lib/prawn/measurements.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # measurements.rb: Conversions from other measurements to PDF points 3 | # 4 | # Copyright December 2008, Florian Witteler. All Rights Reserved. 5 | # 6 | module Prawn 7 | module Measurements 8 | 9 | # ============================================================================ 10 | #metric conversions 11 | def cm2mm(cm) 12 | return cm*10 13 | end 14 | 15 | def dm2mm(dm) 16 | return dm*100 17 | end 18 | 19 | def m2mm(m) 20 | return m*1000 21 | end 22 | 23 | # ============================================================================ 24 | # imperial conversions 25 | # from http://en.wikipedia.org/wiki/Imperial_units 26 | 27 | def ft2in(ft) 28 | return ft * 12 29 | end 30 | 31 | def yd2in(yd) 32 | return yd*36 33 | end 34 | 35 | 36 | # ============================================================================ 37 | # PostscriptPoint-converisons 38 | 39 | def in2pt(inch) 40 | return inch * 72 41 | end 42 | 43 | def ft2pt(ft) 44 | return in2pt(ft2in(ft)) 45 | end 46 | 47 | def yd2pt(yd) 48 | return in2pt(yd2in(yd)) 49 | end 50 | 51 | def mm2pt(mm) 52 | return mm*(72 / 25.4) 53 | end 54 | 55 | def cm2pt(cm) 56 | return mm2pt(cm2mm(cm)) 57 | end 58 | 59 | def dm2pt(dm) 60 | return mm2pt(dm2mm(dm)) 61 | end 62 | 63 | def m2pt(m) 64 | return mm2pt(m2mm(m)) 65 | end 66 | 67 | def pt2mm(pt) 68 | return pt * 1 / mm2pt(1)# (25.4 / 72) 69 | end 70 | end 71 | end -------------------------------------------------------------------------------- /lib/prawn/security/arcfour.rb: -------------------------------------------------------------------------------- 1 | # Implementation of the "ARCFOUR" algorithm ("alleged RC4 (tm)"). Implemented 2 | # as described at: 3 | # http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt 4 | # 5 | # "RC4" is a trademark of RSA Data Security, Inc. 6 | # 7 | # Copyright August 2009, Brad Ediger. All Rights Reserved. 8 | # 9 | # This is free software. Please see the LICENSE and COPYING files for details. 10 | 11 | class Arcfour 12 | def initialize(key) 13 | # Convert string key to Array of integers 14 | key = key.unpack('c*') if key.is_a?(String) 15 | 16 | # 1. Allocate an 256 element array of 8 bit bytes to be used as an S-box 17 | # 2. Initialize the S-box. Fill each entry first with it's index 18 | @sbox = (0..255).to_a 19 | 20 | # 3. Fill another array of the same size (256) with the key, repeating 21 | # bytes as necessary. 22 | s2 = [] 23 | while s2.length < 256 24 | s2 += key 25 | end 26 | s2 = s2[0, 256] 27 | 28 | # 4. Set j to zero and initialize the S-box 29 | j = 0 30 | (0..255).each do |i| 31 | j = (j + @sbox[i] + s2[i]) % 256 32 | @sbox[i], @sbox[j] = @sbox[j], @sbox[i] 33 | end 34 | 35 | @i = @j = 0 36 | end 37 | 38 | def encrypt(string) 39 | string.unpack('c*').map{|byte| byte ^ key_byte}.pack('c*') 40 | end 41 | 42 | private 43 | 44 | # Produces the next byte of key material in the stream (3.2 Stream Generation) 45 | def key_byte 46 | @i = (@i + 1) % 256 47 | @j = (@j + @sbox[@i]) % 256 48 | @sbox[@i], @sbox[@j] = @sbox[@j], @sbox[@i] 49 | @sbox[(@sbox[@i] + @sbox[@j]) % 256] 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /examples/m17n/win_ansi_charset.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Prints a list of all of the glyphs that can be rendered by Adobe's built 4 | # in fonts, along with their character widths and WinAnsi codes. Be sure 5 | # to pass these glyphs as UTF-8, and Prawn will transcode them for you. 6 | # 7 | require File.expand_path(File.join(File.dirname(__FILE__), 8 | %w[.. example_helper])) 9 | 10 | FONT_SIZE = 9.5 11 | 12 | Prawn::Document.generate("win-ansi.pdf") do 13 | @skip_encoding = true 14 | 15 | x = 0 16 | y = bounds.top 17 | 18 | fields = [[20, :right], [8, :left], [12, :center], [30, :right], [8, :left], [0, :left]] 19 | 20 | font "Helvetica", :size => FONT_SIZE 21 | 22 | Prawn::Encoding::WinAnsi::CHARACTERS.each_with_index do |name, index| 23 | next if name == ".notdef" 24 | y -= FONT_SIZE 25 | 26 | if y < FONT_SIZE 27 | y = bounds.top - FONT_SIZE 28 | x += 170 29 | end 30 | 31 | code = "%d." % index 32 | char = index.chr 33 | 34 | width = 1000 * width_of(char, :size => FONT_SIZE) / FONT_SIZE 35 | size = "%d" % width 36 | 37 | data = [code, nil, char, size, nil, name] 38 | dx = x 39 | fields.zip(data).each do |(total_width, align), field| 40 | if field 41 | width = width_of(field, :size => FONT_SIZE) 42 | 43 | case align 44 | when :left then offset = 0 45 | when :right then offset = total_width - width 46 | when :center then offset = (total_width - width)/2 47 | end 48 | 49 | draw_text(field, :at => [dx + offset, y]) 50 | end 51 | 52 | dx += total_width 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /examples/text/dfont.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require File.expand_path(File.join(File.dirname(__FILE__), 4 | %w[.. example_helper])) 5 | 6 | DFONT_FILE = "#{Prawn::BASEDIR}/data/fonts/Action Man.dfont" 7 | puts "There are #{Prawn::Font::DFont.font_count(DFONT_FILE)} fonts in #{DFONT_FILE}:" 8 | Prawn::Font::DFont.named_fonts(DFONT_FILE).each do |name| 9 | puts "* #{name}" 10 | end 11 | 12 | puts 13 | puts "generating sample document in 'dfont.pdf'..." 14 | 15 | Prawn::Document.generate "dfont.pdf" do 16 | fill_color "0000ff" 17 | 18 | font DFONT_FILE, :font => "ActionMan-Bold", :size => 24 19 | text "Introducing Action Man!" 20 | 21 | move_text_position 24 22 | 23 | font_families["Action Man"] = { 24 | :normal => { :file => DFONT_FILE, :font => "ActionMan" }, 25 | :bold => { :file => DFONT_FILE, :font => "ActionMan-Bold" }, 26 | :italic => { :file => DFONT_FILE, :font => "ActionMan-Italic" }, 27 | :bold_italic => { :file => DFONT_FILE, :font => "ActionMan-BoldItalic" } 28 | } 29 | 30 | font "Action Man", :size => 16 31 | text "Action Man is feeling normal here." 32 | 33 | move_text_position 16 34 | 35 | font "Action Man", :style => :bold, :size => 16 36 | text "Action Man is feeling bold here!" 37 | 38 | move_text_position 16 39 | 40 | font "Action Man", :style => :italic, :size => 16 41 | text "Here, we see Action Man feeling italicized. Slick!" 42 | 43 | move_text_position 16 44 | 45 | font "Action Man", :style => :bold_italic, :size => 16 46 | text "Lastly, we observe Mr. Action Man being bold AND italicized. Excellent!" 47 | end 48 | 49 | puts "done" 50 | -------------------------------------------------------------------------------- /examples/bounding_box/bounding_boxes.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # This example demonstrates the basic functionality of Prawn's bounding boxes. 4 | # Note that top level bounding boxes are positioned relative to the margin_box. 5 | # 6 | require File.expand_path(File.join(File.dirname(__FILE__), 7 | %w[.. example_helper])) 8 | 9 | Prawn::Document.generate("bounding_boxes.pdf") do 10 | 11 | # Generates a box with a top-left of [100,600] and a top-right of [300,600] 12 | # The box automatically expands as the cursor moves down the page. Notice 13 | # that the final coordinates are outlined by a top and bottom line drawn 14 | # relatively using calculations from +bounds+. 15 | # 16 | bounding_box [100,600], :width => 200 do 17 | move_down 10 18 | text "The rain in spain falls mainly on the plains " * 5 19 | move_down 20 20 | stroke do 21 | line bounds.top_left, bounds.top_right 22 | line bounds.bottom_left, bounds.bottom_right 23 | end 24 | end 25 | 26 | # Generates a bounding box from [100, cursor], [300, cursor - 200], 27 | # where cursor is the current y position. 28 | # 29 | bounding_box [100,cursor], :width => 200, :height => 200 do 30 | stroke do 31 | circle_at [100,100], :radius => 100 32 | line bounds.top_left, bounds.bottom_right 33 | line bounds.top_right, bounds.bottom_left 34 | end 35 | 36 | # Generates a nested bonding box and strokes its boundaries. Note that 37 | # this box is anchored relative to its parent bounding box, not the 38 | # margin_box 39 | bounding_box [50,150], :width => 100, :height => 100 do 40 | stroke_bounds 41 | end 42 | end 43 | 44 | end 45 | -------------------------------------------------------------------------------- /examples/table/bill.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | require File.expand_path(File.join(File.dirname(__FILE__), 4 | %w[.. example_helper])) 5 | 6 | Prawn::Document.generate("bill.pdf") do 7 | 8 | self.font_size = 9 9 | 10 | Widths = [50, 90, 170, 90, 90, 50] 11 | Headers = ["Date", "Patient Name", "Description", "Charges / Payments", 12 | "Patient Portion Due", "Balance"] 13 | 14 | head = make_table([Headers], :column_widths => Widths) 15 | 16 | data = [] 17 | 18 | def row(date, pt, charges, portion_due, balance) 19 | rows = charges.map { |c| ["", "", c[0], c[1], "", ""] } 20 | 21 | # Date and Patient Name go on the first line. 22 | rows[0][0] = date 23 | rows[0][1] = pt 24 | 25 | # Due and Balance go on the last line. 26 | rows[-1][4] = portion_due 27 | rows[-1][5] = balance 28 | 29 | # Return a Prawn::Table object to be used as a subtable. 30 | make_table(rows) do |t| 31 | t.column_widths = Widths 32 | t.cells.style :borders => [:left, :right], :padding => 2 33 | t.columns(4..5).align = :right 34 | end 35 | 36 | end 37 | 38 | data << row("1/1/2010", "", [["Balance Forward", ""]], "0.00", "0.00") 39 | 50.times do 40 | data << row("1/1/2010", "John", [["Foo", "Bar"], 41 | ["Foo", "Bar"]], "5.00", "0.00") 42 | end 43 | 44 | 45 | # Wrap head and each data element in an Array -- the outer table has only one 46 | # column. 47 | table([[head], *(data.map{|d| [d]})], :header => true, 48 | :row_colors => %w[cccccc ffffff]) do 49 | 50 | row(0).style :background_color => '000000', :text_color => 'ffffff' 51 | cells.style :borders => [] 52 | end 53 | 54 | end 55 | -------------------------------------------------------------------------------- /spec/text_rendering_mode_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper") 4 | 5 | describe "#text_rendering_mode" do 6 | it "should draw the text rendering mode to the document" do 7 | create_pdf 8 | @pdf.text_rendering_mode(:stroke) do 9 | @pdf.text("hello world") 10 | end 11 | contents = PDF::Inspector::Text.analyze(@pdf.render) 12 | contents.text_rendering_mode.first.should == 1 13 | end 14 | it "should not draw the text rendering mode to the document" + 15 | " when the new mode matches the old" do 16 | create_pdf 17 | @pdf.text_rendering_mode(:fill) do 18 | @pdf.text("hello world") 19 | end 20 | contents = PDF::Inspector::Text.analyze(@pdf.render) 21 | contents.text_rendering_mode.should.be.empty 22 | end 23 | it "should restore character spacing to 0" do 24 | create_pdf 25 | @pdf.text_rendering_mode(:stroke) do 26 | @pdf.text("hello world") 27 | end 28 | contents = PDF::Inspector::Text.analyze(@pdf.render) 29 | contents.text_rendering_mode.should == [1,0] 30 | end 31 | it "should function as an accessor when no parameter given" do 32 | create_pdf 33 | @pdf.text_rendering_mode(:fill_stroke) do 34 | @pdf.text("hello world") 35 | @pdf.text_rendering_mode.should == :fill_stroke 36 | end 37 | @pdf.text_rendering_mode.should == :fill 38 | end 39 | it "should raise an exception when passed an invalid mode" do 40 | create_pdf 41 | lambda { @pdf.text_rendering_mode(-1) }.should.raise(ArgumentError) 42 | lambda { @pdf.text_rendering_mode(8) }.should.raise(ArgumentError) 43 | lambda { @pdf.text_rendering_mode(:flil) }.should.raise(ArgumentError) 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /examples/bounding_box/stretched_nesting.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # This example demonstrates how nested bounding boxes work when the outer box is 4 | # stretchy and includes several inner boxes of different sizes. 5 | 6 | require File.expand_path(File.join(File.dirname(__FILE__), 7 | %w[.. example_helper])) 8 | 9 | Prawn::Document.generate("stretched_nesting.pdf", :page_layout => :landscape) do 10 | 11 | def stroke_dashed_bounds 12 | dash(1) 13 | stroke_bounds 14 | undash 15 | end 16 | 17 | bounding_box [100,400], :width => 500 do 18 | 19 | bounding_box [0, bounds.top], :width => 200, :height => 100 do 20 | stroke_bounds 21 | end 22 | 23 | bounding_box [200, bounds.top], :width => 150 do 24 | indent(5) do 25 | text "This box is longest, so it stretches the parent box. \n"*5 26 | end 27 | end 28 | 29 | bounding_box [350, bounds.top], :width => 150 do 30 | text "I AM SANTA CLAUS!!!" 31 | end 32 | 33 | stroke_dashed_bounds 34 | 35 | end 36 | 37 | bounding_box [100, 250], :width => 500 do 38 | 39 | bounding_box [0, bounds.top], :width => 100, :height => 100 do 40 | text "1" 41 | stroke_bounds 42 | end 43 | 44 | bounding_box [125, bounds.top], :width => 50, :height => 25 do 45 | text "2" 46 | stroke_bounds 47 | end 48 | 49 | bounding_box [200, bounds.top - 50], :width => 50, :height => 125 do 50 | text "3" 51 | stroke_bounds 52 | end 53 | 54 | bounding_box [350, bounds.top - 100], :width => 20, :height => 20 do 55 | text "4" 56 | stroke_bounds 57 | end 58 | 59 | bounding_box [400, bounds.height - 150], :width => 100, :height => 100 do 60 | text "5" 61 | stroke_bounds 62 | end 63 | 64 | stroke_dashed_bounds 65 | 66 | end 67 | 68 | end 69 | -------------------------------------------------------------------------------- /lib/prawn/document/span.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | # span.rb : Implements text columns 4 | # 5 | # Copyright September 2008, Gregory Brown. All Rights Reserved. 6 | # 7 | # This is free software. Please see the LICENSE and COPYING files for details. 8 | 9 | module Prawn 10 | class Document 11 | # A span is a special purpose bounding box that allows a column of 12 | # elements to be positioned relative to the margin_box. 13 | # 14 | # Arguments: 15 | # +width+:: The width of the column in PDF points 16 | # 17 | # Options: 18 | # :position:: One of :left, :center, :right or an x offset 19 | # 20 | # This method is typically used for flowing a column of text from one 21 | # page to the next. 22 | # 23 | # span(350, :position => :center) do 24 | # text "Here's some centered text in a 350 point column. " * 100 25 | # end 26 | # 27 | def span(width, options={}) 28 | Prawn.verify_options [:position], options 29 | original_position = self.y 30 | 31 | # FIXME: Any way to move this upstream? 32 | left_boundary = case(options[:position] || :left) 33 | when :left 34 | margin_box.absolute_left 35 | when :center 36 | margin_box.absolute_left + margin_box.width / 2.0 - width /2.0 37 | when :right 38 | margin_box.absolute_right - width 39 | when Numeric 40 | margin_box.absolute_left + options[:position] 41 | else 42 | raise ArgumentError, "Invalid option for :position" 43 | end 44 | 45 | # we need to bust out of whatever nested bounding boxes we're in. 46 | canvas do 47 | bounding_box([left_boundary, 48 | margin_box.absolute_top], :width => width) do 49 | self.y = original_position 50 | yield 51 | end 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /examples/graphics/transformations.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Demonstrates transformations 4 | # 5 | require File.expand_path(File.join(File.dirname(__FILE__), 6 | %w[.. example_helper])) 7 | 8 | Prawn::Document.generate "transformations.pdf" do |pdf| 9 | width = 50 10 | height = 100 11 | 12 | # ROTATION 13 | x = 50 14 | y = pdf.bounds.top - 50 15 | 16 | pdf.stroke_rectangle([x, y], width, height) 17 | pdf.draw_text("reference rectangle", :at => [x + width, y - height]) 18 | pdf.rotate(30, :origin => [x, y]) do 19 | pdf.stroke_rectangle([x, y], width, height) 20 | pdf.draw_text("rectangle rotated around upper-left corner", :at => [x + width, y - height]) 21 | end 22 | 23 | x = 50 24 | y = pdf.bounds.top - 200 25 | 26 | pdf.stroke_rectangle([x, y], width, height) 27 | pdf.draw_text("reference rectangle", :at => [x + width, y - height]) 28 | pdf.rotate(30, :origin => [x + width / 2, y - height / 2]) do 29 | pdf.stroke_rectangle([x, y], width, height) 30 | pdf.draw_text("rectangle rotated around center", :at => [x + width, y - height]) 31 | end 32 | 33 | # SCALE 34 | x = 0 35 | y = pdf.bounds.top - 500 36 | 37 | pdf.stroke_rectangle([x, y], width, height) 38 | pdf.draw_text("reference rectangle", :at => [x + width, y - height]) 39 | pdf.scale(2, :origin => [x, y]) do 40 | pdf.stroke_rectangle([x, y], width, height) 41 | pdf.draw_text("rectangle scaled from upper-left corner", :at => [x + width, y - height]) 42 | end 43 | 44 | x = 150 45 | y = pdf.bounds.top - 400 46 | 47 | pdf.stroke_rectangle([x, y], width, height) 48 | pdf.draw_text("reference rectangle", :at => [x + width, y - height]) 49 | pdf.scale(2, :origin => [x + width / 2, y - height / 2]) do 50 | pdf.stroke_rectangle([x, y], width, height) 51 | pdf.draw_text("rectangle scaled from center", :at => [x + width, y - height]) 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /examples/text/text_box_returning_excess.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # A text box is positioned by a top-left corner, width, and height and is 4 | # essentially an invisible rectangle that the text will flow within. If the 5 | # text exceeds the boundaries, it is either truncated, replaced with some 6 | # ellipses, or set to expand beyond the bottom boundary. 7 | # 8 | require File.expand_path(File.join(File.dirname(__FILE__), 9 | %w[.. example_helper])) 10 | 11 | Prawn::Document.generate("text_box_returning_excess.pdf") do 12 | 13 | # Note that without the initial space in p_break, newlines may be eaten by 14 | # the wrap/unwrap process that happens inside the text box. 15 | p_break = " \n\n" 16 | callout = "Lorem ipsum dolor sit amet" 17 | lorem = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.#{p_break}Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.#{p_break}Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." 18 | 19 | box_height = font.height * 5 20 | 21 | # Add a callout box that the rest of the text should flow around 22 | font_size(18) do 23 | text_box callout, { 24 | :width => 100, 25 | :height => font.height * 3, 26 | :overflow => :truncate, 27 | :at => [100, bounds.top - box_height - 4] 28 | } 29 | end 30 | 31 | excess_text = text_box lorem + p_break + lorem, { 32 | :width => 300, 33 | :height => box_height, 34 | :overflow => :truncate, 35 | :at => [100, bounds.top], 36 | } 37 | 38 | excess_text = text_box excess_text, { 39 | :width => 200, 40 | :height => box_height, 41 | :overflow => :truncate, 42 | :at => [200, bounds.top - box_height], 43 | } 44 | 45 | text_box excess_text, { 46 | :width => 300, 47 | :height => box_height, 48 | :overflow => :expand, 49 | :at => [100, bounds.top - box_height * 2], 50 | } 51 | 52 | end 53 | -------------------------------------------------------------------------------- /lib/prawn/graphics/dash.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | # dash.rb : Implements stroke dashing 4 | # 5 | # Contributed by Daniel Nelson. October, 2009 6 | # 7 | # This is free software. Please see the LICENSE and COPYING files for details. 8 | # 9 | module Prawn 10 | module Graphics 11 | module Dash 12 | 13 | # Sets the dash pattern for stroked lines and curves 14 | # 15 | # length is the length of the dash. If options is not present, 16 | # or options[:space] is nil, then length is also the length of 17 | # the space between dashes 18 | # 19 | # options may contain :space and :phase 20 | # :space is the space between the dashes 21 | # :phase is where in the cycle to begin dashing. For 22 | # example, a phase of 0 starts at the beginning of 23 | # the dash; whereas, if the phase is equal to the 24 | # length of the dash, then stroking will begin at 25 | # the beginning of the space. Default is 0 26 | # 27 | # integers or floats may be used for length and the options 28 | # 29 | # dash units are in PDF points ( 1/72 in ) 30 | # 31 | def dash(length=nil, options={}) 32 | return @dash || undash_hash if length.nil? 33 | 34 | @dash = { :dash => length, 35 | :space => options[:space] || length, 36 | :phase => options[:phase] || 0 } 37 | 38 | write_stroke_dash 39 | end 40 | 41 | alias_method :dash=, :dash 42 | 43 | # Stops dashing, restoring solid stroked lines and curves 44 | # 45 | def undash 46 | @dash = undash_hash 47 | write_stroke_dash 48 | end 49 | 50 | # Returns when stroke is dashed, false otherwise 51 | # 52 | def dashed? 53 | dash != undash_hash 54 | end 55 | 56 | private 57 | 58 | def undash_hash 59 | { :dash => nil, :space => nil, :phase => 0 } 60 | end 61 | 62 | def write_stroke_dash 63 | if @dash[:dash].nil? 64 | add_content "[] 0 d" 65 | return 66 | end 67 | add_content "[#{@dash[:dash]} #{@dash[:space]}] #{@dash[:phase]} d" 68 | end 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /lib/prawn/core/annotations.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | # annotations.rb : Implements low-level annotation support for PDF 4 | # 5 | # Copyright November 2008, Jamis Buck. All Rights Reserved. 6 | # 7 | # This is free software. Please see the LICENSE and COPYING files for details. 8 | # 9 | module Prawn 10 | module Core 11 | 12 | # Provides very low-level support for annotations. 13 | # 14 | module Annotations #:nodoc: 15 | 16 | # Adds a new annotation (section 8.4 in PDF spec) to the current page. 17 | # +options+ must be a Hash describing the annotation. 18 | # 19 | def annotate(options) 20 | state.page.dictionary.data[:Annots] ||= [] 21 | options = sanitize_annotation_hash(options) 22 | state.page.dictionary.data[:Annots] << ref!(options) 23 | return options 24 | end 25 | 26 | # A convenience method for creating Text annotations. +rect+ must be an array 27 | # of four numbers, describing the bounds of the annotation. +contents+ should 28 | # be a string, to be shown when the annotation is activated. 29 | # 30 | def text_annotation(rect, contents, options={}) 31 | options = options.merge(:Subtype => :Text, :Rect => rect, :Contents => contents) 32 | annotate(options) 33 | end 34 | 35 | # A convenience method for creating Link annotations. +rect+ must be an array 36 | # of four numbers, describing the bounds of the annotation. The +options+ hash 37 | # should include either :Dest (describing the target destination, usually as a 38 | # string that has been recorded in the document's Dests tree), or :A (describing 39 | # an action to perform on clicking the link), or :PA (for describing a URL to 40 | # link to). 41 | # 42 | def link_annotation(rect, options={}) 43 | options = options.merge(:Subtype => :Link, :Rect => rect) 44 | annotate(options) 45 | end 46 | 47 | private 48 | 49 | def sanitize_annotation_hash(options) 50 | options = options.merge(:Type => :Annot) 51 | 52 | if options[:Dest].is_a?(String) 53 | options[:Dest] = Prawn::Core::LiteralString.new(options[:Dest]) 54 | end 55 | 56 | options 57 | end 58 | 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /examples/general/measurement_units.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Generates a ruler and also demonstrates prawn/measurement_extensions. 4 | # It's better to run this example and examine its output than to worry about 5 | # its particular implementation, though some might find that interesting as 6 | # well. 7 | # 8 | require File.expand_path(File.join(File.dirname(__FILE__), 9 | %w[.. example_helper])) 10 | 11 | require "prawn/measurement_extensions" 12 | 13 | # this makes the following units available (Millimeters, Centimeters, Decimeters, Meters, Inches, Foot, Yards, Points) 14 | # Methodname is the common abbravation for the unit (mm, cm, dm, m, in, ft, yd, pt) 15 | # Usage: '10.mm'. 16 | # This converts 10mm to PDF points, which Prawn uses internally. 17 | 18 | pdf = Prawn::Document.new( 19 | :page_size => "A4", 20 | :left_margin => 10.mm, # different 21 | :right_margin => 1.cm, # units 22 | :top_margin => 0.1.dm, # work 23 | :bottom_margin => 0.01.m) # well 24 | 25 | pdf.font_size = 6 26 | pdf.line_width = 0.05 27 | 28 | units_long = %w[Millimeters Centimeters Decimeters Inches Foot Points] 29 | units = %w[mm cm dm in ft pt] 30 | offset_multiplier = 2.cm 31 | temp = "Units\n" 32 | 33 | units.each_with_index do |unit, unit_index| #iterate through all units that make sense to display on a sheet of paper 34 | one_unit_in_pt = eval "1.#{unit}" # calc the width of one unit 35 | temp << "1#{unit} => #{one_unit_in_pt}pt\n" #puts converted unit in points 36 | 37 | offset = offset_multiplier * unit_index 38 | pdf.draw_text units[unit_index], :at => [offset + 0.5.mm, pdf.bounds.top - 2.mm] 39 | 40 | pdf.stroke_line(offset, pdf.bounds.top, offset, pdf.bounds.bottom) 41 | 42 | 0.upto(((pdf.bounds.height - 5.mm) / one_unit_in_pt).to_i) do |i| # checks, how many strokes can be drawn 43 | pdf.stroke_line(offset, i * one_unit_in_pt, (i % 5 == 0 ? 6.mm : 3.mm) + offset, i * one_unit_in_pt) # every fifth stroke is twice as large like on a real ruler 44 | pdf.draw_text "#{i}#{unit}", :at => [7.mm + offset, i * one_unit_in_pt] unless unit == "mm" && i % 5 != 0 || unit == "pt" && i % 10 != 0 # avoid text too close to each other 45 | end 46 | end 47 | 48 | pdf.text_box temp, 49 | :width => 5.cm, :height => pdf.font.height * units_long.length, 50 | :at => [offset_multiplier * units_long.length, pdf.bounds.top] 51 | 52 | pdf.render_file "measurement_units.pdf" 53 | -------------------------------------------------------------------------------- /examples/general/outlines.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # This example demonstrates the use of the the outlines option for a new document 4 | # it sets an initial outline item with a title 5 | # 6 | require File.expand_path(File.join(File.dirname(__FILE__), 7 | %w[.. example_helper])) 8 | 9 | Prawn::Document.generate('outlines.pdf') do 10 | text "Page 1. This is the first Chapter. " 11 | start_new_page 12 | text "Page 2. More in the first Chapter. " 13 | start_new_page 14 | text "Page 3. This is the second Chapter. It has a subsection. " 15 | start_new_page 16 | text "Page 4. More in the second Chapter. " 17 | outline.section 'Preface' do 18 | outline.page :title => 'Preface' 19 | end 20 | outline.define do 21 | section 'Chapter 1', :destination => 1, :closed => true do 22 | page :destination => 1, :title => 'Page 1' 23 | page :destination => 2, :title => 'Page 2' 24 | end 25 | section 'Chapter 2', :destination => 3 do 26 | section 'Chapter 2 Subsection' do 27 | page :title => 'Page 3' 28 | end 29 | page :destination => 4, :title => 'Page 4' 30 | end 31 | end 32 | start_new_page 33 | text "Page 5. Appendix" 34 | start_new_page 35 | text "Page 6. More in the Appendix" 36 | outline.section 'Appendix', :destination => 5 do 37 | outline.page :destination => 5, :title => 'Page 5' 38 | outline.page :destination => 6, :title => 'Page 6' 39 | end 40 | go_to_page 4 41 | start_new_page 42 | text "inserted before the Appendix" 43 | outline.update do 44 | insert_section_after 'Chapter 2' do 45 | page :destination => page_number, :title => "Pre-Appendix" 46 | end 47 | end 48 | go_to_page 7 49 | start_new_page 50 | text "One last page" 51 | outline.insert_section_after 'Page 6' do 52 | outline.page :destination => page_number, :title => "Inserted after 6" 53 | end 54 | outline.add_subsection_to 'Chapter 1', :first do 55 | outline.section 'Inserted subsection', :destination => 1 do 56 | outline.page :destination => 1, :title => "Page 1 again" 57 | end 58 | end 59 | start_new_page 60 | text "Really this is the last page." 61 | outline.update do 62 | page :destination => page_number, :title => "Last Page" 63 | end 64 | start_new_page 65 | text "OK, I lied; this is the very last page." 66 | outline.page :destination => page_number, :title => "Very Last Page" 67 | end 68 | -------------------------------------------------------------------------------- /spec/text_spacing_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper") 4 | 5 | describe "#character_spacing" do 6 | it "should draw the character spacing to the document" do 7 | create_pdf 8 | @pdf.character_spacing(10.555555) do 9 | @pdf.text("hello world") 10 | end 11 | contents = PDF::Inspector::Text.analyze(@pdf.render) 12 | contents.character_spacing.first.should == 10.556 13 | end 14 | it "should not draw the character spacing to the document" + 15 | " when the new character spacing matches the old" do 16 | create_pdf 17 | @pdf.character_spacing(0) do 18 | @pdf.text("hello world") 19 | end 20 | contents = PDF::Inspector::Text.analyze(@pdf.render) 21 | contents.character_spacing.should.be.empty 22 | end 23 | it "should restore character spacing to 0" do 24 | create_pdf 25 | @pdf.character_spacing(10.555555) do 26 | @pdf.text("hello world") 27 | end 28 | contents = PDF::Inspector::Text.analyze(@pdf.render) 29 | contents.character_spacing.last.should == 0 30 | end 31 | it "should function as an accessor when no parameter given" do 32 | create_pdf 33 | @pdf.character_spacing(10.555555) do 34 | @pdf.text("hello world") 35 | @pdf.character_spacing.should == 10.555555 36 | end 37 | @pdf.character_spacing.should == 0 38 | end 39 | end 40 | 41 | describe "#word_spacing" do 42 | it "should draw the word spacing to the document" do 43 | create_pdf 44 | @pdf.word_spacing(10.555555) do 45 | @pdf.text("hello world") 46 | end 47 | contents = PDF::Inspector::Text.analyze(@pdf.render) 48 | contents.word_spacing.first.should == 10.556 49 | end 50 | it "should draw the word spacing to the document" + 51 | " when the new word spacing matches the old" do 52 | create_pdf 53 | @pdf.word_spacing(0) do 54 | @pdf.text("hello world") 55 | end 56 | contents = PDF::Inspector::Text.analyze(@pdf.render) 57 | contents.word_spacing.should.be.empty 58 | end 59 | it "should restore word spacing to 0" do 60 | create_pdf 61 | @pdf.word_spacing(10.555555) do 62 | @pdf.text("hello world") 63 | end 64 | contents = PDF::Inspector::Text.analyze(@pdf.render) 65 | contents.word_spacing.last.should == 0 66 | end 67 | it "should function as an accessor when no parameter given" do 68 | create_pdf 69 | @pdf.word_spacing(10.555555) do 70 | @pdf.text("hello world") 71 | @pdf.word_spacing.should == 10.555555 72 | end 73 | @pdf.word_spacing.should == 0 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /bugs/resolved/layout/table_row_background_color_issue.rb: -------------------------------------------------------------------------------- 1 | # As of 96f660660345c7c22923ba51d0124022a3a189ab, table is currently not taking 2 | # in account border widths when filling in rows with background coloring. This 3 | # means the larger the border, the larger the visible gap between rows. 4 | # 5 | # This problem was fixed in 97d9bf083fd9423d17fd1efca36ea675ff34a6d7, but 6 | # there remains a very minor issue when the border size is 1 for the headers. 7 | # Because this almost appears to be a feature display-wise, we will leave it 8 | # alone for now. 9 | # 10 | $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', '..', 'lib') 11 | require "rubygems" 12 | require "prawn" 13 | require "prawn/layout" 14 | 15 | Prawn::Document.generate("table_with_background_color_problems.pdf") do 16 | font "#{Prawn::BASEDIR}/data/fonts/DejaVuSans.ttf" 17 | table [["ὕαλον ϕαγεῖν", "baaar", "1" ], 18 | ["This is","a sample", "2" ], 19 | ["Table", "dont\ncha\nknow?", "3" ], 20 | [ "It", "Rules", "4" ], 21 | [ "It", "Rules", "4" ], 22 | [ "It", "Rules", "4" ], 23 | [ "It", "Rules", "4" ], 24 | [ "It", "Rules", "4" ], 25 | [ "It", "Rules", "4" ], 26 | [ "It", "Rules", "4" ], 27 | [ "It", "Rules", "4" ], 28 | [ "It", "Rules", "4" ], 29 | [ "It", "Rules\nwith an iron fist", "x" ], 30 | [ "It", "Rules", "4" ], 31 | [ "It", "Rules", "4" ], 32 | [ "It", "Rules", "4" ], 33 | [ "It", "Rules", "4" ], 34 | [ "It", "Rules", "4" ], 35 | [ "It", "Rules", "4" ], 36 | [ "It", "Rules", "4" ], 37 | [ "It", "Rules", "4" ], 38 | [ "It", "Rules", "4" ]], 39 | 40 | :font_size => 10, 41 | :horizontal_padding => 10, 42 | :vertical_padding => 3, 43 | :border => 1, 44 | :position => :center, 45 | :headers => ["Column A","Column B","#"], 46 | :row_colors => ["cccccc"] 47 | 48 | pad(20) do 49 | text "This should appear in the original font size" 50 | end 51 | 52 | table [[ "Wide", "columns", "streeetch"], 53 | ["are","mighty fine", "streeeeeeeech"]], 54 | :column_widths => { 0 => 200, 1 => 250 }, :position => 5 55 | 56 | end 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Prawn is copyrighted free software produced by Gregory Brown along with 2 | community contributions. See git log for authorship information. 3 | 4 | Licensing terms follow (License of Ruby 1.8): 5 | 6 | You can redistribute Prawn and/or modify it under either the terms of the GPL 7 | (see COPYING file), or the conditions below: 8 | 9 | 1. You may make and give away verbatim copies of the source form of the 10 | software without restriction, provided that you duplicate all of the 11 | original copyright notices and associated disclaimers. 12 | 13 | 2. You may modify your copy of the software in any way, provided that 14 | you do at least ONE of the following: 15 | 16 | a) place your modifications in the Public Domain or otherwise 17 | make them Freely Available, such as by posting said 18 | modifications to Usenet or an equivalent medium, or by allowing 19 | the author to include your modifications in the software. 20 | 21 | b) use the modified software only within your corporation or 22 | organization. 23 | 24 | c) rename any non-standard executables so the names do not conflict 25 | with standard executables, which must also be provided. 26 | 27 | d) make other distribution arrangements with the author. 28 | 29 | 3. You may distribute the software in object code or executable 30 | form, provided that you do at least ONE of the following: 31 | 32 | a) distribute the executables and library files of the software, 33 | together with instructions (in the manual page or equivalent) 34 | on where to get the original distribution. 35 | 36 | b) accompany the distribution with the machine-readable source of 37 | the software. 38 | 39 | c) give non-standard executables non-standard names, with 40 | instructions on where to get the original software distribution. 41 | 42 | d) make other distribution arrangements with the author. 43 | 44 | 4. You may modify and include the part of the software into any other 45 | software (possibly commercial). 46 | 47 | 5. The scripts and library files supplied as input to or produced as 48 | output from the software do not automatically fall under the 49 | copyright of the software, but belong to whomever generated them, 50 | and may be sold commercially, and may be aggregated with this 51 | software. 52 | 53 | 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR 54 | IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 55 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 56 | PURPOSE. 57 | -------------------------------------------------------------------------------- /examples/text/text_box.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | 4 | require File.expand_path(File.join(File.dirname(__FILE__), 5 | %w[.. example_helper])) 6 | 7 | Prawn::Document.generate("text_box.pdf") do 8 | def get_string(i, j) 9 | case i 10 | when 0 11 | text = "this is left text " * 30 12 | text.insert(48, "\n\n") 13 | when 1 14 | text = "this is center text " * 30 15 | text.insert(54, "\n\n") 16 | when 2 17 | text = "this is right text " * 30 18 | text.insert(51, "\n\n") 19 | end 20 | 21 | case j 22 | when 0 23 | text.split(" ").slice(0..47).join(" ") 24 | when 3 25 | text.delete(" ") 26 | else 27 | text 28 | end 29 | end 30 | 31 | def get_options(i, j) 32 | options = { 33 | :width => bounds.width * 0.3, 34 | :height => bounds.width * 0.3, 35 | :overflow => :ellipses, 36 | :at => [0, 0], 37 | :align => :left, 38 | :document => self 39 | } 40 | 41 | case i 42 | when 0 43 | options[:valign] = :top if j == 0 44 | when 1 45 | options[:align] = :center 46 | options[:valign] = :center if j == 0 47 | when 2 48 | options[:align] = :right 49 | options[:valign] = :bottom if j == 0 50 | end 51 | 52 | case j 53 | when 1 54 | options[:overflow] = :shrink_to_fit 55 | when 2 56 | options[:leading] = font.height * 0.5 57 | options[:overflow] = :truncate 58 | end 59 | options 60 | end 61 | 62 | stroke_color("555555") 63 | 3.times do |i| 64 | 4.times do |j| 65 | options = get_options(i, j) 66 | options[:at][0] = (bounds.width - options[:width]) * 0.5 * i 67 | options[:at][1] = bounds.top - (bounds.height - options[:height]) * 0.33 * j 68 | box = Prawn::Text::Box.new(get_string(i, j), options) 69 | 70 | fill_color("ffeeee") 71 | if i == 1 72 | # bound with a box of a particular size, regardless of how 73 | # much text it contains 74 | fill_and_stroke_rectangle(options[:at], 75 | options[:width], 76 | options[:height]) 77 | else 78 | # bound with a box that exactly fits the printed text using 79 | # dry_run look-ahead 80 | box.render(:dry_run => true) 81 | fill_and_stroke_rectangle(options[:at], 82 | options[:width], 83 | box.height) 84 | end 85 | fill_color("000000") 86 | box.render 87 | end 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /spec/annotations_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper") 4 | 5 | class PageAnnotations 6 | attr_reader :pages 7 | 8 | def self.parse(document) 9 | receiver = new 10 | PDF::Reader.string(document.render, receiver) 11 | return receiver 12 | end 13 | 14 | def initialize 15 | @pages = [] 16 | end 17 | 18 | def begin_page(params) 19 | @pages << params 20 | end 21 | end 22 | 23 | describe "When creating annotations" do 24 | 25 | before(:each) { create_pdf } 26 | 27 | it "should append annotation to current page" do 28 | @pdf.start_new_page 29 | @pdf.annotate(:Rect => [0,0,10,10], :Subtype => :Text, :Contents => "Hello world!") 30 | obj = PageAnnotations.parse(@pdf) 31 | obj.pages[0][:Annots].nil?.should == true 32 | obj.pages[1][:Annots].length.should == 1 33 | end 34 | 35 | it "should force :Type to be :Annot" do 36 | opts = @pdf.annotate(:Rect => [0,0,10,10], :Subtype => :Text, :Contents => "Hello world!") 37 | opts[:Type].should == :Annot 38 | opts = @pdf.annotate(:Type => :Bogus, :Rect => [0,0,10,10], :Subtype => :Text, :Contents => "Hello world!") 39 | opts[:Type].should == :Annot 40 | end 41 | 42 | end 43 | 44 | describe "When creating text annotations" do 45 | 46 | before(:each) do 47 | @rect = [0,0,10,10] 48 | @content = "Hello, world!" 49 | create_pdf 50 | end 51 | 52 | it "should build appropriate annotation" do 53 | opts = @pdf.text_annotation(@rect, @content) 54 | opts[:Type].should == :Annot 55 | opts[:Subtype].should == :Text 56 | opts[:Rect].should == @rect 57 | opts[:Contents].should == @content 58 | end 59 | 60 | it "should merge extra options" do 61 | opts = @pdf.text_annotation(@rect, @content, :Open => true, :Subtype => :Bogus) 62 | opts[:Subtype].should == :Text 63 | opts[:Open].should == true 64 | end 65 | 66 | end 67 | 68 | describe "When creating link annotations" do 69 | 70 | before(:each) do 71 | @rect = [0,0,10,10] 72 | @dest = "home" 73 | create_pdf 74 | end 75 | 76 | it "should build appropriate annotation" do 77 | opts = @pdf.link_annotation(@rect, :Dest => @dest) 78 | opts[:Type].should == :Annot 79 | opts[:Subtype].should == :Link 80 | opts[:Rect].should == @rect 81 | opts[:Dest].should == @dest 82 | end 83 | 84 | it "should merge extra options" do 85 | opts = @pdf.link_annotation(@rect, :Dest => @dest, :Subtype => :Bogus) 86 | opts[:Subtype].should == :Link 87 | opts[:Dest].should == @dest 88 | end 89 | 90 | end 91 | -------------------------------------------------------------------------------- /lib/prawn/core.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # Prawn : A library for PDF generation in Ruby 3 | # 4 | # Copyright April 2008, Gregory Brown. All Rights Reserved. 5 | # 6 | # This is free software. Please see the LICENSE and COPYING files for details. 7 | 8 | require "set" 9 | 10 | %w[ttfunk/lib].each do |dep| 11 | $LOAD_PATH.unshift(File.dirname(__FILE__) + "/../../vendor/#{dep}") 12 | end 13 | 14 | begin 15 | require 'ttfunk' 16 | rescue LoadError 17 | puts "Failed to load ttfunk. If you are running Prawn from git:" 18 | puts " git submodule init" 19 | puts " git submodule update" 20 | exit 21 | end 22 | 23 | module Prawn 24 | extend self 25 | 26 | file = __FILE__ 27 | file = File.readlink(file) if File.symlink?(file) 28 | dir = File.dirname(file) 29 | 30 | # The base source directory for Prawn as installed on the system 31 | # 32 | BASEDIR = File.expand_path(File.join(dir, '..', '..')) 33 | 34 | # Whe set to true, Prawn will verify hash options to ensure only valid keys 35 | # are used. Off by default. 36 | # 37 | # Example: 38 | # >> Prawn::Document.new(:tomato => "Juicy") 39 | # Prawn::Errors::UnknownOption: 40 | # Detected unknown option(s): [:tomato] 41 | # Accepted options are: [:page_size, :page_layout, :left_margin, ...] 42 | # 43 | attr_accessor :debug 44 | 45 | def verify_options(accepted, actual) #:nodoc: 46 | return unless debug || $DEBUG 47 | unless (act=Set[*actual.keys]).subset?(acc=Set[*accepted]) 48 | raise Prawn::Errors::UnknownOption, 49 | "\nDetected unknown option(s): #{(act - acc).to_a.inspect}\n" << 50 | "Accepted options are: #{accepted.inspect}" 51 | end 52 | yield if block_given? 53 | end 54 | 55 | module Configurable #:nodoc: 56 | def configuration(*args) 57 | @config ||= Marshal.load(Marshal.dump(default_configuration)) 58 | if Hash === args[0] 59 | @config.update(args[0]) 60 | elsif args.length > 1 61 | @config.values_at(*args) 62 | elsif args.length == 1 63 | @config[args[0]] 64 | else 65 | @config 66 | end 67 | end 68 | 69 | alias_method :C, :configuration 70 | end 71 | end 72 | 73 | require "prawn/compatibility" 74 | require "prawn/errors" 75 | require "prawn/core/pdf_object" 76 | require "prawn/core/reference" 77 | require "prawn/core/page" 78 | require "prawn/core/object_store" 79 | require "prawn/core/document_state" 80 | require "prawn/core/literal_string" 81 | require "prawn/core/byte_string" 82 | require "prawn/core/name_tree" 83 | require "prawn/core/annotations" 84 | require "prawn/core/destinations" 85 | 86 | -------------------------------------------------------------------------------- /lib/prawn/core/document_state.rb: -------------------------------------------------------------------------------- 1 | module Prawn 2 | module Core 3 | class DocumentState #:nodoc: 4 | def initialize(options) 5 | normalize_metadata(options) 6 | 7 | if options[:template] 8 | @store = Prawn::Core::ObjectStore.new(:template => options[:template]) 9 | else 10 | @store = Prawn::Core::ObjectStore.new(:info => options[:info]) 11 | end 12 | 13 | @version = 1.3 14 | @pages = [] 15 | @page = nil 16 | @trailer = {} 17 | @compress = options.fetch(:compress, false) 18 | @encrypt = options.fetch(:encrypt, false) 19 | @encryption_key = options[:encryption_key] 20 | @optimize_objects = options.fetch(:optimize_objects, false) 21 | @skip_encoding = options.fetch(:skip_encoding, false) 22 | @before_render_callbacks = [] 23 | @on_page_create_callback = nil 24 | end 25 | 26 | attr_accessor :store, :version, :pages, :page, :trailer, :compress, 27 | :encrypt, :encryption_key, :optimize_objects, :skip_encoding, 28 | :before_render_callbacks, :on_page_create_callback 29 | 30 | def populate_pages_from_store(document) 31 | return 0 if @store.page_count <= 0 || @pages.size > 0 32 | 33 | count = (1..@store.page_count) 34 | @pages = count.map do |index| 35 | orig_dict_id = @store.object_id_for_page(index) 36 | Prawn::Core::Page.new(document, :object_id => orig_dict_id) 37 | end 38 | 39 | end 40 | 41 | def normalize_metadata(options) 42 | options[:info] ||= {} 43 | options[:info][:Creator] ||= "Prawn" 44 | options[:info][:Producer] = "Prawn" 45 | 46 | info = options[:info] 47 | end 48 | 49 | def insert_page(page, page_number) 50 | pages.insert(page_number, page) 51 | store.pages.data[:Kids].insert(page_number, page.dictionary) 52 | store.pages.data[:Count] += 1 53 | end 54 | 55 | def on_page_create_action(doc) 56 | on_page_create_callback[doc] if on_page_create_callback 57 | end 58 | 59 | def before_render_actions(doc) 60 | before_render_callbacks.each{ |c| c.call(self) } 61 | end 62 | 63 | def page_count 64 | pages.length 65 | end 66 | 67 | def render_body(output) 68 | store.compact if optimize_objects 69 | store.each do |ref| 70 | ref.offset = output.size 71 | output << (@encrypt ? ref.encrypted_object(@encryption_key) : 72 | ref.object) 73 | end 74 | end 75 | 76 | end 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /lib/prawn/images/jpg.rb: -------------------------------------------------------------------------------- 1 | # encoding: ASCII-8BIT 2 | 3 | # jpg.rb : Extracts the data from a JPG that is needed for embedding 4 | # 5 | # Copyright April 2008, James Healy. All Rights Reserved. 6 | # 7 | # This is free software. Please see the LICENSE and COPYING files for details. 8 | 9 | require 'stringio' 10 | 11 | module Prawn 12 | module Images 13 | # A convenience class that wraps the logic for extracting the parts 14 | # of a JPG image that we need to embed them in a PDF 15 | # 16 | class JPG 17 | attr_reader :width, :height, :bits, :channels 18 | attr_accessor :scaled_width, :scaled_height 19 | 20 | JPEG_SOF_BLOCKS = %W(\xc0 \xc1 \xc2 \xc3 \xc5 \xc6 \xc7 \xc9 \xca \xcb \xcd \xce \xcf) 21 | JPEG_APP_BLOCKS = %W(\xe0 \xe1 \xe2 \xe3 \xe4 \xe5 \xe6 \xe7 \xe8 \xe9 \xea \xeb \xec \xed \xee \xef) 22 | 23 | # Process a new JPG image 24 | # 25 | # :data:: A binary string of JPEG data 26 | # 27 | def initialize(data) 28 | @data = data 29 | data = StringIO.new(data.dup) 30 | 31 | c_marker = "\xff" # Section marker. 32 | data.read(2) # Skip the first two bytes of JPEG identifier. 33 | loop do 34 | marker, code, length = data.read(4).unpack('aan') 35 | raise "JPEG marker not found!" if marker != c_marker 36 | 37 | if JPEG_SOF_BLOCKS.include?(code) 38 | @bits, @height, @width, @channels = data.read(6).unpack("CnnC") 39 | break 40 | end 41 | 42 | buffer = data.read(length - 2) 43 | end 44 | end 45 | 46 | # Build a PDF object representing this image in +document+, and return 47 | # a Reference to it. 48 | # 49 | def build_pdf_object(document) 50 | color_space = case channels 51 | when 1 52 | :DeviceGray 53 | when 3 54 | :DeviceRGB 55 | when 4 56 | :DeviceCMYK 57 | else 58 | raise ArgumentError, 'JPG uses an unsupported number of channels' 59 | end 60 | 61 | obj = document.ref!( 62 | :Type => :XObject, 63 | :Subtype => :Image, 64 | :Filter => :DCTDecode, 65 | :ColorSpace => color_space, 66 | :BitsPerComponent => bits, 67 | :Width => width, 68 | :Height => height, 69 | :Length => @data.size 70 | ) 71 | 72 | # add extra decode params for CMYK images. By swapping the 73 | # min and max values from the default, we invert the colours. See 74 | # section 4.8.4 of the spec. 75 | if color_space == :DeviceCMYK 76 | obj.data[:Decode] = [ 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0 ] 77 | end 78 | 79 | obj << @data 80 | obj 81 | end 82 | 83 | end 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /spec/reference_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper") 4 | 5 | describe "A Reference object" do 6 | it "should produce a PDF reference on #to_s call" do 7 | ref = Prawn::Core::Reference(1,true) 8 | ref.to_s.should == "1 0 R" 9 | end 10 | 11 | it "should allow changing generation number" do 12 | ref = Prawn::Core::Reference(1,true) 13 | ref.gen = 1 14 | ref.to_s.should == "1 1 R" 15 | end 16 | 17 | it "should generate a valid PDF object for the referenced data" do 18 | ref = Prawn::Core::Reference(2,[1,"foo"]) 19 | ref.object.should == "2 0 obj\n#{Prawn::Core::PdfObject([1,"foo"])}\nendobj\n" 20 | end 21 | 22 | it "should automatically open a stream when #<< is used" do 23 | ref = Prawn::Core::Reference(1, :Length => 41) 24 | ref << "BT\n/F1 12 Tf\n72 712 Td\n( A stream ) Tj\nET" 25 | ref.object.should == "1 0 obj\n<< /Length 41\n>>\nstream"+ 26 | "\nBT\n/F1 12 Tf\n72 712 Td\n( A stream ) Tj\nET" + 27 | "\nendstream\nendobj\n" 28 | end 29 | 30 | it "should compress a stream upon request" do 31 | ref = Prawn::Core::Reference(2,{}) 32 | ref << "Hi There " * 20 33 | 34 | cref = Prawn::Core::Reference(2,{}) 35 | cref << "Hi There " * 20 36 | cref.compress_stream 37 | 38 | assert cref.stream.size < ref.stream.size, 39 | "compressed stream expected to be smaller than source but wasn't" 40 | cref.data[:Filter].should == :FlateDecode 41 | end 42 | 43 | it "should copy the data and stream from another ref on #replace" do 44 | from = Prawn::Core::Reference(3, {:foo => 'bar'}) 45 | from << "has a stream too" 46 | 47 | to = Prawn::Core::Reference(4, {:foo => 'baz'}) 48 | to.replace from 49 | 50 | # should preserve identifier but copy data and stream 51 | to.identifier.should == 4 52 | to.data.should == from.data 53 | to.stream.should == from.stream 54 | end 55 | 56 | it "should copy a compressed stream from a compressed ref on #replace" do 57 | from = Prawn::Core::Reference(5, {:foo => 'bar'}) 58 | from << "has a stream too " * 20 59 | from.compress_stream 60 | 61 | to = Prawn::Core::Reference(6, {:foo => 'baz'}) 62 | to.replace from 63 | 64 | to.identifier.should == 6 65 | to.data.should == from.data 66 | to.stream.should == from.stream 67 | to.compressed?.should == true 68 | end 69 | 70 | describe "generated via Prawn::Document" do 71 | it "should return a proper reference on ref!" do 72 | pdf = Prawn::Document.new 73 | pdf.ref!({}).is_a?(Prawn::Core::Reference).should == true 74 | end 75 | 76 | it "should return an identifier on ref" do 77 | pdf = Prawn::Document.new 78 | r = pdf.ref({}) 79 | r.is_a?(Integer).should == true 80 | end 81 | end 82 | end 83 | -------------------------------------------------------------------------------- /spec/repeater_spec.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper") 2 | 3 | describe "Repeaters" do 4 | 5 | it "creates a stamp and increments Prawn::Repeater.count on initialize" do 6 | orig_count = Prawn::Repeater.count 7 | 8 | doc = sample_document 9 | doc.expects(:create_stamp).with("prawn_repeater(#{orig_count})") 10 | 11 | r = repeater(doc, :all) { :do_nothing } 12 | 13 | assert_equal orig_count + 1, Prawn::Repeater.count 14 | end 15 | 16 | it "must provide an :all filter" do 17 | doc = sample_document 18 | r = repeater(doc, :all) { :do_nothing } 19 | 20 | assert (1..doc.page_count).all? { |i| r.match?(i) } 21 | end 22 | 23 | it "must provide an :odd filter" do 24 | doc = sample_document 25 | r = repeater(doc, :odd) { :do_nothing } 26 | 27 | odd, even = (1..doc.page_count).partition { |e| e % 2 == 1 } 28 | 29 | assert odd.all? { |i| r.match?(i) } 30 | assert ! even.any? { |i| r.match?(i) } 31 | end 32 | 33 | it "must be able to filter by an array of page numbers" do 34 | doc = sample_document 35 | r = repeater(doc, [1,2,7]) { :do_nothing } 36 | 37 | assert_equal [1,2,7], (1..10).select { |i| r.match?(i) } 38 | end 39 | 40 | it "must be able to filter by a range of page numbers" do 41 | doc = sample_document 42 | r = repeater(doc, 2..4) { :do_nothing } 43 | 44 | assert_equal [2,3,4], (1..10).select { |i| r.match?(i) } 45 | end 46 | 47 | it "must be able to filter by an arbitrary proc" do 48 | doc = sample_document 49 | r = repeater(doc, lambda { |x| x == 1 or x % 3 == 0 }) 50 | 51 | assert_equal [1,3,6,9], (1..10).select { |i| r.match?(i) } 52 | end 53 | 54 | it "must try to run a stamp if the page number matches" do 55 | doc = sample_document 56 | doc.expects(:stamp) 57 | 58 | repeater(doc, :odd).run(3) 59 | end 60 | 61 | it "must not try to run a stamp if the page number matches" do 62 | doc = sample_document 63 | 64 | doc.expects(:stamp).never 65 | repeater(doc, :odd).run(2) 66 | end 67 | 68 | it "must not try to run a stamp if dynamic is selected" do 69 | doc = sample_document 70 | 71 | doc.expects(:stamp).never 72 | (1..10).each { |p| repeater(doc, :all, true){:do_nothing}.run(p) } 73 | end 74 | 75 | it "must render the block in context of page when dynamic is selected" do 76 | doc = sample_document 77 | 78 | doc.repeat(:all, :dynamic => true) do 79 | draw_text page_number, :at => [500, 0] 80 | end 81 | 82 | text = PDF::Inspector::Text.analyze(doc.render) 83 | assert_equal (1..10).to_a.map{|p| p.to_s}, text.strings 84 | end 85 | 86 | def sample_document 87 | doc = Prawn::Document.new(:skip_page_creation => true) 88 | 10.times { |e| doc.start_new_page } 89 | doc 90 | end 91 | 92 | def repeater(*args, &b) 93 | Prawn::Repeater.new(*args,&b) 94 | end 95 | 96 | end 97 | -------------------------------------------------------------------------------- /lib/prawn/graphics/gradient.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | # gradient.rb : Implements axial gradient 4 | # 5 | # Contributed by Wojciech Piekutowski. November, 2009 6 | # 7 | # This is free software. Please see the LICENSE and COPYING files for details. 8 | # 9 | module Prawn 10 | module Graphics 11 | module Gradient 12 | # Sets the fill gradient from color1 to color2. 13 | # 14 | # It accepts CMYK and RGB colors, like #fill_color. Both colors must be 15 | # of the same type. 16 | # 17 | # point, width and height define a bounding box in which the gradient 18 | # will be rendered. For example, if you want to have page full of text 19 | # with gradually changing color: 20 | # 21 | # pdf.fill_gradient [0, pdf.bounds.height], pdf.bounds.width, 22 | # pdf.bounds.height, 'FF0000', '0000FF' 23 | # pdf.text 'lots of text'*1000 24 | # 25 | # :stroke_bounds - draw gradient bounds 26 | def fill_gradient(point, width, height, color1, color2, options = {}) 27 | set_gradient(:fill, point, width, height, color1, color2, options) 28 | end 29 | 30 | # Sets the stroke gradient from color1 to color2. 31 | # 32 | # See #fill_gradient for details. 33 | def stroke_gradient(point, width, height, color1, color2, options = {}) 34 | set_gradient(:stroke, point, width, height, color1, color2, options) 35 | end 36 | 37 | private 38 | 39 | def set_gradient(type, point, width, height, color1, color2, options) 40 | if options[:stroke_bounds] 41 | stroke_color 0, 0, 0, 100 42 | stroke_rectangle point, width, height 43 | end 44 | 45 | if color_type(color1) != color_type(color2) 46 | raise ArgumentError, 'both colors must be of the same type: RGB or CMYK' 47 | end 48 | 49 | process_color color1 50 | process_color color2 51 | 52 | shader = ref!({ 53 | :FunctionType => 2, 54 | :Domain => [0.0, 1.0], 55 | :C0 => normalize_color(color1), 56 | :C1 => normalize_color(color2), 57 | :N => 1, 58 | }) 59 | 60 | shading = ref!({ 61 | :ShadingType => 2, # axial shading 62 | :ColorSpace => color_type(color1) == :RGB ? :DeviceRGB : :DeviceCMYK, 63 | :Coords => [0.0, 0.0, 1.0, 0.0], 64 | :Function => shader, 65 | :Extend => [true, true], 66 | }) 67 | 68 | x, y = *point 69 | shading_pattern = ref!({ 70 | :PatternType => 2, # shading pattern 71 | :Shading => shading, 72 | :Matrix => [0,-height, -width, 0, x, y], 73 | }) 74 | 75 | patterns = page.resources[:Pattern] ||= {} 76 | id = patterns.empty? ? 'SP1' : patterns.keys.sort.last.succ 77 | patterns[id] = shading_pattern 78 | 79 | set_color type, id, :pattern => true 80 | end 81 | end 82 | end 83 | end 84 | 85 | -------------------------------------------------------------------------------- /examples/text/font_calculations.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # This example is a demonstration of how Prawn does its text positioning, 4 | # meant to assist those that need to do advanced positioning calculations. 5 | # Run the example for a clearer picture of how things work 6 | # 7 | require File.expand_path(File.join(File.dirname(__FILE__), 8 | %w[.. example_helper])) 9 | 10 | Prawn::Document.generate('font_calculations.pdf') do 11 | 12 | def demonstration 13 | font_size 12 14 | move_down 10 15 | 16 | stroke_horizontal_rule 17 | 18 | text "When using flowing text, Prawn will position text\n" + 19 | "starting font.ascender below the baseline, and leave\n" + 20 | "the y-cursor at the baseline of the next line of text" 21 | 22 | stroke_horizontal_rule 23 | 24 | move_down 20 25 | 26 | bl = y - bounds.absolute_bottom 27 | 28 | stroke_horizontal_rule 29 | draw_text "When using text positioned with :at, the baseline is used", :at => [0, bl] 30 | 31 | draw_text "(and the Y-cursor is not moved)", :at => [350, bl] 32 | 33 | colors = { :ascender => "ff0000", 34 | :descender => "00ff00", 35 | :line_gap => "0000ff", 36 | :font_height => "005500" } 37 | 38 | 39 | pad(20) do 40 | text "Calculations Demo", :size => 16 41 | end 42 | 43 | fill_color colors[:ascender] 44 | text "ASCENDER" 45 | 46 | fill_color colors[:descender] 47 | text "DESCENDER" 48 | 49 | fill_color colors[:line_gap] 50 | text "LINEGAP" 51 | 52 | fill_color colors[:font_height] 53 | text "FONT_HEIGHT" 54 | 55 | fill_color "000000" 56 | font_size 16 57 | 58 | move_down 40 59 | 60 | bl = y - bounds.absolute_bottom 61 | draw_text "The quick brown fox jumps over the lazy dog.", :at => [0, bl] 62 | 63 | stroke_color colors[:ascender] 64 | stroke_line [0, bl], [0, bl + font.ascender] 65 | stroke_line [0, bl + font.ascender], [bounds.width, bl + font.ascender] 66 | 67 | stroke_color colors[:descender] 68 | stroke_line [0,bl], [0, bl - font.descender] 69 | stroke_line [0, bl - font.descender], [bounds.width, bl - font.descender] 70 | 71 | stroke_color colors[:line_gap] 72 | stroke_line [0, bl - font.descender], [0,bl - font.descender - font.line_gap] 73 | stroke_line [0, bl - font.descender - font.line_gap], 74 | [bounds.width,bl - font.descender - font.line_gap] 75 | 76 | stroke_color colors[:font_height] 77 | stroke_line [bounds.width, bl + font.ascender], 78 | [bounds.width, bl - font.descender - font.line_gap] 79 | 80 | stroke_color "000000" 81 | fill_color "000000" 82 | end 83 | 84 | text "Using AFM", :size => 20 85 | demonstration 86 | 87 | move_down 75 88 | font "#{Prawn::BASEDIR}/data/fonts/DejaVuSans.ttf" 89 | text "Using TTF", :size => 20 90 | demonstration 91 | 92 | end 93 | -------------------------------------------------------------------------------- /examples/text/text_flow.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Basic text flowing example including the use of bounding boxes. A somewhat 4 | # old example, mostly retained for nostalgia. 5 | # 6 | require File.expand_path(File.join(File.dirname(__FILE__), 7 | %w[.. example_helper])) 8 | 9 | content = <<-EOS 10 | How does 11 | Prawn deal with 12 | white 13 | space 14 | 15 | and 16 | 17 | line 18 | breaks? 19 | EOS 20 | 21 | poem = <<-EOS 22 | GOOD-BYE 23 | 24 | Good-bye, proud world! I'm going home: Thou art not my friend, and I'm not thine. Long through thy weary crowds I roam; A river-ark on the ocean brine, Long I've been tossed like the driven foam: But now, proud world! I'm going home. 25 | 26 | Good-bye to Flattery's fawning face; To Grandeur with his wise grimace; To upstart Wealth's averted eye; To supple Office, low and high; To crowded halls, to court and street; To frozen hearts and hasting feet; To those who go, and those who come; Good-bye, proud world! I'm going home. 27 | 28 | I am going to my own hearth-stone, Bosomed in yon green hills alone,-- secret nook in a pleasant land, Whose groves the frolic fairies planned; Where arches green, the livelong day, Echo the blackbird's roundelay, And vulgar feet have never trod A spot that is sacred to thought and God. 29 | 30 | O, when I am safe in my sylvan home, I tread on the pride of Greece and Rome; And when I am stretched beneath the pines, Where the evening star so holy shines, I laugh at the lore and the pride of man, At the sophist schools and the learned clan; For what are they all, in their high conceit, When man in the bush with God may meet? 31 | EOS 32 | 33 | overflow = "This text should flow gracefully onto the next page, like a stream"+ 34 | " flows elegantly from a mountain lake down into the village below." 35 | 36 | Prawn::Document.generate("flow.pdf") do |pdf| 37 | 38 | pdf.font "Times-Roman" 39 | pdf.stroke_line [pdf.bounds.left, pdf.bounds.top], 40 | [pdf.bounds.right, pdf.bounds.top] 41 | 42 | pdf.text content, :size => 10 43 | 44 | pdf.bounding_box([100,600], :width => 200, :height => 525) do 45 | pdf.stroke_line [pdf.bounds.left, pdf.bounds.top], 46 | [pdf.bounds.right, pdf.bounds.top] 47 | pdf.text poem, :size => 12 48 | end 49 | 50 | pdf.bounding_box([325,600], :width => 200, :height => 525) do 51 | pdf.stroke_line [pdf.bounds.left, pdf.bounds.top], 52 | [pdf.bounds.right, pdf.bounds.top] 53 | pdf.text poem.reverse, :size => 12 54 | end 55 | 56 | pdf.text overflow * 10, :size => 14 57 | 58 | pdf.text "Hooray! We've conquered the evil PDF gods", :size => 36 59 | 60 | pdf.bounding_box([100,450], :width => 300) do 61 | pdf.stroke_line [pdf.bounds.left, pdf.bounds.top], 62 | [pdf.bounds.right, pdf.bounds.top] 63 | pdf.text poem, :size => 10, :leading => 5 64 | end 65 | 66 | pdf.text "And this text automatically goes below the poem", :size => 18 67 | 68 | end 69 | -------------------------------------------------------------------------------- /examples/text/rotated.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Demonstrates transformations 4 | # 5 | require File.expand_path(File.join(File.dirname(__FILE__), 6 | %w[.. example_helper])) 7 | 8 | Prawn::Document.generate "rotated_text.pdf" do |pdf| 9 | pdf.line_width = 1 10 | width = 150 11 | height = 200 12 | half_width = width / 2 13 | half_height = height / 2 14 | angle = 30 15 | 16 | 17 | # AROUND THE CENTER 18 | 19 | x = pdf.bounds.width / 2 - half_width 20 | y = pdf.bounds.height / 2 + half_height 21 | 22 | pdf.stroke_rectangle([x, y], width, height) 23 | pdf.rotate(angle, :origin => [x + half_width, y - half_height]) do 24 | pdf.stroke_rectangle([x, y], width, height) 25 | end 26 | pdf.text_box("rotated around the center " * 10, 27 | :at => [x, y], 28 | :width => width, 29 | :height => height, 30 | :rotate => angle, 31 | :rotate_around => :center) 32 | 33 | 34 | # AROUND THE UPPER_LEFT_CORNER 35 | 36 | x = pdf.bounds.width - width 37 | y = height 38 | 39 | pdf.stroke_rectangle([x, y], width, height) 40 | pdf.rotate(angle, :origin => [x, y]) do 41 | pdf.stroke_rectangle([x, y], width, height) 42 | end 43 | pdf.text_box("rotated around upper left corner " * 10, 44 | :at => [x, y], 45 | :width => width, 46 | :height => height, 47 | :rotate => angle) 48 | 49 | 50 | # AROUND THE UPPER_RIGHT_CORNER 51 | 52 | x = 0 53 | y = height 54 | 55 | pdf.stroke_rectangle([x, y], width, height) 56 | pdf.rotate(angle, :origin => [x + width, y]) do 57 | pdf.stroke_rectangle([x, y], width, height) 58 | end 59 | pdf.text_box("rotated around upper right corner " * 10, 60 | :at => [x, y], 61 | :width => width, 62 | :height => height, 63 | :rotate => angle, 64 | :rotate_around => :upper_right) 65 | 66 | 67 | # AROUND THE LOWER_RIGHT_CORNER 68 | 69 | x = 0 70 | y = pdf.bounds.height 71 | 72 | pdf.stroke_rectangle([x, y], width, height) 73 | pdf.rotate(angle, :origin => [x + width, y - height]) do 74 | pdf.stroke_rectangle([x, y], width, height) 75 | end 76 | pdf.text_box("rotated around lower right corner " * 10, 77 | :at => [x, y], 78 | :width => width, 79 | :height => height, 80 | :rotate => angle, 81 | :rotate_around => :lower_right) 82 | 83 | 84 | # AROUND THE LOWER_LEFT_CORNER 85 | 86 | x = pdf.bounds.width - width 87 | y = pdf.bounds.height 88 | 89 | pdf.stroke_rectangle([x, y], width, height) 90 | pdf.rotate(angle, :origin => [x, y - height]) do 91 | pdf.stroke_rectangle([x, y], width, height) 92 | end 93 | pdf.text_box("rotated around lower left corner " * 10, 94 | :at => [x, y], 95 | :width => width, 96 | :height => height, 97 | :rotate => angle, 98 | :rotate_around => :lower_left) 99 | end 100 | -------------------------------------------------------------------------------- /spec/transparency_spec.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper") 2 | 3 | module TransparencyHelper 4 | def make_transparent(opacity, stroke_opacity=opacity) 5 | @pdf.transparent(opacity, stroke_opacity) do 6 | yield if block_given? 7 | end 8 | end 9 | end 10 | 11 | describe "Document with transparency" do 12 | include TransparencyHelper 13 | 14 | it "the PDF version should be at least 1.4" do 15 | create_pdf 16 | make_transparent(0.5) 17 | str = @pdf.render 18 | str[0,8].should == "%PDF-1.4" 19 | end 20 | 21 | it "a new extended graphics state should be created for "+ 22 | "each unique transparency setting" do 23 | create_pdf 24 | make_transparent(0.5, 0.2) do 25 | make_transparent(0.5, 0.75) 26 | end 27 | extgstates = PDF::Inspector::ExtGState.analyze(@pdf.render).extgstates 28 | extgstates.length.should == 2 29 | end 30 | 31 | it "a new extended graphics state should not be created for "+ 32 | "each duplicate transparency setting" do 33 | create_pdf 34 | make_transparent(0.5, 0.75) do 35 | make_transparent(0.5, 0.75) 36 | end 37 | extgstates = PDF::Inspector::ExtGState.analyze(@pdf.render).extgstates 38 | extgstates.length.should == 1 39 | end 40 | 41 | it "setting the transparency with only one parameter sets the transparency"+ 42 | " for both the fill and the stroke" do 43 | create_pdf 44 | make_transparent(0.5) 45 | extgstate = PDF::Inspector::ExtGState.analyze(@pdf.render).extgstates[0] 46 | extgstate[:opacity].should == 0.5 47 | extgstate[:stroke_opacity].should == 0.5 48 | end 49 | 50 | it "setting the transparency with a numerical parameter and "+ 51 | "a :stroke should set the fill transparency to the numerical parameter "+ 52 | "and the stroke transparency to the option" do 53 | create_pdf 54 | make_transparent(0.5, 0.2) 55 | extgstate = PDF::Inspector::ExtGState.analyze(@pdf.render).extgstates[0] 56 | extgstate[:opacity].should == 0.5 57 | extgstate[:stroke_opacity].should == 0.2 58 | end 59 | 60 | it "should enforce the valid range of 0.0 to 1.0" do 61 | create_pdf 62 | make_transparent(-0.5, -0.2) 63 | extgstate = PDF::Inspector::ExtGState.analyze(@pdf.render).extgstates[0] 64 | extgstate[:opacity].should == 0.0 65 | extgstate[:stroke_opacity].should == 0.0 66 | 67 | create_pdf 68 | make_transparent(2.0, 3.0) 69 | extgstate = PDF::Inspector::ExtGState.analyze(@pdf.render).extgstates[0] 70 | extgstate[:opacity].should == 1.0 71 | extgstate[:stroke_opacity].should == 1.0 72 | end 73 | 74 | describe "with more than one page" do 75 | include TransparencyHelper 76 | 77 | it "the extended graphic state resource should be added to both pages" do 78 | create_pdf 79 | make_transparent(0.5, 0.2) 80 | @pdf.start_new_page 81 | make_transparent(0.5, 0.2) 82 | extgstates = PDF::Inspector::ExtGState.analyze(@pdf.render).extgstates 83 | extgstate = extgstates[0] 84 | extgstates.length.should == 2 85 | extgstate[:opacity].should == 0.5 86 | extgstate[:stroke_opacity].should == 0.2 87 | end 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /lib/prawn/document/snapshot.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | # snapshot.rb : Implements transactional rendering for Prawn 4 | # 5 | # Copyright August 2009, Brad Ediger. All Rights Reserved. 6 | # 7 | # This is free software. Please see the LICENSE and COPYING files for details. 8 | 9 | require 'delegate' 10 | 11 | module Prawn 12 | class Document 13 | module Snapshot 14 | 15 | RollbackTransaction = Class.new(StandardError) 16 | 17 | # Call this within a +transaction+ block to roll back the transaction and 18 | # prevent any of its data from being rendered. You must reset the 19 | # y-position yourself if you have performed any drawing operations that 20 | # modify it. 21 | # 22 | def rollback 23 | raise RollbackTransaction 24 | end 25 | 26 | # Run a block of drawing operations, to be completed atomically. If 27 | # +rollback+ is called or a RollbackTransaction exception is raised 28 | # inside the block, all actions taken inside the block will be rolled 29 | # back (with the exception of y-position, which you must restore 30 | # yourself). 31 | # 32 | # Returns true on success, or false if the transaction was rolled back. 33 | # 34 | def transaction 35 | snap = take_snapshot 36 | yield 37 | true 38 | rescue RollbackTransaction 39 | restore_snapshot(snap) 40 | false 41 | end 42 | 43 | private 44 | 45 | # Takes a current snapshot of the document's state, sufficient to 46 | # reconstruct it after it was amended. 47 | # 48 | def take_snapshot 49 | # current_page holds a ref to the Pages dictionary which grows 50 | # monotonically as data is added to the document, so we share that 51 | # between the old and new copies. 52 | {:page_content => state.page.content.deep_copy, 53 | :current_page => state.page.dictionary.deep_copy(share=[:Parent]), 54 | :page_number => page_number, 55 | :page_kids => state.store.pages.data[:Kids].map{|kid| kid.identifier}, 56 | :dests => names? && 57 | Marshal.load(Marshal.dump(names.data[:Dests]))} 58 | end 59 | 60 | # Rolls the page state back to the state of the given snapshot. 61 | # 62 | def restore_snapshot(shot) 63 | page = state.page 64 | # Because these objects are referenced by identifier from the Pages 65 | # dictionary, we can't just restore them over the current refs in 66 | # page_content and current_page. We have to restore them over the old 67 | # ones. 68 | page.content = shot[:page_content].identifier 69 | page.content.replace shot[:page_content] 70 | 71 | page.dictionary = shot[:current_page].identifier 72 | page.dictionary.replace shot[:current_page] 73 | page.dictionary.data[:Contents] = page.content 74 | 75 | self.page_number = shot[:page_number] 76 | 77 | state.store.pages.data[:Kids] = shot[:page_kids].map{|id| state.store[id]} 78 | state.store.pages.data[:Count] = shot[:page_kids].size 79 | 80 | if shot[:dests] 81 | names.data[:Dests] = shot[:dests] 82 | end 83 | end 84 | 85 | end 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /lib/prawn/core/destinations.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | # prawn/core/destinations.rb : Implements destination support for PDF 4 | # 5 | # Copyright November 2008, Jamis Buck. All Rights Reserved. 6 | # 7 | # This is free software. Please see the LICENSE and COPYING files for details. 8 | 9 | module Prawn 10 | module Core 11 | module Destinations #:nodoc: 12 | 13 | # The maximum number of children to fit into a single node in the Dests tree. 14 | NAME_TREE_CHILDREN_LIMIT = 20 #:nodoc: 15 | 16 | # The Dests name tree in the Name dictionary (see Prawn::Document::Internal#names). 17 | # This name tree is used to store named destinations (PDF spec 8.2.1). 18 | # (For more on name trees, see section 3.8.4 in the PDF spec.) 19 | # 20 | def dests 21 | names.data[:Dests] ||= ref!(Prawn::Core::NameTree::Node.new(self, NAME_TREE_CHILDREN_LIMIT)) 22 | end 23 | 24 | # Adds a new destination to the dests name tree (see #dests). The 25 | # +reference+ parameter will be converted into a Prawn::Reference if 26 | # it is not already one. 27 | # 28 | def add_dest(name, reference) 29 | reference = ref!(reference) unless reference.is_a?(Prawn::Core::Reference) 30 | dests.data.add(name, reference) 31 | end 32 | 33 | # Return a Dest specification for a specific location (and optional zoom 34 | # level). 35 | # 36 | def dest_xyz(left, top, zoom=nil, page=current_page) 37 | [page, :XYZ, left, top, zoom] 38 | end 39 | 40 | # Return a Dest specification that will fit the given page into the 41 | # viewport. 42 | # 43 | def dest_fit(page=current_page) 44 | [page, :Fit] 45 | end 46 | 47 | # Return a Dest specification that will fit the given page horizontally 48 | # into the viewport, aligned vertically at the given top coordinate. 49 | # 50 | def dest_fit_horizontally(top, page=current_page) 51 | [page, :FitH, top] 52 | end 53 | 54 | # Return a Dest specification that will fit the given page vertically 55 | # into the viewport, aligned horizontally at the given left coordinate. 56 | # 57 | def dest_fit_vertically(left, page=current_page) 58 | [page, :FitV, left] 59 | end 60 | 61 | # Return a Dest specification that will fit the given rectangle into the 62 | # viewport, for the given page. 63 | # 64 | def dest_fit_rect(left, bottom, right, top, page=current_page) 65 | [page, :FitR, left, bottom, right, top] 66 | end 67 | 68 | # Return a Dest specfication that will fit the given page's bounding box 69 | # into the viewport. 70 | # 71 | def dest_fit_bounds(page=current_page) 72 | [page, :FitB] 73 | end 74 | 75 | # Same as #dest_fit_horizontally, but works on the page's bounding box 76 | # instead of the entire page. 77 | # 78 | def dest_fit_bounds_horizontally(top, page=current_page) 79 | [page, :FitBH, top] 80 | end 81 | 82 | # Same as #dest_fit_vertically, but works on the page's bounding box 83 | # instead of the entire page. 84 | # 85 | def dest_fit_bounds_vertically(left, page=current_page) 86 | [page, :FitBV, left] 87 | end 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /spec/grid_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper") 3 | 4 | describe "A document's grid" do 5 | before do 6 | @pdf = Prawn::Document.new 7 | end 8 | 9 | it "should allow definition of a grid" do 10 | @pdf.define_grid(:columns => 5, :rows => 8, :gutter => 0.1) 11 | @pdf.grid.columns.should == 5 12 | @pdf.grid.rows.should == 8 13 | @pdf.grid.gutter.should == 0.1 14 | end 15 | 16 | describe "when a grid is defined" do 17 | before do 18 | @num_columns = 5 19 | @num_rows = 8 20 | @gutter = 10.0 21 | @pdf.define_grid( 22 | :columns => @num_columns, 23 | :rows => @num_rows, 24 | :gutter => @gutter 25 | ) 26 | end 27 | 28 | it "should compute the column width" do 29 | (@pdf.grid.column_width * @num_columns.to_f + 30 | @gutter * (@num_columns - 1).to_f).should == @pdf.bounds.width 31 | end 32 | 33 | it "should compute the row height" do 34 | (@pdf.grid.row_height * @num_rows.to_f + 35 | @gutter * (@num_rows - 1).to_f).should == @pdf.bounds.height 36 | end 37 | 38 | it "should give the edges of a grid box" do 39 | grid_width = (@pdf.bounds.width.to_f - 40 | (@gutter * (@num_columns - 1).to_f )) / @num_columns.to_f 41 | grid_height = (@pdf.bounds.height.to_f - 42 | (@gutter * (@num_rows - 1).to_f ))/ @num_rows.to_f 43 | 44 | exp_tl_x = (grid_width + @gutter.to_f) * 4.0 45 | exp_tl_y = @pdf.bounds.height.to_f - (grid_height + @gutter.to_f) 46 | 47 | @pdf.grid(1,4).top_left.should == [exp_tl_x, exp_tl_y] 48 | @pdf.grid(1,4).top_right.should == [exp_tl_x + grid_width, exp_tl_y] 49 | @pdf.grid(1,4).bottom_left.should == [exp_tl_x, exp_tl_y - grid_height] 50 | @pdf.grid(1,4).bottom_right.should == [exp_tl_x + grid_width, exp_tl_y - grid_height] 51 | end 52 | 53 | it "should give the edges of a multiple grid boxes" do 54 | # Hand verified. Cheating a bit. Don't tell. 55 | @pdf.grid([1,3], [2,5]).top_left.should == [330.0, 628.75] 56 | @pdf.grid([1,3], [2,5]).top_right.should == [650.0, 628.75] 57 | @pdf.grid([1,3], [2,5]).bottom_left.should == [330.0, 456.25] 58 | @pdf.grid([1,3], [2,5]).bottom_right.should == [650.0, 456.25] 59 | end 60 | 61 | it "should draw outlines without changing global default colors to grid color" do 62 | @pdf.grid.show_all('cccccc') 63 | 64 | colors = PDF::Inspector::Graphics::Color.analyze(@pdf.render) 65 | colors.fill_color.should.not == [0.8,0.8,0.8] 66 | colors.stroke_color.should.not == [0.8,0.8,0.8] 67 | 68 | # Hardcoded default color as I haven't been able to come up with a stable converter 69 | # between fill_color without lots code. 70 | colors.fill_color.should == [0.0,0.0,0.0] 71 | colors.stroke_color.should == [0.0,0.0,0.0] 72 | end 73 | 74 | it "should draw outlines without curent color settings" do 75 | @pdf.fill_color "ccff00" 76 | @pdf.stroke_color "ffcc00" 77 | 78 | @pdf.grid.show_all 79 | 80 | colors = PDF::Inspector::Graphics::Color.analyze(@pdf.render) 81 | colors.fill_color.should == [0.8,1.0,0.0] 82 | colors.stroke_color.should == [1.0,0.8,0.0] 83 | end 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /lib/prawn/core/reference.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | # reference.rb : Implementation of PDF indirect objects 4 | # 5 | # Copyright April 2008, Gregory Brown. All Rights Reserved. 6 | # 7 | # This is free software. Please see the LICENSE and COPYING files for details. 8 | 9 | require 'zlib' 10 | 11 | module Prawn 12 | module Core 13 | 14 | class Reference #:nodoc: 15 | 16 | attr_accessor :gen, :data, :offset, :stream, :live, :identifier 17 | 18 | def initialize(id, data) 19 | @identifier = id 20 | @gen = 0 21 | @data = data 22 | @compressed = false 23 | @stream = nil 24 | end 25 | 26 | def object 27 | output = "#{@identifier} #{gen} obj\n" << 28 | Prawn::Core::PdfObject(data) << "\n" 29 | if @stream 30 | output << "stream\n" << @stream << "\nendstream\n" 31 | end 32 | output << "endobj\n" 33 | end 34 | 35 | def <<(data) 36 | raise 'Cannot add data to a stream that is compressed' if @compressed 37 | (@stream ||= "") << data 38 | end 39 | 40 | def to_s 41 | "#{@identifier} #{gen} R" 42 | end 43 | 44 | def compress_stream 45 | @stream = Zlib::Deflate.deflate(@stream) 46 | @data[:Filter] = :FlateDecode 47 | @data[:Length] ||= @stream.length 48 | @compressed = true 49 | end 50 | 51 | def compressed? 52 | @compressed 53 | end 54 | 55 | # Creates a deep copy of this ref. If +share+ is provided, shares the 56 | # given dictionary entries between the old ref and the new. 57 | # 58 | def deep_copy(share=[]) 59 | r = dup 60 | 61 | if r.data.is_a?(Hash) 62 | # Copy each entry not in +share+. 63 | (r.data.keys - share).each do |k| 64 | r.data[k] = Marshal.load(Marshal.dump(r.data[k])) 65 | end 66 | else 67 | r.data = Marshal.load(Marshal.dump(r.data)) 68 | end 69 | 70 | r.stream = Marshal.load(Marshal.dump(r.stream)) 71 | r 72 | end 73 | 74 | # Replaces the data and stream with that of other_ref. Preserves compressed 75 | # status. 76 | def replace(other_ref) 77 | @data = other_ref.data 78 | @stream = other_ref.stream 79 | @compressed = other_ref.compressed? 80 | end 81 | 82 | # Marks this and all referenced objects live, recursively. 83 | def mark_live 84 | return if @live 85 | @live = true 86 | referenced_objects.each { |o| o.mark_live } 87 | end 88 | 89 | private 90 | 91 | # All objects referenced by this one. Used for GC. 92 | def referenced_objects(obj=@data) 93 | case obj 94 | when self.class 95 | [] 96 | when Hash 97 | obj.values.map{|v| [v] + referenced_objects(v) } 98 | when Array 99 | obj.map{|v| [v] + referenced_objects(v) } 100 | else [] 101 | end.flatten.grep(self.class) 102 | end 103 | 104 | end 105 | 106 | module_function 107 | 108 | def Reference(*args, &block) #:nodoc: 109 | Reference.new(*args, &block) 110 | end 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /lib/prawn/errors.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # errors.rb : Implements custom error classes for Prawn 4 | # 5 | # Copyright April 2008, Gregory Brown. All Rights Reserved. 6 | # 7 | # This is free software. Please see the LICENSE and COPYING files for details. 8 | # 9 | module Prawn 10 | module Errors 11 | 12 | # This error is raised when Prawn::PdfObject() encounters a Ruby object it 13 | # cannot convert to PDF 14 | # 15 | FailedObjectConversion = Class.new(StandardError) 16 | 17 | # This error is raised when Document#page_layout is set to anything 18 | # other than :portrait or :landscape 19 | # 20 | InvalidPageLayout = Class.new(StandardError) 21 | 22 | # This error is raised when a method requiring a current page is called 23 | # without being on a page. 24 | # 25 | NotOnPage = Class.new(StandardError) 26 | 27 | # This error is raised when Prawn cannot find a specified font 28 | # 29 | UnknownFont = Class.new(StandardError) 30 | 31 | # Raised when Prawn is asked to draw something into a too-small box 32 | # 33 | CannotFit = Class.new(StandardError) 34 | 35 | # Raised if group() is called with a block that is too big to be 36 | # rendered in the current context. 37 | # 38 | CannotGroup = Class.new(StandardError) 39 | 40 | # This error is raised when Prawn is being used on a M17N aware VM, 41 | # and the user attempts to add text that isn't compatible with UTF-8 42 | # to their document 43 | # 44 | IncompatibleStringEncoding = Class.new(StandardError) 45 | 46 | # This error is raised when Prawn encounters an unknown key in functions 47 | # that accept an options hash. This usually means there is a typo in your 48 | # code or that the option you are trying to use has a different name than 49 | # what you have specified. 50 | # 51 | UnknownOption = Class.new(StandardError) 52 | 53 | # this error is raised when a user attempts to embed an image of an unsupported 54 | # type. This can either a completely unsupported format, or a dialect of a 55 | # supported format (ie. some types of PNG) 56 | UnsupportedImageType = Class.new(StandardError) 57 | 58 | # This error is raised when a named element has alredy been 59 | # created. For example, in the stamp module, stamps must have 60 | # unique names within a document 61 | NameTaken = Class.new(StandardError) 62 | 63 | # This error is raised when a name is not a valid format 64 | InvalidName = Class.new(StandardError) 65 | 66 | # This error is raised when an object is attempted to be 67 | # referenced by name, but no such name is associated with an object 68 | UndefinedObjectName = Class.new(StandardError) 69 | 70 | # This error is raised when a required option has not been set 71 | RequiredOption = Class.new(StandardError) 72 | 73 | # This error is raised when a requested outline item with a given title does not exist 74 | UnknownOutlineTitle = Class.new(StandardError) 75 | 76 | # This error is raised when a block is required, but not provided 77 | BlockRequired = Class.new(StandardError) 78 | 79 | # This error is rased when a graphics method is called with improper arguments 80 | InvalidGraphicsPath = Class.new(StandardError) 81 | 82 | # This error is raised when Prawn fails to load a template file 83 | # 84 | TemplateError = Class.new(StandardError) 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /lib/prawn/core/text/wrap.rb: -------------------------------------------------------------------------------- 1 | require "prawn/core/text/line_wrap" 2 | 3 | module Prawn 4 | module Core 5 | module Text 6 | module Wrap #:nodoc: 7 | 8 | def initialize(text, options) 9 | @line_wrap = Prawn::Core::Text::LineWrap.new 10 | end 11 | 12 | # #wrap is part of the developer API. Override it in extensions to Prawn 13 | # that inherit Text::Box but need a different placement algorithm. 14 | # #wrap is where the actual placement of text happens. If @inked is 15 | # false, then all the placement computations should be performed, and 16 | # unprinted text returned, but no text should actually be drawn to the 17 | # PDF. This enables look-ahead computations that need to know whether all 18 | # the text was printed under a set of conditions or how tall the text was 19 | # under certain conditions. 20 | # 21 | # #wrap is called from several places within box.rb and relies on 22 | # certain conditions established by render. Do not call #wrap from 23 | # outside of Text::Box or its descendants. 24 | # 25 | # #wrap should set the following instance variables: 26 | # @text:: the text that was printed 27 | # @line_height:: the height of the last printed line 28 | # @descender:: the descender height of the last printed line 29 | # @ascender:: the ascender heigth of the last printed line 30 | # @baseline_y:: the base line of the last printed line 31 | # 32 | # Returns any unprinted text 33 | # 34 | def wrap(text) #:nodoc: 35 | @text = nil 36 | remaining_text = text 37 | @line_height = @document.font.height 38 | @descender = @document.font.descender 39 | @ascender = @document.font.ascender 40 | @baseline_y = -@ascender 41 | 42 | printed_lines = [] 43 | 44 | while remaining_text && 45 | remaining_text.length > 0 && 46 | @baseline_y.abs + @descender <= @height 47 | line_to_print = @line_wrap.wrap_line(remaining_text.first_line, 48 | :document => @document, 49 | :kerning => @kerning, 50 | :width => available_width) 51 | 52 | remaining_text = remaining_text.slice(@line_wrap.consumed_char_count.. 53 | remaining_text.length) 54 | include_ellipses = (@overflow == :ellipses && last_line? && 55 | remaining_text.length > 0) 56 | printed_lines << draw_line(line_to_print, @line_wrap.width, 57 | word_spacing_for_this_line, include_ellipses) 58 | @baseline_y -= (@line_height + @leading) 59 | break if @single_line 60 | end 61 | 62 | @text = printed_lines.join("\n") 63 | 64 | remaining_text 65 | end 66 | 67 | private 68 | 69 | def word_spacing_for_this_line 70 | if @align == :justify && 71 | @line_wrap.space_count > 0 && 72 | @line_wrap.width.to_f / available_width.to_f >= 0.75 73 | (available_width - @line_wrap.width) / @line_wrap.space_count 74 | else 75 | 0 76 | end 77 | end 78 | 79 | end 80 | end 81 | end 82 | end 83 | --------------------------------------------------------------------------------