| Server IP : 81.2.241.106 / Your IP : 216.73.216.165 Web Server : Apache/2.4.67 (Debian) System : Linux tranq-f.bakta.org 5.10.0-45-amd64 #1 SMP Debian 5.10.259-1 (2026-07-02) x86_64 User : lisky ( 1002) PHP Version : 7.4.33 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /usr/share/perl5/sdf/ |
Upload File : |
# $Id$
$VERSION{''.__FILE__} = '$Revision$';
#
# >>Title:: Text Format Driver
#
# >>Copyright::
# Copyright (c) 1992-1996, Ian Clatworthy (ianc@mincom.com).
# You may distribute under the terms specified in the LICENSE file.
#
# >>History::
# -----------------------------------------------------------------------
# Date Who Change
# 01-May-97 ianc Applied patches from Tim MacKenzie
# 14-May-96 ianc SDF 2.000
# -----------------------------------------------------------------------
#
# >>Purpose::
# This library provides an [[SDF_DRIVER]] which generates
# plain text files.
#
# >>Description::
#
# >>Limitations::
# All headings are output as plain. i.e. without numbering.
#
# A table of contents is not supported yet.
#
# Word wrapping is not applied to example paragraphs.
#
# Multi-line cells in tables (including nested tables) do not work.
# >>Resources::
#
# >>Implementation::
#
##### Constants #####
# Default right margin
$_TXT_DEFAULT_MARGIN = 70;
# Mapping of characters
%_TXT_CHAR = (
'bullet', '.',
'c', '(c)',
'cent', 'c',
'dagger', '^',
'doubledagger', '#',
'emdash', '-',
'endash', '-',
'emspace', ' ',
'enspace', ' ',
'lbrace', '{',
'lbracket', '[',
'nbdash', '-',
'nbspace', ' ',
'nl', "\n",
'pound', 'L',
'r', '(r)',
'rbrace', '}',
'rbracket', ']',
'tab', "\t",
'tm', '(tm) ',
'yen', 'y',
# From Pod::Text.pm ...
'amp' => '&', # ampersand
'lt' => '<', # left chevron, less-than
'gt' => '>', # right chevron, greater-than
'quot' => '"', # double quote
"Aacute" => "\xC1", # capital A, acute accent
"aacute" => "\xE1", # small a, acute accent
"Acirc" => "\xC2", # capital A, circumflex accent
"acirc" => "\xE2", # small a, circumflex accent
"AElig" => "\xC6", # capital AE diphthong (ligature)
"aelig" => "\xE6", # small ae diphthong (ligature)
"Agrave" => "\xC0", # capital A, grave accent
"agrave" => "\xE0", # small a, grave accent
"Aring" => "\xC5", # capital A, ring
"aring" => "\xE5", # small a, ring
"Atilde" => "\xC3", # capital A, tilde
"atilde" => "\xE3", # small a, tilde
"Auml" => "\xC4", # capital A, dieresis or umlaut mark
"auml" => "\xE4", # small a, dieresis or umlaut mark
"Ccedil" => "\xC7", # capital C, cedilla
"ccedil" => "\xE7", # small c, cedilla
"Eacute" => "\xC9", # capital E, acute accent
"eacute" => "\xE9", # small e, acute accent
"Ecirc" => "\xCA", # capital E, circumflex accent
"ecirc" => "\xEA", # small e, circumflex accent
"Egrave" => "\xC8", # capital E, grave accent
"egrave" => "\xE8", # small e, grave accent
"ETH" => "\xD0", # capital Eth, Icelandic
"eth" => "\xF0", # small eth, Icelandic
"Euml" => "\xCB", # capital E, dieresis or umlaut mark
"euml" => "\xEB", # small e, dieresis or umlaut mark
"Iacute" => "\xCD", # capital I, acute accent
"iacute" => "\xED", # small i, acute accent
"Icirc" => "\xCE", # capital I, circumflex accent
"icirc" => "\xEE", # small i, circumflex accent
"Igrave" => "\xCD", # capital I, grave accent
"igrave" => "\xED", # small i, grave accent
"Iuml" => "\xCF", # capital I, dieresis or umlaut mark
"iuml" => "\xEF", # small i, dieresis or umlaut mark
"Ntilde" => "\xD1", # capital N, tilde
"ntilde" => "\xF1", # small n, tilde
"Oacute" => "\xD3", # capital O, acute accent
"oacute" => "\xF3", # small o, acute accent
"Ocirc" => "\xD4", # capital O, circumflex accent
"ocirc" => "\xF4", # small o, circumflex accent
"Ograve" => "\xD2", # capital O, grave accent
"ograve" => "\xF2", # small o, grave accent
"Oslash" => "\xD8", # capital O, slash
"oslash" => "\xF8", # small o, slash
"Otilde" => "\xD5", # capital O, tilde
"otilde" => "\xF5", # small o, tilde
"Ouml" => "\xD6", # capital O, dieresis or umlaut mark
"ouml" => "\xF6", # small o, dieresis or umlaut mark
"szlig" => "\xDF", # small sharp s, German (sz ligature)
"THORN" => "\xDE", # capital THORN, Icelandic
"thorn" => "\xFE", # small thorn, Icelandic
"Uacute" => "\xDA", # capital U, acute accent
"uacute" => "\xFA", # small u, acute accent
"Ucirc" => "\xDB", # capital U, circumflex accent
"ucirc" => "\xFB", # small u, circumflex accent
"Ugrave" => "\xD9", # capital U, grave accent
"ugrave" => "\xF9", # small u, grave accent
"Uuml" => "\xDC", # capital U, dieresis or umlaut mark
"uuml" => "\xFC", # small u, dieresis or umlaut mark
"Yacute" => "\xDD", # capital Y, acute accent
"yacute" => "\xFD", # small y, acute accent
"yuml" => "\xFF", # small y, dieresis or umlaut mark
"lchevron" => "\xAB", # left chevron (double less than)
"rchevron" => "\xBB", # right chevron (double greater than)
);
# Directive mapping table
%_TXT_HANDLER = (
'tuning', '_TxtHandlerTuning',
'endtuning', '_TxtHandlerEndTuning',
'table', '_TxtHandlerTable',
'row', '_TxtHandlerRow',
'cell', '_TxtHandlerCell',
'endtable', '_TxtHandlerEndTable',
'import', '_TxtHandlerImport',
'inline', '_TxtHandlerInline',
'output', '_TxtHandlerOutput',
'object', '_TxtHandlerObject',
'stylesheet', '',
);
# Phrase directive mapping table
%_TXT_PHRASE_HANDLER = (
'char', '_TxtPhraseHandlerChar',
'import', '_TxtPhraseHandlerImport',
'inline', '_TxtPhraseHandlerInline',
'variable', '_TxtPhraseHandlerVariable',
);
# Table states
$_TXT_INTABLE = 1;
$_TXT_INROW = 2;
$_TXT_INCELL = 3;
##### Variables #####
# Right margin position
$_txt_margin = $SDF_USER'var{'TXT_MARGIN'} || $_TXT_DEFAULT_MARGIN;
# Counters for ordered lists - index is the level
@_txt_list_num = 0;
# Table states and row types
@_txt_tbl_state = ();
@_txt_row_type = ();
# Column number & starting positions for current table
$_txt_col_num = 0;
@_txt_col_posn = ();
# Location of the first line of the current row
$_txt_first_row = 0;
# The current cell text
$_txt_cell_current = '';
##### Routines #####
#
# >>Description::
# {{Y:TxtFormat}} is an SDF driver which outputs plain text files.
#
sub TxtFormat {
local(*data) = @_;
local(@result);
local(@contents);
# Initialise defaults
$_txt_margin = $SDF_USER'var{'TXT_MARGIN'} || $_TXT_DEFAULT_MARGIN;
# Format the paragraphs
@contents = ();
@result = &_TxtFormatSection(*data, *contents);
# Turn into final form and return
return &_TxtFinalise(*result, *contents);
}
#
# >>_Description::
# {{Y:_TxtFormatSection}} formats a set of SDF paragraphs into text.
# If a parameter is passed to contents, then that array is populated
# with a generated Table of Contents.
#
sub _TxtFormatSection {
local(*data, *contents) = @_;
local(@result);
local($prev_tag, $prev_indent);
local($para_tag, $para_text, %para_attrs);
local($directive);
# Process the paragraphs
@result = ();
$prev_tag = '';
$prev_indent = '';
while (($para_text, $para_tag, %para_attrs) = &SdfNextPara(*data)) {
# handle directives
if ($para_tag =~ /^__(\w+)$/) {
$directive = $_TXT_HANDLER{$1};
if (defined &$directive) {
&$directive(*result, $para_text, %para_attrs);
}
else {
&AppTrace("txt", 5, "ignoring internal directive '$1'");
}
next;
}
# Add the paragraph
&_TxtParaAdd(*result, $para_tag, $para_text, *para_attrs, $prev_tag,
$prev_indent, *contents);
}
# Do this stuff before starting next loop iteration
continue {
unless ($para_tag eq 'PB') {
$prev_tag = $para_tag;
$prev_indent = $para_attrs{'in'};
}
}
# Return result
return @result;
}
#
# >>_Description::
# {{Y:_TxtParaAdd}} adds a paragraph.
#
sub _TxtParaAdd {
local(*result, $para_tag, $para_text, *para_attrs, $prev_tag, $prev_indent, *contents) = @_;
# local();
local($in_example);
local($para_fmt);
local($para_override);
local($para);
local($hdg_level);
local($toc_jump);
local($label);
# Set the example flag
$in_example = $SDF_USER'parastyles_category{$para_tag} eq 'example';
# Enumerated lists are the same as list paragraphs at the previous level
if ($para_tag =~ /^LI(\d)$/) {
$para_tag = $1 > 1 ? "L" . ($1 - 1) : 'N';
}
# Get the target format name
$para_fmt = $SDF_USER'parastyles_to{$para_tag};
$para_fmt = $para_tag if $para_fmt eq '';
# Map the attributes
&SdfAttrMap(*para_attrs, 'txt', *SDF_USER'paraattrs_to,
*SDF_USER'paraattrs_map, *SDF_USER'paraattrs_attrs,
$SDF_USER'parastyles_attrs{$para_tag});
# Build the Table of Contents as we go
$toc_jump = '';
if ($para_tag =~ /^([HAP])(\d)$/) {
$hdg_level = $2;
$para_text = &SdfHeadingPrefix($1, $2) . $para_text;
if ($hdg_level <= $SDF_USER'var{'DOC_TOC'} && !$para_attrs{'notoc'}) {
# Build a plain list in SDF
$toc_jump = $para_attrs{'id'};
$toc_jump = "HDR" . ($#contents + 1) if $toc_jump eq '';
push(@contents, "L${hdg_level}[jump='#$toc_jump']$para_text");
}
}
# Handle lists (is this needed for text format?)
elsif ($para_tag =~ /^(L[FUN]?)(\d)$/) {
$para_attrs{'in'} = $2;
}
# Prepend the label, if any (replacing tabs with spaces)
$label = $para_attrs{'label'};
$label = 'Note: ' if ($para_tag eq 'Note' || $para_tag eq 'NB') &&
$label eq '';
$label =~ s/\\t/ /g;
$para_text = "{{2:$label}}$para_text" if $label ne '';
# Indent examples, if necessary
if ($in_example && $para_attrs{'in'}) {
$para_text = " " x ($para_attrs{'in'} * 5) . $para_text;
delete $para_attrs{'in'};
}
# Format the paragraph body
if ($para_attrs{'verbatim'}) {
$para = $para_text;
delete $para_attrs{'verbatim'};
}
else {
$para = &_TxtParaText($para_text);
}
# If we're in a table, prepend the paragraph onto the current cell
if (@_txt_tbl_state) {
if ($para_fmt eq "Line") {
$_txt_cell_current .= "-" x $_txt_cell_width;
return;
}
$_txt_cell_current .= $para;
return;
}
# Build result
if ($para_tag eq 'PB') {
$para = &_TxtElement($para_fmt, $para, %para_attrs);
&_TxtParaAppend(*result, $para);
}
elsif ($in_example && $para_tag eq $prev_tag) {
&_TxtParaAppend(*result, $para);
}
else {
$para = &_TxtElement($para_fmt, $para, %para_attrs);
push(@result, $para);
}
}
#
# >>_Description::
# {{Y:_TxtParaText}} converts SDF paragraph text into TXT.
#
sub _TxtParaText {
local($para_text) = @_;
local($para);
local($state);
local($sect_type, $char_tag, $text, %sect_attrs);
local($added_anchors);
local(@char_fonts);
local($char_font);
local($directive);
# Process the text
$para = '';
$state = 0;
while (($sect_type, $text, $char_tag, %sect_attrs) =
&SdfNextSection(*para_text, *state)) {
#print "char_tag:$char_tag.\n";
# Build the paragraph
if ($sect_type eq 'special') {
$directive = $_TXT_PHRASE_HANDLER{$char_tag};
if (defined &$directive) {
&$directive(*para, $text, %sect_attrs);
}
else {
&AppMsg("warning", "ignoring special phrase '$1' in TXT driver");
}
}
elsif ($sect_type eq 'string') {
$para .= $text;
}
elsif ($sect_type eq 'phrase') {
($text) = &SDF_USER'ExpandLink($text) if $char_tag eq 'L';
$para .= $text;
}
elsif ($sect_type eq 'phrase_end') {
# do nothing
}
else {
&AppMsg("warning", "unknown section type '$sect_type' in TXT driver");
}
}
# Return result
return $para;
}
#
# >>_Description::
# {{Y:_TxtFinalise}} generates the final TXT file.
#
sub _TxtFinalise {
local(*body, *contents) = @_;
# local(@result);
# Return result
return @body;
}
#
# >>_Description::
# {{Y:_TxtElement}} formats a TXT element from a
# tag, text and set of attributes.
#
sub _TxtElement {
local($tag, $text, %attr) = @_;
local($txt);
local($prefix, $label);
local($cnt);
# Handle page breaks
if ($tag eq 'PB') {
return "\f";
}
# For examples, don't word wrap the lines
if ($tag eq 'E') {
$txt = "$text\n";
}
# For lines, output a 'line'
elsif ($tag eq 'Line') {
$txt = ("_" x $_txt_margin) . "\n";
}
# For headings, underline the text unless requested not to
elsif ($tag =~ /^[HAP](\d)/) {
$txt = "$text\n";
unless ($SDF_USER'var{'TXT_HDG_UL_OFF'}){
$char = $1 == 1 ? "=" : "-";
$txt .= ($char x length($text)) . "\n";
}
}
# For list items, add the necessary "label"
elsif ($tag =~ /^(L[FUN]?)(\d)$/) {
$prefix = " " x ($2 * 5);
$label = " " x (($2 - 1) * 5);
if ($1 eq 'LU') {
$label .= "o ";
}
elsif ($1 eq 'L') {
$label .= " ";
}
elsif ($1 eq 'LF') {
$label .= "1. ";
$_txt_list_num[$2] = 1;
}
else {
$cnt = ++$_txt_list_num[$2];
$label .= substr("$cnt. ", 0, 5);
}
$txt = &MiscTextWrap($label . $text, $_txt_margin, $prefix,
'', 1) . "\n";
}
# Otherwise, format as a plain paragraph
else {
$txt = &MiscTextWrap($text, $_txt_margin, '', '', 1) . "\n";
}
# Handle the top attribute
return $para{'top'} ? "\f\n$txt" : $txt;
}
#
# >>_Description::
# {{Y:_TxtParaAppend}} merges {{para}} into the last paragraph
# in {{@result}}. Both paragraphs are assumed to be examples.
#
sub _TxtParaAppend {
local(*result, $para) = @_;
# local();
$result[$#result] .= "$para\n";
}
#
# >>_Description::
# {{Y:_TxtHandlerTuning}} handles the 'tuning' directive.
#
sub _TxtHandlerTuning {
local(*outbuffer, $style, %attr) = @_;
# local();
# do nothing
}
#
# >>_Description::
# {{Y:_TxtHandlerEndTuning}} handles the 'endtuning' directive.
#
sub _TxtHandlerEndTuning {
local(*outbuffer, $style, %attr) = @_;
# local();
# do nothing
}
#
# >>_Description::
# {{Y:_TxtHandlerTable}} handles the 'table' directive.
#
sub _TxtHandlerTable {
local(*outbuffer, $columns, %attr) = @_;
# local();
local($tbl_title);
# Update the state
push(@_txt_tbl_state, $_TXT_INTABLE);
push(@_txt_row_type, '');
# Calculate the column positions (rounded)
@_txt_col_posn = &SdfColPositions($columns, $attr{'format'}, $_txt_margin);
# Add the title, if any
$tbl_title = $attr{'title'};
if ($tbl_title ne '') {
push(@outbuffer, "$tbl_title\n");
}
}
#
# >>_Description::
# {{Y:_TxtHandlerRow}} handles the 'row' directive.
#
sub _TxtHandlerRow {
local(*outbuffer, $text, %attr) = @_;
# local();
local($current_state);
# Finalise the previous row, if any
$current_state = $_txt_tbl_state[$#_txt_tbl_state];
unless ($current_state == $_TXT_INTABLE) {
&_TxtFinalisePrevRow(*outbuffer, $_txt_row_type[$#_txt_row_type]);
}
# Start the new row
push(@outbuffer, "");
$_txt_col_num = 0;
$_txt_first_row = $#outbuffer;
# Update the state
$_txt_tbl_state[$#_txt_tbl_state] = $_TXT_INROW;
$_txt_row_type[$#_txt_row_type] = $text;
}
#
# >>_Description::
# {{Y:_TxtHandlerCell}} handles the 'cell' directive.
#
sub _TxtHandlerCell {
local(*outbuffer, $text, %attr) = @_;
# local();
local($state);
local($padding);
local($tmp);
# Finalise the old cell, if any
$state = $_txt_tbl_state[$#_txt_tbl_state];
if ($state eq $_TXT_INCELL) {
&_TxtFinishCell(*outbuffer);
if ($_txt_col_num > 0) {
foreach $tmp ($_txt_first_row .. $#outbuffer) {
$padding = $_txt_col_posn[$_txt_col_num - 1] -
length($outbuffer[$tmp]);
$padding = 1 if ($padding <= 0);
$outbuffer[$tmp] .= " " x $padding;
}
}
}
# Update the state
$_txt_tbl_state[$#_txt_tbl_state] = $_TXT_INCELL;
$_txt_cell_margin = ($_txt_col_num>0?$_txt_col_posn[$_txt_col_num-1]:0);
$_txt_col_num+=$attr{cols};
if ($_txt_col_num > $#_txt_col_posn + 1) {
$_txt_cell_width = $_txt_margin - $_txt_cell_margin - 1;
} else {
$_txt_cell_width = $_txt_col_posn[$_txt_col_num-1] -
$_txt_cell_margin - 1;
}
%_txt_cell_attrs = %attr;
$_txt_cell_current = "";
}
#
# >>_Description::
# {{Y:_TxtFinishCell}} adds the cell text to the output
#
sub _TxtFinishCell {
local(*outbuffer) = @_;
$_txt_cell_current =~ s/(\S)\s+/$1 /g;
local @lines = split(/\n/,
&MiscTextWrap($_txt_cell_current, $_txt_cell_width,"",'',1));
local $tmp;
foreach $tmp ($#outbuffer+1..$_txt_first_row+$#lines) {
push(@outbuffer, " " x $_txt_cell_margin);
}
foreach $tmp (0..$#lines) {
if ($_txt_cell_attrs{'align'} eq "Center") {
$outbuffer[$_txt_first_row+$tmp] .=
" " x (($_txt_cell_width - length($lines[$tmp]))/2);
} elsif ($_txt_cell_attrs{'align'} eq "Right") {
$outbuffer[$_txt_first_row+$tmp] .=
" " x ($_txt_cell_width - length($lines[$tmp]));
}
$outbuffer[$_txt_first_row+$tmp] .= $lines[$tmp];
}
$_txt_cell_current = "";
}
#
# >>_Description::
# {{Y:_TxtHandlerEndTable}} handles the 'endtable' directive.
#
sub _TxtHandlerEndTable {
local(*outbuffer, $text, %attr) = @_;
# local();
local($state);
local($row_type);
# Finalise the table
$state = pop(@_txt_tbl_state);
$row_type = pop(@_txt_row_type);
if ($state eq $_TXT_INCELL) {
&_TxtFinalisePrevRow(*outbuffer, $row_type);
$outbuffer[$#outbuffer] .= "\n";
}
elsif ($state eq $_TXT_INROW) {
&_TxtFinalisePrevRow(*outbuffer, $row_type);
$outbuffer[$#outbuffer] .= "\n";
}
}
#
# >>_Description::
# {{Y:_TxtFinalisePrevRow}} finalises the previous row, if any.
#
sub _TxtFinalisePrevRow {
local(*outbuffer, $row_type) = @_;
# local();
local($line_row);
local($prefix);
local($tmp);
local($_);
&_TxtFinishCell(*outbuffer);
foreach $tmp ($_txt_first_row..$#outbuffer) {
$outbuffer[$tmp] =~ s/ *(\n*)$/$1/;
}
# If the last row was a heading, underline it
if ($row_type eq 'Heading') {
$line_row = "";
$tmp = 0;
foreach (@_txt_col_posn) {
$line_row .= ("."x($_-$tmp-1))." ";
$tmp = $_;
}
push(@outbuffer, $line_row);
}
}
#
# >>_Description::
# {{Y:_TxtHandlerImport}} handles the import directive.
#
sub _TxtHandlerImport {
local(*outbuffer, $filepath, %attr) = @_;
# local();
local($para);
# Build the result
&_TxtPhraseHandlerImport(*para, $filepath, %attr);
push(@outbuffer, "$para\n");
}
#
# >>_Description::
# {{Y:_TxtHandlerInline}} handles the inline directive.
#
sub _TxtHandlerInline {
local(*outbuffer, $text, %attr) = @_;
# local();
# Check we can handle this format
my $target = $attr{'target'};
return unless $target eq 'txt' || $target eq 'text';
# Build the result
push(@outbuffer, $text);
}
#
# >>_Description::
# {{Y:_TxtHandlerOutput}} handles the 'output' directive.
#
sub _TxtHandlerOutput {
local(*outbuffer, $text, %attrs) = @_;
# local();
# do nothing
}
#
# >>_Description::
# {{Y:_TxtHandlerObject}} handles the 'object' directive.
#
sub _TxtHandlerObject {
local(*outbuffer, $text, %attrs) = @_;
# local();
# Save the margin, if necessary
if ($text eq 'Variable' && $attrs{'Name'} eq 'TXT_MARGIN') {
$_txt_margin = $attrs{'value'};
}
}
#
# >>_Description::
# {{Y:_TxtPhraseHandlerChar}} handles the 'char' phrase directive.
#
sub _TxtPhraseHandlerChar {
local(*para, $text, %attr) = @_;
# local();
# Map those we know about it
if (defined($_TXT_CHAR{$text})) {
$para .= $_TXT_CHAR{$text};
}
else {
$para .= $text;
}
}
#
# >>_Description::
# {{Y:_TxtPhraseHandlerImport}} handles the 'import' phrase directive.
#
sub _TxtPhraseHandlerImport {
local(*para, $filepath, %attr) = @_;
# local();
local($name, $value);
$para .= "** Unable to import figure $filepath **";
}
#
# >>_Description::
# {{Y:_TxtPhraseHandlerInline}} handles the 'inline' phrase directive.
#
sub _TxtPhraseHandlerInline {
local(*para, $text, %attr) = @_;
# local();
# Build the result
$para .= $text;
}
#
# >>_Description::
# {{Y:_TxtPhraseHandlerVariable}} handles the 'variable' phrase directive.
#
sub _TxtPhraseHandlerVariable {
local(*para, $text, %attr) = @_;
# local();
# do nothing
}
# package return value
1;