summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfukachan <fukachan>2012-12-13 13:52:24 +0000
committerfukachan <fukachan>2012-12-13 13:52:24 +0000
commit44e12ff7a8c3c479e16486a4c8422bd78212832e (patch)
tree0b1be5c4701e7c00bb8365d17e256dd7ea4e9fd5
parent8704fc7e183127d138a11a2236eddb492ade8849 (diff)
downloadfml8-44e12ff7a8c3c479e16486a4c8422bd78212832e.tar.gz
fml8-44e12ff7a8c3c479e16486a4c8422bd78212832e.tar.bz2
fml8-44e12ff7a8c3c479e16486a4c8422bd78212832e.zip
create HTML::EntitiesLite, defined from HTML-Parser-3.69.
modified HTML::FromText to use it.
-rw-r--r--cpan/MANIFEST11
-rw-r--r--cpan/lib/HTML/EntitiesLite.pm500
-rw-r--r--cpan/lib/HTML/FromText.pm1292
3 files changed, 1211 insertions, 592 deletions
diff --git a/cpan/MANIFEST b/cpan/MANIFEST
index 8c307b08..e5763887 100644
--- a/cpan/MANIFEST
+++ b/cpan/MANIFEST
@@ -31,4 +31,13 @@ Time-modules 2011.0517 unknown (your own risk)
Unicode-Japanese 0.47 perl itself
-$FML: MANIFEST,v 1.19 2012/02/25 13:08:13 fukachan Exp $
+
+#
+# below, hacked by fukachan@fml.org.
+#
+
+# defined from HTML-Parser-3.69.
+HTML-EntitiesLite 3.69p1 perl itself
+
+
+$FML: MANIFEST,v 1.20 2012/04/15 07:24:03 fukachan Exp $
diff --git a/cpan/lib/HTML/EntitiesLite.pm b/cpan/lib/HTML/EntitiesLite.pm
new file mode 100644
index 00000000..4423b02b
--- /dev/null
+++ b/cpan/lib/HTML/EntitiesLite.pm
@@ -0,0 +1,500 @@
+#
+# modified from package HTML::Entities in HTML-Parser-3.69;
+#
+# $FML$
+# This library is free software; you can redistribute it and/or modify
+# it under the same terms as Perl itself.
+#
+# HTML-Parser-3.69 COPYRIGHT
+# (C) 1995-2009 Gisle Aas. All rights reserved.
+# (C) 1999-2000 Michael A. Chase. All rights reserved.
+# This library is free software; you can redistribute it and/or modify
+# it under the same terms as Perl itself.
+
+# modified from package HTML::Entities;
+package HTML::EntitiesLite;
+
+=encoding utf8
+
+=head1 NAME
+
+HTML::EntitiesLite - Encode or decode strings with HTML entities
+
+=head1 SYNOPSIS
+
+ use HTML::EntitiesLite;
+
+ $a = "V&aring;re norske tegn b&oslash;r &#230res";
+ decode_entities($a);
+ encode_entities($a, "\200-\377");
+
+For example, this:
+
+ $input = "vis-à-vis Beyoncé's naïve\npapier-mâché résumé";
+ print encode_entities($input), "\n"
+
+Prints this out:
+
+ vis-&agrave;-vis Beyonc&eacute;'s na&iuml;ve
+ papier-m&acirc;ch&eacute; r&eacute;sum&eacute;
+
+=head1 DESCRIPTION
+
+This module deals with encoding and decoding of strings with HTML
+character entities. The module provides the following functions:
+
+=over 4
+
+=item decode_entities( $string, ... )
+
+This routine replaces HTML entities found in the $string with the
+corresponding Unicode character. Unrecognized entities are left alone.
+
+If multiple strings are provided as argument they are each decoded
+separately and the same number of strings are returned.
+
+If called in void context the arguments are decoded in-place.
+
+This routine is exported by default.
+
+=item _decode_entities( $string, \%entity2char )
+
+=item _decode_entities( $string, \%entity2char, $expand_prefix )
+
+This will in-place replace HTML entities in $string. The %entity2char
+hash must be provided. Named entities not found in the %entity2char
+hash are left alone. Numeric entities are expanded unless their value
+overflow.
+
+The keys in %entity2char are the entity names to be expanded and their
+values are what they should expand into. The values do not have to be
+single character strings. If a key has ";" as suffix,
+then occurrences in $string are only expanded if properly terminated
+with ";". Entities without ";" will be expanded regardless of how
+they are terminated for compatibility with how common browsers treat
+entities in the Latin-1 range.
+
+If $expand_prefix is TRUE then entities without trailing ";" in
+%entity2char will even be expanded as a prefix of a longer
+unrecognized name. The longest matching name in %entity2char will be
+used. This is mainly present for compatibility with an MSIE
+misfeature.
+
+ $string = "foo&nbspbar";
+ _decode_entities($string, { nb => "@", nbsp => "\xA0" }, 1);
+ print $string; # will print "foo bar"
+
+This routine is exported by default.
+
+=item encode_entities( $string )
+
+=item encode_entities( $string, $unsafe_chars )
+
+This routine replaces unsafe characters in $string with their entity
+representation. A second argument can be given to specify which characters to
+consider unsafe. The unsafe characters is specified using the regular
+expression character class syntax (what you find within brackets in regular
+expressions).
+
+The default set of characters to encode are control chars, high-bit chars, and
+the C<< < >>, C<< & >>, C<< > >>, C<< ' >> and C<< " >> characters. But this,
+for example, would encode I<just> the C<< < >>, C<< & >>, C<< > >>, and C<< "
+>> characters:
+
+ $encoded = encode_entities($input, '<>&"');
+
+and this would only encode non-plain ascii:
+
+ $encoded = encode_entities($input, '^\n\x20-\x25\x27-\x7e');
+
+This routine is exported by default.
+
+=item encode_entities_numeric( $string )
+
+=item encode_entities_numeric( $string, $unsafe_chars )
+
+This routine works just like encode_entities, except that the replacement
+entities are always C<&#xI<hexnum>;> and never C<&I<entname>;>. For
+example, C<encode_entities("r\xF4le")> returns "r&ocirc;le", but
+C<encode_entities_numeric("r\xF4le")> returns "r&#xF4;le".
+
+This routine is I<not> exported by default. But you can always
+export it with C<use HTML::EntitiesLite qw(encode_entities_numeric);>
+or even C<use HTML::EntitiesLite qw(:DEFAULT encode_entities_numeric);>
+
+=back
+
+All these routines modify the string passed as the first argument, if
+called in a void context. In scalar and array contexts, the encoded or
+decoded string is returned (without changing the input string).
+
+If you prefer not to import these routines into your namespace, you can
+call them as:
+
+ use HTML::EntitiesLite ();
+ $decoded = HTML::EntitiesLite::decode($a);
+ $encoded = HTML::EntitiesLite::encode($a);
+ $encoded = HTML::EntitiesLite::encode_numeric($a);
+
+The module can also export the %char2entity and the %entity2char
+hashes, which contain the mapping from all characters to the
+corresponding entities (and vice versa, respectively).
+
+=head1 COPYRIGHT
+
+Copyright 1995-2006 Gisle Aas. All rights reserved.
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
+
+use strict;
+use vars qw(@ISA @EXPORT @EXPORT_OK $VERSION);
+use vars qw(%entity2char %char2entity);
+
+require 5.004;
+require Exporter;
+@ISA = qw(Exporter);
+
+# XXX modified: disable here, we not use decode_entities().
+# @EXPORT = qw(encode_entities decode_entities _decode_entities);
+@EXPORT = qw(encode_entities);
+@EXPORT_OK = qw(%entity2char %char2entity encode_entities_numeric);
+
+$VERSION = "3.69";
+sub Version { $VERSION; }
+
+# XXX modified: disable here, we not use decode_entities().
+# require HTML::Parser; # for fast XS implemented decode_entities
+
+
+%entity2char = (
+ # Some normal chars that have special meaning in SGML context
+ amp => '&', # ampersand
+'gt' => '>', # greater than
+'lt' => '<', # less than
+ quot => '"', # double quote
+ apos => "'", # single quote
+
+ # PUBLIC ISO 8879-1986//ENTITIES Added Latin 1//EN//HTML
+ AElig => chr(198), # capital AE diphthong (ligature)
+ Aacute => chr(193), # capital A, acute accent
+ Acirc => chr(194), # capital A, circumflex accent
+ Agrave => chr(192), # capital A, grave accent
+ Aring => chr(197), # capital A, ring
+ Atilde => chr(195), # capital A, tilde
+ Auml => chr(196), # capital A, dieresis or umlaut mark
+ Ccedil => chr(199), # capital C, cedilla
+ ETH => chr(208), # capital Eth, Icelandic
+ Eacute => chr(201), # capital E, acute accent
+ Ecirc => chr(202), # capital E, circumflex accent
+ Egrave => chr(200), # capital E, grave accent
+ Euml => chr(203), # capital E, dieresis or umlaut mark
+ Iacute => chr(205), # capital I, acute accent
+ Icirc => chr(206), # capital I, circumflex accent
+ Igrave => chr(204), # capital I, grave accent
+ Iuml => chr(207), # capital I, dieresis or umlaut mark
+ Ntilde => chr(209), # capital N, tilde
+ Oacute => chr(211), # capital O, acute accent
+ Ocirc => chr(212), # capital O, circumflex accent
+ Ograve => chr(210), # capital O, grave accent
+ Oslash => chr(216), # capital O, slash
+ Otilde => chr(213), # capital O, tilde
+ Ouml => chr(214), # capital O, dieresis or umlaut mark
+ THORN => chr(222), # capital THORN, Icelandic
+ Uacute => chr(218), # capital U, acute accent
+ Ucirc => chr(219), # capital U, circumflex accent
+ Ugrave => chr(217), # capital U, grave accent
+ Uuml => chr(220), # capital U, dieresis or umlaut mark
+ Yacute => chr(221), # capital Y, acute accent
+ aacute => chr(225), # small a, acute accent
+ acirc => chr(226), # small a, circumflex accent
+ aelig => chr(230), # small ae diphthong (ligature)
+ agrave => chr(224), # small a, grave accent
+ aring => chr(229), # small a, ring
+ atilde => chr(227), # small a, tilde
+ auml => chr(228), # small a, dieresis or umlaut mark
+ ccedil => chr(231), # small c, cedilla
+ eacute => chr(233), # small e, acute accent
+ ecirc => chr(234), # small e, circumflex accent
+ egrave => chr(232), # small e, grave accent
+ eth => chr(240), # small eth, Icelandic
+ euml => chr(235), # small e, dieresis or umlaut mark
+ iacute => chr(237), # small i, acute accent
+ icirc => chr(238), # small i, circumflex accent
+ igrave => chr(236), # small i, grave accent
+ iuml => chr(239), # small i, dieresis or umlaut mark
+ ntilde => chr(241), # small n, tilde
+ oacute => chr(243), # small o, acute accent
+ ocirc => chr(244), # small o, circumflex accent
+ ograve => chr(242), # small o, grave accent
+ oslash => chr(248), # small o, slash
+ otilde => chr(245), # small o, tilde
+ ouml => chr(246), # small o, dieresis or umlaut mark
+ szlig => chr(223), # small sharp s, German (sz ligature)
+ thorn => chr(254), # small thorn, Icelandic
+ uacute => chr(250), # small u, acute accent
+ ucirc => chr(251), # small u, circumflex accent
+ ugrave => chr(249), # small u, grave accent
+ uuml => chr(252), # small u, dieresis or umlaut mark
+ yacute => chr(253), # small y, acute accent
+ yuml => chr(255), # small y, dieresis or umlaut mark
+
+ # Some extra Latin 1 chars that are listed in the HTML3.2 draft (21-May-96)
+ copy => chr(169), # copyright sign
+ reg => chr(174), # registered sign
+ nbsp => chr(160), # non breaking space
+
+ # Additional ISO-8859/1 entities listed in rfc1866 (section 14)
+ iexcl => chr(161),
+ cent => chr(162),
+ pound => chr(163),
+ curren => chr(164),
+ yen => chr(165),
+ brvbar => chr(166),
+ sect => chr(167),
+ uml => chr(168),
+ ordf => chr(170),
+ laquo => chr(171),
+'not' => chr(172), # not is a keyword in perl
+ shy => chr(173),
+ macr => chr(175),
+ deg => chr(176),
+ plusmn => chr(177),
+ sup1 => chr(185),
+ sup2 => chr(178),
+ sup3 => chr(179),
+ acute => chr(180),
+ micro => chr(181),
+ para => chr(182),
+ middot => chr(183),
+ cedil => chr(184),
+ ordm => chr(186),
+ raquo => chr(187),
+ frac14 => chr(188),
+ frac12 => chr(189),
+ frac34 => chr(190),
+ iquest => chr(191),
+'times' => chr(215), # times is a keyword in perl
+ divide => chr(247),
+
+ ( $] > 5.007 ? (
+ 'OElig;' => chr(338),
+ 'oelig;' => chr(339),
+ 'Scaron;' => chr(352),
+ 'scaron;' => chr(353),
+ 'Yuml;' => chr(376),
+ 'fnof;' => chr(402),
+ 'circ;' => chr(710),
+ 'tilde;' => chr(732),
+ 'Alpha;' => chr(913),
+ 'Beta;' => chr(914),
+ 'Gamma;' => chr(915),
+ 'Delta;' => chr(916),
+ 'Epsilon;' => chr(917),
+ 'Zeta;' => chr(918),
+ 'Eta;' => chr(919),
+ 'Theta;' => chr(920),
+ 'Iota;' => chr(921),
+ 'Kappa;' => chr(922),
+ 'Lambda;' => chr(923),
+ 'Mu;' => chr(924),
+ 'Nu;' => chr(925),
+ 'Xi;' => chr(926),
+ 'Omicron;' => chr(927),
+ 'Pi;' => chr(928),
+ 'Rho;' => chr(929),
+ 'Sigma;' => chr(931),
+ 'Tau;' => chr(932),
+ 'Upsilon;' => chr(933),
+ 'Phi;' => chr(934),
+ 'Chi;' => chr(935),
+ 'Psi;' => chr(936),
+ 'Omega;' => chr(937),
+ 'alpha;' => chr(945),
+ 'beta;' => chr(946),
+ 'gamma;' => chr(947),
+ 'delta;' => chr(948),
+ 'epsilon;' => chr(949),
+ 'zeta;' => chr(950),
+ 'eta;' => chr(951),
+ 'theta;' => chr(952),
+ 'iota;' => chr(953),
+ 'kappa;' => chr(954),
+ 'lambda;' => chr(955),
+ 'mu;' => chr(956),
+ 'nu;' => chr(957),
+ 'xi;' => chr(958),
+ 'omicron;' => chr(959),
+ 'pi;' => chr(960),
+ 'rho;' => chr(961),
+ 'sigmaf;' => chr(962),
+ 'sigma;' => chr(963),
+ 'tau;' => chr(964),
+ 'upsilon;' => chr(965),
+ 'phi;' => chr(966),
+ 'chi;' => chr(967),
+ 'psi;' => chr(968),
+ 'omega;' => chr(969),
+ 'thetasym;' => chr(977),
+ 'upsih;' => chr(978),
+ 'piv;' => chr(982),
+ 'ensp;' => chr(8194),
+ 'emsp;' => chr(8195),
+ 'thinsp;' => chr(8201),
+ 'zwnj;' => chr(8204),
+ 'zwj;' => chr(8205),
+ 'lrm;' => chr(8206),
+ 'rlm;' => chr(8207),
+ 'ndash;' => chr(8211),
+ 'mdash;' => chr(8212),
+ 'lsquo;' => chr(8216),
+ 'rsquo;' => chr(8217),
+ 'sbquo;' => chr(8218),
+ 'ldquo;' => chr(8220),
+ 'rdquo;' => chr(8221),
+ 'bdquo;' => chr(8222),
+ 'dagger;' => chr(8224),
+ 'Dagger;' => chr(8225),
+ 'bull;' => chr(8226),
+ 'hellip;' => chr(8230),
+ 'permil;' => chr(8240),
+ 'prime;' => chr(8242),
+ 'Prime;' => chr(8243),
+ 'lsaquo;' => chr(8249),
+ 'rsaquo;' => chr(8250),
+ 'oline;' => chr(8254),
+ 'frasl;' => chr(8260),
+ 'euro;' => chr(8364),
+ 'image;' => chr(8465),
+ 'weierp;' => chr(8472),
+ 'real;' => chr(8476),
+ 'trade;' => chr(8482),
+ 'alefsym;' => chr(8501),
+ 'larr;' => chr(8592),
+ 'uarr;' => chr(8593),
+ 'rarr;' => chr(8594),
+ 'darr;' => chr(8595),
+ 'harr;' => chr(8596),
+ 'crarr;' => chr(8629),
+ 'lArr;' => chr(8656),
+ 'uArr;' => chr(8657),
+ 'rArr;' => chr(8658),
+ 'dArr;' => chr(8659),
+ 'hArr;' => chr(8660),
+ 'forall;' => chr(8704),
+ 'part;' => chr(8706),
+ 'exist;' => chr(8707),
+ 'empty;' => chr(8709),
+ 'nabla;' => chr(8711),
+ 'isin;' => chr(8712),
+ 'notin;' => chr(8713),
+ 'ni;' => chr(8715),
+ 'prod;' => chr(8719),
+ 'sum;' => chr(8721),
+ 'minus;' => chr(8722),
+ 'lowast;' => chr(8727),
+ 'radic;' => chr(8730),
+ 'prop;' => chr(8733),
+ 'infin;' => chr(8734),
+ 'ang;' => chr(8736),
+ 'and;' => chr(8743),
+ 'or;' => chr(8744),
+ 'cap;' => chr(8745),
+ 'cup;' => chr(8746),
+ 'int;' => chr(8747),
+ 'there4;' => chr(8756),
+ 'sim;' => chr(8764),
+ 'cong;' => chr(8773),
+ 'asymp;' => chr(8776),
+ 'ne;' => chr(8800),
+ 'equiv;' => chr(8801),
+ 'le;' => chr(8804),
+ 'ge;' => chr(8805),
+ 'sub;' => chr(8834),
+ 'sup;' => chr(8835),
+ 'nsub;' => chr(8836),
+ 'sube;' => chr(8838),
+ 'supe;' => chr(8839),
+ 'oplus;' => chr(8853),
+ 'otimes;' => chr(8855),
+ 'perp;' => chr(8869),
+ 'sdot;' => chr(8901),
+ 'lceil;' => chr(8968),
+ 'rceil;' => chr(8969),
+ 'lfloor;' => chr(8970),
+ 'rfloor;' => chr(8971),
+ 'lang;' => chr(9001),
+ 'rang;' => chr(9002),
+ 'loz;' => chr(9674),
+ 'spades;' => chr(9824),
+ 'clubs;' => chr(9827),
+ 'hearts;' => chr(9829),
+ 'diams;' => chr(9830),
+ ) : ())
+);
+
+
+# Make the opposite mapping
+while (my($entity, $char) = each(%entity2char)) {
+ $entity =~ s/;\z//;
+ $char2entity{$char} = "&$entity;";
+}
+delete $char2entity{"'"}; # only one-way decoding
+
+# Fill in missing entities
+for (0 .. 255) {
+ next if exists $char2entity{chr($_)};
+ $char2entity{chr($_)} = "&#$_;";
+}
+
+my %subst; # compiled encoding regexps
+
+sub encode_entities
+{
+ return undef unless defined $_[0];
+ my $ref;
+ if (defined wantarray) {
+ my $x = $_[0];
+ $ref = \$x; # copy
+ } else {
+ $ref = \$_[0]; # modify in-place
+ }
+ if (defined $_[1] and length $_[1]) {
+ unless (exists $subst{$_[1]}) {
+ # Because we can't compile regex we fake it with a cached sub
+ my $chars = $_[1];
+ $chars =~ s,(?<!\\)([]/]),\\$1,g;
+ $chars =~ s,(?<!\\)\\\z,\\\\,;
+ my $code = "sub {\$_[0] =~ s/([$chars])/\$char2entity{\$1} || num_entity(\$1)/ge; }";
+ $subst{$_[1]} = eval $code;
+ die( $@ . " while trying to turn range: \"$_[1]\"\n "
+ . "into code: $code\n "
+ ) if $@;
+ }
+ &{$subst{$_[1]}}($$ref);
+ } else {
+ # Encode control chars, high bit chars and '<', '&', '>', ''' and '"'
+ $$ref =~ s/([^\n\r\t !\#\$%\(-;=?-~])/$char2entity{$1} || num_entity($1)/ge;
+ }
+ $$ref;
+}
+
+sub encode_entities_numeric {
+ local %char2entity;
+ return &encode_entities; # a goto &encode_entities wouldn't work
+}
+
+
+sub num_entity {
+ sprintf "&#x%X;", ord($_[0]);
+}
+
+# Set up aliases
+*encode = \&encode_entities;
+*encode_numeric = \&encode_entities_numeric;
+*encode_numerically = \&encode_entities_numeric;
+# *decode = \&decode_entities;
+
+1;
diff --git a/cpan/lib/HTML/FromText.pm b/cpan/lib/HTML/FromText.pm
index 2b7f1753..29089819 100644
--- a/cpan/lib/HTML/FromText.pm
+++ b/cpan/lib/HTML/FromText.pm
@@ -1,736 +1,846 @@
-require 5.004;
+package HTML::FromText;
+
+=head1 NAME
+
+HTML::FromText - Convert plain text to HTML.
+
+=head1 SYNOPSIS
+
+ use HTML::FromText;
+ text2html( $text, %options );
+
+ # or
+
+ use HTML::FromText ();
+ my $t2h = HTML::FromText->new( \%options );
+ my $html = $t2h->parse( $html );
+
+=cut
+
use strict;
+use Scalar::Util qw[blessed];
+use HTML::EntitiesLite qw[encode_entities];
+use Text::Tabs qw[expand];
+use Email::Find::addrspec qw[$Addr_spec_re];
+use Exporter::Lite;
+
+use vars qw[$VERSION @EXPORT @DECORATORS $PROTOCOLS];
+
+$VERSION = '2.05';
+@EXPORT = qw[text2html];
+@DECORATORS = qw[urls email bold underline];
+$PROTOCOLS = qr/
+ afs | cid | ftp | gopher |
+ http | https | mid | news |
+ nntp | prospero | telnet | wais
+ /x;
-package HTML::FromText;
-use Carp;
-use Exporter;
-use Text::Tabs 'expand';
-use vars qw($RCSID $VERSION $QUIET @EXPORT @ISA);
-
-@ISA = qw(Exporter);
-@EXPORT = qw(text2html);
-$RCSID = q$Id: FromText.pm,v 1.14 1999/10/06 10:53:37 garethr Exp $;
-$VERSION = '1.005';
-$QUIET = 0;
-
-# This list of protocols is taken from RFC 1630: "Universal Resource
-# Identifiers in WWW". The protocol "file" is omitted because
-# experience suggests that it results in many false positives; "https"
-# postdates RFC 1630. The protocol "mailto" is handled separately, by
-# the email address matching code.
-
-my $protocol = join '|',
- qw(afs cid ftp gopher http https mid news nntp prospero telnet wais);
-
-# The regular expressions matching email addresses use the following
-# syntax elements from RFC 822. I can't use the full details of
-# structured field bodies, because that would give too many false
-# positives. (See Tom Christiansen's ckaddr.gz for a full
-# implementation of the RFC 822.)
-#
-# addr-spec = local-part "@" domain
-# local-part = word *("." word)
-# word = atom
-# domain = sub-domain *("." sub-domain)
-# sub-domain = domain-ref
-# domain-ref = atom
-# atom = 1*<any CHAR except specials, SPACE and CTLs>
-# specials = "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "\"
-# / <"> / "." / "[" / "]"
-#
-# I have ignored quoting, domain literals and comments.
-#
-# Note that '&' can legally appear in email addresses (for example,
-# 'fred&barney@stonehenge.com'). If the 'metachars' option is passed to
-# text2html then I must use '&amp;' to recognize '&'. Thus the regular
-# expression $atom[0] recognizes an atom in the case where the option
-# 'metachars' is false; $atom[1] recognizes an atom in the case where
-# 'metachars' is true. Similarly for the regular expressions $email[0]
-# and $email[1], which recognize email addresses.
-
-my @atom =
- ( '[!#$%&\'*+\\-/0123456789=?ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~]+',
- '(?:&amp;|[!#$%\'*+\\-/0123456789=?ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~])+' );
-
-my @email = ( "$atom[0](\\.$atom[0])*\@$atom[0](\\.$atom[0])*",
- "$atom[1](\\.$atom[1])*\@$atom[1](\\.$atom[1])*" );
-
-my @alignments = ( '', '', ' ALIGN="RIGHT"', ' ALIGN="CENTER"' );
-
-sub string2html ($$) {
- my $options = $_[1];
- for ($_[0]) { # Modify in-place.
-
- # METACHARS: mark up HTML metacharacters as corresponding entities.
- if ($options->{metachars}) {
- s/&/&amp;/g;
- s/</&lt;/g;
- s/>/&gt;/g;
- s/\"/&quot;/g;
- }
+=head1 DESCRIPTION
- # EMAIL, URLS: spot electronic mail addresses and turn them into
- # links. Note (1) if `urls' is set but not `email', then only
- # addresses prefixed by `mailto:' will be marked up; (2) that we leave
- # the `mailto:' prefix in the anchor text.
- if ($options->{email} or $options->{urls}) {
- s|((?:mailto:)?)($email[$options->{metachars}?1:0])|
- ($options->{email} or $1)
- ? "<TT><A HREF=\"mailto:$2\">$1$2</A></TT>" : $2|gex;
- }
+C<HTML::FromText> converts plain text to HTML. There are a handfull of
+options that shape the conversion. There is a utility function,
+C<text2html>, that's exported by default. This function is simply a short-
+cut to the Object Oriented interface described in detail below.
- # URLS: mark up URLs as links (note that `mailto' links are handled
- # above).
- if ($options->{urls}) {
- s|\b((?:$protocol):\S+[\w/])|<TT><A HREF="$1">$1</A></TT>|g;
- }
+=head2 Methods
- # BOLD: mark up words in *asterisks* as bold.
- if ($options->{bold}) {
- s#(^|\s)\*([^*]+)\*(?=\s|$)#$1<B>$2</B>#g;
- }
+The following methods may be used as the public interface.
- # UNDERLINE: mark up words in _underscores_ as underlined.
- if ($options->{underline}) {
- s#(^|\s)_([^_]+?)_(?=\s|$)#$1<U>$2</U>#g;
- }
- }
+=head3 new
- return $_[0];
-}
+ my $t2h = HTML::FromText->new({
+ paras => 1,
+ blockcode => 1,
+ tables => 1,
+ bullets => 1,
+ numbers => 1,
+ urls => 1,
+ email => 1,
+ bold => 1,
+ underline => 1,
+ });
-sub text2html {
- local $_ = shift; # Take a copy; don't modify in-place.
- return $_ unless $_;
-
- my %options = ( metachars => 1, @_ );
-
- # Check options for sanity.
- unless ($QUIET) {
- carp "text2html: `spaces' will be ignored since `lines' is not specified"
- if $options{spaces} and not $options{lines};
- if ($options{paras}) {
- if ($options{blockparas}) {
- foreach my $o (qw(blockquotes blockcode)) {
- carp "text2html: `$o' will be ignored since `blockparas' is specified" if $options{$o};
- }
- } elsif ($options{blockcode} and $options{blockquotes}) {
- carp "text2html: `blockquotes' will be ignored since `blockcode' is specified";
- }
- } else {
- foreach my $o (qw(bullets numbers blockquotes blockparas blockcode
- title headings tables)) {
- carp "text2html: `$o' will be ignored since `paras' is not specified"
- if $options{$o};
- }
- }
- }
-
- # Expand tabs.
- $_ = join "\n", expand(split /\r?\n/);
-
- # PRE: put text in <PRE> element.
- if ($options{pre}) {
- string2html($_, \%options);
- s|^|<PRE>|;
- s|$|</PRE>|;
- }
-
- # LINES: preserve line breaks from original text.
- elsif ($options{lines}) {
- string2html($_, \%options);
- s/\n/<BR>\n/gm;
-
- # SPACES: preserve spaces from original text.
- s/ /&nbsp;/g if $options{spaces};
- }
-
- # PARAS: treat text as sequence of paragraphs.
- elsif ($options{paras}) {
- my @paras;
-
- # Remove initial and final blank lines.
- s/^(?:\s*?\n)+//;
- s/(?:\n\s*?)+$//;
-
- # Split on a different regexp depending on what kinds of paragraphs
- # will be recognised later. The idea is that bulleted lists like
- # this:
- #
- # * item 1
- # * item 2
- #
- # will be recognised as multiple paragraphs if the 'bullets' option
- # is supplied, but as a single paragraph otherwise. (Similarly for
- # numbered lists).
- if ($options{bullets} and $options{numbers}) {
- @paras = split
- /(?:\s*\n)+ # (0 or more blank lines, followed by LF)
- (?:\s*\n # Either 1 or more blank lines, or
- |(?=\s*[*-]\s+ # bulleted item follows, or
- |\s*(?:\d+)[.\)\]]?\s+)) # numbered item follows
- /x;
- } elsif ($options{bullets}) {
- @paras = split
- /(?:\s*\n)+ # (0 or more blank lines, followed by LF)
- (?:\s*\n # Either 1 or more blank lines, or
- |(?=\s*[*-]\s+)) # bulleted item follows.
- /x;
- } elsif ($options{numbers}) {
- @paras = split
- /(?:\s*\n)+ # (0 or more blank lines, followed by LF)
- (?:\s*\n # Either 1 or more blank lines, or
- |(?=\s*(?:\d+)[.\)\]]?\s+)) # numbered item follows.
- /x;
- } else {
- @paras = split
- /\s*\n(?:\s*\n)+ # 1 or more blank lines.
- /x;
- }
+Constructs a new C<HTML::FromText> object using the given
+configuration. The resulting object can parse lots of objects using the
+C<parse> method.
- my $last = ''; # List type (OL/UL) of last paragraph
- my $this; # List type (OL/UL) of this paragraph
- my $first = 1; # True if this is first paragraph
-
- foreach (@paras) {
- my (@rows,@starts,@ends);
- $this = '';
-
- # TITLE: mark up first paragraph as level-1 heading.
- if ($options{title} and $first) {
- string2html($_,\%options);
- s|^|<H1>|;
- s|$|</H1>|;
- }
-
- # HEADINGS: mark up paragraphs with numbers at the start of the
- # first line as headings.
- elsif ($options{headings} and /^(\d+(\.\d+)*)\.?\s/) {
- my $number = $1;
- my $level = 1 + ($number =~ tr/././);
- $level = 6 if $level > 6;
- string2html($_,\%options);
- s|^|<H$level>|;
- s|$|</H$level>|;
- }
-
- # BULLETS: mark up paragraphs starting with bullets as items in an
- # unnumbered list.
- elsif ($options{bullets} and /^\s*[*-]\s+/) {
- string2html($_,\%options);
- s/^\s*[*-]\s+/<LI><P>/;
- s|$|</P>|;
- $this = 'UL';
- }
-
- # NUMBERS: mark up paragraphs starting with numbers as items in a
- # numbered list.
- elsif ($options{numbers} and /^\s*(\d+)[.\)\]]?\s+/) {
- string2html($_,\%options);
- s/^\s*(\d+)[.\)\]]?\s+/<LI VALUE="$1"><P>/;
- s|$|</P>|;
- $this = 'OL';
- }
-
- # TABLES: spot and mark up tables. We combine the lines of the
- # paragraph using the string bitwise or (|) operator, the result
- # being in $spaces. A character in $spaces is a space only if
- # there was a space at that position in every line of the
- # paragraph. $space can be used to search for contiguous spaces
- # that occur on all lines of the paragraph. If this results in at
- # least two columns, the paragraph is identified as a table.
- #
- # Note that this option appears before the various 'blockquotes'
- # options because a table may well have whitespace to the left, in
- # which case it must not be incorrectly recognised as a
- # blockquote.
- elsif ($options{tables} and do {
- @rows = split /\n/, $_;
- my $spaces;
- my $max = 0;
- my $min = length;
- foreach my $row (@rows) {
- ($spaces |= $row) =~ tr/ /\xff/c;
- $min = length $row if length $row < $min;
- $max = length $row if $max < length $row;
- }
- $spaces = substr $spaces, 0, $min;
- push(@starts, 0) unless $spaces =~ /^ /;
- while ($spaces =~ /((?:^| ) +)(?=[^ ])/g) {
- push @ends, pos($spaces) - length $1;
- push @starts, pos($spaces);
- }
- shift(@ends) if $spaces =~ /^ /;
- push(@ends, $max);
-
- # Two or more rows and two or more columns indicate a table.
- 2 <= @rows and 2 <= @starts
- }) {
- # For each column, guess whether it should be left, centre or
- # right aligned by examining all cells in that column for space
- # to the left or the right. A simple majority among those cells
- # that actually have space to one side or another decides (if no
- # alignment gets a majority, left alignment wins by default).
-
- my @align;
- foreach my $col (0 .. $#starts) {
- my @count = (0, 0, 0, 0);
- foreach my $row (@rows) {
- my $width = $ends[$col] - $starts[$col];
- my $cell = substr $row, $starts[$col], $width;
- ++ $count[($cell =~ /^ / ? 2 : 0)
- + ($cell =~ / $/ || length($cell) < $width ? 1 : 0)];
- }
- $align[$col] = 0;
- my $population = $count[1] + $count[2] + $count[3];
- foreach (1 .. 3) {
- if ($count[$_] * 2 > $population) {
- $align[$col] = $_;
- last;
- }
- }
- }
+Options to C<new> are passed by name, with the value being either true
+or false. If true, the option will be turned on. If false, it will be
+turned off. The following outlines all the options.
- foreach my $row (@rows) {
- $row = join '', '<TR>', (map {
- my $cell = substr $row, $starts[$_], $ends[$_] - $starts[$_];
- $cell =~ s/^ +//;
- $cell =~ s/ +$//;
- string2html($cell,\%options);
- ('<TD', $alignments[$align[$_]], '>', $cell, '</TD>')
- } 0 .. $#starts), '</TR>';
- }
- my $tag = $starts[0] == 0 ? 'P' : 'BLOCKQUOTE';
- $_ = join "\n", "<$tag><TABLE>", @rows, "</TABLE></$tag>";
- }
-
- # BLOCKPARAS, BLOCKCODE, BLOCKQUOTES: mark up indented paragraphs
- # as block quotes of various kinds.
- elsif (($options{blockparas} or $options{blockquotes}
- or $options{blockcode}) and /^(\s+).*(?:\n\1.*)*$/) {
- string2html($_,\%options);
-
- # Every line in the paragraph starts with at white space, the common
- # whitespace being in $1. Remove the common initial whitespace,
- s/^$1//gm;
-
- # BLOCKPARAS: treat as a paragraph.
- if ($options{blockparas}) {
- s|^|<P>|;
- s|$|</P>|;
- }
-
- # BLOCKCODE, BLOCKQUOTES: preserve line breaks.
- else {
- s/\n/<BR>\n/gm;
-
- # BLOCKCODE: preserve spaces, use fixed-width font.
- if ($options{blockcode}) {
- s| |&nbsp;|g;
- s|^|<TT>|;
- s|$|</TT>|;
- }
- }
- s|^|<BLOCKQUOTE>|;
- s|$|</BLOCKQUOTE>|;
- }
-
- # Didn't match any of the above, so just an ordinary paragraph.
- else {
- string2html($_,\%options);
- s|^|<P>|;
- s|$|</P>|;
- }
-
- # Insert <UL>, </UL>, <OL> or </OL> if this paragraph belongs to a
- # different list type than the previous one.
- if ($this ne $last) {
- s|^|<$this>| if ($this ne '');
- s|^|</$last>| if ($last ne '');
- }
- $last = $this;
- $first = 0;
- }
- if ($this ne '') {
- push @paras, "</$this>";
- }
- $_ = join "\n", @paras;
- }
-
- # None of PRE, LINES, PARAS specified: apply basic transformations.
- else {
- string2html($_,\%options);
- }
- return $_;
-}
+=head4 Decorators
-1;
+=over 5
-__END__
+=item metachars
-=head1 NAME
+This option is on by default.
-HTML::FromText - mark up text as HTML
+All characters that are unsafe for HTML display will be encoded using
+C<HTML::Entities::encode_entities()>.
-=head1 SYNOPSIS
+=item urls
- use HTML::FromText;
- print text2html($text, urls => 1, paras => 1, headings => 1);
+This option is off by default.
-=head1 DESCRIPTION
+Replaces URLs with links.
-The C<text2html> function marks up plain text as HTML. By default it
-expands tabs and converts HTML metacharacters into the corresponding
-entities. More complicated transformations, such as splitting the text
-into paragraphs or marking up bulleted lists, can be carried out by
-setting the appropriate options.
+=item email
-=head1 SUMMARY OF OPTIONS
+This option is off by default.
-These options always apply:
+Replaces email addresses with C<mailto:> links.
- metachars Convert HTML metacharacters to entity references
- urls Convert URLs to links
- email Convert email addresses to links
- bold Mark up words with *asterisks* in bold
- underline Mark up words with _underscores_ as underlined
+=item bold
-You can then choose to treat the text according to one of these options:
+This option is off by default.
- pre Treat text as preformatted
- lines Treat text as line-oriented
- paras Treat text as paragraph-oriented
+Replaces text surrounded by asterisks (C<*>) with the same text
+surrounded by C<strong> tags.
-(If more than one of these is specified, C<pre> takes precedence over
-C<lines> which takes precedence over C<paras>.) The following option
-applies when the C<lines> option is specified:
+=item underline
+
+This option is off by default.
- spaces Preserve spaces from the original text
+Replaces text surrownded by underscores (C<_>) with the same text
+surrounded by C<span> tags with an underline style.
-The following options apply when the C<paras> option is specified:
+=back
- blockparas Mark up indented paragraphs as block quote
- blockquotes Ditto, also preserve lines from original
- blockcode Ditto, also preserve spaces from original
- bullets Mark up bulleted paragraphs as unordered list
- headings Mark up headings
- numbers Mark up numbered paragraphs as ordered list
- tables Mark up tables
- title Mark up first paragraph as level 1 heading
+=head4 Output Modes
-C<text2html> will issue a warning if it is passed nonsensical options,
-for example C<headings> but not C<paras>. These warnings can be
-supressed by setting $HTML::FromText::QUIET to true.
+The following are three output modes and the options associated with
+them. They are listed in order of precidence. If none of these modes are
+supplied, the basic decorators are applied to the text in whole.
-=head1 OPTIONS
+=over 5
-=over 4
+=item B<pre>
+
+This option is off by default.
+
+Wraps the entire text in C<pre> tags.
+
+=item B<lines>
+
+This option is off by default.
+
+Preserves line breaks by inserting C<br> tags at the end of each line.
+
+This mode has further options.
+
+=over 5
+
+=item spaces
+
+This option is off by default.
+
+All spaces are HTML encoded.
+
+=back
+
+=item B<paras>
+
+This option is off by default.
+
+Preserves paragraphs by wrapping them in C<p> tags.
+
+This mode has further options.
+
+=over 5
+
+=item bullets
+
+This option is off by default.
+
+Convert bulleted lists into unordered lists (C<ul>). Bullets can be
+either an asterisk (C<*>) or a hyphen (C<->). Lists can be nested.
+
+=item numbers
+
+This option is off by default.
+
+Convert numbered lists into ordered lists (C<ol>). Numbered lists are
+identified by numerals. Lists may be nested.
+
+=item headings
+
+This option is off by default.
+
+Convert paragraphs identified as headings into HTML headings at
+the appropriate level. The heading C<1. Top> would be heading
+level one (C<h1>). The heading C<2.5.1. Blah> would be heading
+level three (C<h3>).
+
+=item title
+
+This option is off by default.
+
+Convert the first paragraph to a heading level one (C<h1>).
+
+=item tables
+
+This option is off by default.
+
+Convert paragraphs identified as tables to HTML tables. Tables are two
+or more rows and two or more columns. Columns should be separated by two
+or more spaces.
+
+=back
+
+The following options apply specifically to indented paragraphs. They
+are listed in order of precidence.
+
+=over 5
=item blockparas
+This option is off by default.
+
+Convert indented paragraphs to block quotes using the C<blockquote> tag.
+
=item blockquotes
+Convert indented paragraphs as C<blockparas> would, but also preserving
+line breaks.
+
=item blockcode
-These options cause to C<text2html> to spot paragraphs where every line
-begins with whitespace, and mark them up as block quotes. If more than
-one of these options is specified, C<blockparas> takes precedence over
-C<blockcode>, which takes precedence over C<blockquotes>. All three
-options are ignored unless the C<paras> option is also set.
+Convert indented paragraphs as C<blockquotes> would, but also preserving
+spaces using C<pre> tags.
-The C<blockparas> option marks up the paragraph as a block quote with no
-other changes. For example,
+=back
- Turing wrote,
+=back
- I propose to consider the question,
- "Can machines think?"
+=cut
-becomes
+sub new {
+ my ($class, $options) = @_;
+ $options ||= {};
+ $class->_croak("Options must be a hash reference")
+ if ref($options) ne 'HASH';
+
+ my %options = (
+ metachars => 1,
+ urls => 0,
+ email => 0,
+ bold => 0,
+ underline => 0,
+
+ pre => 0,
+
+ lines => 0,
+ spaces => 0,
+
+ paras => 0,
+ bullets => 0,
+ numbers => 0,
+ headings => 0,
+ title => 0,
+ blockparas => 0,
+ blockquotes => 0,
+ blockcode => 0,
+ tables => 0,
+
+ %{ $options },
+ );
+
+ my %self = (
+ options => \%options,
+ text => '',
+ html => '',
+ );
+
+ return bless \%self, blessed($class) || $class;
+}
- <P>Turing wrote,</P>
- <BLOCKQUOTE>I propose to consider the question,
- &quot;Can machines think?&quot;</BLOCKQUOTE>
+=head3 parse
-The C<blockquotes> option preserves line breaks in the original text.
-For example,
+ my $html = $t2h->parse( $text );
- From "The Waste Land":
+Parses text supplied as a single scalar string and returns the HTML as a
+single scalar string. All the tabs in your text will be expanded using
+C<Text::Tabs::expand()>.
- Phlebas the Phoenecian, a fortnight dead,
- Forgot the cry of gulls, and the deep sea swell
+=cut
-becomes
+sub parse {
+ my ($self, $text) = @_;
- <P>From &quot;The Waste Land&quot;:</P>
- <BLOCKQUOTE>Phlebas the Phoenecian, a fortnight dead,<BR>
- Forgot the cry of gulls, and the deep sea swell</BLOCKQUOTE>
+ $text = join "\n", expand( split /\n/, $text );
-The C<blockcode> option preserves line breaks and spaces in the original
-text and renders the paragraph in a fixed-width font. For example:
+ $self->{text} = $text;
+ $self->{html} = $text;
+ $self->{paras} = undef;
- Here's how to output numbers with commas:
+ my $options = $self->{options};
- sub commify {
- local $_ = shift;
- 1 while s/^(-?\d+)(\d{3})/$1,$2/;
- $_;
- }
+ $self->metachars if $options->{metachars};
-becomes
+ if ( $options->{pre} ) { $self->pre }
+ elsif ( $options->{lines} ) { $self->lines }
+ elsif ( $options->{paras} ) { $self->paras }
- <P>Here's how to output numbers with commas:</P>
- <BLOCKQUOTE><TT>sub&nbsp;commify&nbsp;{<BR>
- &nbsp;&nbsp;local&nbsp;$_&nbsp;=&nbsp;shift;<BR>
- &nbsp;&nbsp;1&nbsp;while&nbsp;s/^(-?\d+)(\d{3})/$1,$2/;<BR>
- &nbsp;&nbsp;$_;<BR>
- }</TT></BLOCKQUOTE>
+ $options->{$_} and $self->$_ foreach @DECORATORS;
-=item bold
+ return $self->{html};
+}
-Words surrounded with asterisks are marked up in bold, so C<*abc*>
-becomes C<E<lt>BE<gt>abcE<lt>/BE<gt>>.
+=head2 Functions
-=item bullets
+=head3 text2html
-Spots bulleted paragraphs (beginning with optional whitespace, an
-asterisk or hyphen, and whitespace) and marks them up as an unordered
-list. Bulleted paragraphs don't have to be separated by blank lines.
-For example,
+ my $html = text2html(
+ $text,
+ urls => 1,
+ email => 1,
+ );
- Shopping list:
+Functional interface that just wraps the OO interface. This function is
+exported by default. If you don't want it you can C<require> the module
+or C<use> it with an empty list.
- * apples
- * pears
+ require HTML::FromText;
+ # or ...
+ use HTML::FromText ();
-becomes
+=cut
- <P>Shopping list:</P>
- <UL><LI><P>apples</P>
- <LI><P>pears</P>
- </UL>
+sub text2html {
+ my ($text, %options) = @_;
+ HTML::FromText->new(\%options)->parse($text);
+}
-This option is ignored unless the C<paras> option is set.
+=head2 Subclassing
+
+B<Note:> At the time of this release, the internals of C<HTML::FromText>
+are in a state of development and cannot be expected to stay the same
+from release to release. I expect that release version B<3.00> will be
+analogous to a C<1.00> release of other software. This is because the
+current maintainer has rewritten this distribution from the ground up
+for the C<2.x> series. You have been warned.
+
+The following methods may be used for subclassing C<HTML::FromText>
+to create your own text to HTML conversions. Each of these methods
+is passed just one argument, the object (C<$self>), unless
+otherwise stated.
+
+The structure of C<$self> is as follows for this release.
+
+ {
+ options => {
+ option_name => $value,
+ ...
+ },
+ text => $text, # as passed to parse(), with tabs expanded
+ html => $html, # the HTML that will be returned from parse()
+ }
-=item email
+=head3 pre
-Spots email addresses in the text and converts them to links. For example
+Used when C<pre> mode is specified.
- Mail me at web@perl.com.
+Should set C<< $self->{html} >>.
-becomes
+Return value is ignored.
- Mail me at <TT><A HREF="mailto:web@perl.com">web@perl.com</A></TT>.
+=cut
-=item headings
+sub pre {
+ my ($self) = @_;
+ $self->{html} = join $self->{html}, '<pre class="hft-pre">', '</pre>';
+}
-Spots headings (paragraphs starting with numbers) and marks them up as
-headings of the appropriate level. For example,
+=head3 lines
- 1. Introduction
+Used when C<lines> mode is specified.
- 1.1 Background
+Implements the C<spaces> option internally when the option is set to a
+true value.
- 1.1.1 Previous work
+Should set C<< $self->{html} >>.
- 2. Conclusion
+Return value is ignored.
-becomes
+=cut
- <H1>1. Introduction</H1>
- <H2>1.1 Background</H2>
- <H3>1.1.1 Previous work</H3>
- <H1>2. Conclusion</H1>
+sub lines {
+ my ($self) = @_;
+ $self->{html} =~ s[ ][&nbsp;]g if $self->{options}->{spaces};
+ $self->{html} =~ s[$][<br />]gm;
+ $self->{html} =~ s[^][<div class="hft-lines">];
+ $self->{html} =~ s[$][</div>];
+}
-This option is ignored unless the C<paras> option is set.
+=head3 paras
-=item lines
+Used when the C<paras> mode is specified.
-Formats the text so as to preserve line breaks. For example,
+Splits C<< $self->{text} >> into paragraphs internally and sets up
+C<< $self->{paras} >> as follows.
- Line 1
- Line 2
+ paras => {
+ 0 => {
+ text => $text, # paragraph text
+ html => $html, # paragraph html
+ },
+ ... # and so on for all paragraphs
+ },
-becomes
+Implements the C<title> option internally when the option is turned on.
- Line 1<BR>
- Line 2
+Converts any normal paragraphs to HTML paragraphs (surrounded by C<p>
+tags) internally.
-If two or more of the options C<pre>, C<lines> and C<paras> are set,
-then C<pre> takes precedence over C<lines>, which takes precedence over
-C<paras>.
+Should set C<< $self->{html} >>.
-=item metachars
+Return value is ignored.
-Converts HTML metacharacters into their corresponding entity references.
-Ampersand (C<E<amp>>) becomes C<E<amp>amp;>, less than (C<E<lt>>)
-becomes C<E<amp>lt;>, greater than (C<E<gt>>) becomes C<E<amp>gt;>, and
-quote (") becomes C<E<amp>quot;>. This option is 1 by default.
+=cut
-=item numbers
+sub paras {
+ my ($self) = @_;
-Spots numbered paragraphs (beginning with whitespace, digits, an
-optional period/parenthesis/bracket, and whitespace) and marks them up
-as an ordered list. Numbered paragraphs don't have to be separated by
-blank lines. For example,
+ my $options = $self->{options};
+ my @paras = split /\n{2,}/, $self->{html};
+ my %paras = map { $_, { text => $paras[$_], html => undef } } 0 .. $#paras;
+ $self->{paras} = \%paras;
- To do:
+ $self->{paras}->{0}->{html} = join(
+ $self->{paras}->{0}->{text},
+ q[<h1 class="hft-title">], "</h1>\n"
+ ) if $options->{title};
- 1. Write thesis
- 2. Submit it
- 3. Celebrate
+ $self->headings if $options->{headings};
+ $self->bullets if $options->{bullets};
+ $self->numbers if $options->{numbers};
-becomes
+ $self->tables if $options->{tables};
- <P>To do:</P>
- <OL><LI VALUE="1"><P>Write thesis</P>
- <LI VALUE="2"><P>Submit it</P>
- <LI VALUE="3"><P>Celebrate</P>
- </OL>
+ if ( $options->{blockparas} ) { $self->blockparas }
+ elsif ( $options->{blockquotes} ) { $self->blockquotes }
+ elsif ( $options->{blockcode} ) { $self->blockcode }
-This option is ignored unless the C<paras> option is set.
+ $self->_manipulate_paras(sub { qq[<p class="hft-paras">$_[0]</p>\n] });
-=item paras
+ $self->{html} = join "\n", map $paras{$_}->{html},
+ sort { $a <=> $b } keys %paras;
+}
-Format the text into paragraphs. Paragraphs are separated by one or
-more blank lines. For example,
+=head3 headings
- Paragraph 1
+Used to format headings when the C<headings> option is turned on.
- Paragraph 2
+Return value is ignored.
-becomes
+=cut
- <P>Paragraph 1</P>
- <P>Paragraph 2</P>
+sub headings {
+ my ($self) = @_;
+ my $heading = qr/\d+\./;
-If two or more of the options C<pre>, C<lines> and C<paras> are set,
-then C<pre> takes precedence over C<lines>, which takes precedence over
-C<paras>.
+ $self->_manipulate_paras(sub{
+ my ($text) = @_;
+ return unless $text =~ m[^((?:$heading)+)\s+];
-=item pre
+ my $depth; $depth++ for split /\./, $1;
-Wrap the whole input in a C<E<lt>PREE<gt>> element. For example,
+ qq[<h$depth class="hft-headings">$text</h$depth>\n];
+ });
+}
- preformatted
- text
+=head3 bullets
-becomes
+Format bulleted lists when the C<bullets> option is turned on.
- <PRE>preformatted
- text</PRE>
+Return value is ignored.
-If two or more of the options C<pre>, C<lines> and C<paras> are set,
-then C<pre> takes precedence over C<lines>, which takes precedence over
-C<paras>.
+=cut
-=item spaces
+sub bullets {
+ my ($self) = @_;
+ $self->_format_list( qr/[*]/, 'ul', 'hft-bullets' );
+ $self->_format_list( qr/[-]/, 'ul', 'hft-bullets' );
+}
-Preserves spaces throughout the text. For example,
+=head3 numbers
- Line 1
- Line 2
- Line 3
+Format numbered lists when the C<numbers> option is turned on.
-becomes
+Return value is ignored.
- Line 1<BR>
- &nbsp;Line&nbsp;&nbsp;2<BR>
- &nbsp;&nbsp;Line&nbsp;&nbsp;&nbsp;3
+=cut
-This option is ignored unless the C<lines> option is set.
+sub numbers {
+ my ($self) = @_;
+ $self->_format_list( qr/[0-9]/, 'ol', 'hft-numbers');
+}
-=item tables
+=head3 tables
-Spots tables and marks them up appropriately. Columns must be separated
-by two or more spaces (this prevents accidental incorrect recognition of
-a paragraph where interword spaces happen to line up). If there are two
-or more rows in a paragraph and all rows share the same set of (two or
-more) columns, the paragraph is assumed to be a table. For example
+Format tables when the C<tables> option is turned on.
- -e File exists.
- -z File has zero size.
- -s File has nonzero size (returns size).
+Return value is ignored.
-becomes
+=cut
- <P><TABLE>
- <TR><TD>-e</TD><TD>File exists.</TD></TR>
- <TR><TD>-z</TD><TD>File has zero size.</TD></TR>
- <TR><TD>-s</TD><TD>File has nonzero size (returns size).</TD></TR>
- </TABLE></P>
+sub tables {
+ my ($self) = @_;
-C<text2html> guesses for each column whether it is intended to be left,
-centre or right aligned.
+ $self->_manipulate_paras(sub{
+ my ($text) = $self->_remove_indent( $_[0] );
-This option is ignored unless the C<paras> option is set.
+ my @lines = split /\n/, $text;
+ my $columns = $self->_table_find_columns(
+ $self->_table_initial_spaces( split //, $lines[0] ),
+ [ @lines[1 .. $#lines] ],
+ );
-=item title
+ return unless $columns;
+ $self->_table_create( $columns, \@lines );
+ });
+}
-Formats the first paragraph of the text as a first-level heading.
-For example,
+=head3 blockparas
- Paragraph 1
+Used when the C<blockparas> option is turned on.
- Paragraph 2
+Return value is ignored.
-becomes
+=cut
- <H1>Paragraph 1</H1>
- <P>Paragraph 2</P>
+sub blockparas {
+ my ($self) = @_;
+ my $paras = $self->{paras};
+
+ $self->_manipulate_paras(sub{
+ my ($text) = $self->_remove_indent( $_[0], 1 );
+ my ($pnum, $paras) = @_[1,2];
+ return unless $text;
+
+ $self->_consolidate_blocks(
+ ( exists $paras->{$pnum - 1} ? $paras->{$pnum -1} : undef ),
+ 'blockparas', 1,
+ qq[<blockquote class="hft-blockparas"><p>$text</p></blockquote>\n],
+ );
+ });
+}
-This option is ignored unless the C<paras> option is set.
+=head3 blockquotes
-=item underline
+Used when the C<blockquotes> option is turned on.
-Words surrounded with underscores are marked up with underline, so C<_abc_>
-becomes C<E<lt>UE<gt>abcE<lt>/UE<gt>>.
+Return value is ignored.
-=item urls
+=cut
-Spots Uniform Resource Locators (URLs) in the text and converts them
-to links. For example
+sub blockquotes {
+ my ($self) = @_;
+ my $paras = $self->{paras};
- See https://perl.com/.
+ $self->_manipulate_paras(sub {
+ my ($text) = $self->_remove_indent( $_[0], 1 );
+ return unless $text;
-becomes
+ $text =~ s[\n|$][<br />\n]g;
- See <TT><A HREF="https://perl.com/">https://perl.com/</A></TT>.
+ qq[<blockquote class="hft-blockquotes"><div>$text</div></blockquote>\n];
+ });
+}
-=back
+=head3 blockcode
-=head1 SEE ALSO
+Used when the C<blockcode> option is turned on.
+
+Return value is ignored.
+
+=cut
+
+sub blockcode {
+ my ($self) = @_;
+ my $paras = $self->{paras};
+
+ $self->_manipulate_paras(sub {
+ my ($text) = $self->_remove_indent( $_[0], 1 );
+ my ($pnum, $paras) = @_[1,2];
+ return unless $text;
+
+ $text =~ s[^][<pre>];
+ $text =~ s[$][</pre>];
+ $self->_consolidate_blocks(
+ ( exists $paras->{$pnum - 1} ? $paras->{$pnum -1} : undef ),
+ 'blockcode', 0,
+ qq[<blockquote class="hft-blockcode">$text</blockquote>\n],
+ );
+ });
+}
+
+=head3 urls
+
+Turn urls into links when C<urls> option is turned on.
+
+Should operate on C<< $self->{html} >>.
+
+Return value is ignored.
+
+=cut
+
+sub urls {
+ my ($self) = @_;
+ $self->{html} =~ s[\b((?:$PROTOCOLS):[^\s<]+[\w/])]
+ [<a href="$1" class="hft-urls">$1</a>]og;
+}
+
+=head3 email
+
+Turn email addresses into C<mailto:> links when C<email> option is
+turned on.
+
+Should operate on C<< $self->{html} >>.
+
+Return value is ignored.
+
+=cut
+
+sub email {
+ my ($self) = @_;
+ $self->{html} =~ s[($Addr_spec_re)]
+ [<a href="mailto:$1" class="hft-email">$1</a>]og;
+}
+
+=head3 underline
+
+Underline things between _underscores_ when C<underline> option is
+turned on.
+
+Should operate on C<< $self->{html} >>.
+
+Return value is ignored.
+
+=cut
+
+sub underline {
+ my ($self) = @_;
+ $self->{html} =~ s[(?:^|(?<=\W))((_)([^\\_\n]*(?:\\.[^\\_\n]*)*)(_))(?:(?=\W)|$)]
+ [<span class="hft-underline" style="text-decoration: underline">$3</span>]g;
+}
+
+=head3 bold
+
+Bold things between *asterisks* when C<bold> option is turned on.
+
+Should operate on C<< $self->{html} >>.
-The C<HTML::Entities> module (part of the LWP package) provides
-functions for encoding and decoding HTML entities.
+Return value is ignored.
-Tom Christiansen has a complete implementation of RFC 822 structured
-field bodies. See
-C<http://www.perl.com/CPAN/authors/Tom_Christiansen/scripts/ckaddr.gz>.
+=cut
+
+sub bold {
+ my ($self) = @_;
+ $self->{html} =~ s[(?:^|(?<=\W))((\*)([^\\\*\n]*(?:\\.[^\\\*\n]*)*)(\*))(?:(?=\W)|$)]
+ [<strong class="hft-bold">$3</strong>]g;
+}
+
+=head3 metachars
+
+Encode meta characters when C<metachars> option is turned on.
+
+Should operate on C<< $self->{html} >>.
+
+Return value is ignored.
-Seth Golub's C<txt2html> utility does everything that C<HTML::FromText>
-does, and a few things that it would like to do. See
-C<http://www.thehouse.org/txt2html/>.
+=cut
+
+sub metachars {
+ my ($self) = @_;
+ $self->{html} = encode_entities( $self->{html} );
+}
-RFC 822: "Standard for the Format of ARPA Internet Text Messages"
-describes the syntax of email addresses (the more esoteric features of
-structured field bodies, in particular quoted-strings, domain literals
-and comments, are not recognized by C<HTML::FromText>). See
-C<ftp://src.doc.ic.ac.uk/rfc/rfc822.txt>.
+# private
-RFC 1630: "Universal Resource Identifiers in WWW" lists the protocols
-that may appear in URLs. C<HTML::FromText> also recognizes "https:",
-but ignores "file:" because experience suggests that it results in too
-many false positives. See C<ftp://src.doc.ic.ac.uk/rfc/rfc1630.txt>.
+sub _croak {
+ my ($class, @error) = @_;
+ require Carp;
+ Carp::croak(@error);
+}
+
+sub _carp {
+ my ($class, @error) = @_;
+ require Carp;
+ Carp::carp(@error);
+}
+
+sub _format_list {
+ my ($self, $identifier, $parent, $class) = @_;
+
+ $self->_manipulate_paras(sub {
+ my ($text) = @_;
+ return unless $text =~ m[^\s*($identifier)\s+];
+
+ my ($pos, $html, @open) = (-1, '');
+ foreach my $line ( split /\n(?=\s*$identifier)/, $text ) {
+ $line =~ s[(\s*)$identifier][];
+ my $line_pos = length $1;
+ if ($line_pos > $pos) {
+ $html .= (' ' x $line_pos) . qq[<$parent class="$class">\n];
+ push @open, $line_pos;
+ } elsif ($line_pos < $pos) {
+ until ( $open[-1] <= $line_pos ) {
+ $html .= (' ' x pop @open) . "</$parent>\n";
+ }
+ }
+ $html .= (' ' x ($pos = $line_pos)) . "<li>$line</li>\n";
+ }
+ $html .= "</$parent>\n"x@open;
+ });
+}
+
+sub _manipulate_paras {
+ my ($self, $action) = @_;
+
+ my $paras = $self->{paras};
+
+ foreach my $pnum ( sort { $a <=> $b } keys %{$paras}) {
+ my $para = $paras->{$pnum};
+ $para->{html} = $action->($para->{text}, $pnum, $paras)
+ unless $para->{html};
+ }
+}
+
+sub _table_initial_spaces {
+ my ($self, @chars) = @_;
+
+ my %spaces;
+ foreach ( 0 .. $#chars ) {
+ my ($open_space) = grep { !defined( $_->{end} ) } values %spaces;
+ if ( $chars[$_] eq ' ' ) {
+ $spaces{$_} = {start => $_, end => undef} unless $open_space;
+ } else {
+ if ( $open_space && $_ - $open_space->{start} > 1 ) {
+ $open_space->{end} = $_ - 1;
+ } else {
+ delete $spaces{$open_space->{start}} if $open_space;
+ }
+ }
+ }
+ return \%spaces;
+}
+
+sub _table_find_columns {
+ my ($self, $spaces, $lines) = @_;
+ return unless keys %{$spaces};
+ my %spots;
+ foreach my $line ( @{$lines} ) {
+ foreach my $pos ( sort { $a <=> $b } keys %{$spaces} ) {
+ my $key;
+ $key = $spaces->{$pos}->{start}
+ if substr( $line, $spaces->{$pos}->{start}, 1 ) eq ' ';
+ $key = $spaces->{$pos}->{end}
+ if substr( $line, $spaces->{$pos}->{end}, 1 ) eq ' ' && ! $key;
+ if ( $key ) {
+ $spots{$key}++;
+ $spots{$spaces->{$pos}->{start}}++
+ if $spots{$spaces->{$pos}->{start}} && $key ne $spaces->{$pos}->{start};
+ $spots{$spaces->{$pos}->{end}}++
+ if $key ne $spaces->{$pos}->{end};
+ } else {
+ delete $spaces->{$pos};
+ }
+ }
+ foreach my $spot (sort {$b <=> $a} keys %spots) {
+ if ( substr( $line, $spot, 1 ) ne ' ' ) {
+ delete $spots{$spot};
+ }
+ if ( exists $spaces->{$spot}) {
+ my $space = $spaces->{$spot};
+ if ( exists $spots{$space->{start}} && $spots{$space->{end}}) {
+ delete $spots{$spot};
+ }
+ }
+ }
+ }
+
+
+ my @spots = grep { $spots{$_} == @{$lines} } sort { $a <=> $b } keys %spots;
+ return @spots ? join( '', (
+ map {
+ my $ret = 'A' . ( $spots[$_] - ( $_ == 0 ? 0 : $spots[$_ - 1] ) );
+ $ret eq 'A0' ? () : $ret;
+ } 0 .. $#spots
+ ), 'A*' ) : undef;
+}
+
+sub _table_create {
+ my ($self, $columns, $lines) = @_;
+
+ my $table = qq[<table class="hft-tables">\n];
+ foreach my $line ( @{$lines} ) {
+ $table .= join( '',
+ ' <tr><td>',
+ join(
+ '</td><td>',
+ map { s/^\s+//; s/\s$//; $_ } unpack $columns, $line
+ ),
+ "</td></tr>\n",
+ );
+ }
+ $table .= "</table>\n";
+}
+
+sub _remove_indent {
+ my ($self, $text, $strict) = @_;
+ return if $text !~ m[^(\s+).+(?:\n\1.+)*$] && $strict;
+ $text =~ s[^$1][]mg if $1;
+ return $text;
+}
+
+sub _consolidate_blocks {
+ my ($self, $prev_para, $class, $keep_inner, $html) = @_;
+ if ( $prev_para && $prev_para->{html} =~ m[<blockquote class="hft-$class"><(\w+)>] ) {
+ my $inner_tag = $keep_inner ? '' : qr[</?$1>];
+ $prev_para->{html} =~ s[$inner_tag</blockquote>][];
+ $html =~ s[<blockquote class="hft-$class">$inner_tag][];
+ }
+ return $html;
+}
+
+1;
+
+__END__
+
+=head2 Output
+
+The output from C<HTML::FromText> has been updated to pass XHTML 1.1
+validation. Every HTML tag that should have a CSS class name does. They
+are prefixed with C<hft-> and correspond to the names of the options to
+C<new()> (or C<text2html()>). For example C<hft-lines>, C<hft-paras>,
+and C<hft-urls>.
+
+One important note is the output for C<underline>. Because the <u> tag
+is deprecated in this specification a C<span> is used with a style
+attribute of C<text-decoration: underline>. The class is C<hft-
+underline>. If you want to override the C<text-decoration> style in the
+CSS class you'll need to do so like this.
+
+ text-decoration: none !important;
+
+=head1 SEE ALSO
+
+L<text2html(1)>.
=head1 AUTHOR
-Gareth Rees C<E<lt>garethr@cre.canon.co.ukE<gt>>.
+Casey West <F<casey@geeknest.com>>.
+
+=head1 AUTHOR EMERITUS
+
+Gareth Rees <F<garethr@cre.canon.co.uk>>.
=head1 COPYRIGHT
-Copyright (c) 1999 Canon Research Centre Europe. All rights reserved.
-This module is free software; you can redistribute it and/or modify it
-under the same terms as Perl itself.
+ Copyright (c) 2003 Casey West. All rights reserved.
+ This module is free software; you can redistribute it and/or modify it
+ under the same terms as Perl itself.
=cut