/usr/local/CPAN/pod2lyx/Pod/Lyx.pm
# Pod::Lyx -- Convert POD data to a Lyx file.
#
# Copyright 2000 by Richard Jackson <richardj@1gig.net>
#
# This program is free software; you can redistribute it and/or modify it
# under the same terms as Perl itself.
#
# This module is intended to convert Pod to Lyx format ( the Latex WYSIWYM
# editor ).
#
################################################################################
# Modules and declarations
################################################################################
package Pod::Lyx;
require 5.004;
#use Carp qw(carp croak);
use Pod::Parser;
use Text::Tabs;
use locale;
use strict;
use vars qw(@ISA %ESCAPES $VERSION %LyxCodes $VERSION);
$VERSION = 0.25; # current module version.
@ISA = qw(Pod::Parser);
################################################################################
# Table of supported E<> escapes
################################################################################
# This table was taken from an example pod2lyx converted supplied by
# Amir Karger. And boy am I glad I didn't have to figure this stuff out.
#
%ESCAPES = (
'amp' => '&', # ampersand
'lt' => '<', # left chevron, less-than
'gt' => '>', # right chevron, greater-than
'quot' => '"', # double quote
"Aacute" => "\\i \\'{A}\n", # capital A, acute accent
"aacute" => "\\i \\'{a}\n", # small a, acute accent
"Acirc" => "\\i \\^{A}\n", # capital A, circumflex accent
"acirc" => "\\i \\^{a}\n", # sma a, circumflex accent
"Agrave" => "\\i \\`{A}\n", # capital A, grave accent
"agrave" => "\\i \\`{a}\n", # small a, grave accent
"Aring" => '\\i \\u{A}\n', # capital A, ring
"aring" => '\\i \\u{a}\n', # small a, ring
"Atilde" => '\\i \\~{A}\n', # capital A, tilde
"atilde" => '\\i \\~{a}\n', # small a, tilde
"Auml" => '\\i \\"{A}\n', # capital A, dieresis or umlaut mark
"auml" => '\\i \\"{a}\n', # small a, dieresis or umlaut mark
"Ccedil" => '\\i \\c{C}\n', # capital C, cedilla
"ccedil" => '\\i \\c{c}\n', # small c, cedilla
"Eacute" => "\\i \\'{E}\n", # capital E, acute accent
"eacute" => "\\i \\'{e}\n", # small e, acute accent
"Ecirc" => "\\i \\^{E}\n", # capital E, circumflex accent
"ecirc" => "\\i \\^{e}\n", # small e, circumflex accent
"Egrave" => "\\i \\`{E}\n", # capital E, grave accent
"egrave" => "\\i \\`{e}\n", # small e, grave accent
"Euml" => '\\i \\"{E}\n', # capital E, dieresis or umlaut mark
"euml" => '\\i \\"{e}\n', # small e, dieresis or umlaut mark
"Iacute" => "\\i \\'{I}\n", # capital I, acute accent
"iacute" => "\\i \\'{i}\n", # small i, acute accent
"Icirc" => "\\i \\^{I}\n", # capital I, circumflex accent
"icirc" => "\\i \\^{i}\n", # small i, circumflex accent
"Igrave" => "\\i \\`{I}\n", # capital I, grave accent
"igrave" => "\\i \\`{i}\n", # small i, grave accent
"Iuml" => '\\i \\"{I}\n', # capital I, dieresis or umlaut mark
"iuml" => '\\i \\"{i}\n', # small i, dieresis or umlaut mark
"Ntilde" => '\\i \\~{N}\n', # capital N, tilde
"ntilde" => '\\i \\~{n}\n', # small n, tilde
"Oacute" => "\\i \\'{O}\n", # capital O, acute accent
"oacute" => "\\i \\'{o}\n", # small o, acute accent
"Ocirc" => "\\i \\^{O}\n", # capital O, circumflex accent
"ocirc" => "\\i \\^{o}\n", # small o, circumflex accent
"Ograve" => "\\i \\`{O}\n", # capital O, grave accent
"ograve" => "\\i \\`{o}\n", # small o, grave accent
"Otilde" => "\\i \\~{O}\n", # capital O, tilde
"otilde" => "\\i \\~{o}\n", # small o, tilde
"Ouml" => '\\i \\"{O}\n', # capital O, dieresis or umlaut mark
"ouml" => '\\i \\"{o}\n', # small o, dieresis or umlaut mark
"Uacute" => "\\i \\'{U}\n", # capital U, acute accent
"uacute" => "\\i \\'{u}\n", # small u, acute accent
"Ucirc" => "\\i \\^{U}\n", # capital U, circumflex accent
"ucirc" => "\\i \\^{u}\n", # small u, circumflex accent
"Ugrave" => "\\i \\`{U}\n", # capital U, grave accent
"ugrave" => "\\i \\`{u}\n", # small u, grave accent
"Uuml" => '\\i \\"{U}\n', # capital U, dieresis or umlaut mark
"uuml" => '\\i \\"{u}\n', # small u, dieresis or umlaut mark
"Yacute" => "\\i \\'{Y}\n", # capital Y, acute accent
"yacute" => "\\i \\'{y}\n", # small y, acute accent
"yuml" => '\\i \\"{y}\n', # small y, dieresis or umlaut mark
#TODO how to do ligatures in LyX? Use latin1?
# "AElig" => '\\AE', # capital AE diphthong (ligature)
# "aelig" => '\\ae', # small ae diphthong (ligature)
# "ETH" => '\\OE', # capital Eth, Icelandic
# "eth" => '\\oe', # small eth, Icelandic
# "Oslash" => "\\O", # capital O, slash
# "oslash" => "\\o", # small o, slash
# "szlig" => '\\ss{}', # small sharp s, German (sz ligature)
# "THORN" => '\\L', # capital THORN, Icelandic
# "thorn" => '\\l',, # small thorn, Icelandic
);
# these are LyX codes that can be embeded into a paragraph.
# note the value means that another word follows the current match.
# A 1 means one word follows a 2 means two words follow ect...
%LyxCodes = (
"\\protected_separator" => 0, # a protected separator
"\\series" => 1, # the begining of a series change..
"\\shape" => 1, # a shape change
"\\family" => 1, # a change in font family
"\\backslash" => 0, # a backslash
# their are more codes but these are the ones we are using for now.
);
################################################################################
# Initialization
################################################################################
# Initialize the object. Must be sure to call our parent initializer...
#
sub initialize {
my $self = shift;
# by default use the article textclass..
$$self{textclass} = "article" unless defined $$self{textclass};
$$self{alt} = 0 unless defined $$self{alt};
$$self{indent} = 0;
$$self{tab} = 4 unless defined $$self{tab};
$$self{MODE} = ""; # the current translation mode we are in.
$$self{ListLevel} = 0; # the current list level
$$self{listdata} = ""; # used to hold a list data item...
$$self{outbuff} = ""; # used to hold the output buffer.
$$self{deeper} = 0; # used to indicate that we have forced a
# to a deeper indention level.
$$self{verbatimspace} = 0 unless defined $$self{verbatimspace};
$$self{over} = 0; # indicates wheter or not we have output a
# over command.
$$self{paragraph} = 0; # How many paragraphs have we output while in
# a list/enumerate/itimize context.
$self->SUPER::initialize;
}
################################################################################
#===============================================================================
# Core overrides
#===============================================================================
################################################################################
################################################################################
# command
################################################################################
# Called for each command paragraph. Gets the command, the associated paragraph,
# the line number, and a Pod::Paragraph object.
#
# Just dispatches the command to a method named the same as the command. =cut
# is handled internally by Pod::Parser.
sub command {
my $self = shift;
my $command = shift;
return if $command eq 'pod'; # don't need to process the pod command...
$command = 'cmd_' . $command;
$self->$command(@_);
}
################################################################################
# verbatim
################################################################################
# Called for verbatim paragraph. Gets the paragraph, the line number, and a
# Pod::Paragraph object.
# NOTES:
# 1. We use Lyx-Code for verbatim text.
# 2. Our incomming paragraph has to be reformated such that multiple spaces
# must be converted into protected spaces.
# 3. If there is a blank line following a line of code it will be included
# in the paragraph so we must catch this so a protected newline can be
# inserted. One problem with this is we end up with extra lines in the
# document that don't need to be there. I have an idea how to fix this
# But I haven't implemented it yet.
#
sub verbatim {
my $self = shift;
return if $$self{EXCLUDE};
local $_ = shift;
my($temp);
my($newlines) = 0; # number of newlines to insert after a line of verbatim
# code...
my(@tArray); # temp array holder...
my(@lines); # paragraph lines
# see if this is our first line of verbatam text...
if ( !($$self{MODE} eq "verbatim" ) ) {
$self->mode_switch("verbatim"); # call switching code..
# on a first pass we need to see what the indention level is on the
# source code or verbatim paragraph...
# LyX automaticly indents verbatim paragraphs so we are going to
# remove any leading spaces..
$$self{indent} = 0; # zero our indent setting...
###################################################################
# if the pod only contains code in verbatim blocks we may want to
# reformat it so that it looks nice.
if (defined( $$self{vcode} ) ) {
my @chars = split(//, $_); # split the line so we can count the leading
# spaces...
while ( $chars[$$self{indent}] eq ' ' ) {
$$self{indent}++;
}
}
else {
# set our indent level to the current tab stop
$$self{indent} = $$self{tab}; # this is so it looks ok under LyX....
}
} else {
if ( !($$self{outbuff} eq "" )) {
$self->output($$self{outbuff});
$$self{outbuff} = "";
}
}
# Before we do any thing else we need to see how many newlines are at the
# end of the paragraph so we can format it correctly..
$temp = length($_); # Note is will cause problems if the string is
# in a multibyte encoding
$temp--; # zero base the length...
@tArray = split(//, $_); # split the line up so we can take a look at it..
while ( ( $tArray[$temp] eq "\n" ) and ( $temp >= 0 ) ) {
$newlines++;
$temp--;
}
$newlines--; # The end of paragraph will have at least one new line so
# we don't want to count it..
# Ok we now know how many blank lines follow the paragraph.
# now we need to format it.
# first we need to split the paragraph up into lines
@lines = split(/\n/, $_ );
foreach ( @lines ) {
# First remove the leading spaces from the line. The max
# number of spaces removed will be the indent value we got earler on..
#### forgot to add tab expantion :( NOTE: According to Perlpod tabs are
# to be assumed to be 8 char's in length. But if you take a look at
# Text.pm it assumes a 4 char tab!!! :( Which do I use? Hell I'm gona
# use the 4 chars that Text.pm uses because the output looks right.
# I need to make this an option...
$tabstop = $$self{tab};
$_ = expand($_);
$temp = $$self{indent};
# Now remove indent number of spaces...
# I know this doesn't look good but we can't hard code the indent value
# as I've run across some pod's that use 3 spaces and some that use 4 spaces
# and we only want to remove the indented value and no more!
while ( $temp > 0 ) {
$_ =~ s/^\s//;
$temp--;
}
# now output the Lyx command to have this data look right..
$self->output("\\layout LyX-Code\n\n");
# now pull any newlines off the end of the line..
s/\n+//; # we only want 1 newline so lets make sure we only have one.
s/\\/\n\\backslash \n/g; # must replace backslases note the space
# before the newline needs to be there.
s/\s/\n\\protected_separator \n/g; # replace all spaces
$self->output($_ . "\n");
}
# output any extra newlines that we need. Note: we don't actual out put
# this here we just format the output line
if ($newlines) {
while ($newlines > 0 ) {
$$self{outbuff} .= "\\newline \n \n"; # LyX puts a space after newline
$newlines--; # not sure why but it does...so we
} # are going to as well...
}
}
################################################################################
# textblock
################################################################################
# Called for regular text block. Gets the paragraph, the line number, and
# a Pod::Paragraph object. Perform interpolation and output the results.
#
sub textblock {
my $self = shift;
return if $$self{EXCLUDE};
local $_ = shift;
my $line = shift;
my @input_lines;
my $buffer;
# before we do anything else check for \ and replace it...
s/\\/\n\\backslash \n/g;
if ( !( $$self{MODE} eq "textblock") ) {
# ok we have to determin how this was called which can be a little tricky.
# and we have to make some assumptions about certin things...
# for all list type items we just dumb the paragraph..
if ( ( $$self{MODE} eq "itemize" ) or ( $$self{MODE} eq "enumerate") ) {
$_ = $self->interpolate($_, $line);
$_ = $self->wrap( $_ );
$self->output( $_ );
$$self{paragraph}++;
$$self{MODE} = ""; # must unset the mode.
return;
}
if ( $$self{MODE} eq "list" ) { # lists must be handled differantly..
# Ok I ran across an issue where the list name had multiple words
# in it which would cause the output to not look right. So we need
# to account for it.
# $_ = $$self{listdata} . " " . $_;
# $$self{listdata} = "";
$_ = $self->interpolate($_, $line);
$buffer = $self->wrap( $_ );
# now lets handle the case where their may be more than one word in
# the list name.
$_ = $$self{listdata};
if ( /\\/ ) { # if there are any back slashes that means their is more
# more than one word...
$_ = $self->interpolate($_, $line);
$self->output( $_ . "\n "); # note the space is important.
$self->output($buffer);
} else {
$_ = $self->interpolate($_, $line);
$self->output( $_ . " " . $buffer);
}
$$self{paragraph}++;
$$self{MODE} = ""; # must unset the mode.
$$self{listdata} = ""; # must unset the list data.
return;
}
$self->mode_switch("textblock");
}
#Now actually interpolate and output the paragraph.
$_ = $self->interpolate($_, $line);
$_ = $self->wrap( $_ );
$self->output( "\\layout Standard\n\n" . $_ );
}
################################################################################
# interior_sequence
################################################################################
# Called for interior sequence. Gets the command, argument, and a
# Pod::InteriorSequence object and is expected to return the resulting text.
# Calls code, bold, italic, file, and link to handle those types of
# sequences, and handles S<>, E<>, X<>, and Z<> directly.
sub interior_sequence {
my $self = shift;
my $command = shift;
local $_ = shift;
# before we continue we need to reconize certin cases that can cause problems
# in particular putting bold text on one of the =head1 or =head2 lines.
# in these cases we just return the text with no modifications...
if ( ($$self{MODE} eq "head1") or ($$self{MODE} eq "head2") ) {
return $_;
}
# first lets deal with the most common cases..
#### BOLD #########
if ( $command eq 'B' ) {
return ( "\n\\series bold \n" . $_ . "\n\\series default \n");
}
#### ITALICS ############
if ( $command eq 'I') {
return ( "\n\\shape italic \n" . $_ . "\n\\shape default \n");
}
###### CODE OR TYPEWRITER TEXT ######
if ( $command eq 'C') { # ran across a issue where the typewritertext
# included a \ in it so we need to check for it.
#s/\\/\n\\backslash \n/g; # replace \ with the command "\backslash\n"
return ( "\n\\family typewriter \n" . $_ . "\n\\family default \n");
}
#### NON BREAKING SPACES ##########
if ( $command eq 'S' ) {
return cmd_space($_);
}
##### LINK OR CROSS REFERENCE ##########
if ( $command eq 'L' ) {
return cmd_link($_); # for now do nothing. I intend to come back to this
# once basic functions are working.
}
#### FILE NAME #################
if ( $command eq 'F' ) {
return $_; # for now do nothing. I intend to come back to this
# at a later date.
}
##### INDEX ################
if ( $command eq 'I' ) {
return; # for now do nothing. I intend to come back to this.
}
}
################################################################################
#===============================================================================
# Optional overrides..
#===============================================================================
################################################################################
################################################################################
# begin_pod
################################################################################
# This sub is invoked at the beginning of processing for each POD document that
# is encountered in the input.
#
# This is being overriden so that we can write the LyX header information into
# the output file.
#
sub begin_pod {
my $self = shift;
my $out_fh = $self->output_handle();
my $buffer;
print $out_fh "#pod2lyx created this file on: " . localtime() . "\n";
print $out_fh <<HEAR_DOC;
#LyX 1.1 (C) 1995-2000 Matthias Ettrich and the LyX Team
#For more information on LyX see http://www.lyx.org/
#For more information on pod2lyx see http://.....
\\lyxformat 2.15
\\textclass $$self{textclass}
\\language default
\\inputencoding latin1
\\fontscheme default
\\graphics default
\\paperfontsize default
\\spacing single
\\papersize Default
\\paperpackage a4
\\use_geometry 1
\\use_amsmath 0
\\paperorientation portrait
\\leftmargin 17mm
\\topmargin 12mm
\\rightmargin 17mm
\\bottommargin 12mm
\\secnumdepth 3
\\tocdepth 3
\\paragraph_separation indent
\\defskip medskip
\\quotes_language english
\\quotes_times 2
\\papercolumns 1
\\papersides 1
\\paperpagestyle defualt
HEAR_DOC
# ok the main preamble is done now lets set the title...
# note lyx_title is set by the class user when they create the class..
if (defined $$self{lyx_title} ) {
print $out_fh "\\layout Title\n\n" . $$self{lyx_title} . "\n";
if ( (defined $$self{footnote}) and !($$self{footnote}) ) {
print $out_fh "\\begin_float footnote \n\\layout Standard\n\n";
$buffer = "Generated by pod2lyx V" . $VERSION . " from " . $$self{podfile};
# Ok I don't know why this is but if the line wrapping mode for foot
# notes is differant than every thing else. Basicly if $buffer is
# greater than 82 char's long then the document doesn't look right
# in LyX also the page break will not be reconized either.
my $test = length($buffer);
if ( length($buffer) > 83 ) {
substr($buffer, 82, 0) = "\n";
}
print $out_fh $buffer . "\n\\end_float \n";
}
print $out_fh "\\layout Standard\n";
}
# insert a page break if the class user asked for it...
if (defined $$self{lyx_index_break} and $$self{lyx_index_break} ) {
print $out_fh "\\pagebreak_bottom ";
}
# insert a index if the class user asked for it...
if ( (defined $$self{lyx_index} ) and $$self{lyx_index} ) {
# do we want an index?
print $out_fh "\n\n\\begin_inset LatexCommand \\tableofcontents{}\n\n\\end_inset \n\n\n";
}
# note the blank line needs to be there...
}
################################################################################
# end_pod
################################################################################
# This method is invoked at the end of processing for each POD document.
# we mearly need to output the LyX end code....
sub end_pod {
my $self = shift;
$self->output("\\the_end");
}
################################################################################
#
# Addtional methods specific to Lyx.
#
################################################################################
################################################################################
# output
################################################################################
# Used to output or write the file....
#
sub output {
my $self = shift;
my $out_fh = $self->output_handle();
if( defined($_) ) {
print $out_fh @_;
}
}
################################################################################
# cmd_head1
################################################################################
# First level heading paragraph...
# This outputs the heading and generates a label so we can do cross referances
# later on in the code...
#
sub cmd_head1 {
my $self = shift;
return if $$self{EXCLUDE};
local $_ = shift;
my $label = $_;
$$self{MODE} = "head1";
$_ = $self->interpolate($_, shift);
# if the heading is blank return...
if( $_ =~ /^\n+/ ) {
return;
}
s/\n+//; # srip the newlines off the end so we don't get multiple
# blank lines in the output stream... We only one one newline
# so we force it to that...
$self->output("\\layout Section\n\n" . $_ . "\n");
$label =~ s/\n//g;
$label =~ s/\s/_/g;
$label = "\L$label"; # convert the section name to lower case.
$self->output("\\begin_inset LatexCommand \\label{sec:" . $label . "}\n\n\\end_inset \n\n\n");
}
################################################################################
# cmd_head2
################################################################################
# Second Level heading
#
sub cmd_head2 {
my $self = shift;
return if $$self{EXCLUDE};
local $_ = shift;
my $label = $_;
$$self{MODE} = "head2";
$_ = $self->interpolate($_, shift);
if( $_ =~ /^\n+/ ) {
return;
}
s/\n+//; # srip the newlines off the end. We only want one newline so
# we force the issue...
$self->output("\\layout Subsection\n\n" . $_ . "\n");
$label =~ s/\n//g;
$label =~ s/\s/_/g;
$label = "\L$label"; # convert the section name to lower case.
$self->output("\\begin_inset LatexCommand \\label{sec:" . $label . "}\n\n\\end_inset \n\n\n");
}
################################################################################
# cmd_over
################################################################################
# Start a list!!!
# We need to keep track of the nesting level of lists. We don't realy need
# to worry about how much to indent it but we do need to know the lists level
# for cases of lists of lists.
sub cmd_over {
my $self = shift;
$$self{ListLevel}++; # increase our list level...
if ( $$self{ListLevel} > 1 ) {
$self->output("\\begin_deeper\n");
}
}
################################################################################
# cmd_back
################################################################################
# End a list....
# We need to keep track of the nesting level of lists. All we are doing
# here is decrimenting our list level holder..
sub cmd_back {
my $self = shift;
$$self{ListLevel}--; ## decriment out list level..
# we also have to output the command to LyX to know that we are backing out
# one level..
if ($$self{ListLevel} > 0 ) {
$self->output("\\end_deeper\n");
$$self{MODE} = ""; # this will wipe out a list context..
}
if ($$self{over}) {
$self->output("\\end_deeper\n");
}
}
###############################################################################
# cmd_item
###############################################################################
# An individual list item...
#
# The problem: Pod::Parser pases each =item into this procedure so we must
# determin what type of item this is. The types we must determin are:
# * which means a built list
# # where # equals sum number means a numberd list
# [a-z] equals some list item...
# so we have three modes we need to account for
# 1) a numbered list or enumerate in LyX speek.
# 2) a bullet list or itemize in LyX speek.
# 3) a word list. or list in LyX speek.
sub cmd_item {
my $self = shift;
local $_ = shift;
my $tempbuff = ""; # temp buffer
s/^item //; # strip the item section off the command. Notice the
# space we want that gone as well.
s/\s+$//; # strip all trailing spaces and newlines
if ( $_ eq "*" ) { # we have a bullet list
if (!( $$self{MODE} eq "itemize") ) {
$self->mode_switch("itemize");
}
$self->output("\\layout Itemize\n\n"); # note the data for the item will
# be handled by the standard
# paragraph handler...
$$self{paragraph} = 0; # reset our paragraph counter..
return;
}
if ( /^\d+/ ) { # we have a numbered list...
if ( !( $$self{MODE} eq "enumerate") ) {
$self->mode_switch("enumerate");
}
$self->output("\\layout Enumerate\n\n");
$$self{paragraph} = 0;
return;
}
if ( /\w+/ ) { # do we have a list? NOTE the test for a number list must come
# first or this will not work correctly...
if (!($$self{MODE} eq "list" )) {
$self->mode_switch("list");
}
# Ok I ran into another bug :( Where a list would not have an assiated
# paragraph so we must take care of it here..
if ( !( $$self{listdata} eq "") ) {
$tempbuff = $self->interpolate( $$self{listdata} , 0);
$self->output( $tempbuff . "\n");
}
$self->output("\\layout List\n\\labelwidthstring 00.00.0000\n\n");
# I'm not sure what the labelwidthstring does but it is needed.
# also if the list head has spaces in it we must escape them...
s/\s/\n\\protected_separator\n/g;
$$self{listdata} = $_;
$$self{paragraph} = 0;
return;
}
}
###############################################################################
# cmd_begin
###############################################################################
# Begin a block for a particular translator. Setting VERBATIM triggers
# special handling in textblock().
# What this does is eliminate __PRIVATE__ text blocks...
sub cmd_begin {
my $self = shift;
local $_ = shift;
my($kind) = /^(\S+)/ or return;
if ($kind eq 'text') {
$$self{VERBATIM} = 1;
} else {
$$self{EXCLUDE} = 1;
}
}
################################################################################
# cmd_end
################################################################################
# End a block for a particular translator. We assume that all =begin/=end
# pairs are properly closed..
#
sub cmd_end {
my $self = shift;
$$self{EXCLUDE} = 0;
$$self{VERBATIM} = 0;
}
################################################################################
# cmd_for
################################################################################
# Special format sections: for now we just ignore them.
# But we need a place holder so Pod::Parser won't complain :)
#
sub cmd_for {
return;
}
################################################################################
# wrap
################################################################################
# This function wraps a paragraph such that LyX will understand it correctly..
#
# If I could use Text::Wrap for this I would but the problem is that the
# paragraph can have embeded control seqences that must be formated correctly
# or we will have problems.
# Actualy Text::Wrap works with the current LyX parser but that is no guantee
# that it will work in the future. So I'm writing this to be on the safe side.
#
# Found a bug in this code in that LyX formats a paragraph such that each line
# will wrap at around 78 not 80 and a sentance ends the wraping. NOTE: I call
# this a bug because LyX does somthing differant when savaing a file than what
# I'm doing here. So I'm trying to fixit such that they match as much as
# posable.
sub wrap {
my $self = shift;
local $_ = shift;
my $cols = 80; # line length
my @words; # array of words
my $output; # our output paragraph formated correctly.
my $val; # current word we are working on.
my $curlen = 0; # the current line length.
my $code = 0; # are we processing a LyX code?
my $lines = 0; # how many lines have we output?
# all kinds of stuff can be embeded into a paragraph so we need to make sure
# to catch it all if possible...
@words = split( /\s+/, $_ );
foreach $val (@words) {
# process key words...
if ( ( defined( $LyxCodes{$val} ) ) or $code ) {
if ( $code ) { # are we in a keyword pair?
$output .= $val . "\n"; # yes so stick the next key word on the end.
$code = 0; # reset our code value.
$curlen = 0; # reset our line length..
next; # go back to the top.
}
if ( $LyxCodes{$val} == 0 ) { # is this a single key word?
if ( $curlen > 0 ) {
chop $output; # remove trailing space..
$lines++; # increment the line counter
# only if there is already part of a line
# in the output stream...
}
$output .= "\n" . $val . "\n"; # yes so dump it...
$curlen = 0; # reset our line length..
next;
} else { # has to be a multiword pair
if ($curlen > 0) {
chop $output; # remove trailing space.
$lines++;
}
$output .= "\n" . $val . " ";
$code = 1;
next;
}
}
# Ok this is not a key word so we need to process it...
# Note this section of code will puke when the input has double byte
# chars in it...After I get the single byte stuff working I'll look
# into doing double byte chars..if users ask for it that is.
# I ran into a case where a control sequance starts a paragraph
# that a space and or a \n just will get through
# split so we need to check for it... Well actualy after some work
# what gets through is '' which if this wasn't here
# whould put a space into the stream :( So I removed the checkes
# for \n and ' ' but put a check in for '' so we don't add a space
# into the output stream.
if ( $val eq '' ) {
next; # don't process it....
}
if ( ( $curlen + length($val) + 1 ) > $cols ) {
chop $output;
$output .= "\n " . $val . " ";
$curlen = length($val) + 1;
$lines++; # note control lines don't count!
if ( $val =~ /\.$/ ) {
$output .= "\n ";
$lines++;
}
} else {
if ( ( $curlen == 0 ) and ( $lines > 0 ) ) {
$output .= " " . $val . " ";
$curlen += length($val) + 2;
} else {
if ( $val =~ /\.$/ ) { # is this an end of sentance?
$output .= $val . "\n ";
$curlen = 1; # the space...
$lines++;
next;
}
$output .= $val . " ";
$curlen += length($val) + 1;
}
}
}
chop $output;
$output .= "\n";
return $output;
}
################################################################################
# cmd_space
################################################################################
# this function handles the S<> interer item..
# the problem is that S<> can contain other commands embeded in it so we need
# to watch out for it. Also what we are doing is replacing spaces which makes
# this a little more dificult because we end up having to process this thing
# one char at a time to make it work :( I'm sure a regular expresion guru could
# make this work better but I'm not their yet...
sub cmd_space {
local $_ = shift;
my @lines; # array of chars...
my $output = ''; # our output formated correctly.
@lines = split( /\n/, $_ ); # break the input up into lines....
foreach (@lines) {
if ( /^\\/ ) {
$output .= "\n" . $_ . "\n";
} else {
s/\s/ \n\\protected_separator \n/g;
$output .= $_ . "\n";
}
}
return($output);
}
################################################################################
# cmd_link
################################################################################
# This function handles the L<> interer item..
# Links come in mainly two forms:
# 1) links to items contained in this document
# 2) Links to items outside this document.
#
# We are going to try to handle both of these cases.
sub cmd_link {
local $_ = shift;
my $manpage; # manpage link
my $section; # section ref.
my $output = ''; # out output formated correctly...
# if the pod contained some text for use to output use it...
if ( /\|/ ) {
($output, $manpage) = split(/\|/, $_, 2);
return $output;
}
# Ok the pod did not provide any link text so lests figure out what we need
# to do..
# leading and trailing whitespace isn't important; get rid of it..
#s/^\s+//;
#s/\s+$//;
return $_;
}
################################################################################
# mode_switch
################################################################################
# this function is used when switching modes from one type of paragraph to
# another. It handles a few things that need to be cleaned up when switching
# modes...It also sets the current mode when it needs to be set...
#
sub mode_switch {
my $self = shift;
my $Caller = shift; # who called this function....
if ($$self{MODE} eq "verbatim" ) {
# what we are doing here is removing trailing newlines so we get a more
# compact document. We allow the user of this module to set wether or
# not we do this..
if ( $$self{verbatimspace} ) {
$self->output($$self{outbuff});
}
$$self{outbuff} = "";
}
if ( ($Caller eq "verbatim") and ( ($$self{MODE} eq "list") or
($$self{MODE} eq "itemize") or
($$self{MODE} eq "enumerate")) ) {
if (!($$self{listdata} eq "" ) ) {
$self->output("$$self{listdata} \n");
}
}
if ( $$self{ListLevel} ) {
# We are inside a list so we need to take the apropriate acction..
# NOTE this function does not get called for the first paragraph in a
# list.
# if one of the lists is calling us we don't need to worry about it.
if ( ($Caller eq "list") or ($Caller eq "itemize") or
($Caller eq "enumerate") ) {
if ( $$self{over} ) {
$self->output("\\end_deeper\n");
$$self{over} = 0;
}
$$self{MODE} = $Caller;
return;
}
if ( $$self{over} ) { # have we already moved over?
$self->output("\\end_deeper\n"); # move back...
# now we know that this is not in a list context so what we have
# is another paragraph inside the list so we need to also move it
# over as well...
$self->output("\\begin_deeper\n");
$$self{over} = 1;
$$self{MODE} = $Caller;
return;
}
# other wise we need to fix some things up..
$self->output("\\begin_deeper\n");
$$self{over}++;
$$self{MODE} = $Caller;
return;
}
$$self{MODE} = $Caller;
}
1;