document->paragraphClose(); $params->content .= ''; // Prepare index data $new = array(); foreach ($settings as $key => $value) { $new [$key] = $value; } $new ['type'] = $type; $new ['width'] = $params->document->getAbsWidthMindMargins(); if ($type == 'chapter') { $new ['start_ref'] = $params->document->getPreviousToCItem(1); } else { $new ['start_ref'] = NULL; } // Add new index data $indexesData [] = $new; return ''; } /** * This function builds the actual TOC and replaces the placeholder with it. * It is called in document_end() after all headings have been added to the TOC, see toc_additem(). * The page numbers are just a counter. Update the TOC e.g. in LibreOffice to get the real page numbers! * * The TOC is inserted by the syntax tag '{{odt>toc:setting=value;}};'. * The following settings are supported: * - Title e.g. '{{odt>toc:title=Example;}}'. * Default is 'Table of Contents' (for english, see language files for other languages default value). * - Leader sign, e.g. '{{odt>toc:leader-sign=.;}}'. * Default is '.'. * - Indents (in cm), e.g. '{{odt>toc:indents=indents=0,0.5,1,1.5,2,2.5,3;}};'. * Default is 0.5 cm indent more per level. * - Maximum outline/TOC level, e.g. '{{odt>toc:maxtoclevel=5;}}'. * Default is taken from DokuWiki config setting 'maxtoclevel'. * - Insert pagebreak after TOC, e.g. '{{odt>toc:pagebreak=1;}}'. * Default is '1', means insert pagebreak after TOC. * - Set style per outline/TOC level, e.g. '{{odt>toc:styleL2="color:red;font-weight:900;";}}'. * Default is 'color:black'. * * It is allowed to use defaults for all settings by using '{{odt>toc}}'. * Multiple settings can be combined, e.g. '{{odt>toc:leader-sign=.;indents=0,0.5,1,1.5,2,2.5,3;}}'. */ public static function replaceIndexesPlaceholders(ODTInternalParams $params, array $indexesData, array $toc) { $index_count = count($indexesData); for ($index_no = 0 ; $index_no < $index_count ; $index_no++) { $data = $indexesData [$index_no]; // At the moment it does not make sense to disable links for the TOC // because LibreOffice will insert links on updating the TOC. $data ['create_links'] = true; $indexContent = self::buildIndex($params->document, $toc, $data, $index_no+1); // Replace placeholder with TOC content. $params->content = str_replace ('', $indexContent, $params->content); } } /** * This function builds a TOC or chapter index. * The page numbers are just a counter. Update the TOC e.g. in LibreOffice to get the real page numbers! * * The layout settings are taken from the configuration and $settings. * $settings can include the following options syntax: * - Title e.g. 'title=Example;'. * Default is 'Table of Contents' (for english, see language files for other languages default value). * - Leader sign, e.g. 'leader-sign=.;'. * Default is '.'. * - Indents (in cm), e.g. 'indents=indents=0,0.5,1,1.5,2,2.5,3;'. * Default is 0.5 cm indent more per level. * - Maximum outline/TOC level, e.g. 'maxtoclevel=5;'. * Default is taken from DokuWiki config setting 'maxtoclevel'. * - Insert pagebreak after TOC, e.g. 'pagebreak=1;'. * Default is '1', means insert pagebreak after TOC. * - Set style per outline/TOC level, e.g. 'styleL2="color:red;font-weight:900;";'. * Default is 'color:black'. * * It is allowed to use defaults for all settings by omitting $settings. * Multiple settings can be combined, e.g. 'leader-sign=.;indents=0,0.5,1,1.5,2,2.5,3;'. */ protected static function buildIndex(ODTDocument $doc, array $toc, array $settings, $indexNo) { $stylesL = array(); $stylesLNames = array(); // Get index type $type = $settings ['type']; // It seems to be not supported in ODT to have a different start // outline level than 1. $max_outline_level = 10; if (!empty($settings ['maxlevel'])) { $max_outline_level = $settings ['maxlevel']; } // Determine title, default for table of contents is 'Table of Contents'. // Default for chapter index is empty. // Syntax for 'Test' as title would be "title=test;". $title = ''; if (!empty($settings ['title'])) { $title = $settings ['title']; } // Determine leader-sign, default is '.'. // Syntax for '.' as leader-sign would be "leader_sign=.;". $leader_sign = '.'; if (!empty($settings ['leader_sign'])) { $leader_sign = $settings ['leader_sign']; } // Determine indents, default is '0.5' (cm) per level. // Syntax for a indent of '0.5' for 5 levels would be "indents=0,0.5,1,1.5,2;". // The values are absolute for each level, not relative to the higher level. $indents = '0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5'; if (!empty($settings ['indents'])) { $indents = $settings ['indents']; } // Determine pagebreak, default is on '1'. // Syntax for pagebreak off would be "pagebreak=0;". $pagebreak = true; if (!empty($settings ['pagebreak'])) { $temp = $settings ['pagebreak']; $pagebreak = false; if ( $temp == '1' ) { $pagebreak = true; } else if ( strcasecmp($temp, 'true') == 0 ) { $pagebreak = true; } } // Determine text style for the index heading. $styleH = ''; if (!empty($settings ['style_heading'])) { $styleH = $settings ['style_heading']; } // Determine text styles per level. // Syntax for a style level 1 is "styleL1="color:black;"". // The default style is just 'color:black;'. for ( $count = 0 ; $count < $max_outline_level ; $count++ ) { $stylesL [$count + 1] = 'color:black;'; if (!empty($settings ['styleL'.($count + 1)])) { $stylesL [$count + 1] = $settings ['styleL'.($count + 1)]; } } // Create Heading style if not empty. // Default index heading style is taken from styles.xml $title_style = $doc->getStyleName('contents heading'); if (!empty($styleH)) { $properties = array(); $doc->getCSSStylePropertiesForODT ($properties, $styleH); $properties ['style-parent'] = 'Heading'; $properties ['style-class'] = 'index'; $properties ['style-name'] = 'Contents_20_Heading_'.$indexNo; $properties ['style-display-name'] = 'Contents Heading '.$indexNo; $style_obj = ODTParagraphStyle::createParagraphStyle($properties); $doc->addStyle($style_obj); $title_style = $style_obj->getProperty('style-name'); } // Create paragraph styles $p_styles = array(); $p_styles_auto = array(); $indent = 0; for ( $count = 0 ; $count < $max_outline_level ; $count++ ) { $indent = $indents [$count]; $properties = array(); $doc->getCSSStylePropertiesForODT ($properties, $stylesL [$count+1]); $properties ['style-parent'] = 'Index'; $properties ['style-class'] = 'index'; $properties ['style-position'] = $settings ['width'] - $indent .'cm'; $properties ['style-type'] = 'right'; $properties ['style-leader-style'] = 'dotted'; $properties ['style-leader-text'] = $leader_sign; $properties ['margin-left'] = $indent.'cm'; $properties ['margin-right'] = '0cm'; $properties ['text-indent'] = '0cm'; $properties ['style-name'] = 'ToC '.$indexNo.'- Level '.($count+1); $properties ['style-display-name'] = 'ToC '.$indexNo.', Level '.($count+1); $style_obj = ODTParagraphStyle::createParagraphStyle($properties); // Add paragraph style to common styles. // (It MUST be added to styles NOT to automatic styles. Otherwise LibreOffice will // overwrite/change the style on updating the TOC!!!) $doc->addStyle($style_obj); $p_styles [$count+1] = $style_obj->getProperty('style-name'); // Create a copy of that but with parent set to the copied style // and no class $properties ['style-parent'] = $style_obj->getProperty('style-name'); $properties ['style-class'] = NULL; $properties ['style-name'] = 'ToC Auto '.$indexNo.'- Level '.($count+1); $properties ['style-display-name'] = NULL; $style_obj_auto = ODTParagraphStyle::createParagraphStyle($properties); // Add paragraph style to automatic styles. // (It MUST be added to automatic styles NOT to styles. Otherwise LibreOffice will // overwrite/change the style on updating the TOC!!!) $doc->addAutomaticStyle($style_obj_auto); $p_styles_auto [$count+1] = $style_obj_auto->getProperty('style-name'); } // Create text style for TOC text. // (this MUST be a text style (not paragraph!) and MUST be placed in styles (not automatic styles) to work!) for ( $count = 0 ; $count < $max_outline_level ; $count++ ) { $properties = array(); $doc->getCSSStylePropertiesForODT ($properties, $stylesL [$count+1]); $properties ['style-name'] = 'ToC '.$indexNo.'- Text Level '.($count+1); $properties ['style-display-name'] = 'ToC '.$indexNo.', Level '.($count+1); $style_obj = ODTTextStyle::createTextStyle($properties); $stylesLNames [$count+1] = $style_obj->getProperty('style-name'); $doc->addStyle($style_obj); } // Generate ODT toc tag and content switch ($type) { case 'toc': $tag = 'table-of-content'; $name = 'Table of Contents'; $index_name = 'Table of Contents'; $source_attrs = 'text:outline-level="'.$max_outline_level.'" text:use-index-marks="false"'; break; case 'chapter': $tag = 'table-of-content'; $name = 'Table of Contents'; $index_name = 'Table of Contents'; $source_attrs = 'text:outline-level="'.$max_outline_level.'" text:use-index-marks="false" text:index-scope="chapter"'; break; } $content = ''; $content .= ''; if (!empty($title)) { $content .= ''.$title.''; } else { $content .= ''; } // Create TOC templates per outline level. // The styles listed here need to be the same as later used for the headers. // Otherwise the style of the TOC entries/headers will change after an update. for ( $count = 0 ; $count < $max_outline_level ; $count++ ) { $level = $count + 1; $content .= ''; $content .= ''; $content .= ''; if ($settings ['numbered_headings'] == true) { $content .= ' '; } $content .= ''; $content .= ''; $content .= ''; $content .= ''; $content .= ''; } $content .= ''; $content .= ''; if (!empty($title)) { $content .= ''; $content .= ''.$title.''; $content .= ''; } // Add headers to TOC. $page = 0; $links = $settings ['create_links']; if ($type == 'toc') { $content .= self::getTOCBody ($toc, $p_styles_auto, $stylesLNames, $max_outline_level, $links); } else { $startRef = $settings ['start_ref']; $content .= self::getChapterIndexBody ($toc, $p_styles_auto, $stylesLNames, $max_outline_level, $links, $startRef); } $content .= ''; $content .= ''; // Add a pagebreak if required. if ( $pagebreak ) { $style_name = $doc->createPagebreakStyle(NULL, false); $content .= ''; } // Return index content. return $content; } /** * This function creates the entries for a table of contents. * All heading are included up to level $max_outline_level. * * @param array $p_styles Array of style names for the paragraphs. * @param array $stylesLNames Array of style names for the links. * @param array $max_outline_level Depth of the table of contents. * @param boolean $links Shall links be created. * @return string TOC body entries */ protected static function getTOCBody(array $toc, $p_styles, $stylesLNames, $max_outline_level, $links) { $page = 0; $content = ''; foreach ($toc as $item) { $params = explode (',', $item); // Only add the heading to the TOC if its <= $max_outline_level if ( $params [3] <= $max_outline_level ) { $level = $params [3]; $content .= ''; if ( $links == true ) { $content .= ''; } $content .= $params [2]; $content .= ''; $page++; $content .= $page; if ( $links == true ) { $content .= ''; } $content .= ''; } } return $content; } /** * This function creates the entries for a chapter index. * All headings of the chapter are included uo to level $max_outline_level. * * @param array $p_styles Array of style names for the paragraphs. * @param array $stylesLNames Array of style names for the links. * @param array $max_outline_level Depth of the table of contents. * @param boolean $links Shall links be created. * @param string $startRef Reference-ID of chapter main heading. * @return string TOC body entries */ protected static function getChapterIndexBody(array $toc, $p_styles, $stylesLNames, $max_outline_level, $links, $startRef) { $start_outline = 1; $in_chapter = false; $first = true; $content = ''; foreach ($toc as $item) { $params = explode (',', $item); if ($in_chapter == true || $params [0] == $startRef ) { $in_chapter = true; // Is this the start of a new chapter? if ( $first == false && $params [3] <= $start_outline ) { break; } // Only add the heading to the TOC if its <= $max_outline_level if ( $params [3] <= $max_outline_level ) { $level = $params [3]; $content .= ''; if ( $links == true ) { $content .= ''; } $content .= $params [2]; $content .= ''; $page++; $content .= $page; if ( $links == true ) { $content .= ''; } $content .= ''; } $first = false; } } return $content; } }