1
0
mirror of https://github.com/php/phd.git synced 2026-03-24 07:02:07 +01:00
Files
archived-phd/phpdotnet/phd/Package/Generic/XHTML.php
2024-12-29 19:58:10 +00:00

2523 lines
93 KiB
PHP

<?php
namespace phpdotnet\phd;
abstract class Package_Generic_XHTML extends Format_Abstract_XHTML {
private $myelementmap = array( /* {{{ */
'abstract' => 'div', /* Docbook-xsl prints "abstract"... */
'abbrev' => 'abbr',
'acronym' => 'acronym',
'affiliation' => 'format_suppressed_tags',
'alt' => 'format_suppressed_tags',
'arg' => 'format_suppressed_tags',
'article' => 'format_container_chunk_top',
'author' => array(
/* DEFAULT */ 'format_author',
'authorgroup' => 'format_authorgroup_author',
),
'authorgroup' => 'div',
'authorinitials' => 'format_entry',
'appendix' => 'format_container_chunk_top',
'application' => 'span',
'blockquote' => 'blockquote',
'bibliography' => array(
/* DEFAULT */ 'format_div',
'article' => 'format_chunk',
'book' => 'format_chunk',
'part' => 'format_chunk',
),
'book' => 'format_container_chunk_top',
'caption' => 'format_caption',
'chapter' => 'format_container_chunk_top',
'citetitle' => 'em',
'cmdsynopsis' => 'format_cmdsynopsis',
'co' => 'format_co',
'colophon' => 'format_chunk',
'copyright' => 'format_copyright',
'date' => array(
/* DEFAULT */ 'p',
'revision' => 'format_entry',
),
'editor' => 'format_editor',
'edition' => 'format_suppressed_tags',
'email' => 'format_suppressed_tags',
'errortext' => 'code',
'firstname' => 'format_name',
'footnote' => 'format_footnote',
'footnoteref' => 'format_footnoteref',
'funcdef' => 'format_suppressed_tags',
'funcsynopsis' => 'div',
'funcsynopsisinfo' => 'pre',
'function' => 'span',
'funcprototype' => 'code',
'surname' => 'format_name',
'othername' => 'format_name',
'optional' => 'span',
'honorific' => 'span',
'glossary' => array(
/* DEFAULT */ 'format_div',
'article' => 'format_chunk',
'book' => 'format_chunk',
'part' => 'format_chunk',
),
'calloutlist' => 'format_calloutlist',
'callout' => 'format_callout',
'caution' => 'format_admonition',
'citation' => 'format_citation',
'citerefentry' => 'span',
'code' => 'code',
'collab' => 'span',
'collabname' => 'span',
'contrib' => 'format_suppressed_tags',
'colspec' => 'format_colspec',
'command' => 'strong',
'computeroutput' => 'span',
/* FIXME: This is one crazy stupid workaround for footnotes */
'constant' => array(
/* DEFAULT */ 'format_constant',
'para' => array(
/* DEFAULT */ 'format_constant',
'footnote' => 'format_footnote_constant',
),
),
'constructorsynopsis' => 'format_methodsynopsis',
'destructorsynopsis' => 'format_methodsynopsis',
'emphasis' => 'format_emphasis',
'enumname' => 'span',
'entry' => array (
/* DEFAULT */ 'format_entry',
'row' => array(
/* DEFAULT */ 'format_entry',
'thead' => 'format_th_entry',
'tfoot' => 'format_th_entry',
'tbody' => 'format_entry',
),
),
'envar' => 'var',
'errortype' => 'span',
'errorcode' => 'span',
'example' => 'format_example',
'formalpara' => 'p',
'fieldsynopsis' => array(
/* DEFAULT */ 'format_fieldsynopsis',
'entry' => 'format_div',
),
'figure' => 'div',
'filename' => 'var',
'glossentry' => 'li',
'glossdef' => 'p',
'glosslist' => 'format_itemizedlist',
'glossterm' => 'span',
'holder' => 'span',
'imageobject' => 'format_div',
'imagedata' => 'format_imagedata',
'important' => 'format_admonition',
'index' => array(
/* DEFAULT */ 'format_div',
'article' => 'format_chunk',
'book' => 'format_chunk',
'part' => 'format_chunk',
),
'info' => array(
/* DEFAULT */ 'format_div',
'note' => 'span',
),
'informalexample' => 'format_div',
'informaltable' => 'format_table',
'indexdiv' => 'format_dl',
'indexentry' => 'dd',
'initializer' => 'format_initializer',
'itemizedlist' => 'format_itemizedlist',
'keycap' => 'format_keycap',
'keycombo' => 'format_keycombo',
'legalnotice' => 'format_chunk',
'listitem' => array(
/* DEFAULT */ 'li',
'varlistentry' => 'format_varlistentry_listitem',
),
'literal' => 'format_literal',
'literallayout' => 'pre',
'link' => 'format_link',
'xref' => 'format_xref',
'manvolnum' => 'format_manvolnum',
'inlinemediaobject' => 'format_mediaobject',
'mediaobject' => 'format_mediaobject',
'methodparam' => 'format_methodparam',
'methodsynopsis' => 'format_methodsynopsis',
'methodname' => 'format_methodname',
'member' => 'format_member',
'modifier' => array(
/* DEFAULT */ 'format_modifier',
'methodparam' => 'format_methodparam_modifier',
),
'note' => 'format_note',
'orgname' => 'span',
'othercredit' => 'format_div',
/** Class Synopsis related tags */
'classsynopsis' => 'format_classsynopsis',
'classsynopsisinfo' => 'format_classsynopsisinfo',
'ooclass' => array(
/* DEFAULT */ 'span',
'classsynopsis' => 'format_classsynopsis_generic_oo_tag',
),
'ooexception' => [
/* DEFAULT */ 'span',
'classsynopsis' => 'format_classsynopsis_generic_oo_tag',
],
'oointerface' => array(
/* DEFAULT */ 'span',
'classsynopsis' => 'format_classsynopsis_generic_oo_tag',
'classsynopsisinfo' => 'format_classsynopsisinfo_oointerface',
),
'classname' => [
/* DEFAULT */ 'span',
'ooclass' => [
/* DEFAULT */ 'span',
'classsynopsis' => 'format_classsynopsis_ooclass_classname',
'classsynopsisinfo' => 'format_classsynopsisinfo_ooclass_classname',
],
],
'exceptionname' => [
/* DEFAULT */ 'span',
'ooexception' => [
/* DEFAULT */ 'span',
'classsynopsis' => 'format_classsynopsis_ooclass_classname',
],
],
'interfacename' => array(
/* DEFAULT */ 'span',
'oointerface' => array(
/* DEFAULT */ 'span',
'classsynopsis' => 'format_classsynopsis_oointerface_interfacename',
'classsynopsisinfo' => 'format_classsynopsisinfo_oointerface_interfacename',
),
),
/** Enum synopsis related */
'enumsynopsis' => 'format_enumsynopsis',
'enumname' => [
/* DEFAULT */ 'span',
'enumsynopsis' => 'format_enumsynopsis_enumname'
],
'enumitem' => 'format_enumitem',
'enumidentifier' => 'format_enumidentifier',
'enumvalue' => 'format_enumvalue',
'enumitemdescription' => 'format_enumitemdescription',
'option' => 'format_option',
'orderedlist' => 'format_orderedlist',
'para' => array(
/* DEFAULT */ 'p',
'example' => 'format_example_content',
'footnote' => 'format_footnote_para',
'refsect1' => 'format_refsect1_para',
'question' => 'format_suppressed_tags',
),
'paramdef' => 'format_suppressed_tags',
'parameter' => array(
/* DEFAULT */ 'format_parameter',
'methodparam' => 'format_methodparam_parameter',
),
'part' => 'format_container_chunk_top',
'partintro' => 'format_div',
'personname' => 'format_personname',
'personblurb' => 'format_div',
'phrase' => 'span',
'preface' => 'format_chunk',
'printhistory' => 'format_div',
'primaryie' => 'format_suppressed_tags',
'procedure' => 'format_procedure',
'productname' => 'span',
'programlisting' => 'format_programlisting',
'prompt' => 'span',
'propname' => 'span',
'property' => array(
/* DEFAULT */ 'span',
'classsynopsisinfo' => 'format_varname',
),
'proptype' => 'span',
'pubdate' => 'format_div', /* Docbook-XSL prints "published" */
'refentry' => 'format_chunk',
'refentrytitle' => 'span',
'refpurpose' => 'p',
'reference' => 'format_container_chunk_below',
'refsect1' => 'format_refsect',
'refsect2' => 'format_refsect',
'refsect3' => 'format_refsect',
'refsynopsisdiv' => 'div',
'refname' => 'h1',
'refnamediv' => 'div',
'releaseinfo' => 'div',
'replaceable' => array(
/* DEFAULT */ 'span',
'constant' => 'format_replaceable',
),
'revhistory' => 'format_table',
'revision' => 'format_row',
'revremark' => 'format_entry',
'row' => 'format_row',
'screen' => 'format_screen',
'screenshot' => 'format_div',
'sect1' => 'format_chunk',
'sect2' => 'div',
'sect3' => 'div',
'sect4' => 'div',
'sect5' => 'div',
'section' => array(
/* DEFAULT */ 'div',
'sect1' => 'format_section_chunk',
'chapter' => 'format_section_chunk',
'appendix' => 'format_section_chunk',
'article' => 'format_section_chunk',
'part' => 'format_section_chunk',
'reference' => 'format_section_chunk',
'refentry' => 'format_section_chunk',
'index' => 'format_section_chunk',
'bibliography' => 'format_section_chunk',
'glossary' => 'format_section_chunk',
'colopone' => 'format_section_chunk',
'book' => 'format_section_chunk',
'set' => 'format_section_chunk',
'setindex' => 'format_section_chunk',
'legalnotice' => 'format_section_chunk',
),
'seg' => 'format_seg',
'segmentedlist' => 'format_segmentedlist',
'seglistitem' => 'format_seglistitem',
'segtitle' => 'format_suppressed_tags',
'set' => 'format_container_chunk_top',
'setindex' => 'format_chunk',
'shortaffil' => 'format_suppressed_tags',
'sidebar' => 'format_note',
'simplelist' => 'format_simplelist',
'simplesect' => 'div',
'simpara' => array(
/* DEFAULT */ 'p',
'note' => 'span',
'listitem' => 'span',
'entry' => 'span',
'example' => 'format_example_content',
),
'spanspec' => 'format_suppressed_tags',
'step' => 'format_step',
'superscript' => 'sup',
'subscript' => 'sub',
'systemitem' => 'format_systemitem',
'symbol' => 'span',
'synopsis' => 'pre',
'tag' => 'code',
'table' => 'format_table',
'firstterm' => 'format_term',
'term' => array(
/* DEFAULT */ 'format_term',
'varlistentry' => 'format_varlistentry_term'
),
'tfoot' => 'format_th',
'tbody' => 'format_tbody',
'td' => 'format_th',
'th' => 'format_th',
'thead' => 'format_th',
'tgroup' => 'format_tgroup',
'tip' => 'format_admonition',
'title' => array(
/* DEFAULT */ 'h1',
'example' => 'format_example_title',
'formalpara' => 'h5',
'info' => array(
/* DEFAULT */ 'h1',
'example' => 'format_example_title',
'note' => 'format_note_title',
'table' => 'format_table_title',
'informaltable' => 'format_table_title',
'article' => 'format_container_chunk_top_title',
'appendix' => 'format_container_chunk_top_title',
'book' => 'format_container_chunk_top_title',
'chapter' => 'format_container_chunk_top_title',
'part' => 'format_container_chunk_top_title',
'set' => 'format_container_chunk_top_title',
),
'indexdiv' => 'dt',
'legalnotice' => 'h4',
'note' => 'format_note_title',
'phd:toc' => 'strong',
'procedure' => 'strong',
'refsect1' => 'h3',
'refsect2' => 'h4',
'refsect3' => 'h5',
'section' => 'h2',
'sect1' => 'h2',
'sect2' => 'h3',
'sect3' => 'h4',
'sect4' => 'h5',
'segmentedlist' => 'format_table_title',
'table' => 'format_table_title',
'variablelist' => 'format_variablelist_title',
'article' => 'format_container_chunk_top_title',
'appendix' => 'format_container_chunk_top_title',
'book' => 'format_container_chunk_top_title',
'chapter' => 'format_container_chunk_top_title',
'part' => 'format_container_chunk_top_title',
'set' => 'format_container_chunk_top_title',
),
'titleabbrev' => 'format_suppressed_tags',
'token' => 'code',
'tr' => 'format_row',
'trademark' => 'format_trademark',
'type' => 'span',
'userinput' => 'format_userinput',
'uri' => 'code',
'variablelist' => 'format_variablelist',
'varlistentry' => 'format_varlistentry',
'varname' => array(
/* DEFAULT */ 'var',
'fieldsynopsis' => 'format_fieldsynopsis_varname',
),
'void' => 'format_void',
'warning' => 'format_admonition',
'xref' => 'format_xref',
'year' => 'span',
'quote' => 'q',
'qandaset' => 'format_qandaset',
'qandaentry' => 'dl',
'question' => array(
/* DEFAULT */ 'format_question',
'questions' => 'format_phd_question', // From the PhD namespace
),
'questions' => 'ol', // From the PhD namespace
'answer' => 'dd',
//phpdoc: implemented in the PHP Package
'phpdoc:classref' => 'format_suppressed_tags',
'phpdoc:exception' => 'format_suppressed_tags',
'phpdoc:exceptionref' => 'format_suppressed_tags',
'phpdoc:varentry' => 'format_suppressed_tags',
//phd
'phd:toc' => 'format_phd_toc',
); /* }}} */
private $mytextmap = array(
'segtitle' => 'format_segtitle_text',
'affiliation' => 'format_suppressed_text',
'contrib' => 'format_suppressed_text',
'shortaffil' => 'format_suppressed_text',
'edition' => 'format_suppressed_text',
'programlisting' => 'format_programlisting_text',
'screen' => 'format_screen_text',
'alt' => 'format_alt_text',
'modifier' => array(
/* DEFAULT */ false,
'fieldsynopsis' => 'format_fieldsynopsis_modifier_text',
'methodparam' => 'format_modifier_text',
'methodsynopsis' => 'format_modifier_text',
'constructorsynopsis' => 'format_modifier_text',
'ooclass' => 'format_modifier_text',
'ooexception' => 'format_modifier_text',
'oointerface' => 'format_modifier_text',
),
'replaceable' => array(
/* DEFAULT */ false,
'constant' => 'format_suppressed_text',
),
/** Those are used to retrieve the class/interface name to be able to remove it from method names */
'classname' => [
/* DEFAULT */ false,
'ooclass' => [
/* DEFAULT */ false,
/** This is also used by the legacy display to not display the class name at all */
'classsynopsis' => 'format_classsynopsis_ooclass_classname_text',
]
],
'exceptionname' => [
/* DEFAULT */ false,
'ooexception' => [
/* DEFAULT */ false,
'classsynopsis' => 'format_classsynopsis_oo_name_text',
]
],
'interfacename' => [
/* DEFAULT */ false,
'oointerface' => [
/* DEFAULT */ false,
'classsynopsis' => 'format_classsynopsis_oo_name_text',
]
],
'methodname' => array(
/* DEFAULT */ false,
'constructorsynopsis' => array(
/* DEFAULT */ false,
'classsynopsis' => 'format_classsynopsis_methodsynopsis_methodname_text',
),
'methodsynopsis' => array(
/* DEFAULT */ false,
'classsynopsis' => 'format_classsynopsis_methodsynopsis_methodname_text',
),
'destructorsynopsis' => array(
/* DEFAULT */ false,
'classsynopsis' => 'format_classsynopsis_methodsynopsis_methodname_text',
),
),
'para' => array(
/* DEFAULT */ false,
'footnote' => 'format_footnote_para_text',
),
'property' => [
/* DEFAULT */ 'format_property_text',
],
/* FIXME: This is one crazy stupid workaround for footnotes */
'constant' => array(
/* DEFAULT */ 'format_constant_text',
'para' => array(
/* DEFAULT */ 'format_constant_text',
'footnote' => 'format_footnote_constant_text',
),
),
'literal' => 'format_literal_text',
'email' => 'format_email_text',
'titleabbrev' => 'format_suppressed_text',
'member' => 'format_member_text',
);
/** @var array */
protected $TOC_WRITTEN = [];
/** @var string */
protected $CURRENT_LANG = "";
/** @var string */
protected $CURRENT_CHUNK = "";
/* Current Chunk variables */
protected $cchunk = array();
/* Default Chunk variables */
private $dchunk = array(
"classsynopsis" => [
"close" => false,
"classname" => false,
"interface" => false, // bool: true when in interface
"ooclass" => false,
"oointerface" => false,
"legacy" => true, // legacy rendering
],
"classsynopsisinfo" => array(
"implements" => false,
"ooclass" => false,
),
"examples" => 0,
"fieldsynopsis" => array(
"modifier" => "public",
),
"methodsynopsis" => array(
"returntypes" => array(),
"type_separator" => array(),
"type_separator_stack" => array(),
),
"co" => 0,
"callouts" => 0,
"segmentedlist" => array(
"segtitleclosed" => false,
"segtitleopened" => false,
),
"table" => false,
"procedure" => false,
"mediaobject" => array(
"alt" => false,
),
"footnote" => array(
),
"tablefootnotes" => array(
),
"chunk_id" => null,
"varlistentry" => array(
"listitems" => array(),
),
"simplelist" => array(
"members" => array(),
"type" => null,
"columns" => null,
),
);
protected $pihandlers = array(
'dbhtml' => 'PI_DBHTMLHandler',
'dbtimestamp' => 'PI_DBHTMLHandler',
);
protected $stylesheets = array();
protected $isSectionChunk = array();
protected $params = array();
protected int $exampleCounter = 0;
public function __construct(
Config $config,
OutputHandler $outputHandler
) {
parent::__construct($config, $outputHandler);
$this->registerPIHandlers($this->pihandlers);
$this->setExt($this->config->ext === null ? ".html" : $this->config->ext);
}
public function getDefaultElementMap() {
return $this->myelementmap;
}
public function getDefaultTextMap() {
return $this->mytextmap;
}
public function getChunkInfo() {
return $this->cchunk;
}
public function getDefaultChunkInfo() {
return $this->dchunk;
}
protected function createTOC($id, $name, $props, $depth = 1, $header = true) {
if (!$this->getChildren($id) || $depth == 0) {
return "";
}
$toc = '';
if ($header) {
$toc .= $this->getTocHeader($props);
}
$toc .= "<ul class=\"chunklist chunklist_$name\">\n";
foreach ($this->getChildren($id) as $child) {
$isLDesc = null;
$isSDesc = null;
$long = $this->getLongDescription($child, $isLDesc);
$short = $this->getShortDescription($child, $isSDesc);
$link = $this->createLink($child);
$list = "";
if ($depth > 1 ) {
$list = $this->createTOC($child, $name, $props, $depth -1, false);
}
if ($isLDesc && $isSDesc) {
$toc .= '<li><a href="' . $link . '">' . $short . '</a> — ' . $long . $list . "</li>\n";
} else {
$toc .= '<li><a href="' . $link . '">' . $long . '</a>' . $list . "</li>\n";
}
}
$toc .= "</ul>\n";
return $toc;
}
protected function getTocHeader($props)
{
return '<strong>' . $this->autogen('toc', $props['lang']) . '</strong>';
}
/**
* Handle a <phd:toc> tag.
*/
function format_phd_toc($open, $name, $attrs, $props) {
if ($open) {
return '<div class="phd-toc">';
}
return $this->createToc(
$attrs[Reader::XMLNS_PHD]['element'],
'phd-toc',
$props,
isset($attrs[Reader::XMLNS_PHD]['toc-depth'])
? (int)$attrs[Reader::XMLNS_PHD]['toc-depth'] : 1,
false
) . "</div>\n";
}
public function createLink($for, &$desc = null, $type = Format::SDESC) {
$retval = null;
if (isset($this->indexes[$for])) {
$rsl = $this->indexes[$for];
$retval = $rsl["filename"] . $this->ext;
if ($rsl["filename"] != $rsl["docbook_id"]) {
$retval .= '#' . $rsl["docbook_id"];
}
$desc = $rsl["sdesc"] ?: $rsl["ldesc"];
}
return $retval;
}
protected function createCSSLinks() {
$cssLinks = '';
foreach ((array)$this->stylesheets as $css) {
if ($this->isChunked()) {
$cssLinks .= "<link media=\"all\" rel=\"stylesheet\" type=\"text/css\" href=\"styles/".$css."\" />\n";
} else {
$cssLinks .= "<style type=\"text/css\">\n" . $css . "\n</style>\n";
}
}
return $cssLinks;
}
protected function fetchStylesheet($name = null) {
if (!$this->isChunked()) {
foreach ((array)$this->config->css as $css) {
if ($style = file_get_contents($css)) {
$this->stylesheets[] = $style;
} else {
trigger_error(vsprintf("Stylesheet %s not fetched.", [$css]), E_USER_WARNING);
}
}
return;
}
$stylesDir = $this->getOutputDir();
if (!$stylesDir) {
$stylesDir = $this->config->outputDir;
}
$stylesDir .= 'styles/';
if (file_exists($stylesDir)) {
if (!is_dir($stylesDir)) {
trigger_error("The styles/ directory is a file?", E_USER_ERROR);
}
} else {
if (!mkdir($stylesDir, 0777, true)) {
trigger_error("Can't create the styles/ directory.", E_USER_ERROR);
}
}
foreach ((array)$this->config->css as $css) {
$basename = basename($css);
$dest = md5(substr($css, 0, -strlen($basename))) . '-' . $basename;
if (@copy($css, $stylesDir . $dest)) {
$this->stylesheets[] = $dest;
} else {
trigger_error(vsprintf('Impossible to copy the %s file.', [$css]), E_USER_WARNING);
}
}
}
/* Functions format_* */
public function format_suppressed_tags($open, $name, $attrs, $props) {
/* Ignore it */
return "";
}
public function format_suppressed_text($value, $tag) {
/* Suppress any content */
return "";
}
public function format_link($open, $name, $attrs, $props) {
if ($open) {
$link = $class = $content = "";
if (isset($attrs[Reader::XMLNS_DOCBOOK]["linkend"])) {
$link = $this->createLink($attrs[Reader::XMLNS_DOCBOOK]["linkend"]);
}
elseif (isset($attrs[Reader::XMLNS_XLINK]["href"])) {
$link = $attrs[Reader::XMLNS_XLINK]["href"];
$class = " external";
$content = "&raquo;&nbsp;";
}
if ($props["empty"]) {
$content .= $link ."</a>";
}
return '<a href="' . $link . '" class="' . $name . $class . '">' . $content;
}
return "</a>";
}
public function format_xref($open, $name, $attrs, $props) {
if ($open) {
$desc = "";
$link = $this->createLink($attrs[Reader::XMLNS_DOCBOOK]["linkend"], $desc);
$ret = '<a href="' .$link. '" class="' .$name. '">' .$desc;
if ($props["empty"]) {
return $ret. "</a>";
}
return $ret;
}
return "</a>";
}
public function format_option($open, $name, $attrs) {
if ($open) {
if(!isset($attrs[Reader::XMLNS_DOCBOOK]["role"])) {
$attrs[Reader::XMLNS_DOCBOOK]["role"] = "unknown";
}
$this->pushRole($attrs[Reader::XMLNS_DOCBOOK]["role"]);
return '<strong class="' . $name . ' ' . $this->getRole() . '">';
}
$this->popRole();
return "</strong>";
}
public function format_literal($open, $name, $attrs)
{
if ($open) {
$this->pushRole($attrs[Reader::XMLNS_DOCBOOK]["role"] ?? null);
return '<code class="literal">';
}
$this->popRole();
return '</code>';
}
public function format_literal_text($value, $tag) {
switch ($this->getRole()) {
case 'infdec':
$value = (float)$value;
$p = strpos($value, '.');
$str = substr($value, 0, $p + 1);
$str .= '<span style="text-decoration: overline;">';
$str .= substr($value, $p + 1);
$str .= '</span>';
return $str;
default:
return $this->TEXT($value);
}
}
public function format_copyright($open, $name, $attrs) {
if ($open) {
return '<div class="'.$name.'">&copy; ';
}
return '</div>';
}
public function format_author($open, $name, $attrs, $props) {
if ($open) {
return '<div class="' .$name. ' vcard">';
}
return "</div>";
}
public function format_personname($open, $name, $attrs, $props) {
if ($open) {
return '<span class="' .$name. ' fn">';
}
return "</span>";
}
public function format_name($open, $name, $attrs) {
if ($open) {
$class = "";
switch($name) {
case "firstname":
$class = " given-name";
break;
case "surname":
$class = " family-name";
break;
case "othername":
if (isset($attrs[Reader::XMLNS_DOCBOOK]["role"])) {
/* We might want to add support for other roles */
switch($attrs[Reader::XMLNS_DOCBOOK]["role"]) {
case "nickname":
$class = " nickname";
break;
}
}
break;
}
return ' <span class="' . $name . $class . '">';
}
return '</span> ';
}
public function format_container_chunk_top($open, $name, $attrs, $props) {
$this->cchunk = $this->dchunk;
$this->cchunk["name"] = $name;
if(isset($attrs[Reader::XMLNS_XML]["id"])) {
$id = $attrs[Reader::XMLNS_XML]["id"];
} else {
$id = uniqid("phd");
}
if ($open) {
$this->CURRENT_CHUNK = $id;
$this->notify(Render::CHUNK, Render::OPEN);
return '<div id="' .$id. '" class="' .$name. '">';
}
$this->CURRENT_CHUNK = $id;
$this->notify(Render::CHUNK, Render::CLOSE);
$toc = "";
if (!in_array($id, $this->TOC_WRITTEN ?? [])) {
$toc = $this->createTOC($id, $name, $props);
}
return $toc."</div>";
}
public function format_container_chunk_top_title($open, $name, $attrs, $props) {
if ($open) {
return '<h1>';
}
$id = $this->CURRENT_CHUNK;
$toc = $this->createTOC($id, $name, $props, 2);
$this->TOC_WRITTEN[] = $id;
return '</h1>'.$toc;
}
public function format_container_chunk_below($open, $name, $attrs, $props) {
$this->cchunk = $this->dchunk;
$this->cchunk["name"] = $name;
if(isset($attrs[Reader::XMLNS_XML]["id"])) {
$id = $attrs[Reader::XMLNS_XML]["id"];
} else {
/* FIXME: This will obviously not exist in the db.. */
$id = uniqid("phd");
}
if ($open) {
$this->CURRENT_CHUNK = $id;
$this->notify(Render::CHUNK, Render::OPEN);
return '<div id="' .$attrs[Reader::XMLNS_XML]["id"]. '" class="' .$name. '">';
}
$toc = '<ol>';
if (!in_array($id, $this->TOC_WRITTEN ?? [])) {
$toc = $this->createTOC($id, $name, $props);
}
$toc .= "</ol>\n";
$this->CURRENT_CHUNK = $id;
$this->notify(Render::CHUNK, Render::CLOSE);
return $toc . '</div>';
}
public function format_exception_chunk($open, $name, $attrs, $props) {
return $this->format_container_chunk_below($open, "reference", $attrs, $props);
}
public function format_section_chunk($open, $name, $attrs, $props) {
if ($open) {
if (!isset($attrs[Reader::XMLNS_XML]["id"])) {
$this->isSectionChunk[] = false;
return $this->transformFromMap($open, "div", $name, $attrs, $props);
}
$this->isSectionChunk[] = true;
return $this->format_chunk($open, $name, $attrs, $props);
}
if (array_pop($this->isSectionChunk)) {
return $this->format_chunk($open, $name, $attrs, $props);
}
return $this->transformFromMap($open, "div", $name, $attrs, $props);
}
public function format_chunk($open, $name, $attrs, $props) {
if ($open) {
$this->cchunk = $this->dchunk;
if(isset($attrs[Reader::XMLNS_XML]["id"])) {
$id = $attrs[Reader::XMLNS_XML]["id"];
} else {
$id = uniqid("phd");
}
$class = $name;
if ($name === "refentry") {
//$class .= " -rel-posting";
}
$this->CURRENT_CHUNK = $id;
$this->CURRENT_LANG = $props["lang"];
$this->notify(Render::CHUNK, Render::OPEN);
return '<div id="' .$id. '" class="' .$class. '">';
}
$this->notify(Render::CHUNK, Render::CLOSE);
$str = "";
foreach ($this->cchunk["footnote"] as $k => $note) {
$str .= '<div class="footnote">';
$str .= '<a name="fnid' .$note["id"]. '" href="#fn' .$note["id"]. '"><sup>[' .($k + 1). ']</sup></a>';
$str .= $note["str"];
$str .= "</div>\n";
}
$this->cchunk = $this->dchunk;
return $str. "</div>";
}
public function format_refsect1_para($open, $name, $attrs, $props) {
if ($open) {
switch ($props["sibling"]) {
case "methodsynopsis":
case "constructorsynopsis":
case "destructorsynopsis":
return '<p class="'.$name.' rdfs-comment">';
break;
default:
return '<p class="'.$name.'">';
}
}
return '</p>';
}
public function format_refsect($open, $name, $attrs) {
static $role = 0;
if ($open) {
if(!isset($attrs[Reader::XMLNS_DOCBOOK]["role"])) {
$attrs[Reader::XMLNS_DOCBOOK]["role"] = "unknown-" . ++$role;
}
$this->pushRole($attrs[Reader::XMLNS_DOCBOOK]["role"]);
if (isset($attrs[Reader::XMLNS_XML]["id"])) {
$id = $attrs[Reader::XMLNS_XML]["id"];
}
else {
$id = $name. "-" . $this->CURRENT_CHUNK . "-" . $this->getRole();
}
return '<div class="' .$name.' ' .$this->getRole(). '" id="' . $id . '">';
}
$this->popRole();
return "</div>\n";
}
/** Legacy rendering functions for class synopsis tags that wraps the definition in a class synopsis info tag */
public function format_classsynopsisinfo_oointerface($open, $name, $attrs) {
if ($open) {
if ($this->cchunk["classsynopsisinfo"]["ooclass"] === false) {
return '<span class="' . $name . '">';
}
if ($this->cchunk["classsynopsisinfo"]["implements"] === false) {
$this->cchunk["classsynopsisinfo"]["implements"] = true;
if ($this->cchunk["classsynopsis"]["interface"]) {
return '<span class="'.$name.'"><span class="modifier">extends</span> ';
}
return '<span class="'.$name.'"><span class="modifier">implements</span> ';
}
return '<span class="'.$name.'">, ';
}
return "</span>";
}
public function format_classsynopsisinfo_ooclass_classname($open, $name, $attrs)
{
if ($open) {
if ($this->cchunk["classsynopsisinfo"]["ooclass"] === false) {
$this->cchunk["classsynopsisinfo"]["ooclass"] = true;
if ($this->cchunk["classsynopsis"]["interface"]) {
return '<span class="modifier">interface</span> ';
}
return '<span class="modifier">class</span> ';
}
return ' ';
}
if ($this->cchunk["classsynopsisinfo"]["ooclass"] === true) {
$this->cchunk["classsynopsisinfo"]["ooclass"] = null;
}
return "";
}
public function format_classsynopsisinfo_oointerface_interfacename($open, $name, $attrs)
{
if ($open) {
if ($this->cchunk["classsynopsisinfo"]["ooclass"] === false) {
$this->cchunk["classsynopsisinfo"]["ooclass"] = true;
return '<span class="modifier">interface</span> ';
}
return ' ';
}
if ($this->cchunk["classsynopsisinfo"]["ooclass"] === true) {
$this->cchunk["classsynopsisinfo"]["ooclass"] = null;
}
return "";
}
public function format_classsynopsisinfo($open, $name, $attrs)
{
$this->cchunk["classsynopsisinfo"] = $this->dchunk["classsynopsisinfo"];
if ($open) {
if (isset($attrs[Reader::XMLNS_DOCBOOK]["role"]) && $attrs[Reader::XMLNS_DOCBOOK]["role"] == "comment") {
return '<div class="'.$name.' classsynopsisinfo_comment">/* ';
}
return '<div class="'.$name.'">';
}
if (isset($attrs[Reader::XMLNS_DOCBOOK]["role"]) && $attrs[Reader::XMLNS_DOCBOOK]["role"] == "comment") {
return ' */</div>';
}
assert($this->cchunk["classsynopsis"]["legacy"] === true);
$this->cchunk["classsynopsis"]["close"] = true;
return ' {</div>';
}
/** This method is common between both legacy and new rendering for setting up the classname in the current chunk */
public function format_classsynopsis_ooclass_classname_text($value, $tag) {
/** If this is not defined this is the first ooclass/oointerface/ooexception and thus needs to
* set the class name to be able to remove it from the methods
*/
if (!$this->cchunk["classsynopsis"]["classname"]) {
$this->cchunk["classsynopsis"]["classname"] = $value;
}
// Do not render outside ooclass class name in legacy rendering.
if ($this->cchunk["classsynopsis"]["legacy"]) {
return '';
}
return $this->TEXT($value);
}
/** Class synopsis rendering for new/better markup */
public function format_classsynopsis_oo_name_text($value, $tag) {
/** If this is not defined this is the first ooclass/oointerface/ooexception and thus needs to
* set the class name to be able to remove it from the methods
*/
if (!$this->cchunk["classsynopsis"]["classname"]) {
$this->cchunk["classsynopsis"]["classname"] = $value;
}
return $this->TEXT($value);
}
public function format_classsynopsis_oointerface_interfacename($open, $name, $attrs, $props)
{
if ($this->cchunk["classsynopsis"]["legacy"] === true) {
return $this->transformFromMap($open, 'strong', $name, $attrs, $props);
}
if ($open) {
/* If there has been a class prior this means that we are the first implementing interface
* thus mark the oointerface as already been rendered as the primary tag */
if ($this->cchunk["classsynopsis"]["ooclass"] === true) {
$this->cchunk["classsynopsis"]["oointerface"] = true;
}
/** Actual interface name in bold */
if ($this->cchunk["classsynopsis"]["oointerface"] === false) {
return '<span class="modifier">interface</span> ' . $this->transformFromMap($open, 'strong', $name, $attrs, $props);
}
/* Whitespace for next word */
return ' ';
}
/** Actual interface name in bold */
if ($this->cchunk["classsynopsis"]["oointerface"] === false) {
$this->cchunk["classsynopsis"]["oointerface"] = true;
return '</strong>';
}
/** We don't wrap extended interface in a tag */
if ($this->cchunk["classsynopsis"]['nb_list'] > 1) {
return ',';
}
return '';
}
public function format_classsynopsis_ooclass_classname($open, $name, $attrs, $props)
{
if ($this->cchunk["classsynopsis"]["legacy"] === true) {
return $this->transformFromMap($open, 'strong', $name, $attrs, $props);
}
if ($open) {
/** Actual class name in bold */
if ($this->cchunk["classsynopsis"]["ooclass"] === false) {
/** We force the name: parameter to 'classname' to not break CSS expectations for exceptionanme tags */
return '<span class="modifier">class</span> ' . $this->transformFromMap($open, 'strong', 'classname', $attrs, $props);
}
/* Whitespace for next word */
return ' ';
}
/** Actual class name in bold */
if ($this->cchunk["classsynopsis"]["ooclass"] === false) {
$this->cchunk["classsynopsis"]["ooclass"] = true;
return '</strong>';
}
/** We don't wrap extended class in a tag */
return '';
}
public function format_classsynopsis_generic_oo_tag($open, $name, $attrs, $props)
{
if ($this->cchunk["classsynopsis"]["legacy"] === true) {
return $this->transformFromMap($open, 'span', $name, $attrs, $props);
}
/* Close list of classes + interfaces by "opening" class def with { */
if (!$open) {
if (--$this->cchunk["classsynopsis"]['nb_list'] === 0) {
return ' {</div>';
}
}
return '';
}
public function format_classsynopsis($open, $name, $attrs, $props) {
$this->cchunk["classsynopsis"] = $this->dchunk["classsynopsis"];
/** Legacy presentation does not use the class attribute */
$this->cchunk["classsynopsis"]['legacy'] = !isset($attrs[Reader::XMLNS_DOCBOOK]["class"]);
if ($this->cchunk["classsynopsis"]['legacy']) {
if ($open) {
// Think this just needs to be set on open and it will persist
// Will remove comment after review
if (
isset($attrs[Reader::XMLNS_DOCBOOK]["class"]) &&
$attrs[Reader::XMLNS_DOCBOOK]["class"] == "interface"
) {
$this->cchunk["classsynopsis"]["interface"] = true;
}
return '<div class="'.$name.'">';
}
/* Just always force the ending } to close the class as an opening { should always be present
if ($this->cchunk["classsynopsis"]["close"] === true) {
$this->cchunk["classsynopsis"]["close"] = false;
return "}</div>";
}
return "</div>";
*/
return "}</div>";
}
/* New rendering for more sensible markup:
* We open a fake classsynopsisinfo div to not break the CSS expectations */
if ($open) {
$occurrences = substr_count($props['innerXml'], '</ooclass>')
+ substr_count($props['innerXml'], '</oointerface>')
+ substr_count($props['innerXml'], '</ooexception>');
$this->cchunk["classsynopsis"]['nb_list'] = $occurrences;
return '<div class="classsynopsis"><div class="classsynopsisinfo">';
} else {
return '}</div>';
}
}
public function format_classsynopsis_methodsynopsis_methodname_text($value, $tag) {
$value = $this->TEXT($value);
if ($this->cchunk["classsynopsis"]["classname"] === false) {
return $value;
}
if (strpos($value, '::')) {
$explode = '::';
} elseif (strpos($value, '->')) {
$explode = '->';
} elseif (strpos($value, '-&gt;')) {
$explode = '-&gt;';
} else {
return $value;
}
list($class, $method) = explode($explode, $value);
if ($class !== $this->cchunk["classsynopsis"]["classname"]) {
return $value;
}
return $method;
}
public function format_enumsynopsis($open, $name, $attrs, $props) {
if ($open) {
//return '<div class="enumsynopsis">';
return '<div class="classsynopsis"><div class="classsynopsisinfo">';
} else {
return '}</div>';
}
}
public function format_enumsynopsis_enumname($open, $name, $attrs, $props): string {
if ($open) {
/** Actual enum name in bold */
return '<span class="modifier">enum</span> <strong class="classname">';
//return '<span class="modifier">enum</span> <strong class="enumname">';
}
//return "</strong><br/>{<br/>";
return "</strong><br/>{</div>";
}
public function format_enumitem($open, $name, $attrs, $props) {
if ($open) {
return '<div class="fieldsynopsis">';
}
return '</div>';
}
public function format_enumidentifier($open, $name, $attrs, $props) {
if ($open) {
return ' <span class="modifier">case</span> <span class="classname">';
}
return '</span>';
}
public function format_enumvalue($open, $name, $attrs, $props) {
if ($open) {
return ' = ';
}
return '';
}
public function format_enumitemdescription($open, $name, $attrs, $props) {
if ($open) {
return ';<br/>/** ';
}
return ' */';
}
public function format_emphasis($open, $name, $attrs)
{
$name = "em";
if (isset($attrs[Reader::XMLNS_DOCBOOK]['role'])) {
$role = $attrs[Reader::XMLNS_DOCBOOK]['role'];
if ( $role == "strong" || $role == "bold" ) {
$name = "strong";
}
}
if ($open) {
return "<{$name}>";
} else {
return "</{$name}>";
}
}
public function format_fieldsynopsis($open, $name, $attrs) {
$this->cchunk["fieldsynopsis"] = $this->dchunk["fieldsynopsis"];
if ($open) {
if (isset($attrs[Reader::XMLNS_DOCBOOK]["role"])) {
$this->pushRole($attrs[Reader::XMLNS_DOCBOOK]["role"]);
}
return '<div class="'.$name.'">';
}
if (isset($attrs[Reader::XMLNS_DOCBOOK]["role"])) {
$this->popRole();
}
return ";</div>\n";
}
public function format_fieldsynopsis_modifier_text($value, $tag) {
if ($this->getRole() === "attribute") {
$attribute = trim(strtolower($value), "#[]\\");
$href = Format::getFilename("class.$attribute");
if ($href) {
return '<a href="' . $href . $this->getExt() . '">' .$value. '</a> ';
}
return false;
}
$this->cchunk["fieldsynopsis"]["modifier"] = trim($value);
return $this->TEXT($value);
}
public function format_modifier($open, $name, $attrs, $props) {
if ($open) {
if (isset($attrs[Reader::XMLNS_DOCBOOK]["role"])) {
$this->pushRole($attrs[Reader::XMLNS_DOCBOOK]["role"]);
return '<span class="' . $this->getRole() . '">';
}
return '<span class="modifier">';
}
if (isset($attrs[Reader::XMLNS_DOCBOOK]["role"])) {
if ($attrs[Reader::XMLNS_DOCBOOK]["role"] === "attribute") {
return '</span><br>';
}
$this->popRole();
}
return '</span>';
}
public function format_methodparam_modifier($open, $name, $attrs, $props) {
if ($open) {
if (isset($attrs[Reader::XMLNS_DOCBOOK]["role"])) {
$this->pushRole($attrs[Reader::XMLNS_DOCBOOK]["role"]);
return '<span class="' . $this->getRole() . '">';
}
return '<span class="modifier">';
}
if (isset($attrs[Reader::XMLNS_DOCBOOK]["role"])) {
$this->popRole();
}
return '</span>';
}
public function format_modifier_text($value, $tag) {
if ($this->getRole() === "attribute") {
$attribute = trim(strtolower($value), "#[]\\");
$href = Format::getFilename("class.$attribute");
if ($href) {
return '<a href="' . $href . $this->getExt() . '">' .$value. '</a> ';
}
}
return false;
}
public function format_methodsynopsis($open, $name, $attrs, $props) {
if ($open) {
$this->params = array(
"count" => 0,
"opt" => false,
"init" => false,
"content" => "",
"ellipsis" => '',
"paramCount" => substr_count($props["innerXml"], "<methodparam")
);
$id = (isset($attrs[Reader::XMLNS_XML]["id"]) ? ' id="'.$attrs[Reader::XMLNS_XML]["id"].'"' : '');
return '<div class="'.$name.' dc-description"'.$id.'>';
}
$content = "";
if ($this->params["paramCount"] > 3) {
$content .= "<br>";
}
$content .= ")";
$content .= "</div>\n";
return $content;
}
public function format_methodparam_parameter($open, $name, $attrs, $props)
{
if ($props["empty"]) {
return '';
}
if ($open) {
if (isset($attrs[Reader::XMLNS_DOCBOOK]["role"])) {
return ' <code class="parameter reference">&' . $this->params["ellipsis"] . '$';
}
return ' <code class="parameter">' . $this->params["ellipsis"] . '$';
}
return "</code>";
}
public function format_initializer($open, $name, $attrs) {
if ($open) {
$this->params["init"] = true;
return '<span class="'.$name.'"> = ';
}
return '</span>';
}
public function format_parameter($open, $name, $attrs, $props)
{
if ($props["empty"]) {
return '';
}
if ($open) {
if (isset($attrs[Reader::XMLNS_DOCBOOK]["role"])) {
return '<code class="parameter reference">&';
}
return '<code class="parameter">';
}
return "</code>";
}
public function format_void($open, $name, $attrs, $props) {
if (isset($props['sibling']) && $props['sibling'] == 'methodname') {
return '(';
} else {
return '<span class="type"><a href="language.types.void' . $this->getExt() . '" class="type void">void</a></span>';
}
}
public function format_methodparam($open, $name, $attrs) {
if ($open) {
$content = '';
if ($this->params["count"] === 0) {
$content .= "(";
if ($this->params["paramCount"] > 3) {
$content .= "<br>&nbsp;&nbsp;&nbsp;&nbsp;";
}
}
if (isset($attrs[Reader::XMLNS_DOCBOOK]["choice"]) && $attrs[Reader::XMLNS_DOCBOOK]["choice"] == "opt") {
$this->params["opt"] = true;
} else {
$this->params["opt"] = false;
}
if ($this->params["count"]) {
$content .= ",";
if ($this->params["paramCount"] > 3) {
$content .= "<br>&nbsp;&nbsp;&nbsp;&nbsp;";
} else {
$content .= " ";
}
}
$content .= '<span class="methodparam">';
++$this->params["count"];
if (isset($attrs[Reader::XMLNS_DOCBOOK]["rep"]) && $attrs[Reader::XMLNS_DOCBOOK]["rep"] == "repeat") {
$this->params["ellipsis"] = '...';
} else {
$this->params["ellipsis"] = '';
}
return $content;
}
if ($this->params["opt"] && !$this->params["init"]) {
return '<span class="initializer"> = ?</span></span>';
}
$this->params["init"] = false;
return "</span>";
}
public function format_methodname($open, $name, $attr) {
if ($open) {
return ' <span class="' .$name. '">';
}
return '</span>';
}
public function format_varname($open, $name, $attrs) {
if ($open) {
return '<var class="'.$name.'">$';
}
return "</var>";
}
public function format_fieldsynopsis_varname($open, $name, $attrs) {
if ($open) {
if ($this->cchunk["fieldsynopsis"]["modifier"] === "const") {
return '<var class="fieldsynopsis_varname">';
}
return '<var class="'.$name.'">$';
}
return '</var>';
}
public function format_footnoteref($open, $name, $attrs, $props) {
if ($open) {
$linkend = $attrs[Reader::XMLNS_DOCBOOK]["linkend"];
foreach($this->cchunk["footnote"] as $k => $note) {
if ($note["id"] === $linkend) {
return '<a href="#fnid' .$note["id"]. '"><sup>[' .($k + 1). ']</sup></a>';
}
}
trigger_error("footnoteref ID '$linkend' not found", E_USER_WARNING);
return "";
}
}
public function format_footnote($open, $name, $attrs, $props) {
if ($open) {
$count = count($this->cchunk["footnote"]);
$noteid = isset($attrs[Reader::XMLNS_XML]["id"]) ? $attrs[Reader::XMLNS_XML]["id"] : $count + 1;
$note = array("id" => $noteid, "str" => "");
$this->cchunk["footnote"][$count] = $note;
if ($this->cchunk["table"]) {
$this->cchunk["tablefootnotes"][$count] = $noteid;
}
return '<a href="#fnid' .$noteid. '" name="fn'.$noteid.'"><sup>[' .($count + 1). ']</sup></a>';
}
return "";
}
/* {{{ FIXME: These are crazy workarounds :( */
public function format_footnote_constant($open, $name, $attrs, $props) {
$k = count($this->cchunk["footnote"]) - 1;
$this->cchunk["footnote"][$k]["str"] .= self::format_constant($open, $name, $attrs, $props);
return "";
}
public function format_footnote_constant_text($value, $tag) {
$k = count($this->cchunk["footnote"]) - 1;
$this->cchunk["footnote"][$k]["str"] .= $value;
return "";
}
public function format_footnote_para($open, $name, $attrs, $props) {
$k = count($this->cchunk["footnote"]) - 1;
if ($open) {
$this->cchunk["footnote"][$k]["str"] .= '<span class="para footnote">';
return "";
}
$this->cchunk["footnote"][$k]["str"] .= "</span>";
return "";
}
public function format_footnote_para_text($value, $tag) {
$k = count($this->cchunk["footnote"]) - 1;
$this->cchunk["footnote"][$k]["str"] .= $value;
return "";
}
/* }}} */
public function format_co($open, $name, $attrs, $props) {
if (($open || $props["empty"]) && isset($attrs[Reader::XMLNS_XML]["id"])) {
$co = ++$this->cchunk["co"];
return '<a name="'.$attrs[Reader::XMLNS_XML]["id"].'" id="'.$attrs[Reader::XMLNS_XML]["id"].'">' .str_repeat("*", $co) .'</a>';
}
/* Suppress closing tag if any */
return "";
}
public function format_calloutlist($open, $name, $attrs) {
if ($open) {
$this->cchunk["callouts"] = 0;
return '<table>';
}
return '</table>';
}
public function format_callout($open, $name, $attrs) {
if ($open) {
return '<tr><td><a href="#'.$attrs[Reader::XMLNS_DOCBOOK]["arearefs"].'">' .str_repeat("*", ++$this->cchunk["callouts"]). '</a></td><td>';
}
return "</td></tr>\n";
}
public function format_manvolnum($open, $name, $attrs) {
if ($open) {
return '<span class="'.$name.'">(';
}
return ")</span>";
}
public function format_segmentedlist($open, $name, $attrs) {
$this->cchunk["segmentedlist"] = $this->dchunk["segmentedlist"];
if ($open) {
return '<table class="'.$name.'">';
}
return '</tbody></table>';
}
public function format_segtitle_text($value, $tag) {
$html = '';
if (!$this->cchunk["segmentedlist"]["segtitleopened"]) {
$html .= '<thead><tr>';
}
$html .= '<th>'.$this->TEXT($value).'</th>';
$this->cchunk["segmentedlist"]["segtitleopened"] = true;
// Don't close the row; we'll have to do that in the first seglistitem.
return $html;
}
public function format_seglistitem($open, $name, $attrs) {
if ($open) {
$html = '';
if (!$this->cchunk["segmentedlist"]["segtitleclosed"]) {
$html .= '</tr></thead><tbody>';
$this->cchunk["segmentedlist"]["segtitleclosed"] = true;
}
$html .= '<tr class="'.$name.'">';
return $html;
}
return '</tr>';
}
public function format_seg($open, $name, $attrs) {
if ($open) {
return '<td class="seg">';
}
return '</td>';
}
public function format_procedure($open, $name, $attrs) {
$this->cchunk["procedure"] = false;
if ($open) {
return '<div class="'.$name.'">';
}
return '</ol></div>';
}
public function format_step($open, $name, $attrs) {
if ($open) {
$ret = "";
if ($this->cchunk["procedure"] === false) {
$this->cchunk["procedure"] = true;
$ret = '<ol type="1">';
}
return $ret . "<li>";
}
return '</li>';
}
public function format_variablelist($open, $name, $attrs, $props) {
if ($open) {
if (isset($attrs[Reader::XMLNS_DOCBOOK]["role"])) {
$this->pushRole($attrs[Reader::XMLNS_DOCBOOK]["role"]);
}
$classStr = $headerStr = $idStr = '';
if (isset($attrs[Reader::XMLNS_XML]["id"])) {
$idStr = ' id="' . $attrs[Reader::XMLNS_XML]["id"] . '"';
}
if ($this->getRole() === 'constant_list') {
$tagName = 'table';
$classStr = ' class="doctable table"';
$headerStr = "\n" . $this->indent($props["depth"] + 1) . "<tr>\n"
. $this->indent($props["depth"] + 2) . "<th>"
. $this->autogen('Constants', $props['lang']) . "</th>\n"
. $this->indent($props["depth"] + 2) . "<th>"
. $this->autogen('Description', $props['lang']) . "</th>\n"
. $this->indent($props["depth"] + 1) . "</tr>";
} else {
$tagName = 'dl';
}
return '<' . $tagName . $idStr . $classStr . '>' . $headerStr;
}
$tagName = ($this->getRole() === 'constant_list') ? 'table' : 'dl';
$this->popRole();
return "</" . $tagName . ">";
}
public function format_varlistentry($open, $name, $attrs) {
if ($open) {
if (isset($attrs[Reader::XMLNS_XML]["id"])) {
$this->cchunk['varlistentry']['id'] = $attrs[Reader::XMLNS_XML]["id"];
} else {
unset($this->cchunk['varlistentry']['id']);
}
return ($this->getRole() === 'constant_list') ? '<tr>' : '';
}
return ($this->getRole() === 'constant_list') ? '</tr>' : '';
}
public function format_varlistentry_term($open, $name, $attrs, $props) {
$tagName = ($this->getRole() === 'constant_list') ? 'td' : 'dt';
if ($open) {
if (isset($this->cchunk['varlistentry']['id'])) {
$id = $this->cchunk['varlistentry']['id'];
unset($this->cchunk['varlistentry']['id']);
return '<' . $tagName . ' id="' . $id . '">';
} else {
return "<" . $tagName . ">";
}
}
return "</" . $tagName . ">";
}
public function format_varlistentry_listitem($open, $name, $attrs) {
$tagName = ($this->getRole() === 'constant_list') ? 'td' : 'dd';
if ($open) {
return "<" . $tagName . ">";
}
return "</" . $tagName . ">";
}
public function format_term($open, $name, $attrs, $props) {
if ($open) {
if ($props["sibling"] == $name) {
return '<br /><span class="' .$name. '">';
}
return '<span class="' .$name. '">';
}
return "</span>";
}
public function format_trademark($open, $name, $attrs, $props) {
if ($open) {
return '<span class=' .$name. '">';
}
return '®</span>';
}
public function format_userinput($open, $name, $attrs) {
if ($open) {
return '<strong class="' .$name. '"><code>';
}
return "</code></strong>";
}
public function format_systemitem($open, $name, $attrs) {
if ($open) {
$val = isset($attrs[Reader::XMLNS_DOCBOOK]["role"]) ? $attrs[Reader::XMLNS_DOCBOOK]["role"] : null;
switch($val) {
case "directive":
/* FIXME: Different roles should probably be handled differently */
default:
return '<code class="systemitem ' .$name. '">';
}
}
return "</code>";
}
public function format_example_content($open, $name, $attrs) {
if ($open) {
return '<div class="example-contents"><p>';
}
return "</p></div>";
}
public function format_programlisting($open, $name, $attrs) {
if ($open) {
$this->pushRole($attrs[Reader::XMLNS_DOCBOOK]["role"] ?? null);
return '<div class="example-contents">';
}
$this->popRole();
return "</div>\n";
}
public function format_programlisting_text($value, $tag) {
return $this->CDATA($value);
}
public function format_screen($open, $name, $attrs) {
if ($open) {
if ($this->getRole() !== "examples"
&& $this->getRole() !== "description"
&& $this->getRole() !== "notes"
&& $this->getRole() !== "returnvalues"
&& $this->getRole() !== "parameters") {
$this->pushRole('');
}
return '<div class="example-contents ' .$name. '">';
}
if ($this->getRole() !== "examples"
&& $this->getRole() !== "description"
&& $this->getRole() !== "notes"
&& $this->getRole() !== "returnvalues"
&& $this->getRole() !== "parameters") {
$this->popRole();
}
return '</div>';
}
public function format_constant($open, $name, $attrs, $props)
{
if ($open) {
if (str_contains($props["innerXml"], '<replaceable')) {
$this->pushRole("constant_group");
$this->cchunk["constant"] = $props["innerXml"];
}
return "<strong><code>";
}
if ($this->getRole() === "constant_group") {
$this->popRole();
$value = str_replace(
["<replaceable xmlns=\"http://docbook.org/ns/docbook\">", "</replaceable>"],
["<span class=\"replaceable\">", "</span>"],
strip_tags($this->cchunk["constant"], "<replaceable>")
);
$link = $this->createReplaceableConstantLink(strip_tags($this->cchunk["constant"], "<replaceable>"));
$this->cchunk["constant"] = "";
if ($link === "") {
return $value . '</code></strong>';
}
return '<a href="' . $link . '">' . $value . '</a></code></strong>';
}
return "</code></strong>";
}
/**
* Creates a link to the first constant in the index
* that matches the pattern of a constant containing a <replaceable> tag
* or returns an empty string if no match was found.
*
* This works only with one set of <replaceable> tags in a constant
* e.g. CURLE_<replaceable>*</replaceable> or DOM_<replaceable>*</replaceable>_NODE
*/
private function createReplaceableConstantLink(string $constant): string {
$pattern = "/" . preg_replace(
"/<replaceable.*<\/replaceable>/",
".*",
str_replace(
".",
"\.",
$this->convertConstantNameToId($constant)
)
) ."/";
$matchingConstantId = "";
foreach ($this->indexes as $index) {
if (preg_match($pattern, $index["docbook_id"])) {
$matchingConstantId = $index["docbook_id"];
break;
}
}
return $matchingConstantId === "" ? "" : $this->createLink($matchingConstantId);
}
private function convertConstantNameToId(string $constantName): string {
$tempLinkValue = str_replace(
array("\\", "_"),
array("-", "-"),
strtolower(trim($constantName, "_"))
);
if (str_contains($constantName, '::')) {
// class constant
list($extensionAndClass, $constant) = explode("::", $tempLinkValue);
$normalizedLinkFormat = $extensionAndClass . ".constants." . trim($constant, "-");
} else {
$normalizedLinkFormat = 'constant.' . $tempLinkValue;
}
return $normalizedLinkFormat;
}
public function format_constant_text($value, $tag) {
if ($this->getRole() === "constant_group") {
return "";
}
$normalizedLinkFormat = $this->convertConstantNameToId($value);
$link = $this->createLink($normalizedLinkFormat);
if ($link === null) {
return $value;
}
return '<a href="' . $link . '">' . $value . '</a>';
}
public function format_replaceable($open, $name, $attrs, $props) {
if ($this->getRole() === "constant_group") {
return "";
}
return false;
}
public function format_property_text($value, $tag) {
if (! str_contains($value, '::')) {
return $value;
}
$tempLinkValue = str_replace(
["\\", "_", "$"],
["-", "-", ""],
strtolower(trim($value, "_"))
);
list($extensionAndClass, $property) = explode("::", $tempLinkValue);
$normalizedLinkFormat = $extensionAndClass . ".props." . trim($property, "-");
$link = $this->createLink($normalizedLinkFormat);
if ($link === null || $link === "") {
return $value;
}
return '<a href="' . $link . '">' . $value . '</a>';
}
public function admonition_title($title, $lang)
{
return '<strong class="' .(strtolower($title)). '">' .($this->autogen($title, $lang)). '</strong>';
}
public function format_admonition($open, $name, $attrs, $props) {
if ($open) {
return '<div class="'. $name. '">' .$this->admonition_title($name, $props["lang"]);
}
return "</div>";
}
public function format_authorgroup_author($open, $name, $attrs, $props) {
if ($open) {
if ($props["sibling"] !== $name) {
return '<div class="'.$name.' vcard">' .$this->admonition_title("by", $props["lang"]). ':<br />';
}
return '<div class="'.$name.' vcard">';
}
return "</div>\n";
}
public function format_editor($open, $name, $attrs, $props) {
if ($open) {
return '<div class="editor vcard">' .$this->admonition_title("editedby", $props["lang"]). ': ';
}
return "</div>\n";
}
public function format_note($open, $name, $attrs, $props) {
if ($open) {
return '<blockquote class="note"><p>'.$this->admonition_title("note", $props["lang"]). ': ';
}
return "</p></blockquote>";
}
public function format_note_title($open, $name, $attrs)
{
if ($open) {
return '<strong>';
}
return '</strong><br />';
}
public function format_example($open, $name, $attrs, $props) {
if ($open) {
++$this->exampleCounter;
if (isset($props["id"])) {
return '<div class="' . $name . '" id="' . $props["id"] . '">';
}
return '<div class="' . $name . '" id="' . $this->getGeneratedExampleId($this->exampleCounter - 1) . '">';
}
return '</div>';
}
public function format_example_title($open, $name, $attrs, $props)
{
if ($props["empty"]) {
return "";
}
if ($open) {
return "<p><strong>" . ($this->autogen('example', $props['lang'])
. (isset($this->cchunk["examples"]) ? ++$this->cchunk["examples"] : "")) . " ";
}
return "</strong></p>";
}
public function format_table_title($open, $name, $attrs, $props)
{
if ($props["empty"]) {
return "";
}
if ($open) {
return "<caption><strong>";
}
return "</strong></caption>";
}
public function format_variablelist_title($open, $name, $attrs, $props) {
if ($open) {
return ($this->getRole() === 'constant_list') ? "<caption><strong>" : "<strong>";
}
return ($this->getRole() === 'constant_list') ? "</strong></caption>" : "</strong>";
}
public function format_mediaobject($open, $name, $attrs) {
$this->cchunk["mediaobject"] = $this->dchunk["mediaobject"];
if ($open) {
return '<div class="'.$name.'">';
}
return '</div>';
}
public function format_alt_text($value, $tag) {
$this->cchunk["mediaobject"]["alt"] = $value;
}
public function format_imagedata($open, $name, $attrs) {
$file = $attrs[Reader::XMLNS_DOCBOOK]["fileref"];
if ($newpath = $this->mediamanager->handleFile($file)) {
$curfile = $this->mediamanager->findFile($file);
$width = isset($attrs[Reader::XMLNS_DOCBOOK]["width"]) ? 'width="' . $attrs[Reader::XMLNS_DOCBOOK]["width"] . '"' : '';
$height = isset($attrs[Reader::XMLNS_DOCBOOK]["depth"]) ? 'height="' . $attrs[Reader::XMLNS_DOCBOOK]["depth"] . '"' : '';
$alt = 'alt="' . ($this->cchunk["mediaobject"]["alt"] !== false ? $this->cchunk["mediaobject"]["alt"] : basename($file)) . '"';
// Generate height and width when none are supplied.
if ($curfile && '' === $width . $height) {
list(,,,$dimensions,,,,) = getimagesize($curfile);
} else {
$dimensions = $width . ' ' . $height;
}
// Generate warnings when only 1 dimension supplied or alt is not supplied.
if (!$width xor !$height) {
$this->outputHandler->v('Missing %s attribute for %s', (!$width ? 'width' : 'height'), $file, VERBOSE_MISSING_ATTRIBUTES);
}
if (false === $this->cchunk["mediaobject"]["alt"]) {
$this->outputHandler->v('Missing alt attribute for %s', $file, VERBOSE_MISSING_ATTRIBUTES);
}
return '<img src="' . $newpath . '" ' . $alt . ' ' . $dimensions . ' />';
} else {
return '';
}
}
public function format_table($open, $name, $attrs, $props) {
if ($open) {
$this->cchunk["table"] = true;
// Initialize an empty tgroup in case we never process such element
Format::tgroup(array());
$idstr = '';
if (isset($attrs[Reader::XMLNS_XML]["id"])) {
$idstr = ' id="' . $attrs[Reader::XMLNS_XML]["id"] . '"';
}
return '<table' . $idstr . ' class="doctable ' .$name. '">';
}
$this->cchunk["table"] = false;
$str = "";
if (isset($this->cchunk["tablefootnotes"]) && $this->cchunk["tablefootnotes"]) {
$opts = array(Reader::XMLNS_DOCBOOK => array());
$str = $this->format_tbody(true, "footnote", $opts, $props);
$str .= $this->format_row(true, "footnote", $opts, $props);
$str .= $this->format_entry(true, "footnote", $opts, $props+array("colspan" => $this->getColCount()));
foreach ($this->cchunk["tablefootnotes"] as $k => $noteid) {
$str .= '<div class="footnote">';
$str .= '<a name="fnid' .$noteid. '" href="#fn' .$noteid .'"><sup>[' .($k + 1). ']</sup></a>' .$this->cchunk["footnote"][$k]["str"];
unset($this->cchunk["footnote"][$k]);
$str .= "</div>\n";
}
$str .= $this->format_entry(false, "footnote", $opts, $props);
$str .= $this->format_row(false, "footnote", $opts, $props);
$str .= $this->format_tbody(false, "footnote", $opts, $props);
$this->cchunk["tablefootnotes"] = $this->dchunk["tablefootnotes"];
}
return "$str</table>\n";
}
public function format_tgroup($open, $name, $attrs) {
if ($open) {
Format::tgroup($attrs[Reader::XMLNS_DOCBOOK]);
return '';
}
return '';
}
private static function parse_table_entry_attributes($attrs)
{
$style = array();
$retval = '';
if (!empty($attrs['align'])) {
if ('char' != $attrs['align']) {
$style[] = 'text-align: ' . $attrs['align'];
} elseif (isset($attrs['char'])) {
// There's no analogue in CSS, but as this stuff isn't supported
// in any browser, it is unlikely to appear in DocBook anyway
$retval .= ' align="char" char="'
. htmlspecialchars($attrs["char"], ENT_QUOTES) . '"';
if (isset($attrs['charoff'])) {
$retval .= ' charoff="'
. htmlspecialchars($attrs["charoff"], ENT_QUOTES) . '"';
}
}
}
if (isset($attrs["valign"])) {
$style[] = 'vertical-align: ' . $attrs["valign"];
}
if (isset($attrs["colwidth"])) {
if (preg_match('/^\\d+\\*$/', $attrs['colwidth'])) {
// relative_length measure has no analogue in CSS and is
// unsupported in browsers, leave as is
$retval .= ' width="' . $attrs['colwidth'] . '"';
} else {
// probably fixed width, use inline styles
$style[] = 'width: ' . $attrs['colwidth'];
}
}
return $retval . (empty($style) ? '' : ' style="' . implode('; ', $style) . ';"');
}
public function format_colspec($open, $name, $attrs)
{
if ($open) {
$str = self::parse_table_entry_attributes(Format::colspec($attrs[Reader::XMLNS_DOCBOOK]));
return '<col' . $str . ' />';
}
/* noop */
}
public function format_th($open, $name, $attrs)
{
if ($open) {
if (isset($attrs[Reader::XMLNS_DOCBOOK]['valign'])) {
return '<' . $name . ' style="vertical-align: '
. $attrs[Reader::XMLNS_DOCBOOK]['valign'] . ';">';
} else {
return '<' . $name . '>';
}
}
return "</$name>\n";
}
public function format_tbody($open, $name, $attrs)
{
if ($open) {
if (isset($attrs[Reader::XMLNS_DOCBOOK]['valign'])) {
return '<tbody class="' . $name . '" style="vertical-align: '
. $attrs[Reader::XMLNS_DOCBOOK]['valign'] . ';">';
} else {
return '<tbody class="' . $name . '">';
}
}
return "</tbody>";
}
public function format_row($open, $name, $attrs)
{
if ($open) {
$idstr = '';
if (isset($attrs[Reader::XMLNS_XML]['id'])) {
$idstr = ' id="'. $attrs[Reader::XMLNS_XML]['id']. '"';
}
Format::initRow();
if (isset($attrs[Reader::XMLNS_DOCBOOK]['valign'])) {
return '<tr' . $idstr . ' style="vertical-align: '
. $attrs[Reader::XMLNS_DOCBOOK]['valign'] . ';">';
} else {
return '<tr' . $idstr . '>';
}
}
return "</tr>\n";
}
public function format_th_entry($open, $name, $attrs, $props) {
if ($props["empty"]) {
return '<th class="empty">&nbsp;</th>';
}
if ($open) {
$colspan = Format::colspan($attrs[Reader::XMLNS_DOCBOOK]);
if ($colspan == 1) {
return '<th>';
} else {
return '<th colspan="' .((int)$colspan). '">';
}
}
return '</th>';
}
public function format_entry($open, $name, $attrs, $props) {
if ($props["empty"]) {
return '<td class="empty">&nbsp;</td>';
}
if ($open) {
$dbattrs = (array)Format::getColspec($attrs[Reader::XMLNS_DOCBOOK]);
$retval = "";
if (isset($dbattrs["colname"])) {
for($i=Format::getEntryOffset($dbattrs); $i>0; --$i) {
$retval .= '<td class="empty">&nbsp;</td>';
}
}
/*
* "colspan" is *not* an standard prop, only used to overwrite the
* colspan for <footnote>s in tables
*/
if (isset($props["colspan"])) {
$colspan = $props["colspan"];
} else {
$colspan = Format::colspan($dbattrs);
}
$rowspan = Format::rowspan($dbattrs);
$moreattrs = self::parse_table_entry_attributes($dbattrs);
$sColspan = $colspan == 1 ? '' : ' colspan="' .((int)$colspan) . '"';
$sRowspan = $rowspan == 1 ? '' : ' rowspan="' .((int)$rowspan). '"';
return $retval. '<td' . $sColspan . $sRowspan . $moreattrs. '>';
}
return "</td>";
}
public function format_qandaset($open, $name, $attrs, $props) {
if ($open) {
$xml = "<qandaset>" . $props["innerXml"] . "</qandaset>";
$doc = new \DOMDocument;
$doc->loadXml($xml);
$xp = new \DOMXPath($doc);
$xp->registerNamespace("db", Reader::XMLNS_DOCBOOK);
$questions = $xp->query("//db:qandaentry/db:question");
$retval = '<div class="qandaset"><ol class="qandaset_questions">';
foreach($questions as $node) {
$id = $xp->evaluate("ancestor::db:qandaentry", $node)->item(0)->getAttributeNs(Reader::XMLNS_XML, "id");
/* FIXME: No ID? How can we create an anchor for it then? */
if (!$id) {
$id = uniqid("phd");
}
$retval .= '<li><a href="#'.$id.'">'.htmlentities($node->textContent, ENT_QUOTES, "UTF-8").'</a></li>';
}
$retval .= "</ol></div>";
return $retval;
}
}
public function format_question($open, $name, $attrs, $props) {
if ($open) {
return '<dt><strong>';
}
return '</strong></dt>';
}
public function format_phd_question($open, $name, $attrs, $props) {
if ($open) {
$href = $this->createLink($attrs[Reader::XMLNS_XML]["id"]);
return '<li><a href="' .$href. '">';
}
return '</a></li>';
}
public function format_citation($open, $name, $attrs, $props) {
if ($open) {
return '[<span class="citation">';
}
return '</span>]';
}
public function format_email_text($value) {
return '&lt;<a href="mailto:' . $value . '">' . $value . '</a>&gt;';
}
public function format_bold_paragraph($open, $name, $attrs, $props)
{
if ($props["empty"]) {
return "";
}
if ($open) {
return "<p><strong>";
}
return "</strong></p>";
}
/**
* Functions from the old XHTMLPhDFormat
*/
public function format_legalnotice_chunk($open, $name, $attrs) {
if ($open) {
return '<div id="legalnotice">';
}
return "</div>\n";
}
public function format_div($open, $name, $attrs, $props) {
if ($open) {
return '<div class="' . $name . '">';
}
return '</div>';
}
public function format_screen_text($value, $tag) {
return nl2br($this->TEXT($value));
}
/**
* Renders a <tag class=""> tag.
*
* @return string HTML code
*/
public function format_tag($open, $name, $attrs, $props) {
static $arFixes = array(
'attribute' => array('', ''),
'attvalue' => array('"', '"'),
'comment' => array('&lt;!--', '--&gt;'),
'element' => array('', ''),
'emptytag' => array('&lt;', '/&gt;'),
'endtag' => array('&lt;/', '&gt;'),
'genentity' => array('&amp;', ';'),
'localname' => array('', ''),
'namespace' => array('', ''),
'numcharref' => array('&amp;#', ';'),
'paramentity' => array('%', ';'),
'pi' => array('&lt;?', '?&gt;'),
'prefix' => array('', ''),
'starttag' => array('&lt;', '&gt;'),
'xmlpi' => array('&lt;?', '?&gt;'),
);
if ($props['empty']) {
return '';
}
$class = 'starttag';
if (isset($attrs['class'])) {
$class = $attrs['class'];
}
if (!isset($arFixes[$class])) {
trigger_error('Unknown tag class "' . $class . '"', E_USER_WARNING);
$class = 'starttag';
}
if (!$open) {
return $arFixes[$class][1] . '</code>';
}
return '<code>' . $arFixes[$class][0];
}
public function format_dl($open, $name, $attrs, $props) {
if ($open) {
return '<dl class="' . $name . '">';
}
return '</dl>';
}
public function format_itemizedlist($open, $name, $attrs, $props) {
if ($open) {
return '<ul class="' . $name . '">';
}
return '</ul>';
}
public function format_simplelist($open, $name, $attrs, $props) {
if ($open) {
$this->cchunk["simplelist"]["type"] = $attrs[Reader::XMLNS_DOCBOOK]["type"] ?? "";
$this->cchunk["simplelist"]["columns"] = $attrs[Reader::XMLNS_DOCBOOK]["columns"] ?? 1;
if ($this->cchunk["simplelist"]["columns"] < 1) {
$this->cchunk["simplelist"]["columns"] = 1;
}
if ($this->cchunk["simplelist"]["type"] === "inline") {
return '<span class="' . $name . '">';
}
if ($this->cchunk["simplelist"]["type"] === "vert"
|| $this->cchunk["simplelist"]["type"] === "horiz") {
return '<table class="' . $name . '">' . "\n" . $this->indent($props["depth"] + 1) . "<tbody>\n";
}
return '<ul class="' . $name . '">';
}
$list = match ($this->cchunk["simplelist"]["type"]) {
"inline" => $this->simplelist_format_inline(),
"horiz" => $this->simplelist_format_horizontal($props["depth"])
. $this->indent($props["depth"] + 1) . "</tbody>\n"
. $this->indent($props["depth"]) . "</table>",
"vert" => $this->simplelist_format_vertical($props["depth"])
. $this->indent($props["depth"] + 1) . "</tbody>\n"
. $this->indent($props["depth"]) . "</table>",
default => "</ul>",
};
$this->cchunk["simplelist"] = $this->dchunk["simplelist"];
return $list;
}
private function indent($depth): string {
return $depth > 0 ? str_repeat(' ', $depth) : '';
}
private function simplelist_format_inline() {
return implode(", ", $this->cchunk["simplelist"]["members"]) . '</span>';
}
private function simplelist_format_horizontal($depth) {
return $this->chunkReduceTable(
$this->processTabular(
$this->cchunk["simplelist"]["members"],
$this->cchunk["simplelist"]["columns"],
$depth),
$this->cchunk["simplelist"]["columns"],
$depth
);
}
/** Return formatted rows */
private function chunkReduceTable(array $members, int $cols, int $depth): string
{
$trPadding = $this->indent($depth + 2);
return array_reduce(
array_chunk(
$members,
$cols,
),
fn (string $carry, array $entry) => $carry . $trPadding . "<tr>\n" . implode('', $entry) . $trPadding . "</tr>\n",
''
);
}
/** Pads $members so that number of members = columns x rows */
private function processTabular(array $members, int $cols, int $depth): array
{
$tdPadding = $this->indent($depth + 3);
return array_map(
fn (string $member) => $tdPadding . "<td>$member</td>\n",
/** The padding is done by getting the additive modular inverse which is
* ``-\count($members) % $cols`` but because PHP gives us the mod in negative we need to
* add $cols back to get the positive
*/
[...$members, ...array_fill(0, (-\count($members) % $cols) + $cols, '')]
);
}
private function simplelist_format_vertical($depth) {
$members = $this->processTabular(
$this->cchunk["simplelist"]["members"],
$this->cchunk["simplelist"]["columns"],
$depth
);
// Sort elements so that we get each correct element for the rows to display vertically
uksort(
$members,
fn (int $l, int $r) => $l % $this->cchunk["simplelist"]["columns"] <=> $r % $this->cchunk["simplelist"]["columns"]
);
return $this->chunkReduceTable($members, $this->cchunk["simplelist"]["columns"], $depth);
}
public function format_member($open, $name, $attrs, $props) {
if ($this->cchunk["simplelist"]["type"] === "inline"
|| $this->cchunk["simplelist"]["type"] === "vert"
|| $this->cchunk["simplelist"]["type"] === "horiz") {
$this->appendToBuffer = $open;
if (! $open) {
$this->cchunk["simplelist"]["members"][] = $this->buffer;
}
$this->buffer = '';
return '';
}
if ($open) {
return '<li>';
}
return '</li>';
}
public function format_member_text($value, $tag) {
return $value;
}
public function format_orderedlist($open, $name, $attrs, $props) {
if ($open) {
$numeration = "1";
if (isset($attrs[Reader::XMLNS_DOCBOOK]["numeration"])) {
switch($attrs[Reader::XMLNS_DOCBOOK]["numeration"]) {
case "upperalpha":
$numeration = "A";
break;
case "loweralpha":
$numeration = "a";
break;
case "upperroman":
$numeration = "I";
break;
case "lowerroman":
$numeration = "i";
break;
}
}
return '<ol type="' .$numeration. '">';
}
return '</ol>';
}
/* Support for key inputs is coded like junk */
public function format_keycap($open, $name, $attrs, $props) {
if ($open) {
$content = '';
if ($props['sibling']) {
$content = '+';
}
return $content . '<kbd class="' . $name . '">';
}
return '</kbd>';
}
public function format_keycombo($open, $name, $attrs, $props) {
if (isset($attrs[Reader::XMLNS_DOCBOOK]["action"])) {
if ($attrs[Reader::XMLNS_DOCBOOK]["action"] !== "simul") {
trigger_error(vsprintf('No support for keycombo action = %s', [$attrs[Reader::XMLNS_DOCBOOK]["action"]]), E_USER_WARNING);
}
}
if ($open) {
return '<kbd class="' . $name . '">';
}
return '</kbd>';
}
public function format_whitespace($whitespace, $elementStack, $currentDepth) {
/* The following if is to skip unnecessary whitespaces in the parameter list */
if (
in_array($elementStack[$currentDepth - 1], ['methodsynopsis', 'constructorsynopsis', 'destructorsynopsis'], true)
&& (in_array($elementStack[$currentDepth] ?? "", ["methodname", "methodparam", "type", "void"], true)
|| count($elementStack) === $currentDepth)
) {
return false;
}
/* The following if is to skip whitespace before closing semicolon after property/class constant */
if ($elementStack[$currentDepth - 1] === "fieldsynopsis" && (in_array($elementStack[$currentDepth], ["varname", "initializer"], true))) {
return false;
}
/*
TODO: add trim() in type_text handling method and remove the below
as it doesn't work due to XMLReader including all whitespace
inside the tag in the text
hence no separate significant whitespace here
*/
/* The following if is to skip whitespace inside type elements */
if ($elementStack[$currentDepth - 1] === "type") {
return false;
}
if (
$elementStack[$currentDepth - 1] === "simplelist"
&& ($this->cchunk["simplelist"]["type"] === "inline"
|| $this->cchunk["simplelist"]["type"] === "vert"
|| $this->cchunk["simplelist"]["type"] === "horiz")
) {
return false;
}
/* The following if is to skip unnecessary whitespaces in the implements list */
if (
($elementStack[$currentDepth - 1] === 'classsynopsisinfo' && $elementStack[$currentDepth] === 'oointerface') ||
($elementStack[$currentDepth - 1] === 'oointerface' && $elementStack[$currentDepth] === 'interfacename')
) {
return false;
}
return $whitespace;
}
public function format_caption($open, $name, $attrs, $props) {
return $open ? '<div class="caption">' : '</div>';
}
}