Text-Flow-0.01/0000755000076500007650000000000010624331524014117 5ustar stevanstevan00000000000000Text-Flow-0.01/Build.PL0000444000076500007650000000114110624331524015406 0ustar stevanstevan00000000000000use Module::Build; use strict; use warnings; my $build = Module::Build->new( module_name => 'Text::Flow', license => 'perl', requires => { 'Moose' => '0.21' }, optional => { 'Test::PDF' => '0.01', }, build_requires => { 'Test::More' => '0.62', 'Test::Exception' => '0.21', 'Test::LongString' => '0', 'List::Util' => '0', }, create_makefile_pl => 'traditional', recursive_test_files => 1, add_to_cleanup => [ 'META.yml', '*.bak', '*.gz', 'Makefile.PL', ], ); $build->create_build_script; Text-Flow-0.01/Changes0000444000076500007650000000023510624331524015410 0ustar stevanstevan00000000000000Revision history for Perl extension Moose 0.01 - I needed a text wrapping module which was a little more flexible then the other ones out there.Text-Flow-0.01/lib/0000755000076500007650000000000010624331524014665 5ustar stevanstevan00000000000000Text-Flow-0.01/lib/Text/0000755000076500007650000000000010624331524015611 5ustar stevanstevan00000000000000Text-Flow-0.01/lib/Text/Flow/0000755000076500007650000000000010624331524016520 5ustar stevanstevan00000000000000Text-Flow-0.01/lib/Text/Flow/Wrap.pm0000444000076500007650000001230210624331524017763 0ustar stevanstevan00000000000000 package Text::Flow::Wrap; use Moose; our $VERSION = '0.01'; our $AUTHORITY = 'cpan:STEVAN'; has 'check_width' => ( is => 'rw', isa => 'CodeRef', required => 1, ); has 'word_boundry' => (is => 'rw', isa => 'Str', default => " "); has 'paragraph_boundry' => (is => 'rw', isa => 'Str', default => "\n"); has 'word_break' => (is => 'rw', isa => 'Str', default => " "); has 'line_break' => (is => 'rw', isa => 'Str', default => "\n"); has 'paragraph_break' => (is => 'rw', isa => 'Str', default => "\n\n"); sub wrap { my ($self, $text) = @_; $self->reassemble_paragraphs( $self->disassemble_paragraphs($text) ); } sub reassemble_paragraphs { my ($self, $paragraphs) = @_; join $self->paragraph_break => map { $self->reassemble_paragraph($_) } @$paragraphs; } sub reassemble_paragraph { my ($self, $paragraph) = @_; join $self->line_break => @$paragraph; } sub disassemble_paragraphs { my ($self, $text) = @_; my @paragraphs = split $self->paragraph_boundry => $text; my @output; foreach my $paragraph (@paragraphs) { push @output => $self->disassemble_paragraph($paragraph); } return \@output; } sub disassemble_paragraph { my ($self, $text) = @_; my @output = (''); my @words = split $self->word_boundry => $text; my $work_break = $self->word_break; foreach my $word (@words) { my $padded_word = ($word . $work_break); my $canidate = ($output[-1] . $padded_word); if ($self->check_width->($canidate)) { $output[-1] = $canidate; } else { push @output => ($padded_word); } } # NOTE: # remove that final word break character chop $output[-1] if substr($output[-1], -1, 1) eq $work_break; return \@output; } no Moose; 1; __END__ =pod =head1 NAME Text::Flow::Wrap - Flexible word wrapping for not just ASCII output. =head1 SYNOPSIS use Text::Flow::Wrap; # for regular ASCII usage ... my $wrapper = Text::Flow::Wrap->new( check_width => sub { length($_[0]) < 70 }, ); # for non-ASCII usage ... my $wrapper = Text::Flow::Wrap->new( check_width => sub { $pdf->get_text_width($_[0]) < 500 }, ); my $text = $wrapper->wrap($text); =head1 DESCRIPTION The main purpose of this module is to provide text wrapping features without being tied down to ASCII based output and fixed-width fonts. My needs were for sophisticated test control in PDF and GIF output formats in particular. =head1 METHODS =over 4 =item B This constructs a new Text::Flow::Wrap module whose C<%params> set the values of the attributes listed below. =item B This method will accept a bunch of text, it will then return a new string which is wrapped to the expected width. =back =head2 Attribute Accessors =over 4 =item B This attribute is required, and must be a CODE reference. This will be used to determine if the width of the text is appropriate. It will get as an argument, a string which is should check the width of. It should return a Boolean value, true if the string is not exceeded the max width and false if it has. =item B This is the line break character used when assembling and disassembling the text, it defaults to the newline character C<\n>. =item B This is the paragraph boundry marker used when disassembling the text, it defaults to the string C<\n>. =item B This is the paragraph breaker used when re-assembling the text, it defaults to the string C<\n\n>. =item B This is the word boundry marker used when disassembling the text, it defaults to a single space character. =item B This is the paragraph breaker used when re-assembling the text, it defaults to a single space character. =back =head2 Paragraph Disassembling These methods deal with breaking up the paragraphs into its parts, which can then be processed through the re-assembling methods. These methods are mostly used internally, but more sophisticated tools might need to access them as well (see Text::Flow). =over 4 =item B =item B =back =head2 Paragraph Reassembling These methods deal with putting the paragraph parts back together after the disassembling methods have done thier work. These methods are mostly used internally, but more sophisticated tools might need to access them as well (see Text::Flow) =over 4 =item B =item B =back =head2 Introspection =over 4 =item B Returns the Moose meta object associated with this class. =back =head1 BUGS All complex software has bugs lurking in it, and this module is no exception. If you find a bug please either email me, or add the bug to cpan-RT. =head1 AUTHOR Stevan Little Estevan@iinteractive.comE =head1 COPYRIGHT AND LICENSE Copyright 2007 by Infinity Interactive, Inc. L This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut Text-Flow-0.01/lib/Text/Flow.pm0000444000076500007650000001134010624331524017053 0ustar stevanstevan00000000000000 package Text::Flow; use Moose; use Text::Flow::Wrap; our $VERSION = '0.01'; our $AUTHORITY = 'cpan:STEVAN'; has 'check_height' => ( is => 'rw', isa => 'CodeRef', required => 1, ); has 'wrapper' => ( is => 'rw', isa => 'Text::Flow::Wrap', required => 1, ); sub flow { my ($self, $text) = @_; my $paragraphs = $self->wrapper->disassemble_paragraphs($text); my @sections = ([]); foreach my $paragraph (@$paragraphs) { push @{$sections[-1]} => []; foreach my $line (@$paragraph) { unless ($self->check_height->($sections[-1])) { push @sections => [[]]; } push @{$sections[-1]->[-1]} => $line; } } #use Data::Dumper; #warn Dumper \@sections; return map { chomp; $_; } map { $self->wrapper->reassemble_paragraphs($_); } @sections; } no Moose; 1; __END__ =pod =head1 NAME Text::Flow - Flexible text flowing and word wrapping for not just ASCII output. =head1 SYNOPSIS use Text::Flow; # use it on ASCII text ... my $flow = Text::Flow->new( check_height => sub { my $paras = shift; sum(map { scalar @$_ } @$paras) <= 10; }, wrapper => Text::Flow::Wrap->new( check_width => sub { length($_[0]) < 70 } ), ); my @sections = $flow->flow($text); # @sections will now be an array of strings, each string # will contain no more than 10 lines of text each of which # will be no longer then 70 characters long # or use it on text in a PDF file ... my $flow = Text::Flow->new( check_height => sub { my $paras = shift; (sum(map { scalar @$_ } @$paras) * $pdf->get_font_height) < 200; }, wrapper => Text::Flow::Wrap->new( check_width => sub { my $string = shift; $pdf->get_string_width($string) < 100 }, ) ); my @sections = $flow->flow($text); # @sections will now be an array of strings, each string # will contain text which will fit within 200 pts and # each line will be no longer then 100 pts wide =head1 DESCRIPTION This module provides a flexible way to wrap and flow text for both ASCII and non-ASCII outputs. =head2 Another Text Wrap module!!?!?! The main purpose of this module is to provide text wrapping and flowing features without being tied down to ASCII based output and fixed-width fonts. My needs were for a more sophisticated text control in PDF and GIF output formats in particular. =head1 METHODS =over 4 =item B This constructs the new Text::Flow instance, and accepts parameters for the C and C variables. =item B This is the accessor for the internally help Text::Flow::Wrapper instance which is used by Text::Flow to wrap the individual lines. =item B This is the accessor for the CODE ref which is used to check the height of the current paragraph. It gets as an argument, an array-ref of paragraphs, each of which is also an array-ref of text lines. The most common usage is shown in the SYNOPSIS above, but you can really do anything you want with them. It is expected to return a Boolean value, true if the height is still okay, and false if the max height has been reached. =item B This method preforms the text flowing. It returns an array of strings which can be treated as complete blocks of text. It will handle paragraph breaks and line breaks for you. =back =head2 Introspection =over 4 =item B Returns the Moose meta object associated with this class. =back =head1 TODO I wanted to write some tests for using this with GD modules as well. I suppose we will have to wait until 0.02 for that. =head1 SIMILAR MODULES There are a bunch of other text wrapping and flowing modules out there, but they are all meant for use in ASCII outout only. This just didn't work for my needs, so I wrote this one. If you need to wrap ASCII text and want to do it in a much simpler manner then this module provides, then I suggest checking out those modules. This module is specifically made for when those other modules are no longer powerful and flexible enough. =head1 BUGS All complex software has bugs lurking in it, and this module is no exception. If you find a bug please either email me, or add the bug to cpan-RT. =head1 AUTHOR Stevan Little Estevan@iinteractive.comE =head1 COPYRIGHT AND LICENSE Copyright 2007 by Infinity Interactive, Inc. L This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut Text-Flow-0.01/Makefile.PL0000444000076500007650000000114210624331524016065 0ustar stevanstevan00000000000000# Note: this file was auto-generated by Module::Build::Compat version 0.03 use ExtUtils::MakeMaker; WriteMakefile ( 'PL_FILES' => {}, 'INSTALLDIRS' => 'site', 'NAME' => 'Text::Flow', 'EXE_FILES' => [], 'VERSION_FROM' => 'lib/Text/Flow.pm', 'PREREQ_PM' => { 'Test::More' => '0.62', 'Test::LongString' => '0', 'List::Util' => '0', 'Test::Exception' => '0.21', 'Moose' => '0.21' } ) ; Text-Flow-0.01/MANIFEST0000444000076500007650000000103210624331524015242 0ustar stevanstevan00000000000000Build.PL Changes Makefile.PL META.yml MANIFEST MANIFEST.SKIP README lib/Text/Flow.pm lib/Text/Flow/Wrap.pm t/001_basic_ascii_text_flow.t t/002_ascii_text_flow_w_para.t t/003_basic_pdf_text_flow.t t/004_basic_pdf_text_flow_w_para.t t/010_basic_ascii_word_wrap.t t/011_ascii_word_wrap_w_para.t t/012_basic_pdf_text_wrap.t t/013_pdf_text_wrap_w_para.t t/pod.t t/pod_coverage.t t/lib/PDFGen.pm t/pdfs/003_basic_pdf_text_flow.pdf t/pdfs/004_basic_pdf_text_flow_w_para.pdf t/pdfs/012_basic_pdf_text_wrap.pdf t/pdfs/013_pdf_text_wrap_w_para.pdf Text-Flow-0.01/MANIFEST.SKIP0000444000076500007650000000024510624331524016014 0ustar stevanstevan00000000000000^_build ^Build$ ^blib ~$ \.bak$ ^MANIFEST\.SKIP$ CVS \.svn \.DS_Store cover_db \..*\.sw.?$ ^Makefile$ ^pm_to_blib$ ^MakeMaker-\d ^blibdirs$ \.old$ ^#.*#$ ^\.# ^TODO$Text-Flow-0.01/META.yml0000444000076500007650000000116710624331524015373 0ustar stevanstevan00000000000000--- name: Text-Flow version: 0.01 author: - 'Stevan Little Estevan@iinteractive.comE' abstract: Flexible text flowing and word wrapping for not just ASCII output. license: perl resources: license: http://dev.perl.org/licenses/ requires: Moose: 0.21 build_requires: List::Util: 0 Test::Exception: 0.21 Test::LongString: 0 Test::More: 0.62 provides: Text::Flow: file: lib/Text/Flow.pm version: 0.01 Text::Flow::Wrap: file: lib/Text/Flow/Wrap.pm version: 0.01 generated_by: Module::Build version 0.2805 meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.2.html version: 1.2 Text-Flow-0.01/README0000444000076500007650000000101210624331524014767 0ustar stevanstevan00000000000000Text-Flow version 0.01 =========================== See the individual module documentation for more information INSTALLATION To install this module type the following: perl Makefile.PL make make test make install DEPENDENCIES This module requires these other modules and libraries: Moose COPYRIGHT AND LICENCE Copyright (C) 2007 Infinity Interactive, Inc. http://www.iinteractive.com This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Text-Flow-0.01/t/0000755000076500007650000000000010624331524014362 5ustar stevanstevan00000000000000Text-Flow-0.01/t/001_basic_ascii_text_flow.t0000444000076500007650000000656410624331524021464 0ustar stevanstevan00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More no_plan => 1; use Test::LongString; use List::Util 'sum'; BEGIN { use_ok('Text::Flow'); use_ok('Text::Flow::Wrap'); } my $wrapper = Text::Flow->new( check_height => sub { my $paras = shift; sum(map { scalar @$_ } @$paras) <= 10; }, wrapper => Text::Flow::Wrap->new( check_width => sub { length($_[0]) < 70 } ), ); my $orig_text = join "" => ; { my @sections = $wrapper->flow($orig_text); is_string( $sections[0], q{Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis lobortis nisl in ante. Vestibulum dignissim facilisis turpis. Nunc rutrum sapien sed eros. Donec facilisis placerat dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean convallis, urna eget mattis accumsan, dolor augue condimentum est, id aliquam pede eros eget metus. Proin felis. Nam hendrerit velit et lorem. Nulla ac mauris in nibh ornare porta. Fusce sodales porta orci. Aenean dolor. Proin nec ligula non eros tristique interdum. Sed aliquet ipsum vel leo. Cras a urna vel tortor molestie tincidunt. Aenean risus. Quisque luctus ipsum sit amet massa. Class aptent taciti sociosqu ad litora torquent per }, '... the string flowed successfully'); is_string( $sections[1], q{conubia nostra, per inceptos hymenaeos. Nam lacus mauris, sagittis volutpat, rutrum eget, commodo non, elit.}, '... the string flowed successfully'); } # shorten it ... $wrapper->wrapper->check_width(sub { length($_[0]) < 45 }); { my @sections = $wrapper->flow($orig_text); is_string( $sections[0], q{Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis lobortis nisl in ante. Vestibulum dignissim facilisis turpis. Nunc rutrum sapien sed eros. Donec facilisis placerat dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean convallis, urna eget mattis accumsan, dolor augue condimentum est, id aliquam pede eros eget metus. Proin felis. Nam hendrerit velit et lorem. Nulla ac mauris in nibh }, '... the string flowed successfully'); is_string( $sections[1], q{ornare porta. Fusce sodales porta orci. Aenean dolor. Proin nec ligula non eros tristique interdum. Sed aliquet ipsum vel leo. Cras a urna vel tortor molestie tincidunt. Aenean risus. Quisque luctus ipsum sit amet massa. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Nam lacus mauris, sagittis volutpat, rutrum eget, commodo non, elit.}, '... the string flowed successfully'); } __DATA__ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis lobortis nisl in ante. Vestibulum dignissim facilisis turpis. Nunc rutrum sapien sed eros. Donec facilisis placerat dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean convallis, urna eget mattis accumsan, dolor augue condimentum est, id aliquam pede eros eget metus. Proin felis. Nam hendrerit velit et lorem. Nulla ac mauris in nibh ornare porta. Fusce sodales porta orci. Aenean dolor. Proin nec ligula non eros tristique interdum. Sed aliquet ipsum vel leo. Cras a urna vel tortor molestie tincidunt. Aenean risus. Quisque luctus ipsum sit amet massa. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Nam lacus mauris, sagittis volutpat, rutrum eget, commodo non, elit.Text-Flow-0.01/t/002_ascii_text_flow_w_para.t0000444000076500007650000000670110624331524021646 0ustar stevanstevan00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More no_plan => 1; use Test::LongString; use List::Util 'sum'; BEGIN { use_ok('Text::Flow'); use_ok('Text::Flow::Wrap'); } my $wrapper = Text::Flow->new( check_height => sub { my $paras = shift; sum(map { scalar(@$_) + 1 } @$paras) <= 10; }, wrapper => Text::Flow::Wrap->new( check_width => sub { length($_[0]) < 70 } ), ); my $orig_text = join "" => ; { my @sections = $wrapper->flow($orig_text); is_string( $sections[0], q{Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis lobortis nisl in ante. Vestibulum dignissim facilisis turpis. Nunc rutrum sapien sed eros. Donec facilisis placerat dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean convallis, urna eget mattis accumsan, dolor augue condimentum est, id aliquam pede eros eget metus. Proin felis. Nam hendrerit velit et lorem. Nulla ac mauris in nibh ornare porta. Fusce sodales porta orci. Aenean dolor. Proin nec ligula non eros tristique interdum. Sed aliquet ipsum vel leo. }, '... the string wrapped successfully'); is_string( $sections[1], q{Cras a urna vel tortor molestie tincidunt. Aenean risus. Quisque luctus ipsum sit amet massa. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Nam lacus mauris, sagittis volutpat, rutrum eget, commodo non, elit.}, '... the string wrapped successfully'); } # shorten it ... $wrapper->wrapper->check_width(sub { length($_[0]) < 45 }); { my @sections = $wrapper->flow($orig_text); is_string( $sections[0], q{Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis lobortis nisl in ante. Vestibulum dignissim facilisis turpis. Nunc rutrum sapien sed eros. Donec facilisis placerat dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean convallis, urna eget mattis accumsan, dolor augue condimentum est, id aliquam pede eros eget metus. }, '... the string wrapped successfully'); is_string( $sections[1], q{Proin felis. Nam hendrerit velit et lorem. Nulla ac mauris in nibh ornare porta. Fusce sodales porta orci. Aenean dolor. Proin nec ligula non eros tristique interdum. Sed aliquet ipsum vel leo. Cras a urna vel tortor molestie tincidunt. Aenean risus. Quisque luctus ipsum sit amet massa. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per }, '... the string wrapped successfully'); is_string( $sections[2], q{inceptos hymenaeos. Nam lacus mauris, sagittis volutpat, rutrum eget, commodo non, elit.}, '... the string wrapped successfully'); } __DATA__ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis lobortis nisl in ante. Vestibulum dignissim facilisis turpis. Nunc rutrum sapien sed eros. Donec facilisis placerat dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean convallis, urna eget mattis accumsan, dolor augue condimentum est, id aliquam pede eros eget metus. Proin felis. Nam hendrerit velit et lorem. Nulla ac mauris in nibh ornare porta. Fusce sodales porta orci. Aenean dolor. Proin nec ligula non eros tristique interdum. Sed aliquet ipsum vel leo. Cras a urna vel tortor molestie tincidunt. Aenean risus. Quisque luctus ipsum sit amet massa. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Nam lacus mauris, sagittis volutpat, rutrum eget, commodo non, elit.Text-Flow-0.01/t/003_basic_pdf_text_flow.t0000444000076500007650000000550410624331524021140 0ustar stevanstevan00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More; BEGIN { eval "use Test::PDF; use t::lib::PDFGen;"; plan skip_all => "Test::PDF and pdflib_pl required to run PDF tests" if $@; } plan no_plan => 1; use List::Util 'sum'; BEGIN { use_ok('Text::Flow'); use_ok('Text::Flow::Wrap'); } my $PDF_TEST_FILE = 'test_flow.pdf'; { my $pdf = PDFGen->new( pdf_filename => $PDF_TEST_FILE ); $pdf->open_page(height => 300, width => 600); my $font_height = $pdf->font_height; my $orig_start_top = 290; my $start_top = $orig_start_top; my $start_left = 50; my $width = 100; my $height = 200; # create the wrapper .. my $flow = Text::Flow->new( check_height => sub { my $paras = shift; (sum(map { scalar @$_ } @$paras) * $font_height) < 200; }, wrapper => Text::Flow::Wrap->new( check_width => $pdf->get_string_width_function(width => $width), ) ); my $orig_text = join "" => ; my @sections = $flow->flow($orig_text); my $line_top = $start_top; $start_top -= $font_height; my $left = $start_left; foreach my $i (0 .. $#sections) { # draw rectangles to mark the width we want $pdf->draw_rect( left => $left, top => $line_top, width => $width, height => $height + $font_height, ); foreach my $line (split "\n" => $sections[$i]) { $pdf->draw_text( text => $line, left => $left, top => $start_top, ); $start_top -= $font_height; } $start_top = $orig_start_top - $font_height; $left += ($width + 10); } $pdf->close_page; $pdf->write_file; } cmp_pdf($PDF_TEST_FILE, 't/pdfs/003_basic_pdf_text_flow.pdf', '... our PDFs are identical'); unlink($PDF_TEST_FILE); __DATA__ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis lobortis nisl in ante. Vestibulum dignissim facilisis turpis. Nunc rutrum sapien sed eros. Donec facilisis placerat dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean convallis, urna eget mattis accumsan, dolor augue condimentum est, id aliquam pede eros eget metus. Proin felis. Nam hendrerit velit et lorem. Nulla ac mauris in nibh ornare porta. Fusce sodales porta orci. Aenean dolor. Proin nec ligula non eros tristique interdum. Sed aliquet ipsum vel leo. Cras a urna vel tortor molestie tincidunt. Aenean risus. Quisque luctus ipsum sit amet massa. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Nam lacus mauris, sagittis volutpat, rutrum eget, commodo non, elit.Text-Flow-0.01/t/004_basic_pdf_text_flow_w_para.t0000444000076500007650000000553510624331524022476 0ustar stevanstevan00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More; BEGIN { eval "use Test::PDF; use t::lib::PDFGen;"; plan skip_all => "Test::PDF and pdflib_pl required to run PDF tests" if $@; } plan no_plan => 1; use List::Util 'sum'; BEGIN { use_ok('Text::Flow'); use_ok('Text::Flow::Wrap'); } my $PDF_TEST_FILE = 'test_flow_w_para.pdf'; { my $pdf = PDFGen->new( pdf_filename => $PDF_TEST_FILE ); $pdf->open_page(height => 300, width => 600); my $font_height = $pdf->font_height; my $orig_start_top = 290; my $start_top = $orig_start_top; my $start_left = 50; my $width = 150; my $height = 150; # create the wrapper .. my $flow = Text::Flow->new( check_height => sub { my $paras = shift; (sum(map { scalar @$_ || 1 } @$paras) * $font_height) < $height; }, wrapper => Text::Flow::Wrap->new( check_width => $pdf->get_string_width_function(width => $width), ) ); my $orig_text = join "" => ; my @sections = $flow->flow($orig_text); my $line_top = $start_top; $start_top -= $font_height; my $left = $start_left; foreach my $i (0 .. $#sections) { # draw rectangles to mark the width we want $pdf->draw_rect( left => $left, top => $line_top, width => $width, height => $height + $font_height, ); foreach my $line (split "\n" => $sections[$i]) { $pdf->draw_text( text => $line, left => $left, top => $start_top, ); $start_top -= $font_height; } $start_top = $orig_start_top - $font_height; $left += ($width + 10); } $pdf->close_page; $pdf->write_file; } cmp_pdf($PDF_TEST_FILE, 't/pdfs/004_basic_pdf_text_flow_w_para.pdf', '... our PDFs are identical'); unlink($PDF_TEST_FILE); __DATA__ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis lobortis nisl in ante. Vestibulum dignissim facilisis turpis. Nunc rutrum sapien sed eros. Donec facilisis placerat dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean convallis, urna eget mattis accumsan, dolor augue condimentum est, id aliquam pede eros eget metus. Proin felis. Nam hendrerit velit et lorem. Nulla ac mauris in nibh ornare porta. Fusce sodales porta orci. Aenean dolor. Proin nec ligula non eros tristique interdum. Sed aliquet ipsum vel leo. Cras a urna vel tortor molestie tincidunt. Aenean risus. Quisque luctus ipsum sit amet massa. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Nam lacus mauris, sagittis volutpat, rutrum eget, commodo non, elit.Text-Flow-0.01/t/010_basic_ascii_word_wrap.t0000444000076500007650000000772510624331524021455 0ustar stevanstevan00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More no_plan => 1; use Test::LongString; BEGIN { use_ok('Text::Flow::Wrap'); } my $wrapper = Text::Flow::Wrap->new( check_width => sub { length($_[0]) < 70 }, ); my $orig_text = join "" => ; is_string( $wrapper->wrap($orig_text), q{Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis lobortis nisl in ante. Vestibulum dignissim facilisis turpis. Nunc rutrum sapien sed eros. Donec facilisis placerat dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean convallis, urna eget mattis accumsan, dolor augue condimentum est, id aliquam pede eros eget metus. Proin felis. Nam hendrerit velit et lorem. Nulla ac mauris in nibh ornare porta. Fusce sodales porta orci. Aenean dolor. Proin nec ligula non eros tristique interdum. Sed aliquet ipsum vel leo. Cras a urna vel tortor molestie tincidunt. Aenean risus. Quisque luctus ipsum sit amet massa. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Nam lacus mauris, sagittis volutpat, rutrum eget, commodo non, elit.}, '... the string wrapped successfully'); # shorten it ... $wrapper->check_width(sub { length($_[0]) < 45 }); is_string( $wrapper->wrap($orig_text), q{Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis lobortis nisl in ante. Vestibulum dignissim facilisis turpis. Nunc rutrum sapien sed eros. Donec facilisis placerat dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean convallis, urna eget mattis accumsan, dolor augue condimentum est, id aliquam pede eros eget metus. Proin felis. Nam hendrerit velit et lorem. Nulla ac mauris in nibh ornare porta. Fusce sodales porta orci. Aenean dolor. Proin nec ligula non eros tristique interdum. Sed aliquet ipsum vel leo. Cras a urna vel tortor molestie tincidunt. Aenean risus. Quisque luctus ipsum sit amet massa. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Nam lacus mauris, sagittis volutpat, rutrum eget, commodo non, elit.}, '... the string wrapped successfully'); # shorten it again ... $wrapper->check_width(sub { length($_[0]) < 25 }); is_string( $wrapper->wrap($orig_text), q{Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis lobortis nisl in ante. Vestibulum dignissim facilisis turpis. Nunc rutrum sapien sed eros. Donec facilisis placerat dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean convallis, urna eget mattis accumsan, dolor augue condimentum est, id aliquam pede eros eget metus. Proin felis. Nam hendrerit velit et lorem. Nulla ac mauris in nibh ornare porta. Fusce sodales porta orci. Aenean dolor. Proin nec ligula non eros tristique interdum. Sed aliquet ipsum vel leo. Cras a urna vel tortor molestie tincidunt. Aenean risus. Quisque luctus ipsum sit amet massa. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Nam lacus mauris, sagittis volutpat, rutrum eget, commodo non, elit.}, '... the string wrapped successfully'); __DATA__ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis lobortis nisl in ante. Vestibulum dignissim facilisis turpis. Nunc rutrum sapien sed eros. Donec facilisis placerat dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean convallis, urna eget mattis accumsan, dolor augue condimentum est, id aliquam pede eros eget metus. Proin felis. Nam hendrerit velit et lorem. Nulla ac mauris in nibh ornare porta. Fusce sodales porta orci. Aenean dolor. Proin nec ligula non eros tristique interdum. Sed aliquet ipsum vel leo. Cras a urna vel tortor molestie tincidunt. Aenean risus. Quisque luctus ipsum sit amet massa. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Nam lacus mauris, sagittis volutpat, rutrum eget, commodo non, elit.Text-Flow-0.01/t/011_ascii_word_wrap_w_para.t0000444000076500007650000000773110624331524021643 0ustar stevanstevan00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More no_plan => 1; use Test::LongString; BEGIN { use_ok('Text::Flow::Wrap'); } my $wrapper = Text::Flow::Wrap->new( check_width => sub { length($_[0]) < 70 }, ); my $orig_text = join "" => ; is_string( $wrapper->wrap($orig_text), q{Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis lobortis nisl in ante. Vestibulum dignissim facilisis turpis. Nunc rutrum sapien sed eros. Donec facilisis placerat dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean convallis, urna eget mattis accumsan, dolor augue condimentum est, id aliquam pede eros eget metus. Proin felis. Nam hendrerit velit et lorem. Nulla ac mauris in nibh ornare porta. Fusce sodales porta orci. Aenean dolor. Proin nec ligula non eros tristique interdum. Sed aliquet ipsum vel leo. Cras a urna vel tortor molestie tincidunt. Aenean risus. Quisque luctus ipsum sit amet massa. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Nam lacus mauris, sagittis volutpat, rutrum eget, commodo non, elit.}, '... the string wrapped successfully'); # shorten it ... $wrapper->check_width(sub { length($_[0]) < 45 }); is_string( $wrapper->wrap($orig_text), q{Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis lobortis nisl in ante. Vestibulum dignissim facilisis turpis. Nunc rutrum sapien sed eros. Donec facilisis placerat dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean convallis, urna eget mattis accumsan, dolor augue condimentum est, id aliquam pede eros eget metus. Proin felis. Nam hendrerit velit et lorem. Nulla ac mauris in nibh ornare porta. Fusce sodales porta orci. Aenean dolor. Proin nec ligula non eros tristique interdum. Sed aliquet ipsum vel leo. Cras a urna vel tortor molestie tincidunt. Aenean risus. Quisque luctus ipsum sit amet massa. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Nam lacus mauris, sagittis volutpat, rutrum eget, commodo non, elit.}, '... the string wrapped successfully'); # shorten it again ... $wrapper->check_width(sub { length($_[0]) < 25 }); is_string( $wrapper->wrap($orig_text), q{Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis lobortis nisl in ante. Vestibulum dignissim facilisis turpis. Nunc rutrum sapien sed eros. Donec facilisis placerat dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean convallis, urna eget mattis accumsan, dolor augue condimentum est, id aliquam pede eros eget metus. Proin felis. Nam hendrerit velit et lorem. Nulla ac mauris in nibh ornare porta. Fusce sodales porta orci. Aenean dolor. Proin nec ligula non eros tristique interdum. Sed aliquet ipsum vel leo. Cras a urna vel tortor molestie tincidunt. Aenean risus. Quisque luctus ipsum sit amet massa. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Nam lacus mauris, sagittis volutpat, rutrum eget, commodo non, elit.}, '... the string wrapped successfully'); __DATA__ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis lobortis nisl in ante. Vestibulum dignissim facilisis turpis. Nunc rutrum sapien sed eros. Donec facilisis placerat dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean convallis, urna eget mattis accumsan, dolor augue condimentum est, id aliquam pede eros eget metus. Proin felis. Nam hendrerit velit et lorem. Nulla ac mauris in nibh ornare porta. Fusce sodales porta orci. Aenean dolor. Proin nec ligula non eros tristique interdum. Sed aliquet ipsum vel leo. Cras a urna vel tortor molestie tincidunt. Aenean risus. Quisque luctus ipsum sit amet massa. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Nam lacus mauris, sagittis volutpat, rutrum eget, commodo non, elit.Text-Flow-0.01/t/012_basic_pdf_text_wrap.t0000444000076500007650000000407410624331524021143 0ustar stevanstevan00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More; BEGIN { eval "use Test::PDF; use t::lib::PDFGen;"; plan skip_all => "Test::PDF and pdflib_pl required to run PDF tests" if $@; } plan no_plan => 1; BEGIN { use_ok('Text::Flow::Wrap'); } my $PDF_TEST_FILE = 'test_wrap.pdf'; { my $pdf = PDFGen->new( pdf_filename => $PDF_TEST_FILE ); # create the wrapper .. my $wrapper = Text::Flow::Wrap->new( check_width => $pdf->get_string_width_function(width => 300), ); my $orig_text = join "" => ; my $wrapped_text = $wrapper->wrap($orig_text); $pdf->open_page(height => 400, width => 400); my $start_top = 390; my $font_height = $pdf->font_height; # draw a rectangle to mark the width we want $pdf->draw_line(left => 50, top => $start_top, width => 300); $start_top -= $font_height; foreach my $line (split "\n" => $wrapped_text) { $pdf->draw_text( text => $line, left => 50, top => $start_top, ); $start_top -= $font_height; } $pdf->close_page; $pdf->write_file; } cmp_pdf($PDF_TEST_FILE, 't/pdfs/012_basic_pdf_text_wrap.pdf', '... our PDFs are identical'); unlink($PDF_TEST_FILE); __DATA__ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis lobortis nisl in ante. Vestibulum dignissim facilisis turpis. Nunc rutrum sapien sed eros. Donec facilisis placerat dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean convallis, urna eget mattis accumsan, dolor augue condimentum est, id aliquam pede eros eget metus. Proin felis. Nam hendrerit velit et lorem. Nulla ac mauris in nibh ornare porta. Fusce sodales porta orci. Aenean dolor. Proin nec ligula non eros tristique interdum. Sed aliquet ipsum vel leo. Cras a urna vel tortor molestie tincidunt. Aenean risus. Quisque luctus ipsum sit amet massa. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Nam lacus mauris, sagittis volutpat, rutrum eget, commodo non, elit.Text-Flow-0.01/t/013_pdf_text_wrap_w_para.t0000444000076500007650000000410310624331524021325 0ustar stevanstevan00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More; BEGIN { eval "use Test::PDF; use t::lib::PDFGen;"; plan skip_all => "Test::PDF and pdflib_pl required to run PDF tests" if $@; } plan no_plan => 1; BEGIN { use_ok('Text::Flow::Wrap'); } my $PDF_TEST_FILE = 'test_wrap_w_para.pdf'; { my $pdf = PDFGen->new( pdf_filename => $PDF_TEST_FILE ); # create the wrapper .. my $wrapper = Text::Flow::Wrap->new( check_width => $pdf->get_string_width_function(width => 300), ); my $orig_text = join "" => ; my $wrapped_text = $wrapper->wrap($orig_text); $pdf->open_page(height => 400, width => 400); my $start_top = 390; my $font_height = $pdf->font_height; # draw a rectangle to mark the width we want $pdf->draw_line(left => 50, top => $start_top, width => 300); $start_top -= $font_height; foreach my $line (split "\n" => $wrapped_text) { $pdf->draw_text( text => $line, left => 50, top => $start_top, ); $start_top -= $font_height; } $pdf->close_page; $pdf->write_file; } cmp_pdf($PDF_TEST_FILE, 't/pdfs/013_pdf_text_wrap_w_para.pdf', '... our PDFs are identical'); unlink($PDF_TEST_FILE); __DATA__ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis lobortis nisl in ante. Vestibulum dignissim facilisis turpis. Nunc rutrum sapien sed eros. Donec facilisis placerat dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean convallis, urna eget mattis accumsan, dolor augue condimentum est, id aliquam pede eros eget metus. Proin felis. Nam hendrerit velit et lorem. Nulla ac mauris in nibh ornare porta. Fusce sodales porta orci. Aenean dolor. Proin nec ligula non eros tristique interdum. Sed aliquet ipsum vel leo. Cras a urna vel tortor molestie tincidunt. Aenean risus. Quisque luctus ipsum sit amet massa. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Nam lacus mauris, sagittis volutpat, rutrum eget, commodo non, elit. Text-Flow-0.01/t/lib/0000755000076500007650000000000010624331524015130 5ustar stevanstevan00000000000000Text-Flow-0.01/t/lib/PDFGen.pm0000444000076500007650000000505610624331524016535 0ustar stevanstevan00000000000000 package PDFGen; use Moose; use MooseX::Params::Validate; use pdflib_pl; has 'pdf' => (is => 'rw'); has 'pdf_filename' => (is => 'rw'); has 'font' => (is => 'rw'); has 'font_size' => (is => 'rw', default => 12); has 'font_height' => (is => 'rw'); sub BUILD { my ($self, $params) = @_; my $pdf = pdflib_pl::PDF_new(); (pdflib_pl::PDF_open_file($pdf, $self->pdf_filename) != -1) || die "cannot open file for PDF document : $!"; my $font = pdflib_pl::PDF_findfont($pdf, 'Helvetica', 'host', 0); (defined($font)) || die "Could not load font"; $self->font($font); $self->pdf($pdf); } sub get_string_width_function { my ($self, %params) = validate(\@_, width => { isa => 'Int' }, ); sub { pdflib_pl::PDF_stringwidth( $self->pdf, $_[0], $self->font, $self->font_size ) < $params{width} } } sub open_page { my ($self, %params) = validate(\@_, width => { isa => 'Int' }, height => { isa => 'Int' } ); pdflib_pl::PDF_begin_page($self->pdf, $params{width}, $params{height}); pdflib_pl::PDF_setfont($self->pdf, $self->font, $self->font_size); $self->font_height(pdflib_pl::PDF_get_value($self->pdf, "leading", 0)); } sub draw_line { my ($self, %params) = validate(\@_, top => { isa => 'Int' }, left => { isa => 'Int' }, width => { isa => 'Int' }, height => { isa => 'Int', default => 1 } ); pdflib_pl::PDF_rect($self->pdf, $params{left}, $params{top}, $params{width}, $params{height}); pdflib_pl::PDF_fill($self->pdf); } sub draw_rect { my ($self, %params) = validate(\@_, top => { isa => 'Int' }, left => { isa => 'Int' }, width => { isa => 'Int' }, height => { isa => 'Int' } ); pdflib_pl::PDF_rect( $self->pdf, $params{left}, ($params{top} - $params{height}), $params{width}, $params{height}, ); pdflib_pl::PDF_stroke($self->pdf); } sub draw_text { my ($self, %params) = validate(\@_, top => { isa => 'Int' }, left => { isa => 'Int' }, text => { isa => 'Str' }, ); pdflib_pl::PDF_show_xy( $self->pdf, $params{text}, $params{left}, $params{top}, ); } sub close_page { pdflib_pl::PDF_end_page((shift)->pdf); } sub write_file { pdflib_pl::PDF_close((shift)->pdf); } 1; __END__Text-Flow-0.01/t/pdfs/0000755000076500007650000000000010624331524015316 5ustar stevanstevan00000000000000Text-Flow-0.01/t/pdfs/003_basic_pdf_text_flow.pdf0000444000076500007650000000370510624331524022403 0ustar stevanstevan00000000000000%PDF-1.6 % 1 0 obj [/PDF/ImageB/ImageC/ImageI/Text] endobj 5 0 obj <> stream xuOo1)R iQ'hBF'4䵷So&1\߼sWteϻs0ϻal]2NzsқBTRLԥXUM Ix Z7&|ӴPH+D-lHؼR[X N):; Zp;jP-:ʭfXxRTěěfl $sn迪{'ߧB3\x[(e&HjV'6Jrmħ) V/^>e?qL>hC?R/ endstream endobj 6 0 obj 623 endobj 7 0 obj <> >> endobj 8 0 obj <> endobj 3 0 obj <> endobj 9 0 obj <> endobj 4 0 obj <> endobj 2 0 obj <> endobj 10 0 obj <> endobj xref 0 11 0000000000 65535 f 0000000015 00000 n 0000001515 00000 n 0000000948 00000 n 0000001414 00000 n 0000000063 00000 n 0000000758 00000 n 0000000777 00000 n 0000000832 00000 n 0000001030 00000 n 0000001569 00000 n trailer <<57A55712062404A031E6EA9D1DA46177>] >> startxref 1617 %%EOF Text-Flow-0.01/t/pdfs/004_basic_pdf_text_flow_w_para.pdf0000444000076500007650000000367210624331524023740 0ustar stevanstevan00000000000000%PDF-1.6 % 1 0 obj [/PDF/ImageB/ImageC/ImageI/Text] endobj 5 0 obj <> stream x}TnAWeI !8CNQŽ=vlaƻFb[~TU]ߟVynn%N]S>M߼yA1zo}n~5WARKƻ(&Ij@FnOb5ut5[~,N%u.IG?%&fKX{ cV#PO 9<8W O%fl$p>:+όA4KȎH>%Moy65i Ec\9|/]ud*6)J6&];{>K{% jZҚcH/TY9x}h / ̉bB-6[Ħ9L8ݾ@ǡg7P=ۣV"awbHOGSÕ,w&z=RagL y;#jr&&+p{Y?> >> endobj 8 0 obj <> endobj 3 0 obj <> endobj 9 0 obj <> endobj 4 0 obj <> endobj 2 0 obj <> endobj 10 0 obj <> endobj xref 0 11 0000000000 65535 f 0000000015 00000 n 0000001504 00000 n 0000000937 00000 n 0000001403 00000 n 0000000063 00000 n 0000000747 00000 n 0000000766 00000 n 0000000821 00000 n 0000001019 00000 n 0000001558 00000 n trailer <<022A1FEF8EDA5DA41EAFAB2675D74A17>] >> startxref 1606 %%EOF Text-Flow-0.01/t/pdfs/012_basic_pdf_text_wrap.pdf0000444000076500007650000000357610624331524022413 0ustar stevanstevan00000000000000%PDF-1.6 % 1 0 obj [/PDF/ImageB/ImageC/ImageI/Text] endobj 5 0 obj <> stream xuSM0WP ht Oi:ȱ${hy3w{zP}9v];x{񘲌S| )SJ> >> endobj 8 0 obj <> endobj 3 0 obj <> endobj 9 0 obj <> endobj 4 0 obj <> endobj 2 0 obj <> endobj 10 0 obj <> endobj xref 0 11 0000000000 65535 f 0000000015 00000 n 0000001444 00000 n 0000000877 00000 n 0000001343 00000 n 0000000063 00000 n 0000000687 00000 n 0000000706 00000 n 0000000761 00000 n 0000000959 00000 n 0000001498 00000 n trailer <] >> startxref 1546 %%EOF Text-Flow-0.01/t/pdfs/013_pdf_text_wrap_w_para.pdf0000444000076500007650000000361210624331524022573 0ustar stevanstevan00000000000000%PDF-1.6 % 1 0 obj [/PDF/ImageB/ImageC/ImageI/Text] endobj 5 0 obj <> stream xuS0+Reat Oi;ȱx%$ 5ڦ͛G|v;zCYSнǿ:t݋e$Jɧ25Q'bgbU2Ixݎ^5, HPj)@@kh-wE ȉ-(go5:2 O*x.EqBLd6U7:SWqRnJ) bTeuT GciYpl>qjLr$bX8\UZQ0I=q@j,尯a=y7} Ӟ`WS>KO6ehihڇ`558P/P(2`9t_*sU@ -AMcT=BkWltjjb* i3>3^h7 BKitkTmZ@5KJ _`ڦސOS(4 A!`žs$ Q,s?mw2$A6X'R^e=vsЮR61VKO[qL>5{7}=t\sh2 endstream endobj 6 0 obj 564 endobj 7 0 obj <> >> endobj 8 0 obj <> endobj 3 0 obj <> endobj 9 0 obj <> endobj 4 0 obj <> endobj 2 0 obj <> endobj 10 0 obj <> endobj xref 0 11 0000000000 65535 f 0000000015 00000 n 0000001456 00000 n 0000000889 00000 n 0000001355 00000 n 0000000063 00000 n 0000000699 00000 n 0000000718 00000 n 0000000773 00000 n 0000000971 00000 n 0000001510 00000 n trailer <] >> startxref 1558 %%EOF Text-Flow-0.01/t/pod.t0000444000076500007650000000025710624331524015333 0ustar stevanstevan00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More; eval "use Test::Pod 1.14"; plan skip_all => "Test::Pod 1.14 required for testing POD" if $@; all_pod_files_ok(); Text-Flow-0.01/t/pod_coverage.t0000444000076500007650000000031610624331524017202 0ustar stevanstevan00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More; eval "use Test::Pod::Coverage 1.04"; plan skip_all => "Test::Pod::Coverage 1.04 required for testing POD coverage" if $@; all_pod_coverage_ok();