####################################################################################################################################
# DOC RENDER MODULE
####################################################################################################################################
package pgBackRestDoc::Common::DocRender;
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use Exporter qw(import);
our @EXPORT = qw();
use JSON::PP;
use Storable qw(dclone);
use pgBackRestDoc::Common::DocManifest;
use pgBackRestDoc::Common::Log;
use pgBackRestDoc::Common::String;
####################################################################################################################################
# XML tag/param constants
####################################################################################################################################
use constant XML_SECTION_PARAM_ANCHOR => 'anchor';
push @EXPORT, qw(XML_SECTION_PARAM_ANCHOR);
use constant XML_SECTION_PARAM_ANCHOR_VALUE_NOINHERIT => 'no-inherit';
push @EXPORT, qw(XML_SECTION_PARAM_ANCHOR_VALUE_NOINHERIT);
####################################################################################################################################
# Render tags for various output types
####################################################################################################################################
my $oRenderTag =
{
'markdown' =>
{
'quote' => ['"', '"'],
'b' => ['**', '**'],
'i' => ['_', '_'],
# 'bi' => ['_**', '**_'],
'ul' => ["\n", ""],
'ol' => ["\n", "\n"],
'li' => ['- ', "\n"],
'id' => ['`', '`'],
'file' => ['`', '`'],
'path' => ['`', '`'],
'cmd' => ['`', '`'],
'param' => ['`', '`'],
'setting' => ['`', '`'],
'pg-setting' => ['`', '`'],
'code' => ['`', '`'],
# 'code-block' => ['```', '```'],
# 'exe' => [undef, ''],
'backrest' => [undef, ''],
'proper' => ['', ''],
'postgres' => ['PostgreSQL', ''],
'admonition' => ["\n> **", "\n"],
},
'text' =>
{
'quote' => ['"', '"'],
'b' => ['', ''],
'i' => ['', ''],
# 'bi' => ['', ''],
'ul' => ["\n", "\n"],
'ol' => ["\n", "\n"],
'li' => ['* ', "\n"],
'id' => ['', ''],
'host' => ['', ''],
'file' => ['', ''],
'path' => ['', ''],
'cmd' => ['', ''],
'br-option' => ['', ''],
'pg-setting' => ['', ''],
'param' => ['', ''],
'setting' => ['', ''],
'code' => ['', ''],
'code-block' => ['', ''],
'exe' => [undef, ''],
'backrest' => [undef, ''],
'proper' => ['', ''],
'postgres' => ['PostgreSQL', ''],
'admonition' => ["\n", "\n\n"],
},
'latex' =>
{
'quote' => ['``', '"'],
'b' => ['\textbf{', '}'],
'i' => ['\textit{', '}'],
# 'bi' => ['', ''],
'ul' => ["\\begin{itemize}\n", "\\end{itemize}\n"],
# 'ol' => ["\n", "\n"],
'li' => ['\item ', "\n"],
'id' => ['\textnormal{\texttt{', '}}'],
'host' => ['\textnormal{\textbf{', '}}'],
'file' => ['\textnormal{\texttt{', '}}'],
'path' => ['\textnormal{\texttt{', '}}'],
'cmd' => ['\textnormal{\texttt{', "}}"],
'user' => ['\textnormal{\texttt{', '}}'],
'br-option' => ['', ''],
# 'param' => ['\texttt{', '}'],
# 'setting' => ['\texttt{', '}'],
'br-option' => ['\textnormal{\texttt{', '}}'],
'br-setting' => ['\textnormal{\texttt{', '}}'],
'pg-option' => ['\textnormal{\texttt{', '}}'],
'pg-setting' => ['\textnormal{\texttt{', '}}'],
'code' => ['\textnormal{\texttt{', '}}'],
# 'code' => ['\texttt{', '}'],
# 'code-block' => ['', ''],
# 'exe' => [undef, ''],
'backrest' => [undef, ''],
'proper' => ['\textnormal{\texttt{', '}}'],
'postgres' => ['PostgreSQL', ''],
'admonition' => ["\n\\begin{leftbar}\n\\textit{\\textbf{", "}\n\\end{leftbar}\n"],
},
'html' =>
{
'quote' => ['', '
'],
'b' => ['', ''],
'i' => ['', ''],
# 'bi' => ['', ''],
'ul' => ['
'],
'ol' => ['', '
'],
'li' => ['', ''],
'id' => ['', ''],
'host' => ['', ''],
'file' => ['', ''],
'path' => ['', ''],
'cmd' => ['', ''],
'user' => ['', ''],
'br-option' => ['', ''],
'br-setting' => ['', ''],
'pg-option' => ['', ''],
'pg-setting' => ['', ''],
'code' => ['', ''],
'code-block' => ['', ''],
'exe' => [undef, ''],
'setting' => ['', ''], # ??? This will need to be fixed
'backrest' => [undef, ''],
'proper' => ['', ''],
'postgres' => ['PostgreSQL', ''],
'admonition' => ['', '
'],
}
};
####################################################################################################################################
# CONSTRUCTOR
####################################################################################################################################
sub new
{
my $class = shift; # Class name
# Create the class hash
my $self = {};
bless $self, $class;
# Assign function parameters, defaults, and log debug info
(
my $strOperation,
$self->{strType},
$self->{oManifest},
$self->{bExe},
$self->{strRenderOutKey},
) =
logDebugParam
(
__PACKAGE__ . '->new', \@_,
{name => 'strType'},
{name => 'oManifest', required => false},
{name => 'bExe', required => false},
{name => 'strRenderOutKey', required => false}
);
# Create JSON object
$self->{oJSON} = JSON::PP->new()->allow_nonref();
# Initialize project tags
$$oRenderTag{markdown}{backrest}[0] = "{[project]}";
$$oRenderTag{markdown}{exe}[0] = "{[project-exe]}";
$$oRenderTag{text}{backrest}[0] = "{[project]}";
$$oRenderTag{text}{exe}[0] = "{[project-exe]}";
$$oRenderTag{latex}{backrest}[0] = "{[project]}";
$$oRenderTag{latex}{exe}[0] = "\\textnormal\{\\texttt\{[project-exe]}}\}\}";
$$oRenderTag{html}{backrest}[0] = "{[project]}";
$$oRenderTag{html}{exe}[0] = "{[project-exe]}";
if (defined($self->{strRenderOutKey}))
{
# Copy page data to self
my $oRenderOut =
$self->{oManifest}->renderOutGet($self->{strType} eq 'latex' ? 'pdf' : $self->{strType}, $self->{strRenderOutKey});
# If these are the backrest docs then load the reference
if ($self->{oManifest}->isBackRest())
{
$self->{oReference} =
new pgBackRestDoc::Common::DocConfig(${$self->{oManifest}->sourceGet('reference')}{doc}, $self);
}
if (defined($$oRenderOut{source}) && $$oRenderOut{source} eq 'reference' && $self->{oManifest}->isBackRest())
{
if ($self->{strRenderOutKey} eq 'configuration')
{
$self->{oDoc} = $self->{oReference}->helpConfigDocGet();
}
elsif ($self->{strRenderOutKey} eq 'command')
{
$self->{oDoc} = $self->{oReference}->helpCommandDocGet();
}
else
{
confess &log(ERROR, "cannot render $self->{strRenderOutKey} from source $$oRenderOut{source}");
}
}
elsif (defined($$oRenderOut{source}) && $$oRenderOut{source} eq 'release' && $self->{oManifest}->isBackRest())
{
require pgBackRestDoc::Custom::DocCustomRelease;
pgBackRestDoc::Custom::DocCustomRelease->import();
$self->{oDoc} =
(new pgBackRestDoc::Custom::DocCustomRelease(
${$self->{oManifest}->sourceGet('release')}{doc},
defined($self->{oManifest}->variableGet('dev')) && $self->{oManifest}->variableGet('dev') eq 'y'))->docGet();
}
else
{
$self->{oDoc} = ${$self->{oManifest}->sourceGet($self->{strRenderOutKey})}{doc};
}
$self->{oSource} = $self->{oManifest}->sourceGet($$oRenderOut{source});
}
if (defined($self->{strRenderOutKey}))
{
# Build the doc
$self->build($self->{oDoc});
# Get required sections
foreach my $strPath (@{$self->{oManifest}->{stryRequire}})
{
if (substr($strPath, 0, 1) ne '/')
{
confess &log(ERROR, "path ${strPath} must begin with a /");
}
if (!defined($self->{oSection}->{$strPath}))
{
confess &log(ERROR, "required section '${strPath}' does not exist");
}
if (defined(${$self->{oSection}}{$strPath}))
{
$self->required($strPath);
}
}
}
if (defined($self->{oDoc}))
{
$self->{bToc} = !defined($self->{oDoc}->paramGet('toc', false)) || $self->{oDoc}->paramGet('toc') eq 'y' ? true : false;
$self->{bTocNumber} =
$self->{bToc} &&
(!defined($self->{oDoc}->paramGet('toc-number', false)) || $self->{oDoc}->paramGet('toc-number') eq 'y') ? true : false;
}
# Return from function and log return values if any
return logDebugReturn
(
$strOperation,
{name => 'self', value => $self}
);
}
####################################################################################################################################
# Set begin and end values for a tag
####################################################################################################################################
sub tagSet
{
my $self = shift;
my $strTag = shift;
my $strBegin = shift;
my $strEnd = shift;
$oRenderTag->{$self->{strType}}{$strTag}[0] = defined($strBegin) ? $strBegin : '';
$oRenderTag->{$self->{strType}}{$strTag}[1] = defined($strEnd) ? $strEnd : '';
}
####################################################################################################################################
# variableReplace
#
# Replace variables in the string.
####################################################################################################################################
sub variableReplace
{
my $self = shift;
return defined($self->{oManifest}) ? $self->{oManifest}->variableReplace(shift, $self->{strType}) : shift;
}
####################################################################################################################################
# variableSet
#
# Set a variable to be replaced later.
####################################################################################################################################
sub variableSet
{
my $self = shift;
return $self->{oManifest}->variableSet(shift, shift);
}
####################################################################################################################################
# variableGet
#
# Get the current value of a variable.
####################################################################################################################################
sub variableGet
{
my $self = shift;
return $self->{oManifest}->variableGet(shift);
}
####################################################################################################################################
# Get pre-execute list for a host
####################################################################################################################################
sub preExecute
{
my $self = shift;
my $strHost = shift;
if (defined($self->{preExecute}{$strHost}))
{
return @{$self->{preExecute}{$strHost}};
}
return;
}
####################################################################################################################################
# build
#
# Build the section map and perform filtering.
####################################################################################################################################
sub build
{
my $self = shift;
my $oNode = shift;
my $oParent = shift;
my $strPath = shift;
my $strPathPrefix = shift;
# &log(INFO, " node " . $oNode->nameGet());
my $strName = $oNode->nameGet();
if (defined($oParent))
{
# Evaluate if condition -- when false the node will be removed
if (!$self->{oManifest}->evaluateIf($oNode))
{
my $strDescription;
if (defined($oNode->nodeGet('title', false)))
{
$strDescription = $self->processText($oNode->nodeGet('title')->textGet());
}
&log(DEBUG, " filtered ${strName}" . (defined($strDescription) ? ": ${strDescription}" : ''));
$oParent->nodeRemove($oNode);
return;
}
}
else
{
&log(DEBUG, ' build document');
$self->{oSection} = {};
}
# Build section
if ($strName eq 'section')
{
my $strSectionId = $oNode->paramGet('id');
&log(DEBUG, "build section [${strSectionId}]");
# Set path and parent-path for this section
if (defined($strPath))
{
$oNode->paramSet('path-parent', $strPath);
}
$strPath .= '/' . $oNode->paramGet('id');
&log(DEBUG, " path ${strPath}");
${$self->{oSection}}{$strPath} = $oNode;
$oNode->paramSet('path', $strPath);
# If depend is not set then set it to the last section
my $strDepend = $oNode->paramGet('depend', false);
my $oContainerNode = defined($oParent) ? $oParent : $self->{oDoc};
my $oLastChild;
my $strDependPrev;
foreach my $oChild ($oContainerNode->nodeList('section', false))
{
if ($oChild->paramGet('id') eq $oNode->paramGet('id'))
{
if (defined($oLastChild))
{
$strDependPrev = $oLastChild->paramGet('id');
}
elsif (defined($oParent->paramGet('depend', false)))
{
$strDependPrev = $oParent->paramGet('depend');
}
last;
}
$oLastChild = $oChild;
}
if (defined($strDepend))
{
if (defined($strDependPrev) && $strDepend eq $strDependPrev && !$oNode->paramTest('depend-default'))
{
&log(WARN,
"section '${strPath}' depend is set to '${strDepend}' which is the default, best to remove" .
" because it may become obsolete if a new section is added in between");
}
}
else
{
$strDepend = $strDependPrev;
}
# If depend is defined make sure it exists
if (defined($strDepend))
{
# If this is a relative depend then prepend the parent section
if (index($strDepend, '/') != 0)
{
if (defined($oParent->paramGet('path', false)))
{
$strDepend = $oParent->paramGet('path') . '/' . $strDepend;
}
else
{
$strDepend = "/${strDepend}";
}
}
if (!defined($self->{oSection}->{$strDepend}))
{
confess &log(ERROR, "section '${strSectionId}' depend '${strDepend}' is not valid");
}
}
if (defined($strDepend))
{
$oNode->paramSet('depend', $strDepend);
}
if (defined($strDependPrev))
{
$oNode->paramSet('depend-default', $strDependPrev);
}
# Set log to true if this section has an execute list. This helps reduce the info logging by only showing sections that are
# likely to take a log time.
$oNode->paramSet('log', $self->{bExe} && $oNode->nodeList('execute-list', false) > 0 ? true : false);
# If section content is being pulled from elsewhere go get the content
if ($oNode->paramTest('source'))
{
my $oSource = ${$self->{oManifest}->sourceGet($oNode->paramGet('source'))}{doc};
# Section should not already have title defined, it should come from the source doc
if ($oNode->nodeTest('title'))
{
confess &log(ERROR, "cannot specify title in section that sources another document");
}
# Set title from source doc's title
$oNode->nodeAdd('title')->textSet($oSource->paramGet('title'));
foreach my $oSection ($oSource->nodeList('section'))
{
push(@{${$oNode->{oDoc}}{children}}, $oSection->{oDoc});
}
# Set path prefix to modify all section paths further down
$strPathPrefix = $strPath;
# Remove source so it is not included again later
$oNode->paramSet('source', undef);
}
}
# Build link
elsif ($strName eq 'link')
{
&log(DEBUG, 'build link [' . $oNode->valueGet() . ']');
# If the path prefix is set and this is a section
if (defined($strPathPrefix) && $oNode->paramTest('section'))
{
my $strNewPath = $strPathPrefix . $oNode->paramGet('section');
&log(DEBUG, "modify link section from '" . $oNode->paramGet('section') . "' to '${strNewPath}'");
$oNode->paramSet('section', $strNewPath);
}
}
# Store block defines
elsif ($strName eq 'block-define')
{
my $strBlockId = $oNode->paramGet('id');
if (defined($self->{oyBlockDefine}{$strBlockId}))
{
confess &log(ERROR, "block ${strBlockId} is already defined");
}
$self->{oyBlockDefine}{$strBlockId} = dclone($oNode->{oDoc}{children});
$oParent->nodeRemove($oNode);
}
# Copy blocks
elsif ($strName eq 'block')
{
my $strBlockId = $oNode->paramGet('id');
if (!defined($self->{oyBlockDefine}{$strBlockId}))
{
confess &log(ERROR, "block ${strBlockId} is not defined");
}
my $strNodeJSON = $self->{oJSON}->encode($self->{oyBlockDefine}{$strBlockId});
foreach my $oVariable ($oNode->nodeList('block-variable-replace', false))
{
my $strVariableKey = $oVariable->paramGet('key');
my $strVariableReplace = $oVariable->valueGet();
$strNodeJSON =~ s/\{\[$strVariableKey\]\}/$strVariableReplace/g;
}
my ($iReplaceIdx, $iReplaceTotal) = $oParent->nodeReplace($oNode, $self->{oJSON}->decode($strNodeJSON));
# Build any new children that were added
my $iChildIdx = 0;
foreach my $oChild ($oParent->nodeList(undef, false))
{
if ($iChildIdx >= $iReplaceIdx && $iChildIdx < ($iReplaceIdx + $iReplaceTotal))
{
$self->build($oChild, $oParent, $strPath, $strPathPrefix);
}
$iChildIdx++;
}
}
# Check for pre-execute statements
elsif ($strName eq 'execute')
{
if ($self->{oManifest}->{bPre} && $oNode->paramGet('pre', false, 'n') eq 'y')
{
# Add to pre-execute list
my $strHost = $self->variableReplace($oParent->paramGet('host'));
push(@{$self->{preExecute}{$strHost}}, $oNode);
# Skip this command so it doesn't get executed twice
$oNode->paramSet('skip', 'y')
}
}
# Iterate all text nodes
if (defined($oNode->textGet(false)))
{
foreach my $oChild ($oNode->textGet()->nodeList(undef, false))
{
if (ref(\$oChild) ne "SCALAR")
{
$self->build($oChild, $oNode, $strPath, $strPathPrefix);
}
}
}
# Iterate all non-text nodes
foreach my $oChild ($oNode->nodeList(undef, false))
{
if (ref(\$oChild) ne "SCALAR")
{
$self->build($oChild, $oNode, $strPath, $strPathPrefix);
# If the child should be logged then log the parent as well so the hierarchy is complete
if ($oChild->nameGet() eq 'section' && $oChild->paramGet('log', false, false))
{
$oNode->paramSet('log', true);
}
}
}
}
####################################################################################################################################
# required
#
# Build a list of required sections
####################################################################################################################################
sub required
{
my $self = shift;
my $strPath = shift;
my $bDepend = shift;
# If node is not found that means the path is invalid
my $oNode = ${$self->{oSection}}{$strPath};
if (!defined($oNode))
{
confess &log(ERROR, "invalid path ${strPath}");
}
# Only add sections that are listed dependencies
if (!defined($bDepend) || $bDepend)
{
# Match section and all child sections
foreach my $strChildPath (sort(keys(%{$self->{oSection}})))
{
if ($strChildPath =~ /^$strPath$/ || $strChildPath =~ /^$strPath\/.*$/)
{
if (!defined(${$self->{oSectionRequired}}{$strChildPath}))
{
my @stryChildPath = split('/', $strChildPath);
&log(INFO, (' ' x (scalar(@stryChildPath) - 2)) . " require section: ${strChildPath}");
${$self->{oSectionRequired}}{$strChildPath} = true;
}
}
}
}
# Get the path of the current section's parent
my $strParentPath = $oNode->paramGet('path-parent', false);
if ($oNode->paramTest('depend'))
{
foreach my $strDepend (split(',', $oNode->paramGet('depend')))
{
if ($strDepend !~ /^\//)
{
if (!defined($strParentPath))
{
$strDepend = "/${strDepend}";
}
else
{
$strDepend = "${strParentPath}/${strDepend}";
}
}
$self->required($strDepend, true);
}
}
elsif (defined($strParentPath))
{
$self->required($strParentPath, false);
}
}
####################################################################################################################################
# isRequired
#
# Is it required to execute the section statements?
####################################################################################################################################
sub isRequired
{
my $self = shift;
my $oSection = shift;
if (!defined($self->{oSectionRequired}))
{
return true;
}
my $strPath = $oSection->paramGet('path');
defined(${$self->{oSectionRequired}}{$strPath}) ? true : false;
}
####################################################################################################################################
# processTag
####################################################################################################################################
sub processTag
{
my $self = shift;
# Assign function parameters, defaults, and log debug info
my
(
$strOperation,
$oTag
) =
logDebugParam
(
__PACKAGE__ . '->processTag', \@_,
{name => 'oTag', trace => true}
);
my $strBuffer = "";
my $strType = $self->{strType};
my $strTag = $oTag->nameGet();
if (!defined($strTag))
{
require Data::Dumper;
confess Dumper($oTag);
}
if ($strTag eq 'link')
{
my $strUrl = $oTag->paramGet('url', false);
if (!defined($strUrl))
{
my $strPage = $self->variableReplace($oTag->paramGet('page', false));
# If this is a page URL
if (defined($strPage))
{
# If the page wasn't rendered then point at the website
if (!defined($self->{oManifest}->renderOutGet($strType, $strPage, true)))
{
$strUrl = '{[backrest-url-base]}/' . $oTag->paramGet('page') . '.html';
}
# Else point locally
else
{
if ($strType eq 'html' || $strType eq 'markdown')
{
$strUrl =
$oTag->paramGet('page', false) . '.' .
($strType eq 'html' ? $strType : '.md');
}
else
{
confess &log(ERROR, "page links not supported for type ${strType}, value '" . $oTag->valueGet() . "'");
}
}
}
else
{
my $strSection = $oTag->paramGet('section');
my $oSection = ${$self->{oSection}}{$strSection};
if (!defined($oSection))
{
confess &log(ERROR, "section link '${strSection}' does not exist");
}
if (!defined($strSection))
{
confess &log(ERROR, "link with value '" . $oTag->valueGet() . "' must defined url, page, or section");
}
if ($strType eq 'html')
{
$strUrl = '#' . substr($strSection, 1);
}
elsif ($strType eq 'latex')
{
$strUrl = $strSection;
}
else
{
$strUrl = lc($self->processText($oSection->nodeGet('title')->textGet()));
$strUrl =~ s/[^\w\- ]//g;
$strUrl =~ s/ /-/g;
$strUrl = '#' . $strUrl;
}
}
}
if ($strType eq 'html')
{
$strBuffer = '' . $oTag->valueGet() . '';
}
elsif ($strType eq 'markdown')
{
$strBuffer = '[' . $oTag->valueGet() . '](' . $strUrl . ')';
}
elsif ($strType eq 'latex')
{
if ($oTag->paramTest('url'))
{
$strBuffer = "\\href{$strUrl}{" . $oTag->valueGet() . "}";
}
else
{
$strBuffer = "\\hyperref[$strUrl]{" . $oTag->valueGet() . "}";
}
}
else
{
confess "'link' tag not valid for type ${strType}";
}
}
else
{
my $strStart = $$oRenderTag{$strType}{$strTag}[0];
my $strStop = $$oRenderTag{$strType}{$strTag}[1];
if (!defined($strStart) || !defined($strStop))
{
confess &log(ERROR, "invalid type ${strType} or tag ${strTag}");
}
$strBuffer .= $strStart;
# Admonitions in the reference materials are tags of the text element rather than field elements of the document so special
# handling is required
if ($strTag eq 'admonition')
{
$strBuffer .= $self->processAdmonitionStart($oTag);
}
if ($strTag eq 'p' || $strTag eq 'title' || $strTag eq 'li' || $strTag eq 'code-block' || $strTag eq 'summary'
|| $strTag eq 'admonition')
{
$strBuffer .= $self->processText($oTag);
}
elsif (defined($oTag->valueGet()))
{
$strBuffer .= $oTag->valueGet();
}
else
{
foreach my $oSubTag ($oTag->nodeList(undef, false))
{
$strBuffer .= $self->processTag($oSubTag);
}
}
if ($strTag eq 'admonition')
{
$strBuffer .= $self->processAdmonitionEnd($oTag);
}
$strBuffer .= $strStop;
}
# Return from function and log return values if any
return logDebugReturn
(
$strOperation,
{name => 'strBuffer', value => $strBuffer, trace => true}
);
}
####################################################################################################################################
# processAdmonitionStart
####################################################################################################################################
sub processAdmonitionStart
{
my $self = shift;
# Assign function parameters, defaults, and log debug info
my
(
$strOperation,
$oTag
) =
logDebugParam
(
__PACKAGE__ . '->processAdmonitionStart', \@_,
{name => 'oTag', trace => true}
);
my $strType = $self->{strType};
my $strBuffer = '';
# Note that any changes to the way the HTML, markdown or latex display tags may also need to be made here
if ($strType eq 'html')
{
my $strType = $oTag->paramGet('type');
$strBuffer = '' . uc($strType) . ':
' .
'';
}
elsif ($strType eq 'text' || $strType eq 'markdown')
{
$strBuffer = uc($oTag->paramGet('type')) . ": ";
}
elsif ($strType eq 'latex')
{
$strBuffer = uc($oTag->paramGet('type')) . ": }";
}
# Return from function and log return values if any
return logDebugReturn
(
$strOperation,
{name => 'strBuffer', value => $strBuffer, trace => true}
);
}
####################################################################################################################################
# processAdmonitionEnd
####################################################################################################################################
sub processAdmonitionEnd
{
my $self = shift;
# Assign function parameters, defaults, and log debug info
my
(
$strOperation,
$oTag
) =
logDebugParam
(
__PACKAGE__ . '->processAdmonitionEnd', \@_,
{name => 'oTag', trace => true}
);
my $strType = $self->{strType};
my $strBuffer = '';
# Note that any changes to the way the HTML, markdown or latex display tags may also need to be made here
if ($strType eq 'html')
{
$strBuffer = '
';
}
# Return from function and log return values if any
return logDebugReturn
(
$strOperation,
{name => 'strBuffer', value => $strBuffer, trace => true}
);
}
####################################################################################################################################
# processText
####################################################################################################################################
sub processText
{
my $self = shift;
# Assign function parameters, defaults, and log debug info
my
(
$strOperation,
$oText
) =
logDebugParam
(
__PACKAGE__ . '->processText', \@_,
{name => 'oText', trace => true}
);
my $strType = $self->{strType};
my $strBuffer = '';
foreach my $oNode ($oText->nodeList(undef, false))
{
if (ref(\$oNode) eq "SCALAR")
{
if ($oNode =~ /\"/)
{
confess &log(ERROR, "unable to process quotes in string (use instead):\n${oNode}");
}
$strBuffer .= $oNode;
}
else
{
$strBuffer .= $self->processTag($oNode);
}
}
#
# if ($strType eq 'html')
# {
# # $strBuffer =~ s/^\s+|\s+$//g;
#
# $strBuffer =~ s/\n/\
\n/g;
# }
# if ($strType eq 'markdown')
# {
# $strBuffer =~ s/^\s+|\s+$//g;
$strBuffer =~ s/ +/ /g;
$strBuffer =~ s/^ //smg;
# }
if ($strType eq 'latex')
{
$strBuffer =~ s/\&mdash\;/---/g;
$strBuffer =~ s/\<\;/\\=/\$\\geq\$/g;
# $strBuffer =~ s/\_/\\_/g;
# If not a code-block, which is to be taken AS IS, then escape special characters in latex
if ($oText->nameGet() ne 'code-block')
{
# If the previous character is not already a slash (e.g. not already escaped) then insert a slash
$strBuffer =~ s/(?nameGet() eq 'list-item')
{
$strBuffer =~ s/\[/\{\[/g;
$strBuffer =~ s/\]/\]\}/g;
}
$strBuffer =~ s/\©\;/{\\textcopyright}/g;
$strBuffer =~ s/\&trade\;/{\\texttrademark}/g;
$strBuffer =~ s/\®\;/{\\textregistered}/g;
$strBuffer =~ s/\&rarr\;/{\\textrightarrow}/g;
# Escape all ampersands after making any other conversions above
$strBuffer =~ s/(?\=/g;
}
$strBuffer = $self->variableReplace($strBuffer);
# Return from function and log return values if any
return logDebugReturn
(
$strOperation,
{name => 'strBuffer', value => $strBuffer, trace => true}
);
}
1;