| blib/lib/EBook/MOBI/Driver/POD.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 235 | 246 | 95.5 |
| branch | 120 | 140 | 85.7 |
| condition | 28 | 39 | 71.7 |
| subroutine | 23 | 23 | 100.0 |
| pod | 4 | 10 | 40.0 |
| total | 410 | 458 | 89.5 |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | package EBook::MOBI::Driver::POD; | ||||||
| 2 | |||||||
| 3 | our $VERSION = '0.7'; # VERSION (hook for Dist::Zilla::Plugin::OurPkgVersion) | ||||||
| 4 | |||||||
| 5 | 9 | 9 | 1160 | use strict; | |||
| 9 | 13 | ||||||
| 9 | 349 | ||||||
| 6 | 9 | 9 | 50 | use warnings; | |||
| 9 | 19 | ||||||
| 9 | 297 | ||||||
| 7 | |||||||
| 8 | 9 | 9 | 51 | use Pod::Parser; | |||
| 9 | 19 | ||||||
| 9 | 414 | ||||||
| 9 | 9 | 9 | 5411 | use EBook::MOBI::Driver; | |||
| 9 | 21 | ||||||
| 9 | 409 | ||||||
| 10 | our @ISA = qw(Pod::Parser EBook::MOBI::Driver); | ||||||
| 11 | |||||||
| 12 | 9 | 9 | 9503 | use Text::Trim; | |||
| 9 | 5752 | ||||||
| 9 | 602 | ||||||
| 13 | 9 | 9 | 8744 | use HTML::Entities; | |||
| 9 | 69674 | ||||||
| 9 | 3304 | ||||||
| 14 | 9 | 9 | 99 | use Carp; | |||
| 9 | 19 | ||||||
| 9 | 575 | ||||||
| 15 | 9 | 9 | 12790 | use EBook::MOBI::Converter; | |||
| 9 | 187 | ||||||
| 9 | 983 | ||||||
| 16 | 9 | 9 | 9799 | use IO::String; | |||
| 9 | 31762 | ||||||
| 9 | 511 | ||||||
| 17 | |||||||
| 18 | # This constants are used for internal replacement | ||||||
| 19 | # See interior_sequence() and _html_enc() for usage | ||||||
| 20 | 9 | 1217 | use constant { GT => '1_qpdhcn_thisStringShouldNeverOccurInInput', | ||||
| 21 | LT => '2_udtcqk_thisStringShouldNeverOccurInInput', | ||||||
| 22 | AMP => '3_pegjyq_thisStringShouldNeverOccurInInput', | ||||||
| 23 | COL => '4_jdkmso_thisStringShouldNeverOccurInInput', | ||||||
| 24 | QUO => '5_wuehlo_thisStringShouldNeverOccurInInput', | ||||||
| 25 | DQUO=> '6_jrgwpm_thisStringShouldNeverOccurInInput', | ||||||
| 26 | 9 | 9 | 74 | }; | |||
| 9 | 21 | ||||||
| 27 | |||||||
| 28 | # IMPORTANT | ||||||
| 29 | # This constant ist JUST a shortcut for readability. | ||||||
| 30 | # Because it is used in hases ($parser->{}) a + is used so that it is not | ||||||
| 31 | # interpreted as a string, so it looks like this: $parser->{+P . 'bla'} | ||||||
| 32 | # See http://perldoc.perl.org/constant.html for details | ||||||
| 33 | 9 | 9 | 47 | use constant { P => 'EBook_MOBI_Pod2Mhtml_' }; | |||
| 9 | 16 | ||||||
| 9 | 43758 | ||||||
| 34 | |||||||
| 35 | # Overwrite sub of Pod::Parser | ||||||
| 36 | sub begin_input { | ||||||
| 37 | 33 | 33 | 0 | 1902 | my $parser = shift; | ||
| 38 | 33 | 188 | my $out_fh = $parser->output_handle(); # handle for parsing output | ||||
| 39 | |||||||
| 40 | 33 | 213 | $parser->{+P . 'toMobi'} = EBook::MOBI::Converter->new(); | ||||
| 41 | |||||||
| 42 | 33 | 157 | $parser->debug_msg('found POD, parsing...'); | ||||
| 43 | |||||||
| 44 | # make sure that this variable is set to 0 at beginning | ||||||
| 45 | 33 | 71 | $parser->{+P . 'listcontext'} = 0; | ||||
| 46 | 33 | 54 | $parser->{+P . 'listjustwentback'} = 0; | ||||
| 47 | 33 | 740 | $parser->{+P . 'begin'} = ''; | ||||
| 48 | } | ||||||
| 49 | |||||||
| 50 | # Overwrite sub of Pod::Parser | ||||||
| 51 | sub end_input { | ||||||
| 52 | 33 | 33 | 0 | 1801 | my $parser = shift; | ||
| 53 | 33 | 143 | my $out_fh = $parser->output_handle(); | ||||
| 54 | |||||||
| 55 | 33 | 388 | $parser->debug_msg('...end of POD reached'); | ||||
| 56 | } | ||||||
| 57 | |||||||
| 58 | # Overwrite sub of Pod::Parser | ||||||
| 59 | # Here all POD commands starting with '=' are handled | ||||||
| 60 | sub command { | ||||||
| 61 | 181 | 181 | 0 | 15520 | my ($parser, $command, $paragraph, $line_num) = @_; | ||
| 62 | 181 | 711 | my $out_fh = $parser->output_handle(); # handle for parsing output | ||||
| 63 | |||||||
| 64 | # IMAGE is an unofficial command introduced by Renee, its very simple: | ||||||
| 65 | # =image PATH_TO_IMAGE ANY TEXT FOLLOWING UNTIL END OF LINE | ||||||
| 66 | 181 | 100 | 441 | if ($command eq 'image') { | |||
| 67 | |||||||
| 68 | 1 | 314 | |||||
| 69 | "WARNING: the unofficial POD command '=image' is deprecated.\n"; | ||||||
| 70 | |||||||
| 71 | # With this regex we parse the content, coming with the command. | ||||||
| 72 | # An example could look like this: | ||||||
| 73 | # $paragraph = '/home/user/picture.jpg Pic1: A Camel' | ||||||
| 74 | 1 | 50 | 11 | if ($paragraph =~ m/(\S*)\s*(.*)/g) { | |||
| 75 | 1 | 4 | my $img_path = $1; # e.g.: '/home/user/picture.jpg' | ||||
| 76 | 1 | 3 | my $img_desc = $2; # e.g.: 'A Camel' | ||||
| 77 | |||||||
| 78 | # We convert special chars to HTML, but only in the | ||||||
| 79 | # description, not in the path! | ||||||
| 80 | 1 | 4 | $img_desc = _html_enc($img_desc); | ||||
| 81 | |||||||
| 82 | # We print out an html image tag. | ||||||
| 83 | # e.g.: |
||||||
| 84 | # recindex is MOBI specific, its the number of the picture, | ||||||
| 85 | # pointing into the picture records of the Mobi-format | ||||||
| 86 | 1 | 14 | print $out_fh | ||||
| 87 | $parser->{+P . 'toMobi'} | ||||||
| 88 | ->image($img_path, $img_desc); | ||||||
| 89 | } | ||||||
| 90 | } | ||||||
| 91 | # POD compatible additional syntax to process images | ||||||
| 92 | # =for image PATH_TO_IMAGE ANY TEXT FOLLOWING UNTIL END OF LINE | ||||||
| 93 | 181 | 100 | 899 | if ($command eq 'for') { | |||
| 100 | |||||||
| 100 | |||||||
| 50 | |||||||
| 100 | |||||||
| 100 | |||||||
| 94 | |||||||
| 95 | # With this regex we parse the content, coming with the command. | ||||||
| 96 | # An example could look like this: | ||||||
| 97 | # $paragraph = 'image /home/user/picture.jpg Pic1: A Camel' | ||||||
| 98 | 1 | 50 | 8 | if ($paragraph =~ m/image\s*(\S*)\s*(.*)/g) { | |||
| 99 | 1 | 3 | my $img_path = $1; # e.g.: '/home/user/picture.jpg' | ||||
| 100 | 1 | 3 | my $img_desc = $2; # e.g.: 'A Camel' | ||||
| 101 | |||||||
| 102 | # We convert special chars to HTML, but only in the | ||||||
| 103 | # description, not in the path! | ||||||
| 104 | 1 | 3 | $img_desc = _html_enc($img_desc); | ||||
| 105 | |||||||
| 106 | # We print out an html image tag. | ||||||
| 107 | # e.g.: |
||||||
| 108 | # recindex is MOBI specific, its the number of the picture, | ||||||
| 109 | # pointing into the picture records of the Mobi-format | ||||||
| 110 | 1 | 6 | print $out_fh | ||||
| 111 | $parser->{+P . 'toMobi'} | ||||||
| 112 | ->image($img_path, $img_desc); | ||||||
| 113 | } | ||||||
| 114 | } | ||||||
| 115 | # Lists are a bit complex. The commands 'over', 'back' and 'item' | ||||||
| 116 | # are used. They exchange state over a global variable. This state | ||||||
| 117 | # is the listcontext, which can be: 'begin', 'ul' or 'ol'. | ||||||
| 118 | # OVER: starts the listcontext | ||||||
| 119 | elsif ($command eq 'over') { | ||||||
| 120 | |||||||
| 121 | # If we reach an 'over' command we can't do anything yet | ||||||
| 122 | # because we don't know if it will be an ordered or an | ||||||
| 123 | # unordered list! So we just set a global variable to 'begin', | ||||||
| 124 | # the first item call can then know that it is the first item | ||||||
| 125 | # and that it defines the rest of the list type. | ||||||
| 126 | |||||||
| 127 | 25 | 100 | 62 | if (exists $parser->{+P . 'list'}) { | |||
| 128 | # if we reach here, this means that this is a nested list | ||||||
| 129 | 9 | 14 | $parser->{+P . 'listlvl'}++; | ||||
| 130 | } | ||||||
| 131 | else { | ||||||
| 132 | 16 | 31 | $parser->{+P . 'listlvl'} = 0; | ||||
| 133 | } | ||||||
| 134 | |||||||
| 135 | |||||||
| 136 | 25 | 30 | push @{$parser->{+P . 'list'}} | ||||
| 25 | 1522 | ||||||
| 137 | , { | ||||||
| 138 | type => '' , | ||||||
| 139 | items => 0 , | ||||||
| 140 | state => 'over' , | ||||||
| 141 | contentInCmd => 1 , | ||||||
| 142 | blockquotes => 0 , | ||||||
| 143 | }; | ||||||
| 144 | } | ||||||
| 145 | # BACK: ends the listcontext | ||||||
| 146 | elsif ($command eq 'back') { | ||||||
| 147 | |||||||
| 148 | 25 | 38 | my $lvl = $parser->{+P . 'listlvl'}; | ||||
| 149 | |||||||
| 150 | # print end-tag according to the lists type | ||||||
| 151 | 25 | 100 | 83 | if ($parser->{+P . 'list'}->[$lvl]->{type} eq 'ul') { | |||
| 100 | |||||||
| 50 | |||||||
| 152 | 14 | 45 | print $out_fh '' . "\n"; # close last item | ||||
| 153 | 14 | 235 | print $out_fh '' . "\n"; | ||||
| 154 | } | ||||||
| 155 | elsif ($parser->{+P . 'list'}->[$lvl]->{type} eq 'ol') { | ||||||
| 156 | 6 | 19 | print $out_fh '' . "\n"; # close last item | ||||
| 157 | 6 | 100 | print $out_fh '' . "\n"; | ||||
| 158 | } | ||||||
| 159 | elsif | ||||||
| 160 | ($parser->{+P . 'list'}->[$lvl]->{type} | ||||||
| 161 | eq 'blockquote') { | ||||||
| 162 | # list is processed | ||||||
| 163 | # there where no items... | ||||||
| 164 | } | ||||||
| 165 | else { | ||||||
| 166 | 0 | 0 | carp 'POD parsing error. Undefined listcontext: ' | ||||
| 167 | . $parser->{+P . 'listcontext'}; | ||||||
| 168 | } | ||||||
| 169 | |||||||
| 170 | # DELETE if list is finish | ||||||
| 171 | 25 | 100 | 346 | if ($parser->{+P . 'listlvl'} == 0) { | |||
| 172 | 16 | 36 | delete $parser->{+P . 'listlvl'}; | ||||
| 173 | 16 | 47 | delete $parser->{+P . 'list'}; | ||||
| 174 | 16 | 428 | delete $parser->{+P . 'listjustwentback'}; | ||||
| 175 | } | ||||||
| 176 | else { | ||||||
| 177 | 9 | 16 | $parser->{+P . 'list'}->[$lvl]->{state} = 'back'; | ||||
| 178 | 9 | 15 | $parser->{+P . 'listlvl'}--; | ||||
| 179 | 9 | 403 | $parser->{+P . 'listjustwentback'} = 1; | ||||
| 180 | } | ||||||
| 181 | } | ||||||
| 182 | # CUT: end of POD | ||||||
| 183 | elsif ($command eq 'cut') { | ||||||
| 184 | # We don't need to do anything here... | ||||||
| 185 | } | ||||||
| 186 | elsif ($command eq 'begin') { | ||||||
| 187 | 1 | 50 | 6 | if ($paragraph =~ m/^\W*(\w+)\W*$/) { | |||
| 188 | 1 | 2 | my $begin_name = $1; | ||||
| 189 | 1 | 42 | $parser->{+P . 'begin'} = $begin_name; | ||||
| 190 | } | ||||||
| 191 | } | ||||||
| 192 | elsif ($command eq 'end') { | ||||||
| 193 | 1 | 50 | 7 | if ($paragraph =~ m/^\W*(\w+)\W*$/) { | |||
| 194 | 1 | 2 | my $end_name = $1; | ||||
| 195 | 1 | 50 | 5 | if ($parser->{+P . 'begin'} eq $end_name) { | |||
| 196 | 1 | 43 | $parser->{+P . 'begin'} = ''; | ||||
| 197 | } | ||||||
| 198 | else { | ||||||
| 199 | 0 | 0 | croak 'no nested begin/end supported'; | ||||
| 200 | } | ||||||
| 201 | } | ||||||
| 202 | } | ||||||
| 203 | # if we reach this ELSE, this means that the command can only be | ||||||
| 204 | # of type HEAD or ITEM (so they contain some text!) | ||||||
| 205 | else { | ||||||
| 206 | # first we remove all whitespace from begin and end of the title | ||||||
| 207 | 128 | 363 | trim $paragraph; | ||||
| 208 | # then we call interpolate so that 'interior_sequence' is called. | ||||||
| 209 | # this is replacing inline POD. | ||||||
| 210 | 128 | 10134 | my $expansion = $parser->interpolate($paragraph, $line_num); | ||||
| 211 | # then we replace special chars with HTML entities | ||||||
| 212 | 128 | 404 | $expansion = _html_enc($expansion); | ||||
| 213 | |||||||
| 214 | # Now we just need to print the text with the matching HTML tag | ||||||
| 215 | 128 | 100 | 694 | if ($command eq 'head0') { | |||
| 100 | |||||||
| 100 | |||||||
| 100 | |||||||
| 100 | |||||||
| 100 | |||||||
| 216 | # head0 gets only printed if the option is set! | ||||||
| 217 | # (head0 is not official POD standard) | ||||||
| 218 | 17 | 100 | 47 | if ($parser->head0_mode()) { | |||
| 219 | # before every head1 we insert a "mobi-pagebreak" | ||||||
| 220 | # but not before the first one! | ||||||
| 221 | 9 | 100 | 100 | 66 | if (exists $parser->{+P . 'firstH1passed'} | ||
| 66 | |||||||
| 222 | and exists $parser->{+P . 'pages'} | ||||||
| 223 | and $parser->{+P . 'pages'} | ||||||
| 224 | ) { | ||||||
| 225 | 3 | 15 | print $out_fh | ||||
| 226 | $parser->{+P . 'toMobi'}->pagebreak(); | ||||||
| 227 | } | ||||||
| 228 | else { | ||||||
| 229 | 6 | 16 | $parser->{+P . 'firstH1passed'} = 1; | ||||
| 230 | } | ||||||
| 231 | |||||||
| 232 | 9 | 86 | print $out_fh | ||||
| 233 | $parser->{+P . 'toMobi'}->title($expansion, 1); | ||||||
| 234 | } | ||||||
| 235 | } | ||||||
| 236 | elsif ($command eq 'head1') { | ||||||
| 237 | # we need to check to which level we translate the headings... | ||||||
| 238 | 38 | 100 | 98 | if ($parser->head0_mode()) { | |||
| 239 | 9 | 37 | print $out_fh | ||||
| 240 | $parser->{+P . 'toMobi'}->title($expansion, 2); | ||||||
| 241 | } | ||||||
| 242 | else { | ||||||
| 243 | # before every head1 we insert a "mobi-pagebreak" | ||||||
| 244 | # but not before the first one! | ||||||
| 245 | 29 | 100 | 100 | 176 | if (exists $parser->{+P . 'firstH1passed'} | ||
| 66 | |||||||
| 246 | and exists $parser->{+P . 'pages'} | ||||||
| 247 | and $parser->{+P . 'pages'} | ||||||
| 248 | ) { | ||||||
| 249 | 2 | 16 | print $out_fh | ||||
| 250 | $parser->{+P . 'toMobi'}->pagebreak(); | ||||||
| 251 | } | ||||||
| 252 | else { | ||||||
| 253 | 27 | 63 | $parser->{+P . 'firstH1passed'} = 1; | ||||
| 254 | } | ||||||
| 255 | |||||||
| 256 | 29 | 157 | print $out_fh | ||||
| 257 | $parser->{+P . 'toMobi'}->title($expansion, 1); | ||||||
| 258 | } | ||||||
| 259 | } | ||||||
| 260 | elsif ($command eq 'head2') { | ||||||
| 261 | # we need to check to which level we translate the headings... | ||||||
| 262 | 12 | 100 | 44 | if ($parser->head0_mode()) { | |||
| 263 | 4 | 18 | print $out_fh | ||||
| 264 | $parser->{+P . 'toMobi'}->title($expansion, 3); | ||||||
| 265 | } | ||||||
| 266 | else { | ||||||
| 267 | 8 | 38 | print $out_fh | ||||
| 268 | $parser->{+P . 'toMobi'}->title($expansion, 2); | ||||||
| 269 | } | ||||||
| 270 | } | ||||||
| 271 | elsif ($command eq 'head3') { | ||||||
| 272 | # we need to check to which level we translate the headings... | ||||||
| 273 | 1 | 50 | 3 | if ($parser->head0_mode()) { | |||
| 274 | 0 | 0 | print $out_fh | ||||
| 275 | $parser->{+P . 'toMobi'}->title($expansion, 4); | ||||||
| 276 | } | ||||||
| 277 | else { | ||||||
| 278 | 1 | 5 | print $out_fh | ||||
| 279 | $parser->{+P . 'toMobi'}->title($expansion, 3); | ||||||
| 280 | } | ||||||
| 281 | } | ||||||
| 282 | elsif ($command eq 'head4') { | ||||||
| 283 | # we need to check to which level we translate the headings... | ||||||
| 284 | 1 | 50 | 4 | if ($parser->head0_mode()) { | |||
| 285 | 0 | 0 | print $out_fh | ||||
| 286 | $parser->{+P . 'toMobi'}->title($expansion, 5); | ||||||
| 287 | } | ||||||
| 288 | else { | ||||||
| 289 | 1 | 5 | print $out_fh | ||||
| 290 | $parser->{+P . 'toMobi'}->title($expansion, 4); | ||||||
| 291 | } | ||||||
| 292 | } | ||||||
| 293 | # ITEM: lists items | ||||||
| 294 | elsif ($command eq 'item') { | ||||||
| 295 | |||||||
| 296 | # If we are still in listcontext 'begin' this means that this is | ||||||
| 297 | # the first item of the list, which will be used to figure out | ||||||
| 298 | # the type of the list. | ||||||
| 299 | 58 | 147 | my $lvl = $parser->{+P . 'listlvl'}; | ||||
| 300 | |||||||
| 301 | 58 | 103 | $parser->{+P . 'list'}->[$lvl]->{items}++; | ||||
| 302 | |||||||
| 303 | 58 | 100 | 119 | if ($parser->{+P . 'list'}->[$lvl]->{items} == 1){ | |||
| 304 | |||||||
| 305 | # if we are already in a list... | ||||||
| 306 | 20 | 100 | 66 | 128 | if ($parser->{+P . 'list'}->[$lvl]->{state} | ||
| 100 | |||||||
| 307 | eq 'over' | ||||||
| 308 | and $lvl > 0 | ||||||
| 309 | and | ||||||
| 310 | $parser->{+P . 'list'}->[$lvl-1]->{items} | ||||||
| 311 | > 0 | ||||||
| 312 | ) { | ||||||
| 313 | # we need to close the last item! | ||||||
| 314 | 6 | 24 | print $out_fh '' . "\n"; | ||||
| 315 | } | ||||||
| 316 | |||||||
| 317 | # is there a digit at first, if yes this is an ordered list | ||||||
| 318 | 20 | 100 | 220 | if ($expansion =~ /^\s*\d+\s*(.*)$/) { | |||
| 100 | |||||||
| 100 | |||||||
| 319 | 6 | 13 | $expansion = $1; | ||||
| 320 | 6 | 15 | $parser->{+P . 'list'}->[$lvl] | ||||
| 321 | ->{type} = 'ol'; | ||||||
| 322 | |||||||
| 323 | 6 | 100 | 21 | if ($expansion =~ /[[:alnum:][:punct:]]+/) { | |||
| 324 | 5 | 19 | print $out_fh '
|
||||
| 325 | } | ||||||
| 326 | else { | ||||||
| 327 | 1 | 3 | $parser->{+P . 'list'}->[$lvl]->{contentInCmd} = 0; | ||||
| 328 | 1 | 4 | print $out_fh "
|
||||
| 329 | } | ||||||
| 330 | } | ||||||
| 331 | # is there a '*' at first, if yes this is an unordered list | ||||||
| 332 | elsif ($expansion =~ /^\s*\*{1}\s*(.*)$/) { | ||||||
| 333 | 11 | 24 | $expansion = $1; | ||||
| 334 | 11 | 21 | $parser->{+P . 'list'}->[$lvl]->{type} = 'ul'; | ||||
| 335 | |||||||
| 336 | 11 | 100 | 44 | if ($expansion =~ /[[:alnum:][:punct:]]+/) { | |||
| 337 | 9 | 33 | print $out_fh '
|
||||
| 338 | } | ||||||
| 339 | else { | ||||||
| 340 | 2 | 5 | $parser->{+P . 'list'}->[$lvl]->{contentInCmd} = 0; | ||||
| 341 | 2 | 5 | print $out_fh "
|
||||
| 342 | #\n"; | ||||||
| 343 | } | ||||||
| 344 | } | ||||||
| 345 | # are there only prinable chars? We default to unordered | ||||||
| 346 | elsif ($expansion =~ /[[:alnum:][:punct:]]+/) { | ||||||
| 347 | 1 | 3 | $parser->{+P . 'list'}->[$lvl]->{type} = 'ul'; | ||||
| 348 | 1 | 5 | print $out_fh '
|
||||
| 349 | # do nothing | ||||||
| 350 | } | ||||||
| 351 | # The lists text may be in a normal text section... | ||||||
| 352 | # we default to unordered | ||||||
| 353 | else { | ||||||
| 354 | 2 | 7 | $parser->{+P . 'list'}->[$lvl]->{type} = 'ul'; | ||||
| 355 | 2 | 4 | $parser->{+P . 'list'}->[$lvl]->{contentInCmd} = 0; | ||||
| 356 | 2 | 9 | print $out_fh "
|
||||
| 357 | } | ||||||
| 358 | } | ||||||
| 359 | |||||||
| 360 | # if it is not the first item we save the checks for list-type | ||||||
| 361 | else { | ||||||
| 362 | |||||||
| 363 | # but first we need to close the last item! | ||||||
| 364 | 38 | 100 | 94 | if ($parser->{+P . 'listjustwentback'}) { | |||
| 365 | 7 | 9 | $parser->{+P . 'listjustwentback'} = 0; | ||||
| 366 | } | ||||||
| 367 | else { | ||||||
| 368 | # we need to close the last item! | ||||||
| 369 | 31 | 115 | print $out_fh '' . "\n"; | ||||
| 370 | } | ||||||
| 371 | |||||||
| 372 | 38 | 666 | my $type = | ||||
| 373 | $parser->{+P . 'list'}->[$lvl]->{type}; | ||||||
| 374 | |||||||
| 375 | # then we check the type and extract the content | ||||||
| 376 | 38 | 100 | 87 | if ($type eq 'ol') { | |||
| 377 | 10 | 50 | 47 | if ($expansion =~ /^\s*\d+\s*(.*)$/) { | |||
| 378 | 10 | 23 | $expansion = $1; | ||||
| 379 | } | ||||||
| 380 | } | ||||||
| 381 | 38 | 100 | 81 | if ($type eq 'ul') { | |||
| 382 | 28 | 100 | 107 | if ($expansion =~ /^\s*\*{1}\s*(.*)$/) { | |||
| 383 | 22 | 50 | $expansion = $1; | ||||
| 384 | } | ||||||
| 385 | } | ||||||
| 386 | } | ||||||
| 387 | |||||||
| 388 | # we print the item... but we don't close it! | ||||||
| 389 | # it get's closed by the next item or the =back call | ||||||
| 390 | 58 | 504 | print $out_fh ' |
||||
| 391 | } | ||||||
| 392 | } | ||||||
| 393 | } | ||||||
| 394 | |||||||
| 395 | # Overwrite sub of Pod::Parser | ||||||
| 396 | # Here all code parts of POD get parsed | ||||||
| 397 | sub verbatim { | ||||||
| 398 | 1 | 1 | 0 | 88 | my ($parser, $paragraph, $line_num) = @_; | ||
| 399 | 1 | 6 | my $out_fh = $parser->output_handle(); # handle for parsing output | ||||
| 400 | |||||||
| 401 | # We have to escape the case where there is only a newline, because | ||||||
| 402 | # Pod::Parser calls verbatim() with $paragraph="\n" every time an empty | ||||||
| 403 | # line is found in the Pod. But that is not what we are looking for! | ||||||
| 404 | # We are looking for code-blocks here... | ||||||
| 405 | 1 | 50 | 6 | if ($paragraph eq "\n") { return } | |||
| 0 | 0 | ||||||
| 406 | |||||||
| 407 | # we look for POD inline commands | ||||||
| 408 | 1 | 46 | my $expansion = $parser->interpolate($paragraph, $line_num); | ||||
| 409 | # then for special chars | ||||||
| 410 | 1 | 4 | $expansion = _html_enc($expansion); | ||||
| 411 | # and last but not least we replace whitespace with a HTML tag. | ||||||
| 412 | # this we do only for the verbatim command! | ||||||
| 413 | # this is so, that code format (indenting) is keeped in html | ||||||
| 414 | 1 | 4 | $expansion = _nbsp($expansion); | ||||
| 415 | |||||||
| 416 | # also only in verbatim we replace newline with the tag |
||||||
| 417 | # this is so, that code format is keeped in html | ||||||
| 418 | 1 | 9 | $expansion =~ s/\n/ \n/g; |
||||
| 419 | |||||||
| 420 | # trim must be last, | ||||||
| 421 | # otherwise _nbsp() is not working for the first line | ||||||
| 422 | 1 | 3 | trim $expansion; | ||||
| 423 | |||||||
| 424 | # ok, we are done and print out the result | ||||||
| 425 | 1 | 29 | print $out_fh "$expansion\n"; |
||||
| 426 | } | ||||||
| 427 | |||||||
| 428 | # Overwrite sub of Pod::Parser | ||||||
| 429 | # Here normal POD text paragraphs get parsed | ||||||
| 430 | sub textblock { | ||||||
| 431 | 86 | 86 | 0 | 6410 | my ($parser, $paragraph, $line_num) = @_; | ||
| 432 | 86 | 366 | my $out_fh = $parser->output_handle(); # handle for parsing output | ||||
| 433 | |||||||
| 434 | # we could be in a =begin block so we just check that and return if | ||||||
| 435 | # this is the case | ||||||
| 436 | 86 | 100 | 260 | if ($parser->{+P . 'begin'} eq 'html') { | |||
| 437 | # we are in a html block, so just print the plain thing | ||||||
| 438 | 1 | 3 | print $out_fh " \n"; |
||||
| 439 | 1 | 18 | print $out_fh $paragraph; | ||||
| 440 | 1 | 16 | print $out_fh "\n"; | ||||
| 441 | return | ||||||
| 442 | 1 | 66 | } | ||||
| 443 | |||||||
| 444 | # no begin block... so do the rest of this complicate code! | ||||||
| 445 | |||||||
| 446 | # ok, this one is tricky... | ||||||
| 447 | # textblock() can be called when the parser is actually parsing a list. | ||||||
| 448 | # this happens if the list is written like that: | ||||||
| 449 | # =over | ||||||
| 450 | # | ||||||
| 451 | # =item | ||||||
| 452 | # | ||||||
| 453 | # Text that appears in this sub as $paragraph | ||||||
| 454 | # | ||||||
| 455 | # =back | ||||||
| 456 | # If the text is on the SAME LINE as the =item command, this will not | ||||||
| 457 | # happen. It is only when the text is separated with newline. | ||||||
| 458 | # Ok... we need to check here if we are in a list.. and then do some | ||||||
| 459 | # stuffe to handle that case. | ||||||
| 460 | |||||||
| 461 | # we translate the POD inline commands... | ||||||
| 462 | 85 | 5161 | my $expansion = $parser->interpolate($paragraph, $line_num); | ||||
| 463 | # remove leading and trailing whitespace... | ||||||
| 464 | 85 | 260 | trim $expansion; | ||||
| 465 | # and translate special chars to HTML | ||||||
| 466 | 85 | 1480 | $expansion = _html_enc($expansion); | ||||
| 467 | |||||||
| 468 | # store the list-nesting in a local variable (just for readability) | ||||||
| 469 | 85 | 165 | my $lvl = $parser->{+P . 'listlvl'}; | ||||
| 470 | |||||||
| 471 | # if there is no list WE ARE LUCKY and just print the text as paragraph | ||||||
| 472 | 85 | 100 | 100 | 397 | if (not exists $parser->{+P . 'list'}) { | ||
| 100 | |||||||
| 100 | |||||||
| 50 | |||||||
| 473 | 56 | 271 | print $out_fh ' ' . $expansion . ' ' . "\n"; |
||||
| 474 | } | ||||||
| 475 | # NOOOOOOO... we have a list | ||||||
| 476 | # ok... let's try to figure out what to do! | ||||||
| 477 | |||||||
| 478 | # items and some content found already in the command... | ||||||
| 479 | # ... so we add a before the following textblock. |
||||||
| 480 | elsif ($parser->{+P . 'list'}->[$lvl]->{items} > 0 | ||||||
| 481 | and $parser->{+P . 'list'}->[$lvl]->{contentInCmd} == 1 | ||||||
| 482 | ) { | ||||||
| 483 | 2 | 9 | print $out_fh ' ' . $expansion; |
||||
| 484 | } | ||||||
| 485 | # if there was not yet content found we just print what we have now | ||||||
| 486 | elsif ($parser->{+P . 'list'}->[$lvl]->{items} > 0) { | ||||||
| 487 | 12 | 36 | print $out_fh $expansion; | ||||
| 488 | } | ||||||
| 489 | # if there where no items yet this can only mean that we are in a list | ||||||
| 490 | # without any items but with pure text... so we do blockquotes for | ||||||
| 491 | # each paragraph | ||||||
| 492 | elsif ($parser->{+P . 'list'}->[$lvl]->{items} == 0) { | ||||||
| 493 | |||||||
| 494 | # we set the listtype | ||||||
| 495 | 15 | 24 | $parser->{+P . 'list'}->[$lvl]->{type} = 'blockquote'; | ||||
| 496 | 15 | 25 | $parser->{+P . 'list'}->[$lvl]->{blockquotes}++; | ||||
| 497 | |||||||
| 498 | 15 | 100 | 100 | 69 | if ($parser->{+P . 'list'}->[$lvl]->{blockquotes} == 1 | ||
| 100 | |||||||
| 499 | and $lvl > 0 | ||||||
| 500 | and $parser->{+P . 'list'}->[$lvl-1]->{items} > 0 | ||||||
| 501 | ) { | ||||||
| 502 | 1 | 7 | print $out_fh "\n"; | ||||
| 503 | } | ||||||
| 504 | |||||||
| 505 | # we do some pseudo-indenting | ||||||
| 506 | # TODO: more nice would be real nesting... | ||||||
| 507 | 15 | 48 | for (0..$lvl) { | ||||
| 508 | 21 | 137 | print $out_fh ''; |
||||
| 509 | } | ||||||
| 510 | 15 | 257 | print $out_fh $expansion; | ||||
| 511 | 15 | 227 | for (0..$lvl) { | ||||
| 512 | 21 | 179 | print $out_fh '' ."\n"; | ||||
| 513 | } | ||||||
| 514 | } | ||||||
| 515 | else { | ||||||
| 516 | # we should not reach here... | ||||||
| 517 | 0 | 0 | croak "POD parsing error. Found undefined textblock in a list."; | ||||
| 518 | } | ||||||
| 519 | } | ||||||
| 520 | |||||||
| 521 | # Overwrite sub of Pod::Parser | ||||||
| 522 | # This method is called for handling inline POD, like e.g. B |
||||||
| 523 | sub interior_sequence { | ||||||
| 524 | 19 | 19 | 0 | 43 | my ($parser, $cmd, $arg) = @_; | ||
| 525 | |||||||
| 526 | # IMPORTANT here we do some tricky stuff... | ||||||
| 527 | # what we actually want is this: | ||||||
| 528 | # B |
||||||
| 529 | # but this is not possible, because then the <> would be replaced by | ||||||
| 530 | # HTML entities later on! | ||||||
| 531 | # So that is why we replace like this: | ||||||
| 532 | # < -> constant: LT | ||||||
| 533 | # and | ||||||
| 534 | # > -> constant: GT | ||||||
| 535 | # So B |
||||||
| 536 | # The function which is doing the HTML translation must then replace | ||||||
| 537 | # this words again with < and > (this is what _html_enc() is doing) | ||||||
| 538 | 19 | 100 | 263 | return LT . 'b' . GT . $arg . LT . '/b' . GT if ($cmd eq 'B'); | |||
| 539 | 16 | 100 | 106 | return LT . 'code' . GT . $arg . LT . '/code' . GT if ($cmd eq 'C'); | |||
| 540 | 15 | 100 | 104 | return LT . 'code' . GT . $arg . LT . '/code' . GT if ($cmd eq 'F'); | |||
| 541 | 14 | 100 | 149 | return LT . 'i' . GT . $arg . LT . '/i' . GT if ($cmd eq 'I'); | |||
| 542 | 12 | 100 | 92 | return AMP . $arg . COL if ($cmd eq 'E'); | |||
| 543 | |||||||
| 544 | # if there is an L<> we have to take care a little bit more | ||||||
| 545 | 11 | 50 | 25 | if ($cmd eq 'L') { | |||
| 546 | |||||||
| 547 | # if we have this: | ||||||
| 548 | # L |
||||||
| 549 | # this means that CHI::Driver::File is the name to be displayed | ||||||
| 550 | # and "File" is the link... which we direct to metacpan... | ||||||
| 551 | |||||||
| 552 | # empty vars | ||||||
| 553 | 11 | 16 | my $text = ''; | ||||
| 554 | 11 | 13 | my $link = ''; | ||||
| 555 | |||||||
| 556 | # if named we set the vars | ||||||
| 557 | 11 | 100 | 160 | if ($arg =~ m/^(.*)\|(.*)$/) { | |||
| 558 | 5 | 12 | $text = $1; | ||||
| 559 | 5 | 8 | $link = $2; | ||||
| 560 | } | ||||||
| 561 | |||||||
| 562 | # in case this is not set, we set it to original value | ||||||
| 563 | 11 | 100 | 27 | $link = $arg unless $link; | |||
| 564 | |||||||
| 565 | # the case | ||||||
| 566 | # L | ||||||
| 567 | # for relative sections is not handled well here because we | ||||||
| 568 | # don't know the module like that! | ||||||
| 569 | # so we just print the text as is | ||||||
| 570 | 11 | 100 | 100 | 87 | if($link =~ m%^/(.*)%) { | ||
| 100 | |||||||
| 100 | |||||||
| 571 | 2 | 6 | my $section = $1; | ||||
| 572 | 2 | 100 | 20 | if ($text) { | |||
| 573 | 1 | 50 | return "$text ($section)"; | ||||
| 574 | } | ||||||
| 575 | else { | ||||||
| 576 | 1 | 49 | return DQUO . $section . DQUO; | ||||
| 577 | } | ||||||
| 578 | # EXIT | ||||||
| 579 | } | ||||||
| 580 | |||||||
| 581 | # if the links seems to be http we also just return! | ||||||
| 582 | elsif ($link =~ /^http.*$/ | ||||||
| 583 | or $link =~ /^.*\.{1}\w{2,5}$/ ) { | ||||||
| 584 | # this is a weblink! | ||||||
| 585 | # keep on going... | ||||||
| 586 | } | ||||||
| 587 | |||||||
| 588 | # if no special case we continue... | ||||||
| 589 | elsif ($link =~ m%(.*)/(.*)%) { | ||||||
| 590 | 2 | 5 | my $module = $1; | ||||
| 591 | 2 | 4 | my $section = $2; | ||||
| 592 | 2 | 6 | $section =~ s/"//; | ||||
| 593 | |||||||
| 594 | 2 | 50 | 33 | 11 | if ($module && $section) { | ||
| 0 | 0 | ||||||
| 0 | 0 | ||||||
| 595 | 2 | 7 | $link = "$module#$section"; | ||||
| 596 | } | ||||||
| 597 | elsif ($module && not $section) { | ||||||
| 598 | 0 | 0 | $link = $module; | ||||
| 599 | } | ||||||
| 600 | elsif (not $module && $section) { | ||||||
| 601 | # this case should not happen but you never know | ||||||
| 602 | # (it should be handled in the first if!) | ||||||
| 603 | 0 | 0 | return "\"$section\""; | ||||
| 604 | } | ||||||
| 605 | |||||||
| 606 | # this URL should be valid now | ||||||
| 607 | 2 | 6 | $link = "https://metacpan.org/module/$link"; | ||||
| 608 | |||||||
| 609 | } | ||||||
| 610 | # normal module name | ||||||
| 611 | else { | ||||||
| 612 | # this URL should be valid now | ||||||
| 613 | 2 | 5 | $link = "https://metacpan.org/module/$link"; | ||||
| 614 | } | ||||||
| 615 | |||||||
| 616 | # in case this is not set, we set it to original value | ||||||
| 617 | 9 | 100 | 22 | $text = $arg unless $text; | |||
| 618 | |||||||
| 619 | 9 | 500 | return LT.'a href='.QUO.$link.QUO.GT.$text.LT.'/a'.GT | ||||
| 620 | } | ||||||
| 621 | |||||||
| 622 | # if nothing matches we return the content unformated 'as is' | ||||||
| 623 | 0 | 0 | return $arg; | ||||
| 624 | } | ||||||
| 625 | |||||||
| 626 | sub parse { | ||||||
| 627 | 11 | 11 | 1 | 24 | my ($parser, $input) = @_; | ||
| 628 | |||||||
| 629 | # INPUT: | ||||||
| 630 | 11 | 88 | my $input_fh = IO::String->new($input); | ||||
| 631 | |||||||
| 632 | # OUTPUT: | ||||||
| 633 | # We create this IO-object because Pod::Parser does not provide | ||||||
| 634 | # pure string-data as return of result data | ||||||
| 635 | 11 | 559 | my $buffer4html; # this variable will contain the result!!! | ||||
| 636 | 11 | 144 | my $buffer4html_handle = IO::String->new($buffer4html); | ||||
| 637 | |||||||
| 638 | # we call the parser to parse, result will be in $buffer4html | ||||||
| 639 | 11 | 1118 | $parser->parse_from_filehandle($input_fh, $buffer4html_handle); | ||||
| 640 | |||||||
| 641 | 11 | 42 | return $buffer4html; | ||||
| 642 | }; | ||||||
| 643 | |||||||
| 644 | sub set_options { | ||||||
| 645 | 5 | 5 | 1 | 8 | my $self = shift; | ||
| 646 | 5 | 8 | my $args = shift; | ||||
| 647 | |||||||
| 648 | 5 | 50 | 16 | if (ref($args) eq "HASH") { | |||
| 649 | 5 | 100 | 24 | $self->head0_mode($args->{head0_mode}) if (exists $args->{head0_mode}); | |||
| 650 | 5 | 100 | 25 | $self->pagemode ($args->{pagemode}) if (exists $args->{pagemode}); | |||
| 651 | } | ||||||
| 652 | else { | ||||||
| 653 | 0 | 0 | $self->debug_msg('Plugin options are not in a HASH'); | ||||
| 654 | } | ||||||
| 655 | } | ||||||
| 656 | |||||||
| 657 | sub pagemode { | ||||||
| 658 | 25 | 25 | 1 | 47037 | my ($self, $boolean) = @_; | ||
| 659 | |||||||
| 660 | 25 | 50 | 66 | if (@_ > 1) { | |||
| 661 | 25 | 87 | $self->{+P . 'pages'} = $boolean; | ||||
| 662 | } | ||||||
| 663 | else { | ||||||
| 664 | 0 | 0 | return $self->{+P . 'pages'}; | ||||
| 665 | } | ||||||
| 666 | } | ||||||
| 667 | |||||||
| 668 | sub head0_mode { | ||||||
| 669 | 74 | 74 | 1 | 2009 | my ($self, $boolean) = @_; | ||
| 670 | |||||||
| 671 | 74 | 100 | 165 | if (@_ > 1) { | |||
| 672 | 5 | 17 | $self->{+P . 'head0_mode'} = $boolean; | ||||
| 673 | } | ||||||
| 674 | else { | ||||||
| 675 | 69 | 358 | return $self->{+P . 'head0_mode'}; | ||||
| 676 | } | ||||||
| 677 | } | ||||||
| 678 | |||||||
| 679 | # encode_entities() from HTML::Entities does not translate it correctly | ||||||
| 680 | # this is why I make it here manually as a quick fix | ||||||
| 681 | # don't reall know where how to handle this utf8 problem for now... | ||||||
| 682 | sub _html_enc { | ||||||
| 683 | 216 | 216 | 379 | my $string = shift; | |||
| 684 | |||||||
| 685 | 216 | 552 | $string = encode_entities($string); | ||||
| 686 | # ^ | ||||||
| 687 | 216 | 2564 | my $lt = LT; # | | ||||
| 688 | 216 | 253 | my $gt = GT; # | | ||||
| 689 | 216 | 255 | my $am = AMP; # | | ||||
| 690 | 216 | 364 | my $co = COL; # |-- don't change this order! | ||||
| 691 | 216 | 219 | my $qu = QUO; # | | ||||
| 692 | 216 | 238 | my $dqu= DQUO; # | | ||||
| 693 | 216 | 593 | $string =~ s/$lt/ | ||||
| 694 | 216 | 412 | $string =~ s/$gt/>/g; # | | ||||
| 695 | 216 | 351 | $string =~ s/$am/&/g; # | | ||||
| 696 | 216 | 384 | $string =~ s/$co/;/g; # | | ||||
| 697 | 216 | 369 | $string =~ s/$qu/'/g; # | | ||||
| 698 | 216 | 416 | $string =~ s/$dqu/"/g; #<---| | ||||
| 699 | |||||||
| 700 | 216 | 488 | return $string; | ||||
| 701 | } | ||||||
| 702 | |||||||
| 703 | ## replaces whitespace with html entitie | ||||||
| 704 | sub _nbsp { | ||||||
| 705 | 1 | 1 | 2 | my $string = shift; | |||
| 706 | |||||||
| 707 | 1 | 12 | $string =~ s/\ / /g; | ||||
| 708 | |||||||
| 709 | 1 | 4 | return $string; | ||||
| 710 | } | ||||||
| 711 | |||||||
| 712 | 1; | ||||||
| 713 | |||||||
| 714 | __END__ |