'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', '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', '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', /** Package/Namespace synopsis related tags */ 'packagesynopsis' => 'format_packagesynopsis', 'package' => [ /* DEFAULT */ 'span', 'packagesynopsis' => 'format_packagesynopsis_package', ], /** 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', // MathML (namespace http://www.w3.org/1998/Math/MathML) 'mml:math' => 'format_mml_element', // Token 'mml:mi' => 'format_mml_element', 'mml:mn' => 'format_mml_element', 'mml:mo' => 'format_mml_element', 'mml:mtext' => 'format_mml_element', 'mml:mspace' => 'format_mml_element', 'mml:ms' => 'format_mml_element', // Layout 'mml:mrow' => 'format_mml_element', 'mml:mfrac' => 'format_mml_element', 'mml:msqrt' => 'format_mml_element', 'mml:mroot' => 'format_mml_element', 'mml:mstyle' => 'format_mml_element', 'mml:merror' => 'format_mml_element', 'mml:mpadded' => 'format_mml_element', 'mml:mphantom' => 'format_mml_element', 'mml:mfenced' => 'format_mml_element', 'mml:menclose' => 'format_mml_element', // Scripts and limits 'mml:msub' => 'format_mml_element', 'mml:msup' => 'format_mml_element', 'mml:msubsup' => 'format_mml_element', 'mml:munder' => 'format_mml_element', 'mml:mover' => 'format_mml_element', 'mml:munderover' => 'format_mml_element', 'mml:mmultiscripts' => 'format_mml_element', 'mml:mprescripts' => 'format_mml_element', 'mml:none' => 'format_mml_element', // Tables 'mml:mtable' => 'format_mml_element', 'mml:mtr' => 'format_mml_element', 'mml:mtd' => 'format_mml_element', 'mml:mlabeledtr' => 'format_mml_element', // Semantics 'mml:semantics' => 'format_mml_element', 'mml:annotation' => 'format_mml_element', 'mml:annotation-xml' => 'format_mml_element', // Actions 'mml:maction' => 'format_mml_element', ); /* }}} */ 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( "packagesynopsis" => false, "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; protected int $perPageExampleCounter = 0; protected bool $exampleCounterIsPerPage = false; protected array $perPageExampleIds = []; 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 .= "\n"; return $toc; } protected function getTocHeader($props) { return '' . $this->autogen('toc', $props['lang']) . ''; } /** * Handle a tag. */ function format_phd_toc($open, $name, $attrs, $props) { if ($open) { return '
'; } 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 ) . "
\n"; } /** * Handle MathML elements (mml:* namespace). * Strips the "mml:" prefix and outputs the HTML5 local name. */ public function format_mml_element($open, $name, $attrs, $props) { $localName = substr($name, 4); if ($open) { $attrStr = ''; // Add xmlns on the root element for XHTML compatibility if ($localName === 'math') { $attrStr .= ' xmlns="' . Reader::XMLNS_MATHML . '"'; } // Preserve MathML attributes (stored under XMLNS_DOCBOOK as they have no namespace) foreach ($attrs[Reader::XMLNS_DOCBOOK] as $attr => $val) { $attrStr .= ' ' . $attr . '="' . $this->TEXT($val) . '"'; } // Preserve xml:id as id if (isset($attrs[Reader::XMLNS_XML]["id"])) { $attrStr .= ' id="' . $attrs[Reader::XMLNS_XML]["id"] . '"'; } return '<' . $localName . $attrStr . ($props["empty"] ? '/>' : '>'); } return ''; } 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"]) { if (isset($this->perPageExampleIds[$for])) { $retval .= '#' . $this->perPageExampleIds[$for]; } else { $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 .= "\n"; } else { $cssLinks .= "\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)) { throw new \Error('The styles/ directory is a file?'); } } else { if (!mkdir($stylesDir, 0777, true)) { throw new \Error("Can't create the styles/ directory."); } } 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 = "» "; } if ($props["empty"]) { $content .= $link .""; } return '' . $content; } return ""; } public function format_xref($open, $name, $attrs, $props) { if ($open) { $desc = ""; $link = $this->createLink($attrs[Reader::XMLNS_DOCBOOK]["linkend"], $desc); $ret = '' .$desc; if ($props["empty"]) { return $ret. ""; } return $ret; } return ""; } 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 ''; } $this->popRole(); return ""; } public function format_literal($open, $name, $attrs) { if ($open) { $this->pushRole($attrs[Reader::XMLNS_DOCBOOK]["role"] ?? null); return ''; } $this->popRole(); return ''; } public function format_literal_text($value, $tag) { switch ($this->getRole()) { case 'infdec': $value = (string) (float)$value; $p = strpos($value, '.'); $str = substr($value, 0, $p + 1); $str .= ''; $str .= substr($value, $p + 1); $str .= ''; return $str; default: return $this->TEXT($value); } } public function format_copyright($open, $name, $attrs) { if ($open) { return '
© '; } return '
'; } public function format_author($open, $name, $attrs, $props) { if ($open) { return '
'; } return "
"; } public function format_personname($open, $name, $attrs, $props) { if ($open) { return ''; } return ""; } 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 ' '; } return ' '; } public function format_container_chunk_top($open, $name, $attrs, $props) { $hasAnnotations = array_key_exists('annotations', $attrs[Reader::XMLNS_DOCBOOK]); $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) { if ($hasAnnotations) { $this->pushAnnotations($attrs[Reader::XMLNS_DOCBOOK]["annotations"]); } $this->CURRENT_CHUNK = $id; $this->notify(Render::CHUNK, Render::OPEN); return '
'; } if ($hasAnnotations) { $this->popAnnotations(); } $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."
"; } public function format_container_chunk_top_title($open, $name, $attrs, $props) { if ($open) { return '

'; } $id = $this->CURRENT_CHUNK; $toc = $this->createTOC($id, $name, $props, 2); $this->TOC_WRITTEN[] = $id; return '

'.$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 '
'; } $toc = '
    '; if (!in_array($id, $this->TOC_WRITTEN)) { $toc = $this->createTOC($id, $name, $props); } $toc .= "
\n"; $this->CURRENT_CHUNK = $id; $this->notify(Render::CHUNK, Render::CLOSE); return $toc . '
'; } 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 '
'; } $this->notify(Render::CHUNK, Render::CLOSE); $str = ""; foreach ($this->cchunk["footnote"] as $k => $note) { $str .= '
'; $str .= '[' .($k + 1). ']'; $str .= $note["str"]; $str .= "
\n"; } $this->cchunk = $this->dchunk; return $str. "
"; } public function format_refsect1_para($open, $name, $attrs, $props) { if ($open) { switch ($props["sibling"]) { case "methodsynopsis": case "constructorsynopsis": case "destructorsynopsis": return '

'; break; default: return '

'; } } return '

'; } 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 '
'; } $this->popRole(); return "
\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 ''; } if ($this->cchunk["classsynopsisinfo"]["implements"] === false) { $this->cchunk["classsynopsisinfo"]["implements"] = true; if ($this->cchunk["classsynopsis"]["interface"]) { return 'extends '; } return 'implements '; } return ', '; } return ""; } 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 'interface '; } return 'class '; } 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 'interface '; } 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 '
/* '; } return '
'; } if (isset($attrs[Reader::XMLNS_DOCBOOK]["role"]) && $attrs[Reader::XMLNS_DOCBOOK]["role"] == "comment") { return ' */
'; } assert($this->cchunk["classsynopsis"]["legacy"] === true); $this->cchunk["classsynopsis"]["close"] = true; return ' {
'; } /** 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 'interface ' . $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 ''; } /** 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 'class ' . $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 ''; } /** 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 ' {'; } } 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"]); $inPackageSynopsis = $this->cchunk["packagesynopsis"] ?? false; 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; } if ($inPackageSynopsis) { return ''; } return '
'; } /* 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 "}
"; } return ""; */ if ($inPackageSynopsis) { return '}'; } return "}"; } /* 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'], '') + substr_count($props['innerXml'], '') + substr_count($props['innerXml'], ''); $this->cchunk["classsynopsis"]['nb_list'] = $occurrences; if ($inPackageSynopsis) { return '
'; } return '
'; } else { if ($inPackageSynopsis) { return '}'; } return '}
'; } } 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, '->')) { $explode = '->'; } else { return $value; } list($class, $method) = explode($explode, $value); if ($class !== $this->cchunk["classsynopsis"]["classname"]) { return $value; } return $method; } public function format_packagesynopsis($open, $name, $attrs, $props) { if ($open) { $this->cchunk["packagesynopsis"] = true; return '
'; } $this->cchunk["packagesynopsis"] = false; return '
'; } public function format_packagesynopsis_package($open, $name, $attrs, $props) { if ($open) { return 'namespace '; } return ';
'; } public function format_enumsynopsis($open, $name, $attrs, $props) { $inPackageSynopsis = $this->cchunk["packagesynopsis"] ?? false; if ($open) { if ($inPackageSynopsis) { return '
'; } return '
'; } else { if ($inPackageSynopsis) { return '}'; } return '}
'; } } public function format_enumsynopsis_enumname($open, $name, $attrs, $props): string { if ($open) { /** Actual enum name in bold */ return 'enum '; //return 'enum '; } //return "
{
"; return "

{
"; } public function format_enumitem($open, $name, $attrs, $props) { if ($open) { return '
'; } return '
'; } public function format_enumidentifier($open, $name, $attrs, $props) { if ($open) { return ' case '; } return ''; } public function format_enumvalue($open, $name, $attrs, $props) { if ($open) { return ' = '; } return ''; } public function format_enumitemdescription($open, $name, $attrs, $props) { if ($open) { return '; //'; } 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 ""; } } 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 '
'; } if (isset($attrs[Reader::XMLNS_DOCBOOK]["role"])) { $this->popRole(); } return ";
\n"; } public function format_fieldsynopsis_modifier_text($value, $tag) { $this->cchunk["fieldsynopsis"]["modifier"] = trim($value); if ($this->getRole() === "attribute") { $attribute = trim(strtolower($value), "#[]\\"); $href = Format::getFilename("class.$attribute"); if ($href) { return '' .$value. ' '; } return false; } 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 ''; } return ''; } if (isset($attrs[Reader::XMLNS_DOCBOOK]["role"])) { if ($attrs[Reader::XMLNS_DOCBOOK]["role"] === "attribute") { return '
'; } $this->popRole(); } return '
'; } 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 ''; } return ''; } if (isset($attrs[Reader::XMLNS_DOCBOOK]["role"])) { $this->popRole(); } return ''; } public function format_modifier_text($value, $tag) { if ($this->getRole() === "attribute") { $attribute = trim(strtolower($value), "#[]\\"); $href = Format::getFilename("class.$attribute"); if ($href) { return '' .$value. ' '; } } 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"], "'; } $content = ""; if ($this->params["paramCount"] > 3) { $content .= "
"; } $content .= ")"; $content .= "
\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 ' &' . $this->params["ellipsis"] . '$'; } return ' ' . $this->params["ellipsis"] . '$'; } return ""; } public function format_initializer($open, $name, $attrs) { if ($open) { $this->params["init"] = true; return ' = '; } return ''; } public function format_parameter($open, $name, $attrs, $props) { if ($props["empty"]) { return ''; } if ($open) { if (isset($attrs[Reader::XMLNS_DOCBOOK]["role"])) { return '&'; } return ''; } return ""; } public function format_void($open, $name, $attrs, $props) { if (isset($props['sibling']) && $props['sibling'] == 'methodname') { return '('; } else { return 'void'; } } public function format_methodparam($open, $name, $attrs) { if ($open) { $content = ''; if ($this->params["count"] === 0) { $content .= "("; if ($this->params["paramCount"] > 3) { $content .= "
    "; } } 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 .= "
    "; } else { $content .= " "; } } $content .= ''; ++$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 ' = ?'; } $this->params["init"] = false; return ""; } public function format_methodname($open, $name, $attr) { if ($open) { return ' '; } return ''; } public function format_varname($open, $name, $attrs) { if ($open) { return '$'; } return ""; } public function format_fieldsynopsis_varname($open, $name, $attrs) { if ($open) { if ($this->cchunk["fieldsynopsis"]["modifier"] === "const") { return ''; } return '$'; } return ''; } 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 '[' .($k + 1). ']'; } } 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 '[' .($count + 1). ']'; } 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"] .= ''; return ""; } $this->cchunk["footnote"][$k]["str"] .= ""; 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 '' .str_repeat("*", $co) .''; } /* Suppress closing tag if any */ return ""; } public function format_calloutlist($open, $name, $attrs) { if ($open) { $this->cchunk["callouts"] = 0; return ''; } return '
'; } public function format_callout($open, $name, $attrs) { if ($open) { return '' .str_repeat("*", ++$this->cchunk["callouts"]). ''; } return "\n"; } public function format_manvolnum($open, $name, $attrs) { if ($open) { return '('; } return ")"; } public function format_segmentedlist($open, $name, $attrs) { $this->cchunk["segmentedlist"] = $this->dchunk["segmentedlist"]; if ($open) { return ''; } return '
'; } public function format_segtitle_text($value, $tag) { $html = ''; if (!$this->cchunk["segmentedlist"]["segtitleopened"]) { $html .= ''; } $html .= ''.$this->TEXT($value).''; $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 .= ''; $this->cchunk["segmentedlist"]["segtitleclosed"] = true; } $html .= ''; return $html; } return ''; } public function format_seg($open, $name, $attrs) { if ($open) { return ''; } return ''; } public function format_procedure($open, $name, $attrs) { $this->cchunk["procedure"] = false; if ($open) { return '
'; } return '
'; } public function format_step($open, $name, $attrs) { if ($open) { $ret = ""; if ($this->cchunk["procedure"] === false) { $this->cchunk["procedure"] = true; $ret = '
    '; } return $ret . "
  1. "; } return '
  2. '; } 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) . "\n" . $this->indent($props["depth"] + 2) . "" . $this->autogen('Constants', $props['lang']) . "\n" . $this->indent($props["depth"] + 2) . "" . $this->autogen('Description', $props['lang']) . "\n" . $this->indent($props["depth"] + 1) . ""; } else { $tagName = 'dl'; } return '<' . $tagName . $idStr . $classStr . '>' . $headerStr; } $tagName = ($this->getRole() === 'constant_list') ? 'table' : 'dl'; $this->popRole(); return ""; } 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') ? '' : ''; } return ($this->getRole() === 'constant_list') ? '' : ''; } 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 ""; } public function format_varlistentry_listitem($open, $name, $attrs) { $tagName = ($this->getRole() === 'constant_list') ? 'td' : 'dd'; if ($open) { return "<" . $tagName . ">"; } return ""; } public function format_term($open, $name, $attrs, $props) { if ($open) { if ($props["sibling"] == $name) { return '
    '; } return ''; } return ""; } public function format_trademark($open, $name, $attrs, $props) { if ($open) { return ''; } return '®'; } public function format_userinput($open, $name, $attrs) { if ($open) { return ''; } return ""; } 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 ''; } } return ""; } public function format_example_content($open, $name, $attrs) { if ($open) { return '

    '; } return "

    "; } public function format_programlisting($open, $name, $attrs) { $hasAnnotations = array_key_exists('annotations', $attrs[Reader::XMLNS_DOCBOOK]); if ($open) { $this->pushRole($attrs[Reader::XMLNS_DOCBOOK]["role"] ?? null); if ($hasAnnotations) { $this->pushAnnotations($attrs[Reader::XMLNS_DOCBOOK]["annotations"]); } return '
    '; } if ($hasAnnotations) { $this->popAnnotations(); } $this->popRole(); return "
    \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 '
    '; } if ($this->getRole() !== "examples" && $this->getRole() !== "description" && $this->getRole() !== "notes" && $this->getRole() !== "returnvalues" && $this->getRole() !== "parameters") { $this->popRole(); } return '
    '; } public function format_constant($open, $name, $attrs, $props) { if ($open) { if (str_contains($props["innerXml"], 'pushRole("constant_group"); $this->cchunk["constant"] = $props["innerXml"]; } return ""; } if ($this->getRole() === "constant_group") { $this->popRole(); $value = str_replace( ["", ""], ["", ""], strip_tags($this->cchunk["constant"], "") ); $link = $this->createReplaceableConstantLink(strip_tags($this->cchunk["constant"], "")); $this->cchunk["constant"] = ""; if ($link === "") { return $value . ''; } return '' . $value . '
    '; } return "
    "; } /** * Creates a link to the first constant in the index * that matches the pattern of a constant containing a tag * or returns an empty string if no match was found. * * This works only with one set of tags in a constant * e.g. CURLE_* or DOM_*_NODE */ private function createReplaceableConstantLink(string $constant): string { $pattern = "/" . preg_replace( "//", ".*", 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("-", "-"), trim($this->normalizeFQN($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 '' . $value . ''; } 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( ["\\", "_", "$"], ["-", "-", ""], trim($this->normalizeFQN($value), "_") ); list($extensionAndClass, $property) = explode("::", $tempLinkValue); $normalizedLinkFormat = $extensionAndClass . ".props." . trim($property, "-"); $link = $this->createLink($normalizedLinkFormat); if ($link === null || $link === "") { return $value; } return '' . $value . ''; } protected function normalizeFQN(string $fqn): string { return \ltrim(\strtolower($fqn), "\\"); } public function admonition_title($title, $lang) { return '' .($this->autogen($title, $lang)). ''; } public function format_admonition($open, $name, $attrs, $props) { if ($open) { return '
    ' .$this->admonition_title($name, $props["lang"]); } return "
    "; } public function format_authorgroup_author($open, $name, $attrs, $props) { if ($open) { if ($props["sibling"] !== $name) { return '
    ' .$this->admonition_title("by", $props["lang"]). ':
    '; } return '
    '; } return "
    \n"; } public function format_editor($open, $name, $attrs, $props) { if ($open) { return '
    ' .$this->admonition_title("editedby", $props["lang"]). ': '; } return "
    \n"; } public function format_note($open, $name, $attrs, $props) { if ($open) { return '

    '.$this->admonition_title("note", $props["lang"]). ': '; } return "

    "; } public function format_note_title($open, $name, $attrs) { if ($open) { return ''; } return '
    '; } public function format_example($open, $name, $attrs, $props) { if ($open) { ++$this->exampleCounter; if (isset($props["id"])) { return '
    '; } return '
    '; } return '
    '; } public function format_example_title($open, $name, $attrs, $props) { if ($props["empty"]) { return ""; } if ($open) { return "

    " . ($this->autogen('example', $props['lang']) . (isset($this->cchunk["examples"]) ? ++$this->cchunk["examples"] : "")) . " "; } return "

    "; } public function format_table_title($open, $name, $attrs, $props) { if ($props["empty"]) { return ""; } if ($open) { return ""; } return ""; } public function format_variablelist_title($open, $name, $attrs, $props) { if ($open) { return ($this->getRole() === 'constant_list') ? "" : ""; } return ($this->getRole() === 'constant_list') ? "" : ""; } public function format_mediaobject($open, $name, $attrs) { $this->cchunk["mediaobject"] = $this->dchunk["mediaobject"]; if ($open) { return '
    '; } return '
    '; } 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 ''; } 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 ''; } $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); $str .= $this->format_row(true, "footnote", $opts); $str .= $this->format_entry(true, "footnote", $opts, $props+array("colspan" => $this->getColCount())); foreach ($this->cchunk["tablefootnotes"] as $k => $noteid) { $str .= '
    '; $str .= '[' .($k + 1). ']' .$this->cchunk["footnote"][$k]["str"]; unset($this->cchunk["footnote"][$k]); $str .= "
    \n"; } $str .= $this->format_entry(false, "footnote", $opts, $props); $str .= $this->format_row(false, "footnote", $opts); $str .= $this->format_tbody(false, "footnote", $opts); $this->cchunk["tablefootnotes"] = $this->dchunk["tablefootnotes"]; } return "$str\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 ''; } /* 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 "\n"; } public function format_tbody($open, $name, $attrs) { if ($open) { if (isset($attrs[Reader::XMLNS_DOCBOOK]['valign'])) { return ''; } else { return ''; } } return ""; } 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 ''; } else { return ''; } } return "\n"; } public function format_th_entry($open, $name, $attrs, $props) { if ($props["empty"]) { return ' '; } if ($open) { $colspan = Format::colspan($attrs[Reader::XMLNS_DOCBOOK]); if ($colspan == 1) { return ''; } else { return ''; } } return ''; } public function format_entry($open, $name, $attrs, $props) { if ($props["empty"]) { return ' '; } if ($open) { $dbattrs = (array)Format::getColspec($attrs[Reader::XMLNS_DOCBOOK]); $retval = ""; if (isset($dbattrs["colname"])) { for($i=Format::getEntryOffset($dbattrs); $i>0; --$i) { $retval .= ' '; } } /* * "colspan" is *not* an standard prop, only used to overwrite the * colspan for 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. ''; } return ""; } public function format_qandaset($open, $name, $attrs, $props) { if ($open) { $xml = "" . $props["innerXml"] . ""; $doc = new \DOMDocument; $doc->loadXml($xml); $xp = new \DOMXPath($doc); $xp->registerNamespace("db", Reader::XMLNS_DOCBOOK); $questions = $xp->query("//db:qandaentry/db:question"); $retval = '
      '; 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 .= '
    1. '.htmlentities($node->textContent, ENT_QUOTES, "UTF-8").'
    2. '; } $retval .= "
    "; return $retval; } } public function format_question($open, $name, $attrs, $props) { if ($open) { return '
    '; } return '
    '; } public function format_phd_question($open, $name, $attrs, $props) { if ($open) { $href = $this->createLink($attrs[Reader::XMLNS_XML]["id"]); return '
  3. '; } return '
  4. '; } public function format_citation($open, $name, $attrs, $props) { if ($open) { return '['; } return ']'; } public function format_email_text($value) { return '<' . $value . '>'; } public function format_bold_paragraph($open, $name, $attrs, $props) { if ($props["empty"]) { return ""; } if ($open) { return "

    "; } return "

    "; } /** * Functions from the old XHTMLPhDFormat */ public function format_legalnotice_chunk($open, $name, $attrs) { if ($open) { return '
    '; } return "
    \n"; } public function format_div($open, $name, $attrs, $props) { if ($open) { return '
    '; } return '
    '; } public function format_screen_text($value, $tag) { return nl2br($this->TEXT($value)); } /** * Renders a tag. * * @return string HTML code */ public function format_tag($open, $name, $attrs, $props) { static $arFixes = array( 'attribute' => array('', ''), 'attvalue' => array('"', '"'), 'comment' => array('<!--', '-->'), 'element' => array('', ''), 'emptytag' => array('<', '/>'), 'endtag' => array('</', '>'), 'genentity' => array('&', ';'), 'localname' => array('', ''), 'namespace' => array('', ''), 'numcharref' => array('&#', ';'), 'paramentity' => array('%', ';'), 'pi' => array('<?', '?>'), 'prefix' => array('', ''), 'starttag' => array('<', '>'), 'xmlpi' => array('<?', '?>'), ); 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] . ''; } return '' . $arFixes[$class][0]; } public function format_dl($open, $name, $attrs, $props) { if ($open) { return '
    '; } return '
    '; } public function format_itemizedlist($open, $name, $attrs, $props) { if ($open) { return '
      '; } return '
    '; } 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 ''; } if ($this->cchunk["simplelist"]["type"] === "vert" || $this->cchunk["simplelist"]["type"] === "horiz") { return '' . "\n" . $this->indent($props["depth"] + 1) . "\n"; } return '
      '; } $list = match ($this->cchunk["simplelist"]["type"]) { "inline" => $this->simplelist_format_inline(), "horiz" => $this->simplelist_format_horizontal($props["depth"]) . $this->indent($props["depth"] + 1) . "
    \n" . $this->indent($props["depth"]) . "
    ", "vert" => $this->simplelist_format_vertical($props["depth"]) . $this->indent($props["depth"] + 1) . "\n" . $this->indent($props["depth"]) . "", default => "", }; $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"]) . '
    '; } 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 . "\n" . implode('', $entry) . $trPadding . "\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 . "$member\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 '
  5. '; } return '
  6. '; } 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 '
      '; } return '
    '; } /* 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 . ''; } return ''; } 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 ''; } return ''; } 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 ? '
    ' : '
    '; } public function getGeneratedExampleID($index) { $originalId = parent::getGeneratedExampleID($index); if (! $this->exampleCounterIsPerPage) { return $originalId; } if (preg_match('/^example\-[0-9]+$/', $originalId)) { $this->perPageExampleCounter++; $this->perPageExampleIds[$originalId] = 'example-' . $this->perPageExampleCounter; return $this->perPageExampleIds[$originalId]; } return $originalId; } public function onNewPage(): void { $this->perPageExampleCounter = 0; } }