add contents

This commit is contained in:
Trevor Batley
2025-10-09 15:04:29 +11:00
parent 170362eec1
commit bce7dd054a
2537 changed files with 301282 additions and 0 deletions

View File

@@ -0,0 +1,227 @@
<?php
require_once DOKU_INC.'lib/plugins/odt/ODT/styleset.php';
require_once DOKU_INC.'lib/plugins/odt/ODT/page.php';
/**
* ODTDefaultStyles: class for using the basic styles from styles.xml.
* This is also used if a ODT template is used, as the style names
* need to match the names in styles.xml.
*
* The class is doing nothing for import/export because it expects
* the file styles.xml to be there. So the file is neither read nor written.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
class ODTDefaultStyles extends ODTStyleSet
{
protected $automatic =
'<office:automatic-styles>
<style:page-layout style:name="pm1">
<style:page-layout-properties fo:page-width="21cm" fo:page-height="29.7cm" style:num-format="1" style:print-orientation="portrait" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb" style:footnote-max-height="0cm">
<style:footnote-sep style:width="0.018cm" style:distance-before-sep="0.1cm" style:distance-after-sep="0.1cm" style:adjustment="left" style:rel-width="25%" style:color="#000000"/>
</style:page-layout-properties>
<style:header-style/>
<style:footer-style/>
</style:page-layout>
<style:style style:name="sub" style:family="text">
<style:text-properties style:text-position="-33% 80%"/>
</style:style>
<style:style style:name="sup" style:family="text">
<style:text-properties style:text-position="33% 80%"/>
</style:style>
<style:style style:name="del" style:family="text">
<style:text-properties style:text-line-through-style="solid"/>
</style:style>
<style:style style:name="underline" style:family="text">
<style:text-properties style:text-underline-style="solid"
style:text-underline-width="auto" style:text-underline-color="font-color"/>
</style:style>
<style:style style:name="media" style:family="graphic" style:parent-style-name="Graphics">
<style:graphic-properties style:run-through="foreground" style:wrap="parallel" style:number-wrapped-paragraphs="no-limit"
style:wrap-contour="false" style:vertical-pos="top" style:vertical-rel="baseline" style:horizontal-pos="left"
style:horizontal-rel="paragraph"/>
</style:style>
<style:style style:name="medialeft" style:family="graphic" style:parent-style-name="Graphics">
<style:graphic-properties style:run-through="foreground" style:wrap="parallel" style:number-wrapped-paragraphs="no-limit"
style:wrap-contour="false" style:horizontal-pos="left" style:horizontal-rel="paragraph"/>
</style:style>
<style:style style:name="mediaright" style:family="graphic" style:parent-style-name="Graphics">
<style:graphic-properties style:run-through="foreground" style:wrap="parallel" style:number-wrapped-paragraphs="no-limit"
style:wrap-contour="false" style:horizontal-pos="right" style:horizontal-rel="paragraph"/>
</style:style>
<style:style style:name="mediacenter" style:family="graphic" style:parent-style-name="Graphics">
<style:graphic-properties style:run-through="foreground" style:wrap="none" style:horizontal-pos="center"
style:horizontal-rel="paragraph"/>
</style:style>
<style:style style:name="Table" style:family="table">
<style:table-properties table:border-model="collapsing" fo:margin-top="0.25cm" fo:margin-bottom="0.25cm"/>
</style:style>
<style:style style:name="tablealigncenter" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:paragraph-properties fo:text-align="center"/>
</style:style>
<style:style style:name="tablealignright" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:paragraph-properties fo:text-align="end"/>
</style:style>
<style:style style:name="tablealignleft" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:paragraph-properties fo:text-align="left"/>
</style:style>
<style:style style:name="tableheader" style:family="table-cell">
<style:table-cell-properties fo:padding="0.05cm" fo:border-left="0.002cm solid #000000" fo:border-right="0.002cm solid #000000" fo:border-top="0.002cm solid #000000" fo:border-bottom="0.002cm solid #000000"/>
</style:style>
<style:style style:name="tablecell" style:family="table-cell">
<style:table-cell-properties fo:padding="0.05cm" fo:border-left="0.002cm solid #000000" fo:border-right="0.002cm solid #000000" fo:border-top="0.002cm solid #000000" fo:border-bottom="0.002cm solid #000000"/>
</style:style>
<style:style style:name="legendcenter" style:family="paragraph" style:parent-style-name="Illustration">
<style:paragraph-properties fo:text-align="center"/>
</style:style>
<style:style style:name="Table_Quotation1" style:family="table">
<style:table-properties table:border-model="collapsing" fo:margin-top="0pt" fo:margin-bottom="16.8pt"/>
</style:style>
<style:style style:name="Table_Quotation2" style:family="table">
<style:table-properties table:border-model="collapsing" fo:margin-top="0pt" fo:margin-bottom="0pt"/>
</style:style>
<style:style style:name="Table_Quotation3" style:family="table">
<style:table-properties table:border-model="collapsing" fo:margin-top="0pt" fo:margin-bottom="0pt"/>
</style:style>
<style:style style:name="Table_Quotation4" style:family="table">
<style:table-properties table:border-model="collapsing" fo:margin-top="0pt" fo:margin-bottom="0pt"/>
</style:style>
<style:style style:name="Table_Quotation5" style:family="table">
<style:table-properties table:border-model="collapsing" fo:margin-top="0pt" fo:margin-bottom="0pt"/>
</style:style>
<style:style style:name="Cell_Quotation1" style:family="table-cell">
<style:table-cell-properties fo:margin="0cm" fo:padding-left="6pt" fo:padding-right="6pt" fo:padding-top="0pt" fo:padding-bottom="0pt" fo:border-left="3pt solid #cccccc" fo:border-right="none" fo:border-top="none" fo:border-bottom="none"/>
</style:style>
<style:style style:name="Cell_Quotation2" style:family="table-cell">
<style:table-cell-properties fo:margin="0cm" fo:padding-left="6pt" fo:padding-right="6pt" fo:padding-top="0pt" fo:padding-bottom="0pt" fo:border-left="3pt solid #cccccc" fo:border-right="none" fo:border-top="none" fo:border-bottom="none"/>
</style:style>
<style:style style:name="Cell_Quotation3" style:family="table-cell">
<style:table-cell-properties fo:margin="0cm" fo:padding-left="6pt" fo:padding-right="6pt" fo:padding-top="0pt" fo:padding-bottom="0pt" fo:border-left="3pt solid #cccccc" fo:border-right="none" fo:border-top="none" fo:border-bottom="none"/>
</style:style>
<style:style style:name="Cell_Quotation4" style:family="table-cell">
<style:table-cell-properties fo:margin="0cm" fo:padding-left="6pt" fo:padding-right="6pt" fo:padding-top="0pt" fo:padding-bottom="0pt" fo:border-left="3pt solid #cccccc" fo:border-right="none" fo:border-top="none" fo:border-bottom="none"/>
</style:style>
<style:style style:name="Cell_Quotation5" style:family="table-cell">
<style:table-cell-properties fo:margin="0cm" fo:padding-left="6pt" fo:padding-right="6pt" fo:padding-top="0pt" fo:padding-bottom="0pt" fo:border-left="3pt solid #cccccc" fo:border-right="none" fo:border-top="none" fo:border-bottom="none"/>
</style:style>
</office:automatic-styles>';
// Font definitions. May not be present if in template mode, in which case they will be added to styles.xml
var $fonts = array(
"StarSymbol"=>'<style:font-face style:name="StarSymbol" svg:font-family="StarSymbol"/>', // for bullets
"Bitstream Vera Sans Mono"=>'<style:font-face style:name="Bitstream Vera Sans Mono" svg:font-family="\'Bitstream Vera Sans Mono\'" style:font-family-generic="modern" style:font-pitch="fixed"/>', // for source code
);
/**
* @param null $source
*/
public function import($source=NULL) {
$auto_styles_ret = parent::importFromODT($this->automatic, 'office:automatic-styles');
$styles_ret = parent::importFromODTFile(DOKU_INC.'lib/plugins/odt/styles.xml', 'office:styles');
$master_styles_ret = parent::importFromODTFile(DOKU_INC.'lib/plugins/odt/styles.xml', 'office:master-styles');
if (!$auto_styles_ret || !$styles_ret || !$master_styles_ret) {
return false;
}
return true;
}
/**
* @param null $destination
*/
public function export($root_element) {
return parent::exportToODT($root_element);
}
/**
* Return style name for queired basic style $style.
*
* The class simply returns the corresponding style names
* used in styles.xml.
*
* @param string $style
* @return null|string
*/
public function getStyleName($style) {
switch ($style) {
case 'standard': return 'Standard';
case 'body': return 'Text_20_body';
case 'heading1': return 'Heading_20_1';
case 'heading2': return 'Heading_20_2';
case 'heading3': return 'Heading_20_3';
case 'heading4': return 'Heading_20_4';
case 'heading5': return 'Heading_20_5';
case 'list': return 'List_20_1';
case 'list content': return 'List_20_1_Content';
case 'list first': return 'List_20_1_Content_First';
case 'list last': return 'List_20_1_Content_Last';
case 'numbering': return 'Numbering_20_1';
case 'numbering content': return 'Numbering_20_1_Content';
case 'numbering first': return 'Numbering_20_1_Content_First';
case 'numbering last': return 'Numbering_20_1_Content_Last';
case 'table': return 'Table';
case 'table content': return 'Table_20_Contents';
case 'table heading': return 'Table_20_Heading';
case 'table header': return 'tableheader';
case 'table cell': return 'tablecell';
case 'tablealign center': return 'tablealigncenter';
case 'tablealign right': return 'tablealignright';
case 'tablealign left': return 'tablealignleft';
case 'preformatted': return 'Preformatted_20_Text';
case 'source code': return 'Source_20_Code';
case 'source file': return 'Source_20_File';
case 'horizontal line': return 'Horizontal_20_Line';
case 'footnote': return 'Footnote';
case 'footnote anchor': return 'Footnote_20_Anchor';
case 'footnote characters': return 'Footnote_20_Symbol';
case 'emphasis': return 'Emphasis';
case 'strong': return 'Strong_20_Emphasis';
case 'underline': return 'underline';
case 'sub': return 'sub';
case 'sup': return 'sup';
case 'del': return 'del';
case 'media': return 'media';
case 'media left': return 'medialeft';
case 'media right': return 'mediaright';
case 'media center': return 'mediacenter';
case 'legend center': return 'legendcenter';
case 'graphics': return 'Graphics';
case 'monospace': return 'Source_20_Text';
case 'table quotation1': return 'Table_Quotation1';
case 'table quotation2': return 'Table_Quotation2';
case 'table quotation3': return 'Table_Quotation3';
case 'table quotation4': return 'Table_Quotation4';
case 'table quotation5': return 'Table_Quotation5';
case 'cell quotation1': return 'Cell_Quotation1';
case 'cell quotation2': return 'Cell_Quotation2';
case 'cell quotation3': return 'Cell_Quotation3';
case 'cell quotation4': return 'Cell_Quotation4';
case 'cell quotation5': return 'Cell_Quotation5';
case 'first page': return 'pm1';
case 'internet link': return 'Internet_20_link';
case 'visited internet link': return 'Visited_20_Internet_20_Link';
case 'local link': return 'Local_20_link';
case 'visited local link': return 'Visited_20_Local_20_Link';
case 'contents heading': return 'Contents_20_Heading';
}
// Not supported basic style.
return NULL;
}
/**
* @param string $filename
* @return string
*/
function getMissingFonts($filename) {
$value = '';
$existing_styles = io_readFile($filename);
foreach ($this->fonts as $name=>$xml) {
if (strpos($existing_styles, 'style:name="'.$name.'"') === FALSE) {
$value .= $xml;
}
}
return $value;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,279 @@
<?php
require_once DOKU_PLUGIN . 'odt/ODT/ODTDocument.php';
require_once DOKU_PLUGIN . 'odt/ODT/ODTsettings.php';
/**
* ODTExport:
* Class containing static code for exporting/generating the ODT file.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*/
class ODTExport
{
/**
* Build the document from scratch.
* (code taken from old function 'document_end_scratch')
*
* @param ODTInternalParams $params
* @param string $meta
* @param string $userfields
* @return mixed
*/
protected static function buildFromScratch(ODTInternalParams $params, $meta=null, $userfields=null, $pagestyles=null){
// add defaults
$settings = new ODTSettings();
$params->ZIP->addData('mimetype', 'application/vnd.oasis.opendocument.text', 'mimetype');
$params->ZIP->addData('meta.xml', $meta);
$params->ZIP->addData('settings.xml', $settings->getContent());
$autostyles = $params->styleset->export('office:automatic-styles');
$commonstyles = $params->styleset->export('office:styles');
$masterstyles = $params->styleset->export('office:master-styles');
$value = '<' . '?xml version="1.0" encoding="UTF-8"?' . ">\n";
$value .= '<office:document-content ';
$value .= 'xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" ';
$value .= 'xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" ';
$value .= 'xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" ';
$value .= 'xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" ';
$value .= 'xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" ';
$value .= 'xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" ';
$value .= 'xmlns:xlink="http://www.w3.org/1999/xlink" ';
$value .= 'xmlns:dc="http://purl.org/dc/elements/1.1/" ';
$value .= 'xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" ';
$value .= 'xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" ';
$value .= 'xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" ';
$value .= 'xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" ';
$value .= 'xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" ';
$value .= 'xmlns:math="http://www.w3.org/1998/Math/MathML" ';
$value .= 'xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" ';
$value .= 'xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" ';
$value .= 'xmlns:ooo="http://openoffice.org/2004/office" ';
$value .= 'xmlns:ooow="http://openoffice.org/2004/writer" ';
$value .= 'xmlns:oooc="http://openoffice.org/2004/calc" ';
$value .= 'xmlns:dom="http://www.w3.org/2001/xml-events" ';
$value .= 'xmlns:xforms="http://www.w3.org/2002/xforms" ';
$value .= 'xmlns:xsd="http://www.w3.org/2001/XMLSchema" ';
$value .= 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ';
$value .= 'xmlns:rpt="http://openoffice.org/2005/report" ';
$value .= 'xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" ';
$value .= 'xmlns:xhtml="http://www.w3.org/1999/xhtml" ';
$value .= 'xmlns:grddl="http://www.w3.org/2003/g/data-view#" ';
$value .= 'xmlns:officeooo="http://openoffice.org/2009/office" ';
$value .= 'xmlns:tableooo="http://openoffice.org/2009/table" ';
$value .= 'xmlns:drawooo="http://openoffice.org/2010/draw" ';
$value .= 'xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" ';
$value .= 'xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" ';
$value .= 'xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" ';
$value .= 'xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" ';
$value .= 'xmlns:css3t="http://www.w3.org/TR/css3-text/" ';
$value .= 'office:version="1.2">';
$value .= '<office:scripts/>';
$value .= '<office:font-face-decls>';
$value .= '<style:font-face style:name="OpenSymbol" svg:font-family="OpenSymbol" style:font-charset="x-symbol"/>';
$value .= '<style:font-face style:name="StarSymbol1" svg:font-family="StarSymbol" style:font-charset="x-symbol"/>';
$value .= '<style:font-face style:name="StarSymbol" svg:font-family="StarSymbol"/>';
$value .= '<style:font-face style:name="Tahoma1" svg:font-family="Tahoma"/>';
$value .= '<style:font-face style:name="Lucida Sans Unicode" svg:font-family="&apos;Lucida Sans Unicode&apos;" style:font-pitch="variable"/>';
$value .= '<style:font-face style:name="Tahoma" svg:font-family="Tahoma" style:font-pitch="variable"/>';
$value .= '<style:font-face style:name="Times New Roman" svg:font-family="&apos;Times New Roman&apos;" style:font-family-generic="roman" style:font-pitch="variable"/>';
$value .= '</office:font-face-decls>';
$value .= $autostyles;
$value .= '<office:body>';
$value .= '<office:text>';
$value .= '<office:forms form:automatic-focus="false" form:apply-design-mode="false"/>';
$value .= '<text:sequence-decls>';
$value .= '<text:sequence-decl text:display-outline-level="0" text:name="Illustration"/>';
$value .= '<text:sequence-decl text:display-outline-level="0" text:name="Table"/>';
$value .= '<text:sequence-decl text:display-outline-level="0" text:name="Text"/>';
$value .= '<text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>';
$value .= '</text:sequence-decls>';
$value .= $userfields;
$value .= $params->content;
$value .= '</office:text>';
$value .= '</office:body>';
$value .= '</office:document-content>';
$params->ZIP->addData('content.xml', $value);
// Edit 'styles.xml'
$value = io_readFile(DOKU_PLUGIN.'odt/styles.xml');
// Insert new master page styles
$page = '';
foreach ($pagestyles as $name => $layout_name) {
$page .= '<style:master-page style:name="'.$name.'" style:page-layout-name="'.$layout_name.'"/>';
}
if ( !empty($page) ) {
$value = str_replace('</office:master-styles>', $page.'</office:master-styles>', $value);
}
// Add common styles.
$original = XMLUtil::getElement('office:styles', $value);
$value = str_replace($original, $commonstyles, $value);
// Add automatic styles.
$value = str_replace('<office:automatic-styles/>', $autostyles, $value);
$params->ZIP->addData('styles.xml', $value);
// build final manifest
$params->ZIP->addData('META-INF/manifest.xml', $params->manifest->getContent());
}
/**
* Build the document from the template.
* (code taken from old function 'document_end_scratch')
*
* @param ODTInternalParams $params
* @param string $meta
* @param string $userfields
* @return mixed
*/
protected static function buildFromODTTemplate(ODTInternalParams $params, $meta=null, $userfields=null, $pagestyles=null, $template=NULL, $tempDir=NULL){
// for the temp dir
global $ID;
if ($template == NULL || $tempDir == NULL) {
return;
}
// Temp dir
if (is_dir($tempDir)) { io_rmdir($tempDir,true); }
io_mkdir_p($tempDir);
// Extract template
try {
$ZIPextract = new \splitbrain\PHPArchive\Zip();
$ZIPextract->open($template);
$ZIPextract->extract($tempDir);
$ZIPextract->open($template);
$templateContents = $ZIPextract->contents();
} catch (\splitbrain\PHPArchive\ArchiveIOException $e) {
throw new Exception(' Error extracting the zip archive:'.$template.' to '.$tempDir);
}
// Replace metadata
io_saveFile($tempDir.'/meta.xml', $meta);
// Evtl. copy page format of first page to different style
$first_master = $params->styleset->getStyleAtIndex ('office:master-styles', 0);
if ($first_master != NULL &&
$first_master->getProperty('style-page-layout-name') != $params->document->getStyleName('first page')) {
// The master page of the template references a different page layout style
// then used by us for the first page. Copy the page format settings.
$source = $params->document->getStyle($params->document->getStyleName('first page'));
$dest = $params->document->getStyle($first_master->getProperty('style-page-layout-name'));
if ($source != NULL && $dest != NULL) {
$dest->setProperty('width', $source->getProperty('width'));
$dest->setProperty('height', $source->getProperty('height'));
$dest->setProperty('margin-top', $source->getProperty('margin-top'));
$dest->setProperty('margin-right', $source->getProperty('margin-right'));
$dest->setProperty('margin-bottom', $source->getProperty('margin-bottom'));
$dest->setProperty('margin-left', $source->getProperty('margin-left'));
}
}
$autostyles = $params->styleset->export('office:automatic-styles');
$commonstyles = $params->styleset->export('office:styles');
$masterstyles = $params->styleset->export('office:master-styles');
// Prepare content
$missingfonts = $params->styleset->getMissingFonts($tempDir.'/styles.xml');
// Insert content
$old_content = io_readFile($tempDir.'/content.xml');
if (strpos($old_content, 'DOKUWIKI-ODT-INSERT') !== FALSE) { // Replace the mark
self::replaceInFile('/<text:p[^>]*>DOKUWIKI-ODT-INSERT<\/text:p>/',
$params->content, $tempDir.'/content.xml', true);
} else { // Append to the template
self::replaceInFile('</office:text>', $params->content.'</office:text>', $tempDir.'/content.xml');
}
// Cut off unwanted content
if (strpos($old_content, 'DOKUWIKI-ODT-CUT-START') !== FALSE
&& strpos($old_content, 'DOKUWIKI-ODT-CUT-STOP') !== FALSE) {
self::replaceInFile('/DOKUWIKI-ODT-CUT-START.*DOKUWIKI-ODT-CUT-STOP/',
'', $tempDir.'/content.xml', true);
}
// Insert userfields
if (strpos($old_content, "text:user-field-decls") === FALSE) { // no existing userfields
self::replaceInFile('/<office:text([^>]*)>/U', '<office:text\1>'.$userfields, $tempDir.'/content.xml', TRUE);
} else {
self::replaceInFile('</text:user-field-decls>', substr($userfields,23), $tempDir.'/content.xml');
}
// Insert styles & fonts
$value = io_readFile($tempDir.'/content.xml');
$original = XMLUtil::getElement('office:automatic-styles', $value);
self::replaceInFile($original, $autostyles, $tempDir.'/content.xml');
$value = io_readFile($tempDir.'/styles.xml');
$original = XMLUtil::getElement('office:automatic-styles', $value);
self::replaceInFile($original, $autostyles, $tempDir.'/styles.xml');
$value = io_readFile($tempDir.'/styles.xml');
$original = XMLUtil::getElement('office:styles', $value);
self::replaceInFile($original, $commonstyles, $tempDir.'/styles.xml');
self::replaceInFile('</office:font-face-decls>', $missingfonts.'</office:font-face-decls>', $tempDir.'/styles.xml');
// Insert page styles
$page = '';
foreach ($pagestyles as $name => $layout_name) {
$page .= '<style:master-page style:name="'.$name.'" style:page-layout-name="'.$layout_name.'"/>';
}
if ( !empty($page) ) {
self::replaceInFile('</office:master-styles>', $page.'</office:master-styles>', $tempDir.'/styles.xml');
}
// Add manifest data
self::replaceInFile('</manifest:manifest>', $params->manifest->getExtraContent() . '</manifest:manifest>', $tempDir . '/META-INF/manifest.xml');
// Build the Zip
foreach ($templateContents as $fileInfo) {
if (!$fileInfo->getIsdir()) {
$params->ZIP->addFile($tempDir.'/'.$fileInfo->getPath(), $fileInfo);
}
}
io_rmdir($tempDir,true);
}
/**
* Build the document from the template.
* (code taken from old function 'document_end_scratch')
*
* @param ODTInternalParams $params
* @param string $meta
* @param string $userfields
* @return mixed
*/
public static function buildZIPFile(ODTInternalParams $params, $meta=null, $userfields=null, $pagestyles=null, $template=NULL, $tempDir=NULL){
if ($template == NULL ) {
self::buildFromScratch($params, $meta, $userfields, $pagestyles);
} else {
self::buildFromODTTemplate($params, $meta, $userfields, $pagestyles, $template, $tempDir);
}
$params->ZIP->close();
}
/**
* @param string $from
* @param string $to
* @param string $file
* @param bool $regexp
*/
protected static function replaceInFile($from, $to, $file, $regexp=FALSE) {
$value = io_readFile($file);
if ($regexp) {
$value = preg_replace($from, $to, $value);
} else {
$value = str_replace($from, $to, $value);
}
$file_f = fopen($file, 'w');
fwrite($file_f, $value);
fclose($file_f);
}
}

View File

@@ -0,0 +1,82 @@
<?php
require_once DOKU_PLUGIN . 'odt/ODT/ODTDocument.php';
/**
* ODTFootnote:
* Class containing static code for handling footnotes.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author Andreas Gohr
*/
class ODTFootnote
{
/**
* Open/start a footnote.
*
* All following content will go to the footnote instead of
* the document. To achieve this the previous content
* is moved to $store and $content is cleared
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function footnoteOpen(ODTInternalParams $params, $element=NULL, $attributes=NULL) {
// $element and $attributes are actually unused
// Move current content to store and record footnote
$params->document->store = $params->content;
$params->content = '';
$note = new ODTElementNote();
$params->document->state->enter($note);
$note->setHTMLElement ($element);
}
/**
* Close/end a footnote.
*
* All content is moved to the $footnotes array and the old
* content is restored from $store again.
*
* @author Andreas Gohr
*/
function footnoteClose(ODTInternalParams $params) {
// Close any open paragraph first
$params->document->paragraphClose();
ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement());
$params->document->closeCurrentElement();
// Recover footnote into the stack and restore old content
$footnote = $params->content;
$params->content = $params->document->store;
$params->document->store = '';
// Check to see if this footnote has been seen before
$i = array_search($footnote, $params->document->footnotes);
$label = ($i+1).')';
if ($i === false) {
$i = count($params->document->footnotes);
$label = ($i+1).')';
// Its a new footnote, add it to the $footnotes array
$params->document->footnotes[$i] = $footnote;
$params->content .= '<text:note text:id="ftn'.$i.'" text:note-class="footnote">';
$params->content .= '<text:note-citation text:label="'.$label.'">'.$label.'</text:note-citation>';
$params->content .= '<text:note-body>';
$params->content .= $footnote;
$params->content .= '</text:note-body>';
$params->content .= '</text:note>';
} else {
// Seen this one before - just reference it
$params->document->spanOpen($params->document->getStyleName('footnote anchor'));
$params->content .= '<text:note-ref text:note-class="footnote" text:reference-format="text" text:ref-name="ftn'.$i.'">'.$label.'</text:note-ref>';
$params->document->spanClose();
}
// Only for debugging...
//$params->document->trace_dump .= $params->document->state->toString();
}
}

View File

@@ -0,0 +1,386 @@
<?php
/**
* ODTFrame: Frame handling.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
/** Include ODTDocument.php */
require_once DOKU_PLUGIN . 'odt/ODT/ODTDocument.php';
/**
* ODTFrame:
* Class containing static code for handling frames.
*
* @package ODT\Frame
*/
class ODTFrame
{
static $frameCount = 0;
static $fields = array('background-color' => 'fo:background-color',
'fill-color' => 'draw:fill-color',
'fill' => 'draw:fill',
'stroke-color' => 'svg:stroke-color',
'stroke' => 'draw:stroke',
'stroke-width' => 'svg:stroke-width',
'border' => 'fo:border',
'border-left' => 'fo:border-left',
'border-right' => 'fo:border-right',
'border-top' => 'fo:border-top',
'border-bottom' => 'fo:border-bottom',
'padding-left' => 'fo:padding-left',
'padding-right' => 'fo:padding-right',
'padding-top' => 'fo:padding-top',
'padding-bottom' => 'fo:padding-bottom',
'margin-left' => 'fo:margin-left',
'margin-right' => 'fo:margin-right',
'margin-top' => 'fo:margin-top',
'margin-bottom' => 'fo:margin-bottom',
'vertical-align' => 'draw:textarea-vertical-align',
'horizontal-align' => 'draw:textarea-horizontal-align',
'min-height' => 'fo:min-height',
'background-transparency' => 'style:background-transparency',
'textarea-horizontal-align' => 'draw:textarea-horizontal-align',
'run-through' => 'style:run-through',
'vertical-pos' => 'style:vertical-pos',
'vertical-rel' => 'style:vertical-rel',
'horizontal-pos' => 'style:horizontal-pos',
'horizontal-rel' => 'style:horizontal-rel',
'wrap' => 'style:wrap',
'number-wrapped-paragraphs' => 'style:number-wrapped-paragraphs',
'wrap-influence-on-position' => 'draw:wrap-influence-on-position'
);
/**
* This function opens a textbox in a frame using CSS.
*
* The currently supported CSS properties are:
* background-color, color, padding, margin, display, border-radius, min-height.
* The background-image is simulated using a picture frame.
* FIXME: Find a way to successfuly use the background-image in the graphic style (see comments).
*
* The text box should be closed by calling 'closeTextBox()'.
*
* @param ODTInternalParams $params Commom params.
* @param string $element The element name, e.g. "div"
* @param string $attributes The attributes belonging o the element, e.g. 'class="example"'
*/
public static function openTextBoxUseCSS (ODTInternalParams $params, $element=NULL, $attributes=NULL) {
$frame = $params->document->state->getCurrentFrame();
if ($frame != NULL) {
// Do not open a nested frame as this will make the content ofthe nested frame disappear.
//return;
}
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
$params->elementObj = $params->htmlStack->getCurrentElement();
self::openTextBoxUseProperties ($params, $properties);
}
/**
* This function opens a textbox in a frame.
*
* The currently supported CSS properties are:
* background-color, color, padding, margin, display, border-radius, min-height.
* The background-image is simulated using a picture frame.
* FIXME: Find a way to successfuly use the background-image in the graphic style (see comments).
*
* The text box should be closed by calling 'closeTextBox()'.
*
* @param ODTInternalParams $params Commom params.
* @param array $properties Properties to use for creating the text box
* @param string $element The element name, e.g. "div"
* @param string $attributes The attributes belonging o the element, e.g. 'class="example"'
*/
public static function openTextBoxUseProperties (ODTInternalParams $params, $properties, $element=NULL, $attributes=NULL) {
// Encode frame
self::openFrameUseProperties ($params, $properties, $element, $attributes);
// Create text box
$box = new ODTElementTextBox();
$box_attrs = '';
// If required use round corners.
if ( !empty($properties ['border-radius']) )
$box_attrs .= 'draw:corner-radius="'.$properties ['border-radius'].'"';
$box->setAttributes($box_attrs);
$params->document->state->enter($box);
// Encode box
$params->content .= $box->getOpeningTag($params);
}
/**
* This function closes a textbox (previously opened with openTextBoxUseProperties()).
*
* @param ODTInternalParams $params Commom params.
*/
public static function closeTextBox (ODTInternalParams $params) {
// Close paragraph (if open)
$params->document->paragraphClose();
// Close text box
$params->document->closeCurrentElement();
// Close frame
self::closeFrame($params);
}
/**
* This function opens a multi column frame/text box according to the
* parameters in $properties. Call 'closeMultiColumnTextBox()' to
* close the text box.
*
* @param ODTInternalParams $params Commom params.
* @param array $properties Properties to use
* @see ODTUnknownStyle::createMultiColumnFrameStyle for information
* about supported $properties.
*/
public static function openMultiColumnTextBoxUseProperties (ODTInternalParams $params, $properties) {
if ($element == NULL) {
$element = 'div';
}
// Create style name.
$style_obj = ODTUnknownStyle::createMultiColumnFrameStyle ($properties);
$params->document->addAutomaticStyle($style_obj);
$style_name = $style_obj->getProperty('style-name');
$width_abs = $params->document->getAbsWidthMindMargins (100);
// Group the frame so that they are stacked one on each other.
$params->document->paragraphClose();
$params->document->paragraphOpen();
// Draw a frame with a text box in it. the text box will be left opened
// to grow with the content (requires fo:min-height in $style_name).
if ($params->elementObj == NULL) {
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
}
// Create frame
$frame = new ODTElementFrame($style_name);
self::$frameCount++;
$frame_attrs = 'draw:name="Frame'.self::$frameCount.'" text:anchor-type="paragraph" svg:width="'.$width_abs.'cm" draw:z-index="0">';
$frame->setAttributes($frame_attrs);
$params->document->state->enter($frame);
$frame->setHTMLElement ($element);
// Encode frame
$params->content .= $frame->getOpeningTag($params);
// Create text box
$box = new ODTElementTextBox();
$box_attrs = 'fo:min-height="1pt"';
$box->setAttributes($box_attrs);
$params->document->state->enter($box);
// Encode box
$params->content .= $box->getOpeningTag($params);
}
/**
* This function closes a multi column frame (previously opened with _odtOpenMultiColumnFrame).
*
* @param ODTInternalParams $params Commom params.
*/
public static function closeMultiColumnTextBox (ODTInternalParams $params) {
// Close paragraph (if open)
$params->document->paragraphClose();
// Close text box
$params->document->closeCurrentElement();
// Close frame
ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement());
$params->document->closeCurrentElement();
$params->document->paragraphClose();
$params->document->div_z_index -= 5;
}
/**
* This function opens a textbox in a frame.
*
* The currently supported CSS properties are:
* background-color, color, padding, margin, display, border-radius, min-height.
* The background-image is simulated using a picture frame.
* FIXME: Find a way to successfuly use the background-image in the graphic style (see comments).
*
* The text box should be closed by calling 'closeTextBox()'.
*
* @param ODTInternalParams $params Commom params.
* @param array $properties Properties to use for creating the frame
* @param string $element The element name, e.g. "div"
* @param string $attributes The attributes belonging o the element, e.g. 'class="example"'
*/
public static function openFrameUseProperties (ODTInternalParams $params, $properties, $element=NULL, $attributes=NULL) {
$frame = $params->document->state->getCurrentFrame();
if ($frame != NULL) {
// Do not open a nested frame as this will make the content ofthe nested frame disappear.
//return;
}
if ($element == NULL) {
$element = 'div';
}
$elementObj = $params->elementObj;
// If we are not in a paragraph then open one.
$inParagraph = $params->document->state->getInParagraph();
if (!$inParagraph) {
$params->document->paragraphOpen();
}
$position = $properties ['position'];
$picture = $properties ['background-image'];
$pic_positions = preg_split ('/\s/', $properties ['background-position']);
//$min_height = $properties ['min-height'];
$width = $properties ['width'];
$pic_link = '';
$pic_width = '';
$pic_height = '';
if ( !empty ($picture) ) {
// If a picture/background-image is set in the CSS, than we insert it manually here.
// This is a workaround because ODT does not support the background-image attribute in a span.
$pic_link = $params->document->addFileAsPicture($picture);
list($pic_width, $pic_height) = ODTUtility::getImageSizeString($picture, NULL, NULL, true, $params->units);
}
if ( empty ($width) ) {
$width = '100%';
}
if ( !empty($pic_positions [0]) ) {
$pic_positions [0] = $params->document->toPoints($pic_positions [0], 'x');
}
//if ( empty($min_height) ) {
// $min_height = '1pt';
//}
// Get anchor type
$anchor_type = 'paragraph';
if (!empty($properties ['anchor-type'])) {
$anchor_type = $properties ['anchor-type'];
}
// Get X and Y position.
// X and Y position can be set using 'x' or 'left' and 'y' or 'top'.
$svgX = null;
$svgY = null;
if (!empty($properties ['x'])) {
$svgX = $properties ['x'];
}
if (!empty($properties ['left'])) {
$svgX = $properties ['left'];
}
if (!empty($properties ['y'])) {
$svgY = $properties ['y'];
}
if (!empty($properties ['top'])) {
$svgY = $properties ['top'];
}
// Adjust properties for CSS property 'position' if given
switch ($position) {
case 'absolute':
$anchor_type = 'page';
break;
case 'relative':
$anchor_type = 'paragraph';
break;
case 'static':
$anchor_type = 'paragraph';
$svgX = '0cm';
$svgY = '0cm';
break;
}
// Add our styles.
$style_name = ODTStyle::getNewStylename('Frame');
$style = '<style:style style:name="'.$style_name.'_text_frame" style:family="graphic" style:parent-style-name="Frame">';
$style .= '<style:graphic-properties ';
foreach (self::$fields as $name => $odtName) {
if (!empty($properties [$name])) {
$style .= $odtName.'="'.$properties [$name].'" ';
}
}
$style .= '>';
$style .= '</style:graphic-properties>';
$style .= '</style:style>';
// Add style to our document
// (as unknown style because style-family graphic is not supported)
$style_obj = ODTUnknownStyle::importODTStyle($style);
$params->document->addAutomaticStyle($style_obj);
// Draw a frame with a text box in it. the text box will be left opened
// to grow with the content (requires fo:min-height in $style_name).
if ($elementObj == NULL) {
$throwAway = array();
ODTUtility::openHTMLElement ($params, $throwAway, $element, $attributes);
}
// Create frame
$frame = new ODTElementFrame($style_name.'_text_frame');
self::$frameCount++;
/*$frame_attrs .= 'draw:name="Frame'.self::$frameCount.'"
text:anchor-type="'.$anchor_type.'"
svg:width="'.$width.'" svg:min-height="'.$min_height.'"
draw:z-index="'.($params->document->div_z_index + 0).'"';*/
$frame_attrs .= 'draw:name="Frame'.self::$frameCount.'"
text:anchor-type="'.$anchor_type.'"
svg:width="'.$width.'"
draw:z-index="'.($params->document->div_z_index + 0).'"';
if ($svgX !== NULL) {
$frame_attrs .= ' svg:x="'.$svgX.'"';
}
if ($svgY !== NULL) {
$frame_attrs .= ' svg:y="'.$svgY.'"';
}
if (!empty($properties ['min-height'])) {
$frame_attrs .= ' svg:min-height="'.$properties ['min-height'].'"';
}
if (!empty($properties ['height'])) {
$frame_attrs .= ' svg:height="'.$properties ['height'].'"';
}
$frame->setAttributes($frame_attrs);
$params->document->state->enter($frame);
$frame->setHTMLElement ($element);
// Encode frame
$params->content .= $frame->getOpeningTag($params);
$params->document->div_z_index += 1;
}
/**
* This function closes a textbox (previously opened with openTextBoxUseProperties()).
*
* @param ODTInternalParams $params Commom params.
*/
public static function closeFrame (ODTInternalParams $params) {
$frame = $params->document->state->getCurrentFrame();
if ($frame == NULL) {
// ??? Error. Not table found.
return;
}
// Close paragraph (if open)
$params->document->paragraphClose();
// Eventually adjust frame width.
$frame->adjustWidth ($params);
// Close frame
$element = $params->document->state->getHTMLElement();
ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement());
$params->document->closeCurrentElement();
// Do not close the open paragraph here as it may lead to extra empty lines.
$params->document->div_z_index -= 1;
}
}

View File

@@ -0,0 +1,92 @@
<?php
require_once DOKU_PLUGIN . 'odt/ODT/ODTDocument.php';
/**
* ODTHeading:
* Class containing static code for handling headings.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*/
class ODTHeading
{
/**
* Render a heading
*
* @param string $text the text to display
* @param int $level header level
* @param int $pos byte position in the original source
*/
static public function heading(ODTInternalParams $params, $text, $level, $element=NULL, $attributes=NULL){
// Close any open paragraph first
$params->document->paragraphClose();
$hid = self::headerToLink($params->document, $text, true);
$TOCRef = $params->document->buildTOCReferenceID($text);
$style = $params->document->getStyleName('heading'.$level);
// Change page format if pending
if ( $params->document->pageFormatChangeIsPending() ) {
$pageStyle = $params->document->doPageFormatChange($style);
if ( $pageStyle != NULL ) {
$style = $pageStyle;
// Delete pagebreak, the format change will also introduce a pagebreak.
$params->document->setPagebreakPending(false);
}
}
// Insert pagebreak if pending
if ( $params->document->pagebreakIsPending() ) {
$style = $params->document->createPagebreakStyle ($style);
$params->document->setPagebreakPending(false);
}
$params->content .= '<text:h text:style-name="'.$style.'" text:outline-level="'.$level.'">';
// Insert page bookmark if requested and not done yet.
$params->document->insertPendingPageBookmark();
$params->content .= '<text:bookmark-start text:name="'.$TOCRef.'"/>';
$params->content .= '<text:bookmark-start text:name="'.$hid.'"/>';
$params->content .= $params->document->replaceXMLEntities($text);
$params->content .= '<text:bookmark-end text:name="'.$TOCRef.'"/>';
$params->content .= '<text:bookmark-end text:name="'.$hid.'"/>';
$params->content .= '</text:h>';
// Do not add headings in frames
$frame = $params->document->state->getCurrentFrame();
if ($frame == NULL) {
$params->document->tocAddItemInternal($TOCRef, $hid, $text, $level);
}
}
/**
* Creates a linkid from a headline
*
* @param string $title The headline title
* @param boolean $create Create a new unique ID?
* @return string
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
static protected function headerToLink(ODTDocument $doc, $title,$create=false) {
// FIXME: not DokuWiki dependant function woud be nicer...
$title = str_replace(':','',cleanID($title));
$title = ltrim($title,'0123456789._-');
if(empty($title)) {
$title='section';
}
if($create){
// Make sure tiles are unique
$num = '';
while($doc->headerExists($title.$num)){
($num) ? $num++ : $num = 1;
}
$title = $title.$num;
$doc->addHeader($title);
}
return $title;
}
}

View File

@@ -0,0 +1,275 @@
<?php
require_once DOKU_PLUGIN . 'odt/ODT/ODTDocument.php';
/**
* ODTImage:
* Class containing static code for handling images.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*/
class ODTImage
{
/**
* Adds an image $src to the document.
*
* @param string $src The path to the image file
* @param string $width Width of the picture (NULL=original size)
* @param string $height Height of the picture (NULL=original size)
* @param string $align Alignment
* @param string $title Title
* @param string $style Optional "draw:style-name"
* @param boolean $returnonly Only return code
*
* @see ODTImage::addImage for a detailed description
*/
public static function addImage(ODTInternalParams $params, $src, $width = NULL, $height = NULL, $align = NULL, $title = NULL, $style = NULL, $returnonly = false){
static $z = 0;
$encoded = '';
if (file_exists($src)) {
list($ext,$mime) = mimetype($src);
$name = 'Pictures/'.md5($src).'.'.$ext;
$params->document->addFile($name, $mime, io_readfile($src,false));
} else {
$name = $src;
}
// make sure width and height are available
if (!$width && !$height) {
list($width, $height) = ODTUtility::getImageSizeString($src, $width, $height, true, $params->units);
} else {
list($width, $height) = ODTUtility::getImageSizeString($src, $width, $height, false, $params->units);
}
if($align){
$anchor = 'paragraph';
}else{
$anchor = 'as-char';
}
if (empty($style) || !$params->document->styleExists($style)) {
if (!empty($align)) {
$style = $params->document->getStyleName('media '.$align);
} else {
$style = $params->document->getStyleName('media');
}
}
// Open paragraph if necessary
if (!$params->document->state->getInParagraph()) {
$params->document->paragraphOpen();
}
if ($title) {
$encoded .= '<draw:frame draw:style-name="'.$style.'" draw:name="'.$params->document->replaceXMLEntities($title).' Legend"
text:anchor-type="'.$anchor.'" draw:z-index="0" svg:width="'.$width.'">';
$encoded .= '<draw:text-box>';
$encoded .= '<text:p text:style-name="'.$params->document->getStyleName('legend center').'">';
}
if (!empty($title)) {
$encoded .= '<draw:frame draw:style-name="'.$style.'" draw:name="'.$params->document->replaceXMLEntities($title).'"
text:anchor-type="'.$anchor.'" draw:z-index="'.$z.'"
svg:width="'.$width.'" svg:height="'.$height.'" >';
} else {
$encoded .= '<draw:frame draw:style-name="'.$style.'" draw:name="'.$z.'"
text:anchor-type="'.$anchor.'" draw:z-index="'.$z.'"
svg:width="'.$width.'" svg:height="'.$height.'" >';
}
$encoded .= '<draw:image xlink:href="'.$params->document->replaceXMLEntities($name).'"
xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad"/>';
$encoded .= '</draw:frame>';
if ($title) {
$encoded .= $params->document->replaceXMLEntities($title).'</text:p></draw:text-box></draw:frame>';
}
if($returnonly) {
return $encoded;
} else {
$params->content .= $encoded;
}
$z++;
}
/**
* Adds the content of $string as a SVG picture file to the document.
* The link name which can be used for the ODT draw:image xlink:href
* is returned. The caller is responsible for creating the frame and image tag
* but therefore has full control over it. This means he can also set parameters
* in the odt frame and image tag which can not be changed using the function _odtAddImage.
*
* @author LarsDW223
*
* @param string $string SVG code to add
* @return string
*/
public static function addStringAsSVGImageFile(ODTDocument $doc, $string) {
if ( empty($string) ) { return; }
$ext = '.svg';
$mime = '.image/svg+xml';
$name = 'Pictures/'.md5($string).'.'.$ext;
$doc->addFile($name, $mime, $string);
return $name;
}
/**
* Adds the content of $string as a SVG picture to the document.
* The other parameters behave in the same way as in _odtAddImage.
*
* @author LarsDW223
*
* @param string $string
* @param $width
* @param $height
* @param $align
* @param $title
* @param $style
*/
function addStringAsSVGImage(ODTInternalParams $params, $string, $width = NULL, $height = NULL, $align = NULL, $title = NULL, $style = NULL) {
if ( empty($string) ) { return; }
$name = self::addStringAsSVGImageFile($params->document, $string);
// make sure width and height are available
if (!$width || !$height) {
list($width, $height) = ODTUtility::getImageSizeString($string, $width, $height, true, $params->units);
}
if($align){
$anchor = 'paragraph';
}else{
$anchor = 'as-char';
}
if (!$style or !$params->document->styleExists($style)) {
$style = $params->document->getStyleName('media '.$align);
}
// Open paragraph if necessary
if (!$params->document->state->getInParagraph()) {
$params->document->paragraphOpen();
}
if ($title) {
$params->content .= '<draw:frame draw:style-name="'.$style.'" draw:name="'.$params->document->replaceXMLEntities($title).' Legend"
text:anchor-type="'.$anchor.'" draw:z-index="0" svg:width="'.$width.'">';
$params->content .= '<draw:text-box>';
$params->document->paragraphOpen($$params->document->getStyleName('legend center'));
}
$params->content .= '<draw:frame draw:style-name="'.$style.'" draw:name="'.$params->document->replaceXMLEntities($title).'"
text:anchor-type="'.$anchor.'" draw:z-index="0"
svg:width="'.$width.'" svg:height="'.$height.'" >';
$params->content .= '<draw:image xlink:href="'.$params->document->replaceXMLEntities($name).'"
xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad"/>';
$params->content .= '</draw:frame>';
if ($title) {
$params->content .= $params->document->replaceXMLEntities($title);
$params->document->paragraphClose();
$params->content .= '</draw:text-box></draw:frame>';
}
}
/**
* Adds an image $src to the document using the parameters set in $properties.
* The actually supported properties are:
* - width and height
* - title
* - background-color
* - margin-left, margin-right, margin-top, margin-bottom
*
* @param string $src The path to the image file
* @param array $properties Properties (width, height... see ODTImage::addImageUseProperties)
* @param boolean $returnonly Only return code
*/
public static function addImageUseProperties(ODTInternalParams $params, $src, array $properties, $returnonly = false){
static $z = 0;
ODTUtility::adjustValuesForODT ($properties, $params->units);
$width = $properties ['width'];
$height = $properties ['height'];
$title = $properties ['title'];
$bg_color = $properties ['background-color'];
$encoded = '';
if (file_exists($src)) {
list($ext,$mime) = mimetype($src);
$name = 'Pictures/'.md5($src).'.'.$ext;
$params->document->addFile($name, $mime, io_readfile($src,false));
} else {
$name = $src;
}
// make sure width and height are available
if (!$width || !$height) {
list($width, $height) = ODTUtility::getImageSizeString($src, $width, $height, true, $params->units);
} else {
// Adjust values for ODT
$width = $params->document->toPoints($width, 'x').'pt';
$height = $params->document->toPoints($height, 'y').'pt';
}
if($align){
$anchor = 'paragraph';
}else{
$anchor = 'as-char';
}
// Open paragraph if necessary
if (!$params->document->state->getInParagraph()) {
$params->document->paragraphOpen();
}
// Define graphic style for picture
$style_name = ODTStyle::getNewStylename('span_graphic');
$image_style = '<style:style style:name="'.$style_name.'" style:family="graphic" style:parent-style-name="'.$params->document->getStyleName('graphics').'">';
$image_style .= '<style:graphic-properties style:vertical-pos="middle" style:vertical-rel="text" style:horizontal-pos="from-left" style:horizontal-rel="paragraph" fo:background-color="'.$bg_color.'" style:flow-with-text="true"';
if (!empty($properties ['margin-left'])) {
$image_style .= ' fo:margin-left="'.$properties ['margin-left'].'"';
}
if (!empty($properties ['margin-right'])) {
$image_style .= ' fo:margin-right="'.$properties ['margin-right'].'"';
}
if (!empty($properties ['margin-top'])) {
$image_style .= ' fo:margin-top="'.$properties ['margin-top'].'"';
}
if (!empty($properties ['margin-bottom'])) {
$image_style .= ' fo:margin-bottom="'.$properties ['margin-bottom'].'"';
}
$image_style .= '></style:graphic-properties></style:style>';
// Add style and image to our document
// (as unknown style because style-family graphic is not supported)
$style_obj = ODTUnknownStyle::importODTStyle($image_style);
$params->document->addAutomaticStyle($style_obj);
if ($title) {
$encoded .= '<draw:frame draw:style-name="'.$style_name.'" draw:name="'.$params->document->replaceXMLEntities($title).' Legend"
text:anchor-type="'.$anchor.'" draw:z-index="0" svg:width="'.$width.'">';
$encoded .= '<draw:text-box>';
$encoded .= '<text:p text:style-name="'.$params->document->getStyleName('legend center').'">';
}
if (!empty($title)) {
$encoded .= '<draw:frame draw:style-name="'.$style_name.'" draw:name="'.$params->document->replaceXMLEntities($title).'"
text:anchor-type="'.$anchor.'" draw:z-index="'.$z.'"
svg:width="'.$width.'" svg:height="'.$height.'" >';
} else {
$encoded .= '<draw:frame draw:style-name="'.$style_name.'" draw:name="'.$z.'"
text:anchor-type="'.$anchor.'" draw:z-index="'.$z.'"
svg:width="'.$width.'" svg:height="'.$height.'" >';
}
$encoded .= '<draw:image xlink:href="'.$params->document->replaceXMLEntities($name).'"
xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad"/>';
$encoded .= '</draw:frame>';
if ($title) {
$encoded .= $params->document->replaceXMLEntities($title).'</text:p></draw:text-box></draw:frame>';
}
if($returnonly) {
return $encoded;
} else {
$params->content .= $encoded;
}
$z++;
}
}

View File

@@ -0,0 +1,897 @@
<?php
require_once DOKU_PLUGIN . 'odt/ODT/ODTDocument.php';
/**
* ODTImport:
* Class containing static code for importing ODT or CSS code.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*/
class ODTImport
{
static public $trace_dump = NULL;
static protected $internalRegs = array('heading1' => array('element' => 'h1', 'attributes' => NULL),
'heading2' => array('element' => 'h2', 'attributes' => NULL),
'heading3' => array('element' => 'h3', 'attributes' => NULL),
'heading4' => array('element' => 'h4', 'attributes' => NULL),
'heading5' => array('element' => 'h5', 'attributes' => NULL),
'horizontal line' => array('element' => 'hr', 'attributes' => NULL),
'body' => array('element' => 'p', 'attributes' => NULL),
'emphasis' => array('element' => 'em', 'attributes' => NULL, 'compare' => true),
'strong' => array('element' => 'strong', 'attributes' => NULL, 'compare' => true),
'underline' => array('element' => 'u', 'attributes' => NULL, 'compare' => true),
'monospace' => array('element' => 'code', 'attributes' => NULL),
'del' => array('element' => 'del', 'attributes' => NULL, 'compare' => true),
'preformatted' => array('element' => 'pre', 'attributes' => NULL),
'source code' => array('element' => 'pre', 'attributes' => 'class="code"'),
'source file' => array('element' => 'pre', 'attributes' => 'class="file"'),
);
static protected $table_styles = array('table' => array('element' => 'table', 'attributes' => NULL),
'table header' => array('element' => 'th', 'attributes' => NULL),
'table cell' => array('element' => 'td', 'attributes' => NULL)
);
static protected $link_styles = array(
'internet link' => array('element' => 'a',
'attributes' => NULL,
'pseudo-class' => 'link'),
'visited internet link' => array('element' => 'a',
'attributes' => NULL,
'pseudo-class' => 'visited'),
'local link' => array('element' => 'a',
'attributes' => 'class="wikilink1"',
'pseudo-class' => 'link'),
'visited local link' => array('element' => 'a',
'attributes' => 'class="wikilink1"',
'pseudo-class' => 'visited'),
);
/**
* Import CSS code.
* This is the CSS code import for the new API.
* That means in this function the CSS code is only parsed and stored
* but not immediately imported as styles like in the old API.
*
* The function can be called multiple times.
* All CSS code is handled like being appended.
*
* @param string $cssCode The CSS code to be imported
*/
static protected function importCSSCodeInternal (ODTInternalParams $params, $isFile, $CSSSource, $mediaSel=NULL, $lengthCallback=NULL, $URLCallback=NULL) {
if ($params->import == NULL) {
// No CSS imported yet. Create object.
$params->import = new cssimportnew();
if ( $params->import == NULL ) {
return;
}
$params->import->setMedia ($mediaSel);
}
if ($isFile == false) {
$params->import->importFromString($CSSSource);
} else {
$params->import->importFromFile($CSSSource);
}
// Call adjustLengthValues to make our callback function being called for every
// length value imported. This gives us the chance to convert it once from
// pixel to points.
if ($lengthCallback != NULL) {
$params->import->adjustLengthValues ($lengthCallback);
}
// Call replaceURLPrefixes to make the callers (renderer/page.php) callback
// function being called for every URL to convert it to an absolute path.
if ($URLCallback != NULL) {
$params->import->replaceURLPrefixes ($URLCallback);
}
}
/**
* Import CSS code from a file.
*
* @param ODTInternalParams $params Common params
* @param string $CSSTemplate String containing the path and file name of the CSS file to import
* @param string $media_sel String containing the media selector to use for import (e.g. 'print' or 'screen')
* @param callable $callback Callback for adjusting length values
*/
static public function importCSSFromFile (ODTInternalParams $params, $CSSTemplate, $media_sel=NULL, $lengthCallback=NULL, $URLCallback=NULL, $registrations=NULL, $importStyles=true, $listAlign='right') {
self::importCSSCodeInternal ($params, true, $CSSTemplate, $media_sel, $lengthCallback, $URLCallback);
if ($importStyles) {
self::import_styles_from_css ($params, $media_sel, $registrations, $listAlign);
}
}
/**
* Import CSS code for styles from a string.
*
* @param string $cssCode The CSS code to import
* @param string $mediaSel The media selector to use e.g. 'print'
* @param string $mediaPath Local path to media files
*/
static public function importCSSFromString(ODTInternalParams $params, $cssCode, $media_sel=NULL, $lengthCallback=NULL, $URLCallback=NULL, $registrations=NULL, $importStyles=true, $listAlign='right')
{
self::importCSSCodeInternal ($params, false, $cssCode, $media_sel, $lengthCallback, $URLCallback);
if ($importStyles) {
self::import_styles_from_css ($params, $media_sel, $registrations, $listAlign);
}
}
static protected function importQuotationStyles(ODTInternalParams $params, cssdocument $htmlStack) {
// Reset stack to saved root so next importStyle
// will have the same conditions
$htmlStack->restoreToRoot ();
$disabled = array();
$disabled ['margin'] = 1;
$disabled ['margin-left'] = 1;
$disabled ['margin-right'] = 1;
$disabled ['margin-top'] = 1;
$disabled ['margin-bottom'] = 1;
$disabled ['padding'] = 1;
$disabled ['padding-left'] = 1;
$disabled ['padding-right'] = 1;
$disabled ['padding-top'] = 1;
$disabled ['padding-bottom'] = 1;
for ($level = 1 ; $level < 6 ; $level++) {
// Push our element to import on the stack
$htmlStack->open('blockquote');
$toMatch = $htmlStack->getCurrentElement();
$properties = array();
$params->import->getPropertiesForElement($properties, $toMatch, $params->units);
if (count($properties) == 0) {
// Nothing found. Go to next, DO NOT change existing style!
continue;
}
// Adjust values for ODT
ODTUtility::adjustValuesForODT ($properties, $params->units);
$name = $params->styleset->getStyleName('table quotation'.$level);
$style = $params->styleset->getStyle($name);
if ($style != NULL ) {
if ($level == 1) {
$style->importProperties($properties);
} else {
$style->importProperties($properties, $disabled);
}
}
$name = $params->styleset->getStyleName('cell quotation'.$level);
$style = $params->styleset->getStyle($name);
if ($style != NULL ) {
$style->importProperties($properties);
}
}
// Reset stack to saved root so next importStyle
// will have the same conditions
$htmlStack->restoreToRoot ();
}
static protected function setListStyleImage (ODTInternalParams $params, $style, $level, $file) {
$odt_file = $params->document->addFileAsPicture($file);
if ( $odt_file != NULL ) {
$style->setPropertyForLevel($level, 'list-level-style', 'image');
$style->setPropertyForLevel($level, 'href', $odt_file);
$style->setPropertyForLevel($level, 'type', 'simple');
$style->setPropertyForLevel($level, 'show', 'embed');
$style->setPropertyForLevel($level, 'actuate', 'onLoad');
$style->setPropertyForLevel($level, 'vertical-pos', 'middle');
$style->setPropertyForLevel($level, 'vertical-rel', 'line');
list($width, $height) = ODTUtility::getImageSize($file);
if (empty($width) || empty($height)) {
$width = '0.5';
$height = $width;
}
$style->setPropertyForLevel($level, 'width', $width.'cm');
$style->setPropertyForLevel($level, 'height', $height.'cm');
// ??? Wie berechnen...
$text_indent = ODTUnits::getDigits($style->getPropertyFromLevel($level, 'text-indent'));
$margin_left = ODTUnits::getDigits($style->getPropertyFromLevel($level, 'margin_left'));
$tab_stop_position =
ODTUnits::getDigits($style->getPropertyFromLevel($level, 'list-tab-stop-position'));
$minimum = $margin_left + $text_indent + $width;
if ($minimum > $tab_stop_position) {
$inc = abs($text_indent);
if ($inc == 0 ) {
$inc = 0.5;
}
while ($minimum > $tab_stop_position) {
$tab_stop_position += $inc;
}
}
$style->setPropertyForLevel($level, 'list-tab-stop-position', $tab_stop_position.'cm');
}
}
static protected function importOrderedListStyles(ODTInternalParams $params, cssdocument $htmlStack, $listAlign='right') {
$name = $params->styleset->getStyleName('numbering');
$style = $params->styleset->getStyle($name);
if ($style == NULL ) {
return;
}
// Workaround for ODT format, see end of loop
$name = $params->styleset->getStyleName('numbering first');
$firstStyle = $params->styleset->getStyle($name);
$name = $params->styleset->getStyleName('numbering last');
$lastStyle = $params->styleset->getStyle($name);
// Reset stack to saved root so next importStyle
// will have the same conditions
$htmlStack->restoreToRoot ();
for ($level = 1 ; $level < 11 ; $level++) {
// Push our element to import on the stack
$htmlStack->open('ol');
$toMatch = $htmlStack->getCurrentElement();
$properties = array();
$params->import->getPropertiesForElement($properties, $toMatch, $params->units);
if (count($properties) == 0) {
// Nothing found. Return, DO NOT change existing style!
return;
}
// Push list item element to import on the stack
// (Required to get left margin)
$htmlStack->open('li');
$toMatch = $htmlStack->getCurrentElement();
$li_properties = array();
$params->import->getPropertiesForElement($li_properties, $toMatch, $params->units);
// Adjust values for ODT
ODTUtility::adjustValuesForODT ($properties, $params->units);
if ($properties ['list-style-type'] !== NULL) {
$prefix = NULL;
$suffix = '.';
$numbering = trim($properties ['list-style-type'],'"');
switch ($numbering) {
case 'decimal':
$numbering = '1';
break;
case 'decimal-leading-zero':
$numbering = '1';
$prefix = '0';
break;
case 'lower-alpha':
case 'lower-latin':
$numbering = 'a';
break;
case 'lower-roman':
$numbering = 'i';
break;
case 'none':
$numbering = '';
$suffix = '';
break;
case 'upper-alpha':
case 'upper-latin':
$numbering = 'A';
break;
case 'upper-roman':
$numbering = 'I';
break;
}
$style->setPropertyForLevel($level, 'num-format', $numbering);
if ($prefix !== NULL ) {
$style->setPropertyForLevel($level, 'num-prefix', $prefix);
}
$style->setPropertyForLevel($level, 'num-suffix', $suffix);
// Padding is not inherited so we will only get it for the list root!
if ($level == 1 ) {
$paddingLeft = 0;
if ($properties ['padding-left'] !== NULL) {
$paddingLeft = $params->units->toCentimeters($properties ['padding-left'], 'y');
$paddingLeft = substr($paddingLeft, 0, -2);
}
}
$marginLeft = 1;
if ($li_properties ['margin-left'] !== NULL) {
$marginLeft = $params->units->toCentimeters($li_properties ['margin-left'], 'y');
$marginLeft = substr($marginLeft, 0, -2);
}
// Set list params.
$params->document->setOrderedListParams($level, $listAlign, $paddingLeft, $marginLeft);
}
if ($properties ['list-style-image'] !== NULL && $properties ['list-style-image'] != 'none') {
// It is assumed that the CSS already contains absolute path values only!
// (see replaceURLPrefixes)
$file = $properties ['list-style-image'];
$this->setListStyleImage ($params, $style, $level, $file);
}
// Workaround for ODT format:
// We can not set margins on the list itself.
// So we use extra paragraph styles for the first and last
// list items to set a margin.
if ($level == 1 &&
($properties ['margin-top'] != NULL ||
$properties ['margin-bottom'] != NULL)) {
$set = array ();
$disabled = array ();
// Delete left and right margins as setting them
// would destroy list item indentation
$set ['margin-left'] = NULL;
$set ['margin-right'] = NULL;
$set ['margin-top'] = $properties ['margin-top'];
$set ['margin-bottom'] = '0pt';
$firstStyle->importProperties($set, $disabled);
$set ['margin-bottom'] = $properties ['margin-bottom'];
$set ['margin-top'] = '0pt';
$lastStyle->importProperties($set, $disabled);
}
// Import properties for list paragraph style once.
// Margins MUST be ignored! See extra handling above.
if ($level == 1) {
$disabled = array();
$disabled ['margin-left'] = 1;
$disabled ['margin-right'] = 1;
$disabled ['margin-top'] = 1;
$disabled ['margin-bottom'] = 1;
$name = $params->styleset->getStyleName('numbering content');
$paragraphStyle = $params->styleset->getStyle($name);
$paragraphStyle->importProperties($properties, $disabled);
}
}
// Reset stack to saved root so next importStyle
// will have the same conditions
$htmlStack->restoreToRoot ();
}
static protected function importUnorderedListStyles(ODTInternalParams $params, cssdocument $htmlStack, $listAlign='right') {
$name = $params->styleset->getStyleName('list');
$style = $params->styleset->getStyle($name);
if ($style == NULL ) {
return;
}
// Workaround for ODT format, see end of loop
$name = $params->styleset->getStyleName('list first');
$firstStyle = $params->styleset->getStyle($name);
$name = $params->styleset->getStyleName('list last');
$lastStyle = $params->styleset->getStyle($name);
// Reset stack to saved root so next importStyle
// will have the same conditions
$htmlStack->restoreToRoot ();
for ($level = 1 ; $level < 11 ; $level++) {
// Push our element to import on the stack
$htmlStack->open('ul');
$toMatch = $htmlStack->getCurrentElement();
$properties = array();
$params->import->getPropertiesForElement($properties, $toMatch, $params->units);
if (count($properties) == 0) {
// Nothing found. Return, DO NOT change existing style!
return;
}
// Push list item element to import on the stack
// (Required to get left margin)
$htmlStack->open('li');
$toMatch = $htmlStack->getCurrentElement();
$li_properties = array();
$params->import->getPropertiesForElement($li_properties, $toMatch, $params->units);
// Adjust values for ODT
ODTUtility::adjustValuesForODT ($properties, $params->units);
if ($properties ['list-style-type'] !== NULL) {
switch ($properties ['list-style-type']) {
case 'disc':
case 'bullet':
$sign = '•';
break;
case 'circle':
$sign = '∘';
break;
case 'square':
$sign = '▪';
break;
case 'none':
$sign = ' ';
break;
case 'blackcircle':
$sign = '●';
break;
case 'heavycheckmark':
$sign = '✔';
break;
case 'ballotx':
$sign = '✗';
break;
case 'heavyrightarrow':
$sign = '➔';
break;
case 'lightedrightarrow':
$sign = '➢';
break;
default:
$sign = trim($properties ['list-style-type'],'"');
break;
}
$style->setPropertyForLevel($level, 'text-bullet-char', $sign);
// Padding is not inherited so we will only get it for the list root!
if ($level == 1 ) {
$paddingLeft = 0;
if ($properties ['padding-left'] !== NULL) {
$paddingLeft = $params->units->toCentimeters($properties ['padding-left'], 'y');
$paddingLeft = substr($paddingLeft, 0, -2);
}
}
$marginLeft = 1;
if ($li_properties ['margin-left'] !== NULL) {
$marginLeft = $params->units->toCentimeters($li_properties ['margin-left'], 'y');
$marginLeft = substr($marginLeft, 0, -2);
}
// Set list params.
$params->document->setUnorderedListParams($level, $listAlign, $paddingLeft, $marginLeft);
}
if ($properties ['list-style-image'] !== NULL && $properties ['list-style-image'] != 'none') {
// It is assumed that the CSS already contains absolute path values only!
// (see replaceURLPrefixes)
$file = $properties ['list-style-image'];
/*$file = substr($file, 4);
$file = trim($file, "()'");
if ($media_path [strlen($media_path)-1] != '/') {
$media_path .= '/';
}
$file = $media_path.$file;*/
$this->setListStyleImage ($params, $style, $level, $file);
}
// Workaround for ODT format:
// We can not set margins on the list itself.
// So we use extra paragraph styles for the first and last
// list items to set a margin.
if ($level == 1 &&
($properties ['margin-top'] != NULL ||
$properties ['margin-bottom'] != NULL)) {
$set = array ();
$disabled = array ();
// Delete left and right margins as setting them
// would destroy list item indentation
$set ['margin-left'] = NULL;
$set ['margin-right'] = NULL;
$set ['margin-top'] = $properties ['margin-top'];
$set ['margin-bottom'] = '0pt';
$firstStyle->importProperties($set, $disabled);
$set ['margin-bottom'] = $properties ['margin-bottom'];
$set ['margin-top'] = '0pt';
$lastStyle->importProperties($set, $disabled);
}
// Import properties for list paragraph style once.
// Margins MUST be ignored! See extra handling above.
if ($level == 1) {
$disabled = array();
$disabled ['margin-left'] = 1;
$disabled ['margin-right'] = 1;
$disabled ['margin-top'] = 1;
$disabled ['margin-bottom'] = 1;
$name = $params->styleset->getStyleName('list content');
$paragraphStyle = $params->styleset->getStyle($name);
$paragraphStyle->importProperties($properties, $disabled);
}
}
// Reset stack to saved root so next importStyle
// will have the same conditions
$htmlStack->restoreToRoot ();
}
static protected function importTableStyles(ODTInternalParams $params, cssdocument $htmlStack) {
foreach (self::$table_styles as $style_type => $elementParams) {
$name = $params->styleset->getStyleName($style_type);
$style = $params->styleset->getStyle($name);
if ( $style != NULL ) {
$element = $elementParams ['element'];
$attributes = $elementParams ['attributes'];
// Push our element to import on the stack
$htmlStack->open($element, $attributes);
$toMatch = $htmlStack->getCurrentElement();
$properties = array();
$params->import->getPropertiesForElement($properties, $toMatch, $params->units);
if (count($properties) == 0) {
// Nothing found. Back to top, DO NOT change existing style!
continue;
}
// We have found something.
// First clear the existing layout properties of the style.
$style->clearLayoutProperties();
// Adjust values for ODT
ODTUtility::adjustValuesForODT ($properties, $params->units);
// If the style imported is a table adjust some properties
if ($style->getFamily() == 'table') {
// Move 'width' to 'rel-width' if it is relative
$width = $properties ['width'];
if ($width != NULL) {
if ($properties ['align'] == NULL) {
// If width is set but align not, changing the width
// will not work. So we set it here if not done by the user.
$properties ['align'] = 'center';
}
}
if ($width [strlen($width)-1] == '%') {
$properties ['rel-width'] = $width;
unset ($properties ['width']);
}
// Convert property 'border-model' to ODT
if ( !empty ($properties ['border-collapse']) ) {
$properties ['border-model'] = $properties ['border-collapse'];
unset ($properties ['border-collapse']);
if ( $properties ['border-model'] == 'collapse' ) {
$properties ['border-model'] = 'collapsing';
} else {
$properties ['border-model'] = 'separating';
}
}
}
// Inherit properties for table header paragraph style from
// the properties of the 'th' element
if ($element == 'th') {
$name = $params->styleset->getStyleName('table heading');
$paragraphStyle = $params->styleset->getStyle($name);
// Do not set borders on our paragraph styles in the table.
// Otherwise we will have double borders. Around the cell and
// around the text in the cell!
$disabled = array();
$disabled ['border'] = 1;
$disabled ['border-top'] = 1;
$disabled ['border-right'] = 1;
$disabled ['border-bottom'] = 1;
$disabled ['border-left'] = 1;
// Do not set background/background-color
$disabled ['background-color'] = 1;
$paragraphStyle->clearLayoutProperties();
$paragraphStyle->importProperties($properties, $disabled);
}
// Inherit properties for table content paragraph style from
// the properties of the 'td' element
if ($element == 'td') {
$name = $params->styleset->getStyleName('table content');
$paragraphStyle = $params->styleset->getStyle($name);
// Do not set borders on our paragraph styles in the table.
// Otherwise we will have double borders. Around the cell and
// around the text in the cell!
$disabled = array();
$disabled ['border'] = 1;
$disabled ['border-top'] = 1;
$disabled ['border-right'] = 1;
$disabled ['border-bottom'] = 1;
$disabled ['border-left'] = 1;
// Do not set background/background-color
$disabled ['background-color'] = 1;
$paragraphStyle->clearLayoutProperties();
$paragraphStyle->importProperties($properties, $disabled);
}
$disabled = array();
$style->importProperties($properties, $disabled);
// Reset stack to saved root so next importStyle
// will have the same conditions
$htmlStack->restoreToRoot ();
}
}
}
static protected function importLinkStyles(ODTInternalParams $params, cssdocument $htmlStack) {
foreach (self::$link_styles as $style_type => $elementParams) {
$name = $params->styleset->getStyleName($style_type);
$style = $params->styleset->getStyle($name);
if ( $name != NULL && $style != NULL ) {
$element = $elementParams ['element'];
$attributes = $elementParams ['attributes'];
$pseudo_class = $elementParams ['pseudo-class'];
// Push our element to import on the stack
$htmlStack->open($element, $attributes, $pseudo_class, NULL);
$toMatch = $htmlStack->getCurrentElement();
$properties = array();
$params->import->getPropertiesForElement($properties, $toMatch, $params->units);
if (count($properties) == 0) {
// Nothing found. Back to top, DO NOT change existing style!
continue;
}
// We have found something.
// First clear the existing layout properties of the style.
$style->clearLayoutProperties();
// Adjust values for ODT
ODTUtility::adjustValuesForODT ($properties, $params->units);
$disabled = array();
$style->importProperties($properties, $disabled);
// Reset stack to saved root so next importStyle
// will have the same conditions
$htmlStack->restoreToRoot ();
}
}
}
static protected function importStyle(ODTInternalParams $params, cssdocument $htmlStack, $style_type, $element, $attributes=NULL, array $plain=NULL) {
$name = $params->styleset->getStyleName($style_type);
$style = $params->styleset->getStyle($name);
if ( $style != NULL ) {
// Push our element to import on the stack
$htmlStack->open($element, $attributes);
$toMatch = $htmlStack->getCurrentElement();
$properties = array();
$params->import->getPropertiesForElement($properties, $toMatch, $params->units);
if (count($properties) == 0) {
// Nothing found. Return, DO NOT change existing style!
return;
}
if ($plain != NULL)
{
$diff = array_diff ($properties, $plain);
if (count($diff) == 0) {
// Workaround for some elements, e.g. 'em' and 'del':
// They may have default values from the browser only.
// In that case do not import the style otherwise
// 'em' and 'del' will look like plain text.
// Reset stack to saved root so next importStyle
// will have the same conditions
$htmlStack->restoreToRoot ();
return;
}
}
// We have found something.
// First clear the existing layout properties of the style.
$style->clearLayoutProperties();
// Adjust values for ODT
ODTUtility::adjustValuesForODT ($properties, $params->units);
// In all paragraph styles set the ODT specific attribute join-border = false
if ($style->getFamily() == 'paragraph') {
$properties ['join-border'] = 'false';
}
$disabled = array();
if ($style_type == 'horizontal line') {
// Do not use margin and padding on horizontal line paragraph style!
$disabled ['margin'] = 1;
$disabled ['margin-top'] = 1;
$disabled ['margin-right'] = 1;
$disabled ['margin-bottom'] = 1;
$disabled ['margin-left'] = 1;
$disabled ['padding'] = 1;
$disabled ['padding-top'] = 1;
$disabled ['padding-right'] = 1;
$disabled ['padding-bottom'] = 1;
$disabled ['padding-left'] = 1;
}
$style->importProperties($properties, $disabled);
// Reset stack to saved root so next importStyle
// will have the same conditions
$htmlStack->restoreToRoot ();
}
}
static public function import_styles_from_css (ODTInternalParams $params, $media_sel=NULL, $registrations=NULL, $listAlign='right') {
if ( $params->import != NULL ) {
if (!empty($media_sel)) {
$save = $params->import->getMedia();
$params->import->setMedia($media_sel);
}
// Make a copy of the stack to be sure we do not leave anything behind after import.
$stack = clone $params->htmlStack;
$stack->restoreToRoot();
self::import_styles_from_css_internal($params, $stack, $registrations, $listAlign);
if (!empty($media_sel)) {
$params->import->setMedia($save);
}
}
}
static public function set_page_properties(ODTInternalParams $params, ODTPageLayoutStyle $pageStyle, $media_sel=NULL) {
if ( $params->import != NULL ) {
if (!empty($media_sel)) {
$save = $params->import->getMedia ();
$params->import->setMedia($media_sel);
}
$stack = clone $params->htmlStack;
$stack->restoreToRoot ();
// Set background-color of page
// It is assumed that the last element of the "root" elements hold the backround-color.
// For DokuWiki this is <div class="page group">, see renderer/page.php, function 'load_css()'
$stack->restoreToRoot ();
$properties = array();
$params->import->getPropertiesForElement($properties, $stack->getCurrentElement(), $params->units);
ODTUtility::adjustValuesForODT ($properties, $params->units);
if (!empty($properties ['background-color'])) {
if ($pageStyle != NULL) {
$pageStyle->setProperty('background-color', $properties ['background-color']);
}
}
if (!empty($media_sel)) {
$params->import->setMedia ($save);
}
}
}
static protected function importParagraphDefaultStyle(ODTInternalParams $params) {
// This function MUST be called at the end of import_styles_from_css_internal
// ==> the 'body' paragraph style must have alread been imported!
// Get standard text style ('body')
$styleName = $params->styleset->getStyleName('body');
$body = $params->styleset->getStyle($styleName);
// Copy body paragraph properties to the paragraph default styles
// But not margins and paddings:
// That would also influence the margin and paddings in the
// Table of Contents or in lists
$disabled = array();
$disabled ['margin'] = 1;
$disabled ['margin-top'] = 1;
$disabled ['margin-right'] = 1;
$disabled ['margin-bottom'] = 1;
$disabled ['margin-left'] = 1;
$disabled ['padding'] = 1;
$disabled ['padding-top'] = 1;
$disabled ['padding-right'] = 1;
$disabled ['padding-bottom'] = 1;
$disabled ['padding-left'] = 1;
$default = $params->styleset->getDefaultStyle ('paragraph');
if ($default != NULL && $body != NULL) {
ODTParagraphStyle::copyLayoutProperties ($body, $default, $disabled);
}
}
static protected function importFootnoteStyle(ODTInternalParams $params) {
// This function MUST be called at the end of import_styles_from_css_internal
// ==> the 'body' paragraph style must have alread been imported!
// Get standard text style ('body')
$styleName = $params->styleset->getStyleName('body');
$body = $params->styleset->getStyle($styleName);
// Copy body paragraph properties to the footnote style
// But not margins and paddings.
$disabled = array();
$disabled ['margin'] = 1;
$disabled ['margin-top'] = 1;
$disabled ['margin-right'] = 1;
$disabled ['margin-bottom'] = 1;
$disabled ['margin-left'] = 1;
$disabled ['padding'] = 1;
$disabled ['padding-top'] = 1;
$disabled ['padding-right'] = 1;
$disabled ['padding-bottom'] = 1;
$disabled ['padding-left'] = 1;
$styleName = $params->styleset->getStyleName('footnote');
$footnote = $params->styleset->getStyle($styleName);
if ($footnote != NULL && $body != NULL) {
ODTParagraphStyle::copyLayoutProperties ($body, $footnote, $disabled);
}
}
static protected function import_styles_from_css_internal(ODTInternalParams $params, $htmlStack, $registrations=NULL, $listAlign='right') {
// Import page layout
$name = $params->styleset->getStyleName('first page');
$first_page = $params->styleset->getStyle($name);
if ($first_page != NULL) {
self::set_page_properties($params, $first_page);
}
// Import styles which only require a simple import based on element name and attributes
// Get style of plain text paragraph for comparison
// See importStyle()
$htmlStack->restoreToRoot ();
$htmlStack->open('p');
$toMatch = $htmlStack->getCurrentElement();
$properties = array();
$params->import->getPropertiesForElement($properties, $toMatch, $params->units);
$htmlStack->restoreToRoot ();
$toImport = array_merge (self::$internalRegs, $registrations);
foreach ($toImport as $style => $element) {
if ($element ['compare']) {
self::importStyle($params, $htmlStack,
$style,
$element ['element'],
$element ['attributes'],
$properties);
} else {
self::importStyle($params, $htmlStack,
$style,
$element ['element'],
$element ['attributes'],
NULL);
}
}
// Import table styles
self::importTableStyles($params, $htmlStack);
// Import link styles (require extra pseudo class handling)
self::importLinkStyles($params, $htmlStack);
// Import list styles and list paragraph styles
self::importUnorderedListStyles($params, $htmlStack, $listAlign);
self::importOrderedListStyles($params, $htmlStack, $listAlign);
self::importParagraphDefaultStyle($params);
self::importFootnoteStyle($params);
self::importQuotationStyles($params, $htmlStack);
}
static public function importODTStyles(ODTInternalParams $params, $template=NULL, $tempDir=NULL){
if ($template == NULL || $tempDir == NULL) {
return;
}
// Temp dir
if (is_dir($tempDir)) { io_rmdir($tempDir,true); }
io_mkdir_p($tempDir);
// Extract template
try {
$ZIPextract = new \splitbrain\PHPArchive\Zip();
$ZIPextract->open($template);
$ZIPextract->extract($tempDir);
$ZIPextract->close();
} catch (\splitbrain\PHPArchive\ArchiveIOException $e) {
throw new Exception(' Error extracting the zip archive:'.$template.' to '.$tempDir);
}
// Import styles from ODT template
$params->styleset->importFromODTFile($tempDir.'/content.xml', 'office:automatic-styles', true);
$params->styleset->importFromODTFile($tempDir.'/styles.xml', 'office:automatic-styles', true);
$params->styleset->importFromODTFile($tempDir.'/styles.xml', 'office:styles', true);
$params->styleset->importFromODTFile($tempDir.'/styles.xml', 'office:master-styles', true);
// Cleanup temp dir.
io_rmdir($tempDir,true);
}
}

View File

@@ -0,0 +1,401 @@
<?php
require_once DOKU_PLUGIN . 'odt/ODT/ODTDocument.php';
/**
* ODTIndex:
* Class containing static code for handling indexes.
* Actually these are the table of contents and the chapter index.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*/
class ODTIndex
{
/**
* This function does not really render/insert an index but inserts a placeholder.
* See also replaceIndexesPlaceholders().
*
* @return string
*/
public static function insertIndex(ODTInternalParams $params, array &$indexesData, $type='toc', array $settings=NULL) {
// Insert placeholder
$index_count = count ($indexesData);
$params->document->paragraphClose();
$params->content .= '<index-placeholder no="'.($index_count+1).'"/>';
// 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 ('<index-placeholder no="'.($index_no+1).'"/>', $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 = '<text:'.$tag.' text:style-name="Standard" text:protected="true" text:name="'.$name.'">';
$content .= '<text:'.$tag.'-source '.$source_attrs.'>';
if (!empty($title)) {
$content .= '<text:index-title-template text:style-name="'.$title_style.'">'.$title.'</text:index-title-template>';
} else {
$content .= '<text:index-title-template text:style-name="'.$title_style.'"/>';
}
// 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 .= '<text:'.$tag.'-entry-template text:outline-level="'.$level.'" text:style-name="'.$p_styles [$level].'">';
$content .= '<text:index-entry-link-start text:style-name="'.$stylesLNames [$level].'"/>';
$content .= '<text:index-entry-chapter/>';
if ($settings ['numbered_headings'] == true) {
$content .= '<text:index-entry-span> </text:index-entry-span>';
}
$content .= '<text:index-entry-text/>';
$content .= '<text:index-entry-tab-stop style:type="right" style:leader-char="'.$leader_sign.'"/>';
$content .= '<text:index-entry-page-number/>';
$content .= '<text:index-entry-link-end/>';
$content .= '</text:'.$tag.'-entry-template>';
}
$content .= '</text:'.$tag.'-source>';
$content .= '<text:index-body>';
if (!empty($title)) {
$content .= '<text:index-title text:style-name="Standard" text:name="'.$index_name.'_Head">';
$content .= '<text:p text:style-name="'.$title_style.'">'.$title.'</text:p>';
$content .= '</text:index-title>';
}
// 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 .= '</text:index-body>';
$content .= '</text:'.$tag.'>';
// Add a pagebreak if required.
if ( $pagebreak ) {
$style_name = $doc->createPagebreakStyle(NULL, false);
$content .= '<text:p text:style-name="'.$style_name.'"/>';
}
// 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 .= '<text:p text:style-name="'.$p_styles [$level].'">';
if ( $links == true ) {
$content .= '<text:a xlink:type="simple" xlink:href="#'.$params [0].'" text:style-name="'.$stylesLNames [$level].'" text:visited-style-name="'.$stylesLNames [$level].'">';
}
$content .= $params [2];
$content .= '<text:tab/>';
$page++;
$content .= $page;
if ( $links == true ) {
$content .= '</text:a>';
}
$content .= '</text:p>';
}
}
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 .= '<text:p text:style-name="'.$p_styles [$level].'">';
if ( $links == true ) {
$content .= '<text:a xlink:type="simple" xlink:href="#'.$params [0].'" text:style-name="'.$stylesLNames [$level].'" text:visited-style-name="'.$stylesLNames [$level].'">';
}
$content .= $params [2];
$content .= '<text:tab/>';
$page++;
$content .= $page;
if ( $links == true ) {
$content .= '</text:a>';
}
$content .= '</text:p>';
}
$first = false;
}
}
return $content;
}
}

View File

@@ -0,0 +1,277 @@
<?php
require_once DOKU_PLUGIN . 'odt/ODT/ODTDocument.php';
/**
* ODTList:
* Class containing static code for handling lists.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
class ODTList
{
/**
* Opens a list.
* The list style specifies if the list is an ordered or unordered list.
*
* @param bool $continue Continue numbering?
* @param string $styleName Name of style to use for the list
*/
static public function listOpen(ODTInternalParams $params, $continue=false, $styleName, $element=NULL, $attributes=NULL) {
$params->document->paragraphClose();
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
$list = new ODTElementList($styleName, $continue);
$params->document->state->enter($list);
$list->setHTMLElement ($element);
$params->content .= $list->getOpeningTag();
}
/**
* Close a list
*/
static public function listClose(ODTInternalParams $params) {
$table = $params->document->state->getCurrentTable();
if ($table != NULL && $table->getListInterrupted()) {
// Do not do anything as long as list is interrupted
return;
}
if ($params->document->state->getInListItem()) {
// If we are still inside a list item then close it first,
// to prevent an error or broken document.
$params->document->listItemClose();
}
// Eventually modify last list paragraph first
self::replaceLastListParagraph($params);
ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement());
$list = $params->document->state->getCurrentList();
$params->content .= $list->getClosingTag();
$position = $list->getListLastParagraphPosition();
$params->document->state->leave();
// If we are still in a list save the last paragraph position
// in the current list (needed for nested lists!).
$list = $params->document->state->getCurrentList();
if ($list != NULL) {
$list->setListLastParagraphPosition($position);
}
}
/**
* Open a list item
*
* @param int $level The nesting level
*/
static public function listItemOpen(ODTInternalParams $params, $level, $element=NULL, $attributes=NULL) {
if ($params->document->state == NULL ) {
// ??? Can't be...
return;
}
if ($element == NULL) {
$element = 'li';
}
// Set marker that list interruption has stopped!!!
$table = $params->document->state->getCurrentTable();
if ($table != NULL) {
$table->setListInterrupted(false);
}
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
// Attention:
// we save the list level here but it might be wrong.
// Someone can start a list with level 2 without having created
// a list with level 1 before.
// When the correct list level is needed better use
// $params->document->state->countClass('list'), see table_open().
$list_item = new ODTElementListItem($level);
$params->document->state->enter($list_item);
$list_item->setHTMLElement ($element);
$params->content .= $list_item->getOpeningTag();
}
/**
* Close a list item
*/
static public function listItemClose(ODTInternalParams $params) {
$table = $params->document->state->getCurrentTable();
if ($table != NULL && $table->getListInterrupted()) {
// Do not do anything as long as list is interrupted
return;
}
if ($params->document->state->getInListContent()) {
// If we are still inside list content then close it first,
// to prevent an error or broken document.
$params->document->listContentClose();
}
ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement());
$params->document->closeCurrentElement();
}
/**
* Open a list header
*
* @param int $level The nesting level
*/
static public function listHeaderOpen(ODTInternalParams $params, $level, $element=NULL, $attributes=NULL) {
if ($params->document->state == NULL ) {
// ??? Can't be...
return;
}
if ($element == NULL) {
$element = 'li';
}
// Set marker that list interruption has stopped!!!
$table = $params->document->state->getCurrentTable();
if ($table != NULL) {
$table->setListInterrupted(false);
}
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
// Attention:
// we save the list level here but it might be wrong.
// Someone can start a list with level 2 without having created
// a list with level 1 before.
// When the correct list level is needed better use
// $params->document->state->countClass('list'), see table_open().
$list_header = new ODTElementListHeader($level);
$params->document->state->enter($list_header);
$list_header->setHTMLElement ($element);
$params->content .= $list_header->getOpeningTag();
}
/**
* Close a list header
*/
static public function listHeaderClose(ODTInternalParams $params) {
$table = $params->document->state->getCurrentTable();
if ($table != NULL && $table->getListInterrupted()) {
// Do not do anything as long as list is interrupted
return;
}
ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement());
$params->document->closeCurrentElement();
}
/**
* Open list content/a paragraph in a list item
*/
static public function listContentOpen(ODTInternalParams $params, $element=NULL, $attributes=NULL) {
// The default style for list content is body but it should always be
// overwritten. It's just assigned here to guarantee some style name is
// always set in case of an error also.
$styleName = $params->document->getStyleName('body');
$list = $params->document->state->getCurrentList();
if ($list != NULL) {
$listStyleName = $list->getStyleName();
if ($listStyleName == $params->document->getStyleName('list')) {
$styleName = $params->document->getStyleName('list content');
}
if ($listStyleName == $params->document->getStyleName('numbering')) {
$styleName = $params->document->getStyleName('numbering content');
}
}
$params->document->paragraphOpen($styleName);
}
/**
* Close list content/a paragraph in a list item
*/
static public function listContentClose(ODTInternalParams $params) {
$table = $params->document->state->getCurrentTable();
if ($table != NULL && $table->getListInterrupted()) {
// Do not do anything as long as list is interrupted
return;
}
$params->document->paragraphClose();
}
/**
* The function replaces the last paragraph of a list
* with a style having the properties of 'List_Last_Paragraph'.
*
* The function does NOT change the last paragraph of nested lists.
*/
static protected function replaceLastListParagraph(ODTInternalParams $params) {
$list = $params->document->state->getCurrentList();
if ($list != NULL) {
// We are in a list.
$list_count = $params->document->state->countClass('list');
$position = $list->getListLastParagraphPosition();
if ($list_count != 1 || $position == -1) {
// Do nothing if this is a nested list or the position was not saved
return;
}
$last_p_style = NULL;
if (preg_match('/<text:p text:style-name="[^"]*">/', $params->content, $matches, 0, $position) === 1) {
$last_p_style = substr($matches [0], strlen('<text:p text:style-name='));
$last_p_style = trim($last_p_style, '">');
} else {
// Nothing found???
return;
}
// Create a style for putting a bottom margin for this last paragraph of the list
// (if not done yet, the name must be unique!)
// If we have a standard list content paragraph style then we use the
// corresponding always existing first and last default styles
if ($last_p_style == $params->document->getStyleName('list content')) {
$style_name = $params->document->getStyleName('list last');
} else if ($last_p_style == $params->document->getStyleName('numbering content')) {
$style_name = $params->document->getStyleName('numbering last');
} else {
$style_name = 'LastListParagraph_'.$last_p_style;
if (!$params->document->styleExists($style_name)) {
// ...no, create style as copy of style 'list first' or 'numbering first'
if ($list->getStyleName() == $params->document->getStyleName('list')) {
$style_last = $params->document->getStyleByAlias('list first');
} else {
$style_last = $params->document->getStyleByAlias('numbering first');
}
if ($style_last != NULL) {
$style_body = $params->document->getStyle($last_p_style);
$style_display_name = 'Last '.$style_body->getProperty('style-display-name');
$style_obj = clone $style_last;
if ($style_obj != NULL) {
$style_obj->setProperty('style-name', $style_name);
$style_obj->setProperty('style-parent', $last_p_style);
$style_obj->setProperty('style-display-name', $style_display_name);
$top = $style_last->getProperty('margin-top');
if ($top === NULL) {
$style_obj->setProperty('margin-top', $style_body->getProperty('margin-top'));
}
$params->document->addStyle($style_obj);
}
}
}
}
// Finally replace style name of last paragraph.
$params->content = substr_replace ($params->content,
'<text:p text:style-name="'.$style_name.'">',
$position, strlen($matches[0]));
}
}
}

View File

@@ -0,0 +1,228 @@
<?php
/**
* ODT Paragraph handling.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
* @package ODT\Paragraph
*/
/** Include ODTDocument */
require_once DOKU_PLUGIN . 'odt/ODT/ODTDocument.php';
/**
* ODTParagraph:
* Class containing static code for handling paragraphs.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*/
class ODTParagraph
{
/**
* Open a paragraph
*
* @param ODTInternalParams $params Commom params.
* @param string|null $styleName The style to use.
* @param string $element The element name, e.g. "div"
* @param string $attributes The attributes belonging o the element, e.g. 'class="example"'
*/
public static function paragraphOpen(ODTInternalParams $params, $styleName=NULL, $element=NULL, $attributes=NULL){
if ($element == NULL) {
$element = 'p';
}
if ( empty($styleName) ) {
$styleName = $params->document->getStyleName('body');
}
$list = NULL;
$listItem = $params->document->state->getCurrentListItem();
if ($listItem != NULL) {
// We are in a list item. Is this the list start?
$list = $listItem->getList();
if ($list != NULL) {
// Get list count and Flag if this is the first paragraph in the list
$listCount = $params->document->state->countClass('list');
$isFirst = $list->getListFirstParagraph();
$list->setListFirstParagraph(false);
// Create a style for putting a top margin for this first paragraph of the list
// (if not done yet, the name must be unique!)
if ($listCount == 1 && $isFirst) {
// If we have a standard list content paragraph style then we use the
// corresponding always existing first and last default styles
if ($styleName == $params->document->getStyleName('list content')) {
$styleName = $params->document->getStyleName('list first');
} else if ($styleName == $params->document->getStyleName('numbering content')) {
$styleName = $params->document->getStyleName('numbering first');
} else {
// No standard list paragraph style.
// Has a clone for the first paragraph's style already been created...
$styleNameFirst = 'FirstListParagraph_'.$styleName;
if (!$params->document->styleExists($styleNameFirst)) {
// ...no, create style as copy of style 'list first' or 'numbering first'
if ($list->getStyleName() == $params->document->getStyleName('list')) {
$styleFirstTemplate = $params->document->getStyleByAlias('list first');
} else {
$styleFirstTemplate = $params->document->getStyleByAlias('numbering first');
}
if ($styleFirstTemplate != NULL) {
$styleBody = $params->document->getStyle($styleName);
$styleDisplayName = 'First '.$styleBody->getProperty('style-display-name');
$styleObj = clone $styleFirstTemplate;
if ($styleObj != NULL) {
$styleObj->setProperty('style-name', $styleNameFirst);
$styleObj->setProperty('style-parent', $styleName);
$styleObj->setProperty('style-display-name', $styleDisplayName);
$bottom = $styleFirstTemplate->getProperty('margin-bottom');
if ($bottom === NULL) {
$styleObj->setProperty('margin-bottom', $styleBody->getProperty('margin-bottom'));
}
$params->document->addStyle($styleObj);
$styleName = $styleNameFirst;
}
}
} else {
// ...yes, just use the name
$styleName = $styleNameFirst;
}
}
}
}
}
// Opening a paragraph inside another paragraph is illegal
$inParagraph = $params->document->state->getInParagraph();
if (!$inParagraph) {
if ( $params->document->pageFormatChangeIsPending() ) {
$pageStyle = $params->document->doPageFormatChange($styleName);
if ( $pageStyle != NULL ) {
$styleName = $pageStyle;
// Delete pagebreak, the format change will also introduce a pagebreak.
$params->document->setPagebreakPending(false);
}
}
if ( $params->document->pagebreakIsPending() ) {
$styleName = $params->document->createPagebreakStyle ($styleName);
$params->document->setPagebreakPending(false);
}
// If we are in a list remember paragraph position
if ($list != NULL) {
$list->setListLastParagraphPosition(strlen($params->content));
}
if ($params->elementObj == NULL) {
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
}
$paragraph = new ODTElementParagraph($styleName);
$params->document->state->enter($paragraph);
$params->content .= $paragraph->getOpeningTag();
$paragraph->setHTMLElement ($element);
}
}
/**
* Close a paragraph.
*
* @param ODTInternalParams $params Commom params.
*/
public static function paragraphClose(ODTInternalParams $params){
$paragraph = $params->document->state->getCurrentParagraph();
if ($paragraph != NULL) {
ODTUtility::closeHTMLElement ($params, $paragraph->getHTMLElement());
$params->content .= $paragraph->getClosingTag();
$params->document->state->leave();
}
}
/**
* This function opens a new paragraph using the style as set in the imported CSS $import.
* So, the function requires the helper class 'helper_plugin_odt_cssimport'.
* The CSS style is selected by the element type 'p' and the specified classes in $classes.
* The property 'background-image' is emulated by inserting an image manually in the paragraph.
* If the url from the CSS should be converted to a local path, then the caller can specify a $baseURL.
* The full path will then be $baseURL/background-image.
*
* This function calls _odtParagraphOpenUseProperties. See the function description for supported properties.
*
* The span should be closed by calling '_odtParagraphClose'.
*
* @author LarsDW223
* @param ODTInternalParams $params Commom params.
* @param string $element The element name, e.g. "div"
* @param string $attributes The attributes belonging o the element, e.g. 'class="example"'
*/
public static function paragraphOpenUseCSS(ODTInternalParams $params, $element=NULL, $attributes=NULL){
$inParagraph = $params->document->state->getInParagraph();
if ($inParagraph) {
return;
}
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
$params->elementObj = $params->htmlStack->getCurrentElement();
self::paragraphOpenUseProperties($params, $properties);
}
/**
* This function opens a new paragraph using the style as set in the assoziative array $properties.
* The parameters in the array should be named as the CSS property names e.g. 'color' or 'background-color'.
* The property 'background-image' is emulated by inserting an image manually in the paragraph.
*
* The currently supported properties are:
* background-color, color, font-style, font-weight, font-size, border, font-family, font-variant, letter-spacing,
* vertical-align, line-height, background-image (emulated)
*
* The paragraph must be closed by calling 'p_close'.
*
* @author LarsDW223
*
* @param ODTInternalParams $params Commom params.
* @param array $properties Properties to use.
*/
public static function paragraphOpenUseProperties(ODTInternalParams $params, $properties){
$inParagraph = $params->document->state->getInParagraph();
if ($inParagraph) {
return;
}
$disabled = array ();
$in_paragraph = $params->document->state->getInParagraph();
if ($in_paragraph) {
// opening a paragraph inside another paragraph is illegal
return;
}
$odt_bg = $properties ['background-color'];
$picture = $properties ['background-image'];
if ( !empty ($picture) ) {
// If a picture/background-image is set, than we insert it manually here.
// This is a workaround because ODT background-image works different than in CSS.
// Define graphic style for picture
$style_name = ODTStyle::getNewStylename('span_graphic');
$image_style = '<style:style style:name="'.$style_name.'" style:family="graphic" style:parent-style-name="'.$params->document->getStyleName('graphics').'"><style:graphic-properties style:vertical-pos="middle" style:vertical-rel="text" style:horizontal-pos="from-left" style:horizontal-rel="paragraph" fo:background-color="'.$odt_bg.'" style:flow-with-text="true"></style:graphic-properties></style:style>';
// Add style and image to our document
// (as unknown style because style-family graphic is not supported)
$style_obj = ODTUnknownStyle::importODTStyle($image_style);
$params->document->addAutomaticStyle($style_obj);
ODTImage::addImage ($params, $picture, NULL, NULL, NULL, NULL, $style_name);
}
// Create the style for the paragraph.
//$disabled ['background-image'] = 1;
//FIXME: pass $disabled
$style_obj = ODTParagraphStyle::createParagraphStyle ($properties);
$params->document->addAutomaticStyle($style_obj);
$style_name = $style_obj->getProperty('style-name');
// Open a paragraph
self::paragraphOpen($params, $style_name);
}
}

View File

@@ -0,0 +1,242 @@
<?php
require_once DOKU_PLUGIN . 'odt/ODT/ODTDocument.php';
/**
* ODTParagraph:
* Class containing static code for handling spans.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*/
class ODTSpan
{
/**
* Open a text span.
*
* @param string $styleName The style to use.
*/
public static function spanOpen(ODTInternalParams $params, $styleName, $element=NULL, $attributes=NULL){
if ($element == NULL) {
$element = 'span';
}
if ($params->elementObj == NULL) {
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
}
$span = new ODTElementSpan ($styleName);
$params->document->state->enter($span);
$params->content .= $span->getOpeningTag();
$span->setHTMLElement ($element);
}
/**
* This function opens a new span using the style as set in the imported CSS $import.
* So, the function requires the helper class 'helper_plugin_odt_cssimport'.
* The CSS style is selected by the element type 'span' and the specified classes in $classes.
* The property 'background-image' is not supported by an ODT span. This will be emulated
* by inserting an image manually in the span. If the url from the CSS should be converted to
* a local path, then the caller can specify a $baseURL. The full path will then be $baseURL/background-image.
*
* This function calls _odtSpanOpenUseProperties. See the function description for supported properties.
*
* The span should be closed by calling '_odtSpanClose'.
*
* @author LarsDW223
*
* @param helper_plugin_odt_cssimport $import
* @param $classes
* @param $baseURL
* @param $element
*/
public static function spanOpenUseCSS(ODTInternalParams $params, $element=NULL, $attributes=NULL){
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
$params->elementObj = $params->htmlStack->getCurrentElement();
self::spanOpenUseProperties($params, $properties);
}
/**
* This function opens a new span using the style as set in the assoziative array $properties.
* The parameters in the array should be named as the CSS property names e.g. 'color' or 'background-color'.
* The property 'background-image' is not supported by an ODT span. This will be emulated
* by inserting an image manually in the span.
*
* background-color, color, font-style, font-weight, font-size, border, font-family, font-variant, letter-spacing,
* vertical-align, background-image (emulated)
*
* The span should be closed by calling '_odtSpanClose'.
*
* @author LarsDW223
*
* @param array $properties
*/
public static function spanOpenUseProperties(ODTInternalParams $params, $properties){
$disabled = array ();
$odt_bg = $properties ['background-color'];
$picture = $properties ['background-image'];
if ( !empty ($picture) ) {
// If a picture/background-image is set, than we insert it manually here.
// This is a workaround because ODT does not support the background-image attribute in a span.
// Define graphic style for picture
$style_name = ODTStyle::getNewStylename('span_graphic');
$image_style = '<style:style style:name="'.$style_name.'" style:family="graphic" style:parent-style-name="'.$params->document->getStyleName('graphics').'"><style:graphic-properties style:vertical-pos="middle" style:vertical-rel="text" style:horizontal-pos="from-left" style:horizontal-rel="paragraph" fo:background-color="'.$odt_bg.'" style:flow-with-text="true"></style:graphic-properties></style:style>';
// Add style and image to our document
// (as unknown style because style-family graphic is not supported)
$style_obj = ODTUnknownStyle::importODTStyle($image_style);
$params->document->addAutomaticStyle($style_obj);
ODTImage::addImage ($params, $picture, NULL, NULL, NULL, NULL, $style_name);
}
// Create a text style for our span
$disabled ['background-image'] = 1;
$style_obj = ODTTextStyle::createTextStyle ($properties, $disabled);
$params->document->addAutomaticStyle($style_obj);
$style_name = $style_obj->getProperty('style-name');
// Open span
self::spanOpen($params, $style_name);
}
/**
* Close a text span.
*
* @param string $style_name The style to use.
*/
public static function spanClose(ODTInternalParams $params) {
ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement());
$params->document->closeCurrentElement($params->content);
}
public static function createSpanInternal (ODTInternalParams $params, $attributes) {
// Get properties
$properties = array();
ODTUtility::getHTMLElementProperties ($params, $properties, 'span', $attributes);
// Create automatic style
$properties ['style-name'] = ODTStyle::getNewStylename ('span');
$params->document->createTextStyle($properties, false);
// Return style name
return $properties ['style-name'];
}
public static function generateSpansfromHTMLCode(ODTInternalParams $params, $HTMLCode){
$spans = array ('sup' => array ('open' => '<text:span text:style-name="sup">',
'close' => '</text:span>'),
'sub' => array ('open' => '<text:span text:style-name="sub">',
'close' => '</text:span>'),
'u' => array ('open' => '<text:span text:style-name="underline">',
'close' => '</text:span>'),
'em' => array ('open' => '<text:span text:style-name="Emphasis">',
'close' => '</text:span>'),
'strong' => array ('open' => '<text:span text:style-name="Strong_20_Emphasis">',
'close' => '</text:span>'),
'del' => array ('open' => '<text:span text:style-name="del">',
'close' => '</text:span>'),
);
$parsed = array();
// First examine $HTMLCode and differ between normal content,
// opening tags and closing tags.
$max = strlen ($HTMLCode);
$pos = 0;
while ($pos < $max) {
$found = ODTUtility::getNextTag($HTMLCode, $pos);
if ($found !== false) {
$entry = array();
$entry ['content'] = substr($HTMLCode, $pos, $found [0]-$pos);
if ($entry ['content'] === false) {
$entry ['content'] = '';
}
$parsed [] = $entry;
$tagged = substr($HTMLCode, $found [0], $found [1]-$found [0]+1);
$entry = array();
if ($HTMLCode [$found[1]-1] == '/') {
// Element without content <abc/>, doesn'T make sense, save as content
$entry ['content'] = $tagged;
} else {
if ($HTMLCode [$found[0]+1] != '/') {
$parts = explode(' ', trim($tagged, '<> '), 2);
$entry ['tag-open'] = $parts [0];
if ($parts [1] !== NULL ) {
$entry ['attributes'] = $parts [1];
}
$entry ['tag-orig'] = $tagged;
} else {
$entry ['tag-close'] = trim ($tagged, '<>/ ');
$entry ['tag-orig'] = $tagged;
}
}
$entry ['matched'] = false;
$parsed [] = $entry;
$pos = $found [1]+1;
} else {
$entry = array();
$entry ['content'] = substr($HTMLCode, $pos);
$parsed [] = $entry;
break;
}
}
// Check each array entry.
$checked = array();
for ($out = 0 ; $out < count($parsed) ; $out++) {
if ($checked [$out] !== NULL) {
continue;
}
$found = &$parsed [$out];
if ($found ['content'] !== NULL) {
$checked [$out] = $params->document->replaceXMLEntities($found ['content']);
} else if ($found ['tag-open'] !== NULL) {
$closed = false;
for ($in = $out+1 ; $in < count($parsed) ; $in++) {
$search = &$parsed [$in];
if ($search ['tag-close'] !== NULL &&
$found ['tag-open'] == $search ['tag-close'] &&
$search ['matched'] === false &&
(array_key_exists($found ['tag-open'], $spans) || $found ['tag-open'] == 'span')) {
$closed = true;
$search ['matched'] = true;
// Known and closed tag, convert to ODT
if ($found ['tag-open'] != 'span') {
$checked [$out] = $spans [$found ['tag-open']]['open'];
$checked [$in] = $spans [$found ['tag-open']]['close'];
} else {
$style_name = self::createSpanInternal ($params, $found ['attributes']);
$checked [$out] = '<text:span text:style-name="'.$style_name.'">';
$checked [$in] = '</text:span>';
}
break;
}
}
// Known tag? Closing tag found?
if (!$closed) {
// No, save as content
$checked [$out] = $params->document->replaceXMLEntities($found ['tag-orig']);
}
} else if ($found ['tag-close'] !== NULL) {
// If we find a closing tag it means it did not match
// an opening tag. Convert to content!
$checked [$out] = $params->document->replaceXMLEntities($found ['tag-orig']);
}
}
// Add checked entries to content
for ($index = 0 ; $index < count($checked) ; $index++) {
$params->content .= $checked [$index];
}
}
}

View File

@@ -0,0 +1,358 @@
<?php
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTStateElement.php';
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTRoot.php';
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTElementSpan.php';
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTElementParagraph.php';
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTElementList.php';
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTElementListItem.php';
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTElementListHeader.php';
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTElementTable.php';
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTElementTableColumn.php';
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTElementTableRow.php';
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTElementTableCell.php';
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTElementTableHeaderCell.php';
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTElementFrame.php';
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTElementTextBox.php';
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTElementNote.php';
require_once DOKU_PLUGIN.'odt/ODT/css/cssdocument.php';
/**
* ODTState: class for maintaining the ODT state stack.
*
* In general this is a setter/getter class for ODT states.
* The intention is to get rid of some global state variables.
* Especially the global error-prone $in_paragraph which easily causes
* a document to become invalid if once set wrong. Now each state/element
* can set their own instance of $in_paragraph which hopefully makes it use
* a bit safer. E.g. for a new table-cell or list-item it can be set to false
* because they allow creation of a new paragraph. On leave() we throw the
* current state variables away and are safe back from where we came from.
* So we also don't need to worry about correct re-initialization of global
* variables anymore.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
class ODTState
{
// The ODT document to which this state belongs
protected $document = NULL;
protected $stack = array();
protected $size = 0;
protected $element_counter = array();
/**
* Constructor. Set initial 'root' state.
*/
public function __construct() {
// Stack for maintaining our ODT elements
$this->stack [$this->size] = new ODTElementRoot();
$this->size++;
}
/**
* Get current list item.
* If the function returns NULL then that means that we are
* currently not in a list item.
*
* @return ODTStateElement|NULL
*/
public function getCurrentListItem() {
return $this->findClosestWithClass ('list-item');
}
/**
* Get current frame.
* If the function returns NULL then that means that we are
* currently not in a frame.
*
* @return ODTStateElement|NULL
*/
public function getCurrentFrame() {
return $this->findClosestWithClass ('frame');
}
/**
* Get current list.
* If the function returns NULL then that means that we are
* currently not in a list.
*
* @return ODTStateElement|NULL
*/
public function getCurrentList() {
return $this->findClosestWithClass ('list');
}
/**
* Get current paragraph.
* If the function returns NULL then that means that we are
* currently not in a paragraph.
*
* @return ODTStateElement|NULL
*/
public function getCurrentParagraph() {
// Only search for the paragraph if the current element tells
// us that we are in one. Otherwise we may find the paragraph
// around this current element which might lead to double
// closing of a paragraph == invalid/broken ODT document!
if ($this->getInParagraph()) {
return $this->findClosestWithClass ('paragraph');
}
return NULL;
}
/**
* Get current table.
* If the function returns NULL then that means that we are
* currently not in a table.
*
* @return ODTStateElement|NULL
*/
public function getCurrentTable() {
return $this->findClosestWithClass ('table');
}
/**
* Enter a new state with element name $element and class $clazz.
* E.g. 'text:p' and 'paragraph'.
*
* @param string $element
* @param string $clazz
*/
public function enter(ODTStateElement $element, $attributes=NULL) {
if ($element == NULL ) {
return;
}
$name = $element->getElementName();
// Increase the counter for that element
if ($this->element_counter [$name] == NULL ) {
$this->element_counter [$name] = 1;
} else {
$this->element_counter [$name]++;
}
$element->setCount($this->element_counter [$name]);
// Get the current element
$previous = $this->stack [$this->size-1];
// Add new element to stack
$this->stack [$this->size] = $element;
$this->size++;
// Set the elements style object
if ($this->document != NULL) {
$styleObj = $this->document->getStyle($element->getStyleName());
$element->setStyle($styleObj);
}
// Let the element find its parent
$element->determineParent ($previous);
}
/**
* Get current element on top of the stack.
*
* @return ODTStateElement
*/
public function getCurrent() {
return $this->stack [$this->size-1];
}
/**
* Leave current state. All data of the curent state is thrown away.
*/
public function leave() {
// We always will keep the initial state.
// That means we do nothing if size is 0. This would be a fault anyway.
if ($this->size > 1) {
unset ($this->stack [$this->size-1]);
$this->size--;
}
}
/**
* Reset the state stack/go back to the initial state.
* All states except the root state will be discarded.
*/
public function reset() {
// Throw away any states except the initial state.
// Reset size to 1.
for ($reset = 1 ; $reset < $this->size ; $reset++) {
unset ($this->stack [$reset]);
}
$this->size = 1;
}
/**
* Find the closest state with class $clazz.
*
* @param string $clazz
* @return ODTStateEntry|NULL
*/
public function findClosestWithClass($clazz) {
for ($search = $this->size-1 ; $search > 0 ; $search--) {
if ($this->stack [$search]->getClass() == $clazz) {
return $this->stack [$search];
}
}
// Nothing found.
return NULL;
}
/**
* Find the closest state with class $clazz, return $index.
*
* @param string $clazz
* @param integer|false &$index Index of the found element or false
* @return ODTStateEntry|NULL
*/
public function findClosestWithClassGetIndex($clazz, &$index) {
$index = false;
for ($search = $this->size-1 ; $search > 0 ; $search--) {
if ($this->stack [$search]->getClass() == $clazz) {
$index = $search;
return $this->stack [$search];
}
}
// Nothing found.
return NULL;
}
/**
* toString() function. Only for creating debug dumps.
*
* @return string
*/
public function toString () {
$indent = '';
$string = "Stackdump:\n";
for ($search = 0 ; $search < $this->size ; $search++) {
$string .= $indent . $this->stack [$search]->getElementName().";\n";
$indent .= ' ';
}
return $string;
}
/**
* Find the closest state with class $clazz.
*
* @param string $clazz
* @return ODTStateEntry|NULL
*/
public function countClass($clazz) {
$count = 0;
for ($search = $this->size-1 ; $search > 0 ; $search--) {
if ($this->stack [$search]->getClass() == $clazz) {
$count++;
}
}
return $count;
}
/**
* Find the closest element with element name $name.
*
* @param string $name
* @return ODTStateElement|NULL
*/
public function findClosestWithName($name) {
for ($search = $this->size-1 ; $search > 0 ; $search--) {
if ($this->stack [$search]->getElementName() == $name) {
return $this->stack [$search];
}
}
// Nothing found.
return NULL;
}
/**
* Are we in a table row?
*
* @return bool
*/
public function getInTableRow() {
$this->findClosestWithClassGetIndex('table-row', $tableRowIndex);
$this->findClosestWithClassGetIndex('table', $tableIndex);
if ($tableRowIndex > $tableIndex) {
return true;
}
return false;
}
/**
* Are we in a table cell?
*
* @return bool
*/
public function getInTableCell() {
$this->findClosestWithClassGetIndex('table-cell', $tableCellIndex);
$this->findClosestWithClassGetIndex('table-row', $tableRowIndex);
if ($tableCellIndex > $tableRowIndex) {
return true;
}
return false;
}
/**
* Are we in a list item?
*
* @return bool
*/
public function getInListItem() {
$this->findClosestWithClassGetIndex('list-item', $listItemIndex);
$this->findClosestWithClassGetIndex('list', $listIndex);
if ($listItemIndex > $listIndex) {
return true;
}
return false;
}
/**
* Are we in list content?
*
* @return bool
*/
public function getInListContent() {
// listContentOpen == paragraphOpen,
// so we can simply call getInParagraph()
return $this->getInParagraph();
}
/**
* Are we in a paragraph?
*
* @return bool
*/
public function getInParagraph() {
// Ask the current element
if ($this->size > 0) {
return $this->stack [$this->size-1]->getInParagraph();
} else {
return false;
}
}
/**
* Set the ODTDocument to which this state belongs.
*
* @param ODTDocument $doc
*/
public function setDocument($doc) {
$this->document = $doc;
}
public function getHTMLElement() {
// Ask the current element
if ($this->size > 0) {
return $this->stack [$this->size-1]->getHTMLElement();
} else {
return NULL;
}
}
public function getElementCount($element) {
return $this->element_counter [$element]++;
}
}

View File

@@ -0,0 +1,606 @@
<?php
require_once DOKU_PLUGIN . 'odt/ODT/ODTUnits.php';
require_once DOKU_PLUGIN . 'odt/ODT/ODTDocument.php';
/**
* ODTTable:
* Class containing static code for handling tables.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*/
class ODTTable
{
/**
* Open/start a table
*
* @param int $maxcols maximum number of columns
* @param int $numrows NOT IMPLEMENTED
*/
public static function tableOpen(ODTInternalParams $params, $maxcols = NULL, $numrows = NULL, $tableStyleName=NULL, $element=NULL, $attributes=NULL){
if ($element == NULL) {
$element = 'table';
}
$elementObj = $params->elementObj;
// Close any open paragraph.
$params->document->paragraphClose();
// Do additional actions if the parent element is a list.
// In this case we need to finish the list and re-open it later
// after the table has been closed! --> tables may not be part of a list item in ODT!
$interrupted = false;
if ($tableStyleName == NULL) {
$tableStyleName = $params->document->getStyleName('table');
}
$list_item = $params->document->state->getCurrentListItem();
if ($list_item != NULL) {
// We are in a list item. Query indentation settings.
$list = $list_item->getList();
if ($list != NULL) {
$list_style_name = $list->getStyleName();
$list_style = $params->document->getStyle($list_style_name);
if ($list_style != NULL) {
// The list level stored in the list item/from the parser
// might not be correct. Count 'list' states to get level.
$level = $params->document->state->countClass('list');
// Create a table style for indenting the table.
// We try to achieve this by substracting the list indentation
// from the width of the table and right align it!
// (if not done yet, the name must be unique!)
$count = $params->document->state->getElementCount('table')+1;
$style_name = 'Table'.$count.'_Indentation_Level'.$level;
if (!$params->document->styleExists($style_name)) {
$style_obj = clone $params->document->getStyle($tableStyleName);
$style_obj->setProperty('style-name', $style_name);
if ($style_obj != NULL) {
$max = $params->document->getAbsWidthMindMargins();
$indent = 0 + ODTUnits::getDigits($list_style->getPropertyFromLevel($level, 'margin-left'));
$style_obj->setProperty('margin-left', ($indent).'cm');
if ($style_obj->getProperty('width') == NULL && $style_obj->getProperty('rel-width')) {
$style_obj->setProperty('width', ($max-$indent).'cm');
}
$style_obj->setProperty('align', 'left');
$params->document->addAutomaticStyle($style_obj);
}
}
$tableStyleName = $style_name;
}
}
// Close all open lists and remember their style (may be nested!)
$lists = array();
$first = true;
$iterations = 0;
$list = $params->document->state->getCurrentList();
while ($list != NULL)
{
// Close list items
if ($first == true) {
$first = false;
$params->document->listContentClose();
}
$params->document->listItemClose();
// Now we are in the list state!
// Get the lists style name before closing it.
$lists [] = $list->getStyleName();
// Reset saved last paragraph position to -1 to prevent change of the paragraph style
$list->setListLastParagraphPosition(-1);
$params->document->listClose();
if ($params->document->state == NULL ||
$params->document->state->getCurrent()->getElementName() == 'root') {
break;
}
// List has been closed (and removed from stack). Get next.
$list = $params->document->state->getCurrentList();
// Just to prevent endless loops in case of an error!
$iterations++;
if ($iterations == 50) {
$params->content .= 'Error: ENDLESS LOOP!';
break;
}
}
$interrupted = true;
}
if ($elementObj == NULL) {
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
}
$table = new ODTElementTable($tableStyleName, $maxcols, $numrows);
$params->document->state->enter($table);
if ($interrupted == true) {
// Set marker that list has been interrupted
$table->setListInterrupted(true);
// Save the lists array as temporary data
// in THIS state because this is the state that we get back
// to in table_close!!!
// (we closed the ODT list, we can't access its state info anymore!
// So we use the table state to save the style name!)
$table->setTemp($lists);
}
$table->setHTMLElement ($element);
$params->content .= $table->getOpeningTag();
}
/**
* Close/finish a table
*/
public static function tableClose(ODTInternalParams $params){
$table = $params->document->state->getCurrentTable();
if ($table == NULL) {
// ??? Error. Not table found.
return;
}
if ($params->document->state->getInTableRow()) {
// If we are still inside a table row then close it first,
// to prevent an error or broken document.
$params->document->tableRowClose();
}
$interrupted = $table->getListInterrupted();
$lists = NULL;
if ($interrupted) {
$lists = $table->getTemp();
}
// Eventually adjust table width.
$table->adjustWidth ($params);
// Close the table.
ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement());
$params->content .= $table->getClosingTag($params->content);
$params->document->state->leave();
// Do additional actions required if we interrupted a list,
// see table_open()
if ($interrupted) {
// Re-open list(s) with original style!
// (in revers order of lists array)
$max = count($lists);
for ($index = $max ; $index > 0 ; $index--) {
$params->document->listOpen(true, $lists [$index-1]);
// If this is not the most inner list then we need to open
// a list item too!
if ($index > 0) {
$params->document->listItemOpen($max-$index);
}
}
// DO NOT set marker that list is not interrupted anymore, yet!
// The renderer will still call listcontent_close and listitem_close!
// The marker will only be reset on the next call from the renderer to listitem_open!!!
//$table->setListInterrupted(false);
}
}
/**
* @param array $properties
*/
public static function tableAddColumn (ODTInternalParams $params, $styleNameSet=NULL, &$styleNameGet=NULL){
// Create new column
$column = new ODTElementTableColumn();
$params->document->state->enter($column);
if ($styleNameSet != NULL) {
// Change automatically assigned style name.
$column->setStyleName($styleNameSet);
}
// Return style name to caller.
$styleNameGet = $column->getStyleName();
// Never create any new document content here!!!
// Columns have already been added on table open or are
// re-written on table close.
$params->document->state->leave();
}
/**
* Open a table row
*/
public static function tableRowOpen(ODTInternalParams $params, $styleName=NULL, $element=NULL, $attributes=NULL){
if ($params->document->state->getInTableRow()) {
// If we are still inside a table row then close it first,
// to prevent an error or broken document.
$params->document->tableRowClose();
}
if ($element == NULL) {
$element = 'tr';
}
if ($params->elementObj == NULL) {
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
}
$row = new ODTElementTableRow($styleName);
$params->document->state->enter($row);
$params->content .= $row->getOpeningTag();
$row->setHTMLElement ($element);
}
/**
* Close a table row
*/
public static function tableRowClose(ODTInternalParams $params){
if ($params->document->state->getInTableCell()) {
// If we are still inside a table cell then close it first,
// to prevent an error or broken document.
$params->document->tableCellClose();
}
ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement());
$params->document->closeCurrentElement();
}
/**
* Open a table header cell
*
* @param int $colspan
* @param int $rowspan
* @param string $align left|center|right
*/
public static function tableHeaderOpen(ODTInternalParams $params, $colspan = 1, $rowspan = 1, $align = "left", $cellStyle=NULL, $paragraphStyle=NULL, $element=NULL, $attributes=NULL){
if ($element == NULL) {
$element = 'th';
}
// Are style names given? If not, use defaults.
if (empty($cellStyle)) {
$cellStyle = $params->document->getStyleName('table header');
}
if (empty($paragraphStyle)) {
$paragraphStyle = $params->document->getStyleName('table heading');
}
if ($params->elementObj == NULL) {
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
}
// ODT has no element for the table header.
// We mark the state with a differnt class to be able
// to differ between a normal cell and a header cell.
$header_cell = new ODTElementTableHeaderCell
($cellStyle, $colspan, $rowspan);
$params->document->state->enter($header_cell);
$header_cell->setHTMLElement ($element);
// Encode table (header) cell.
$params->content .= $header_cell->getOpeningTag();
// Open new paragraph with table heading style.
$params->document->paragraphOpen($paragraphStyle);
}
/**
* Close a table header cell
*/
public static function tableHeaderClose(ODTInternalParams $params){
$params->document->paragraphClose();
ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement());
$params->document->closeCurrentElement();
}
/**
* Open a table cell
*
* @param int $colspan
* @param int $rowspan
* @param string $align left|center|right
*/
public static function tableCellOpen(ODTInternalParams $params, $colspan = 1, $rowspan = 1, $align = "left", $cellStyle=NULL, $paragraphStyle=NULL, $element=NULL, $attributes=NULL){
if ($element == NULL) {
$element = 'td';
}
if ($params->document->state->getInTableCell()) {
// If we are still inside a table cell then close it first,
// to prevent an error or broken document.
$params->document->tableCellClose();
}
// Are style names given? If not, use defaults.
if (empty($cellStyle)) {
$cellStyle = $params->document->getStyleName('table cell');
}
if (empty($paragraphStyle)) {
// Open paragraph with required alignment.
if (!$align) $align = "left";
$paragraphStyle = $params->document->getStyleName('tablealign '.$align);
}
if ($params->elementObj == NULL) {
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
}
$cell = new ODTElementTableCell
($cellStyle, $colspan, $rowspan);
$params->document->state->enter($cell);
$cell->setHTMLElement ($element);
// Encode table cell.
$params->content .= $cell->getOpeningTag();
// Open paragraph.
$params->document->paragraphOpen($paragraphStyle);
}
/**
* Close a table cell
*/
public static function tableCellClose(ODTInternalParams $params){
$params->document->paragraphClose();
ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement());
$params->document->closeCurrentElement();
}
/**
* This function opens a new table using the style as set in the imported CSS $import.
* So, the function requires the helper class 'helper_plugin_odt_cssimport'.
* The CSS style is selected by the element type 'td' and the specified classes in $classes.
*
* This function calls _odtTableOpenUseProperties. See the function description for supported properties.
*
* The table should be closed by calling 'table_close()'.
*
* @author LarsDW223
*
* @param cssimportnew $import
* @param $classes
* @param null $baseURL
* @param null $element
* @param null $maxcols
* @param null $numrows
*/
public static function tableOpenUseCSS(ODTInternalParams $params, $maxcols=NULL, $numrows=NULL, $element=NULL, $attributes=NULL){
if ($element == NULL) {
$element = 'table';
}
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
$params->elementObj = $params->htmlStack->getCurrentElement();
self::tableOpenUseProperties($params, $properties, $maxcols, $numrows);
}
/**
* This function opens a new table using the style as set in the assoziative array $properties.
* The parameters in the array should be named as the CSS property names e.g. 'width'.
*
* The currently supported properties are:
* width, border-collapse, background-color
*
* The table must be closed by calling 'table_close'.
*
* @author LarsDW223
*
* @param array $properties
* @param null $maxcols
* @param null $numrows
*/
public static function tableOpenUseProperties (ODTInternalParams $params, $properties, $maxcols = 0, $numrows = 0){
$elementObj = $params->elementObj;
// Eventually adjust table width.
if ( !empty ($properties ['width']) ) {
if ( $properties ['width'] [strlen($properties ['width'])-1] != '%' ) {
// Width has got an absolute value.
// Some units are not supported by ODT for table width (e.g. 'px').
// So we better convert it to points.
$properties ['width'] = $params->document->toPoints($properties ['width'], 'x');
}
}
// Create style.
// FIXME: fix disabled_props, ask state for current max width...
$style_obj = ODTTableStyle::createTableTableStyle($properties, NULL, 17);
$params->document->addAutomaticStyle($style_obj);
$style_name = $style_obj->getProperty('style-name');
// Open the table referencing our style.
$params->elementObj = $elementObj;
self::tableOpen($params, $maxcols, $numrows, $style_name);
}
/**
* @param array $properties
*/
public static function tableAddColumnUseProperties (ODTInternalParams $params, array $properties = NULL){
// Add column and set/query assigned style name
$styleName = $properties ['style-name'];
$styleNameGet = '';
self::tableAddColumn ($params, $styleName, $styleNameGet);
// Overwrite/Create column style for actual column
$properties ['style-name'] = $styleNameGet;
$style_obj = ODTTableColumnStyle::createTableColumnStyle ($properties);
$params->document->addAutomaticStyle($style_obj);
}
/**
* @param helper_plugin_odt_cssimport $import
* @param $classes
* @param null $baseURL
* @param null $element
* @param int $colspan
* @param int $rowspan
*/
public static function tableHeaderOpenUseCSS(ODTInternalParams $params, $colspan = 1, $rowspan = 1, $element=NULL, $attributes=NULL){
if ($element == NULL) {
$element = 'th';
}
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
$params->elementObj = $params->htmlStack->getCurrentElement();
self::tableHeaderOpenUseProperties($params, $properties, $colspan, $rowspan);
}
/**
* @param null $properties
* @param int $colspan
* @param int $rowspan
*/
public static function tableHeaderOpenUseProperties (ODTInternalParams $params, $properties = NULL, $colspan = 1, $rowspan = 1){
// Open cell, second parameter MUST BE true to indicate we are in the header.
self::tableCellOpenUsePropertiesInternal ($params, $properties, true, $colspan, $rowspan);
}
/**
* This function opens a new table row using the style as set in the imported CSS $import.
* So, the function requires the helper class 'helper_plugin_odt_cssimport'.
* The CSS style is selected by the element type 'td' and the specified classes in $classes.
*
* This function calls _odtTableRowOpenUseProperties. See the function description for supported properties.
*
* The row should be closed by calling 'tablerow_close()'.
*
* @author LarsDW223
* @param helper_plugin_odt_cssimport $import
* @param $classes
* @param null $baseURL
* @param null $element
*/
public static function tableRowOpenUseCSS(ODTInternalParams $params, $element=NULL, $attributes=NULL){
if ($element == NULL) {
$element = 'tr';
}
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
$params->elementObj = $params->htmlStack->getCurrentElement();
self::tableRowOpenUseProperties($params, $properties);
}
/**
* @param array $properties
*/
public static function tableRowOpenUseProperties (ODTInternalParams $params, $properties){
// Create style.
$style_obj = ODTTableRowStyle::createTableRowStyle ($properties);
$params->document->addAutomaticStyle($style_obj);
$style_name = $style_obj->getProperty('style-name');
// Open table row with our new style.
self::tableRowOpen($params, $style_name);
}
/**
* This function opens a new table cell using the style as set in the imported CSS $import.
* So, the function requires the helper class 'helper_plugin_odt_cssimport'.
* The CSS style is selected by the element type 'td' and the specified classes in $classes.
*
* This function calls _odtTableCellOpenUseProperties. See the function description for supported properties.
*
* The cell should be closed by calling 'tablecell_close()'.
*
* @author LarsDW223
*
* @param helper_plugin_odt_cssimport $import
* @param $classes
* @param null $baseURL
* @param null $element
*/
public static function tableCellOpenUseCSS(ODTInternalParams $params, $element=NULL, $attributes=NULL, $colspan = 1, $rowspan = 1){
if ($element == NULL) {
$element = 'td';
}
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
$params->elementObj = $params->htmlStack->getCurrentElement();
self::tableCellOpenUseProperties($params, $properties, $colspan, $rowspan);
}
/**
* @param $properties
*/
public static function tableCellOpenUseProperties (ODTInternalParams $params, $properties = NULL, $colspan = 1, $rowspan = 1){
self::tableCellOpenUsePropertiesInternal ($params, $properties, false, $colspan, $rowspan);
}
/**
* @param $properties
* @param bool $inHeader
* @param int $colspan
* @param int $rowspan
*/
static protected function tableCellOpenUsePropertiesInternal (ODTInternalParams $params, $properties, $inHeader = false, $colspan = 1, $rowspan = 1){
$disabled = array ();
// Create style name. (Re-enable background-color!)
$style_obj = ODTTableCellStyle::createTableCellStyle ($properties);
$params->document->addAutomaticStyle($style_obj);
$style_name = $style_obj->getProperty('style-name');
// Create a paragraph style for the paragraph within the cell.
// Disable properties that belong to the table cell style.
$disabled ['border'] = 1;
$disabled ['border-left'] = 1;
$disabled ['border-right'] = 1;
$disabled ['border-top'] = 1;
$disabled ['border-bottom'] = 1;
$disabled ['background-color'] = 1;
$disabled ['background-image'] = 1;
$disabled ['vertical-align'] = 1;
$style_obj = ODTParagraphStyle::createParagraphStyle ($properties, $disabled);
$params->document->addAutomaticStyle($style_obj);
$style_name_paragraph = $style_obj->getProperty('style-name');
// Open header or normal cell.
if ($inHeader) {
self::tableHeaderOpen($params, $colspan, $rowspan, NULL, $style_name, $style_name_paragraph);
} else {
self::tableCellOpen($params, $colspan, $rowspan, NULL, $style_name, $style_name_paragraph);
}
// There might be properties in the table header cell/normal cell which in ODT belong to the
// column, e.g. 'width'. So eventually adjust column style.
self::adjustColumnStyle($params, $properties);
}
static protected function adjustColumnStyle(ODTInternalParams $params, array $properties) {
$table = $params->document->state->getCurrentTable();
if ($table == NULL) {
// ??? Error. Not table found.
return;
}
$curr_column = $table->getTableCurrentColumn();
$table_column_styles = $table->getTableColumnStyles();
$style_name = $table_column_styles [$curr_column-1];
$style_obj = $params->document->getStyle($style_name);
if ($style_obj != NULL) {
if (!empty($properties ['width'])) {
$width = $properties ['width'];
$length = strlen ($width);
$width = $params->document->toPoints($width, 'x');
$style_obj->setProperty('column-width', $width);
}
} else {
self::tableAddColumnUseProperties ($params, $properties);
}
}
}

View File

@@ -0,0 +1,274 @@
<?php
/**
* Simple class to work with units (e.g. 'px', 'pt', 'cm'...)
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
/**
* Class helper_plugin_odt_units
*/
class ODTUnits {
// All static variables with fixed values
// Measure units as defined in "Extensible Stylesheet Language (XSL) Version 1.1"
protected static $xsl_units = array('cm', 'mm', 'in', 'pt', 'pc', 'px', 'em');
protected static $point_in_cm = 0.035277778;
protected static $inch_in_cm = 2.54;
protected static $inch_in_pt = 0.089605556;
protected static $pc_in_cm = 0.423333336;
protected static $pc_in_pt = 12;
protected static $twips_per_point = 20;
// Non static variables, can be changed
protected $px_per_em = 14;
protected $twips_per_pixel_x = 16;
protected $twips_per_pixel_y = 20;
/**
* Strips of the leading digits from $value. So left over will be the unit only.
*
* @param int $value The length value string, e.g. '1cm'.
* @return string The unit of $value, e.g. 'cm'
*/
static public function stripDigits ($value) {
return ltrim ($value, '-.0123456789');
}
/**
* Gets only the digits from $value without the unit.
*
* @param string|int $value The length value string, e.g. '1cm'.
* @return string The digits of $value, e.g. '1'
*/
static public function getDigits ($value) {
$digits = NULL;
$length = strlen ((string)$value);
for ($index = 0 ; $index < $length ; $index++ ) {
if ( is_numeric ($value [$index]) === false &&
$value [$index] != '.' &&
$value [$index] != '-' ) {
break;
}
$digits .= $value [$index];
}
return $digits;
}
/**
* Checks if $unit is a valid XSL unit.
*
* @param string $unit The unit string, e.g. 'cm'.
* @return boolean true if valid, false otherwise
*/
static public function isValidXSLUnit($unit) {
return in_array($unit, self::$xsl_units);
}
/**
* Checks if length value string $value has a valid XSL unit.
*
* @param string|int $value The length value string, e.g. '1cm'.
* @return boolean true if valid, false otherwise
*/
static public function hasValidXSLUnit($value) {
return in_array(self::stripDigits((string)$value), self::$xsl_units);
}
/**
* Sets the pixel per em unit used for px to em conversion.
*
* @param int $value The value to be set.
*/
public function setPixelPerEm ($value) {
$this->px_per_em = $value;
}
/**
* Query the pixel per em unit.
*
* @return int The current value.
*/
public function getPixelPerEm () {
return $this->px_per_em;
}
/**
* Sets the twips per pixel (X axis) used for px to pt conversion.
*
* @param int $value The value to be set.
*/
public function setTwipsPerPixelX ($value) {
$this->twips_per_pixel_x = $value;
}
/**
* Sets the twips per pixel (Y axis) unit used for px to pt conversion.
*
* @param int $value The value to be set.
*/
public function setTwipsPerPixelY ($value) {
$this->twips_per_pixel_y = $value;
}
/**
* Query the twips per pixel (X axis) setting.
*
* @return int The current value.
*/
public function getTwipsPerPixelX () {
return $this->twips_per_pixel_x;
}
/**
* Query the twips per pixel (Y axis) setting.
*
* @return int The current value.
*/
public function getTwipsPerPixelY () {
return $this->twips_per_pixel_y;
}
/**
* Convert pixel (X axis) to points according to the current settings.
*
* @param string|int $pixel String with pixel length value, e.g. '20px'
* @return string The current value.
*/
public function pixelToPointsX ($pixel) {
$pixel = self::getDigits ((string)$pixel);
$value = $pixel * $this->twips_per_pixel_x / self::$twips_per_point;
return round ($value, 2).'pt';
}
/**
* Convert pixel (Y axis) to points according to the current settings.
*
* @param string|int $pixel String with pixel length value, e.g. '20px'
* @return string The current value.
*/
public function pixelToPointsY ($pixel) {
$pixel = self::getDigits ((string)$pixel);
$value = $pixel * $this->twips_per_pixel_y / self::$twips_per_point;
return round ($value, 2).'pt';
}
/**
* Convert length value with valid XSL unit to points.
*
* @param string $value String with length value, e.g. '20px', '20cm'...
* @param string $axis Is the value to be converted a value on the X or Y axis? Default is 'y'.
* Only relevant for conversion from 'px' or 'em'.
* @return string The current value.
*/
public function toPoints ($value, $axis = 'y') {
$unit = self::stripDigits ($value);
if ( $unit == 'pt' ) {
return $value;
}
if ( self::isValidXSLUnit ($unit) === false ) {
// Not a vlaid/supported unit. Return original value.
return $value;
}
$value = self::getDigits ($value);
switch ($unit) {
case 'cm':
$value = round (($value/self::$point_in_cm), 2).'pt';
break;
case 'mm':
$value = round (($value/(10 * self::$point_in_cm)), 2).'pt';
break;
case 'in':
$value = round (($value * self::$inch_in_pt), 2).'pt';
break;
case 'pc':
$value = round (($value * self::$pc_in_pt), 2).'pt';
break;
case 'px':
if ( $axis == 'x' || $axis == 'X' ) {
$value = $this->pixelToPointsX ($value);
} else {
$value = $this->pixelToPointsY ($value);
}
break;
case 'em':
$value = $this->pixelToPointsY ($value * $this->getPixelPerEm());
break;
}
return $value;
}
/**
* Convert length value with valid XSL unit to points.
*
* @param string $value String with length value, e.g. '20px', '20pt'...
* @param string $axis Is the value to be converted a value on the X or Y axis? Default is 'y'.
* Only relevant for conversion from 'px' or 'em'.
* @return string The current value.
*/
public function toCentimeters ($value, $axis = 'y') {
$unit = self::stripDigits ($value);
if ( $unit == 'cm' ) {
return $value;
}
if ( self::isValidXSLUnit ($unit) === false ) {
// Not a vlaid/supported unit. Return original value.
return $value;
}
$value = self::toPoints ($value, $axis);
$value = substr($value, 0, -2);
$value = round (($value * self::$point_in_cm), 2).'cm';
return $value;
}
/**
* Convert length value with valid XSL unit to pixel.
*
* @param string $value String with length value, e.g. '20pt'...
* @param string $axis Is the value to be converted a value on the X or Y axis? Default is 'y'.
* Only relevant for conversion from 'px' or 'em'.
* @return string The current value.
*/
public function toPixel ($value, $axis = 'y') {
$unit = self::stripDigits ($value);
if ( $unit == 'px' ) {
return $value;
}
if ( self::isValidXSLUnit ($unit) === false ) {
// Not a vlaid/supported unit. Return original value.
return $value;
}
$value = self::toPoints ($value, $axis);
$value = substr($value, 0, -2);
if ($axis == 'x') {
$value = round ((($value*self::$twips_per_point)/$this->twips_per_pixel_x), 2).'px';
} else {
$value = round ((($value*self::$twips_per_point)/$this->twips_per_pixel_y), 2).'px';
}
return $value;
}
public function getAbsoluteValue ($value, $base) {
$unit = self::stripDigits ($value);
$value = self::getDigits ($value);
switch ($unit) {
case '%':
$value = ($value * $base)/100;
break;
case 'em':
$value = $value * $base;
break;
default:
// Not an relative value. Just keep it.
break;
}
return $value;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,66 @@
<?php
/**
* ODTManifest: class for maintaining the manifest data of an ODT document.
* Code was previously included in renderer.php.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author Andreas Gohr <andi@splitbrain.org>
* @author Aurelien Bompard <aurelien@bompard.org>
* @author LarsDW223
*/
class ODTManifest
{
var $manifest = array();
/**
* Returns the complete manifest content.
*/
function getContent(){
$value = '<' . '?xml version="1.0" encoding="UTF-8"?' . ">\n";
$value .= '<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" manifest:version="1.2">';
$value .= '<manifest:file-entry manifest:full-path="/" manifest:version="1.2" manifest:media-type="application/vnd.oasis.opendocument.text"/>';
$value .= '<manifest:file-entry manifest:full-path="content.xml" manifest:media-type="text/xml"/>';
$value .= '<manifest:file-entry manifest:full-path="settings.xml" manifest:media-type="text/xml"/>';
$value .= '<manifest:file-entry manifest:full-path="meta.xml" manifest:media-type="text/xml"/>';
$value .= '<manifest:file-entry manifest:full-path="styles.xml" manifest:media-type="text/xml"/>';
$value .= $this->getExtraContent();
$value .= '</manifest:manifest>';
return $value;
}
/**
* Returns only the xml lines containing the dynamically added user content
* files like images etc..
*/
function getExtraContent() {
$value = '';
foreach($this->manifest as $path => $type){
$value .= '<manifest:file-entry manifest:media-type="'.htmlspecialchars($type, ENT_QUOTES, 'UTF-8').
'" manifest:full-path="'.htmlspecialchars($path, ENT_QUOTES, 'UTF-8').'"/>';
}
return $value;
}
/**
* Checks if $name is present or was added to the manifest data.
*
* @param string $name
* @return bool
*/
function exists($name) {
return isset($this->manifest[$name]);
}
/**
* Adds $name with $mime to the manifest data.
*
* @param string $name
* @param string $mime
*/
function add($name, $mime) {
$this->manifest[$name] = $mime;
}
}

View File

@@ -0,0 +1,61 @@
<?php
/**
* ODTMeta: class for maintaining the meta data of an ODT document.
* Code was previously included in renderer.php.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author Andreas Gohr <andi@splitbrain.org>
* @author Aurelien Bompard <aurelien@bompard.org>
* @author LarsDW223
*/
class ODTMeta
{
var $meta = array();
/**
* Constructor. Set initial meta data.
*/
public function __construct() {
$this->meta = array(
'meta:generator' => 'DokuWiki '.getversion(),
'meta:initial-creator' => 'Generated',
'meta:creation-date' => date('Y-m-d\\TH::i:s', null), //FIXME
'dc:creator' => 'Generated',
'dc:date' => date('Y-m-d\\TH::i:s', null),
'dc:language' => 'en-US',
'meta:editing-cycles' => '1',
'meta:editing-duration' => 'PT0S',
);
}
/**
* @param string $title
*/
function setTitle ($title) {
$this->meta ['dc:title'] = $title;
}
/**
* Returns the complete meta content.
*/
function getContent(){
$value = '<' . '?xml version="1.0" encoding="UTF-8"?' . ">\n";
$value .= '<office:document-meta ';
$value .= 'xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" ';
$value .= 'xmlns:xlink="http://www.w3.org/1999/xlink" ';
$value .= 'xmlns:dc="http://purl.org/dc/elements/1.1/" ';
$value .= 'xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" ';
$value .= 'xmlns:ooo="http://openoffice.org/2004/office" ';
$value .= 'xmlns:grddl="http://www.w3.org/2003/g/data-view#" ';
$value .= 'office:version="1.2">';
$value .= '<office:meta>';
# FIXME
foreach($this->meta as $meta_key => $meta_value) {
$value .= '<' . $meta_key . '>' . htmlspecialchars($meta_value, ENT_QUOTES, 'UTF-8') . '</' . $meta_key . '>';
}
$value .= '</office:meta>';
$value .= '</office:document-meta>';
return $value;
}
}

View File

@@ -0,0 +1,30 @@
<?php
/**
* ODTSettings: class for maintaining the settings data of an ODT document.
* Code was previously included in renderer.php.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author Andreas Gohr <andi@splitbrain.org>
* @author Aurelien Bompard <aurelien@bompard.org>
* @author LarsDW223
*/
class ODTSettings
{
var $settings = null;
/**
* Constructor. Set initial meta data.
*/
public function __construct() {
$this->settings = '<' . '?xml version="1.0" encoding="UTF-8"?' . ">\n";
$this->settings .= '<office:document-settings xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" office:version="1.2"><office:settings><config:config-item-set config:name="dummy-settings"><config:config-item config:name="MakeValidatorHappy" config:type="boolean">true</config:config-item></config:config-item-set></office:settings></office:document-settings>';
}
/**
* Returns the complete manifest content.
*/
function getContent(){
return $this->settings;
}
}

View File

@@ -0,0 +1,422 @@
<?php
/**
* XMLUtil: class with helper functions for simple XML handling
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
/**
* The XMLUtil class
*/
class XMLUtil
{
public static function isValidXMLName ($sign) {
if (ctype_alnum($sign) || $sign == ':' || $sign == '-' || $sign == '_') {
return true;
}
return false;
}
/**
* Helper function which returns the opening $element tag
* if found in $xml_code. Otherwise it returns NULL.
*
* @param $element The name of the element
* @param $xmlCode The XML code to search through
* @return string Found opening tag or NULL
*/
public static function getElementOpenTag ($element, $xmlCode) {
$pattern = '/'.$element.'\s[^>]*>/';
if (preg_match ($pattern, $xmlCode, $matches) === 1) {
return $matches [0];
}
return NULL;
}
/**
* Helper function to find the next element $element and return its
* complete definition including opening and closing tag.
*
* THIS FUNCTION DOES NOT HANDLE ELEMENTS WHICH CAN BE NESTED IN THEMSELVES!!!
*
* @param $element The name of the element
* @param $xmlCode The XML code to search through
* @return string Found element or NULL
*/
public static function getElement ($element, $xmlCode, &$endPos=NULL) {
if(empty($element) || empty($xmlCode)) {
return NULL;
}
$pos = 0;
$max = strlen ($xmlCode);
$elementLength = strlen ($element);
// Search the opening tag first.
while ($pos < $max) {
$start = strpos ($xmlCode, '<'.$element, $pos);
if ($start === false) {
// Nothing found.
return NULL;
}
$next = $xmlCode [$start+$elementLength+1];
if ($next == '/' || $next == '>' || ctype_space($next)) {
// Found it.
break;
}
$pos = $start+$elementLength;
}
$pos = $start+$elementLength;
// Search next '>'.
$angle = strpos ($xmlCode, '>', $pos);
if ($angle === false) {
// Opening tag is not terminated.
return NULL;
}
$pos = $angle + 1;
// Is this already the end?
if ($xmlCode [$angle-1] == '/') {
// Yes.
$endPos = $angle+1;
return substr ($xmlCode, $start, $angle-$start+1);
}
// Now, search closing tag.
// (Simple solution which expects there are no child elements
// with the same name. This means we assume the element can not
// be nested in itself!)
$end = strpos ($xmlCode, '</'.$element.'>', $pos);
if ($end === false) {
return NULL;
}
$end += 3 + $elementLength;
// Found closing tag.
$endPos = $end;
return substr ($xmlCode, $start, $end-$start);
}
/**
* Helper function to find the next element $element and return its
* content only without the opening and closing tag of $element itself.
*
* THIS FUNCTION DOES NOT HANDLE ELEMENTS WHICH CAN BE NESTED IN THEMSELVES!!!
*
* @param $element The name of the element
* @param $xmlCode The XML code to search through
* @return string Found element or NULL
*/
public static function getElementContent ($element, $xmlCode, &$endPos=NULL) {
if(empty($element) || empty($xmlCode)) {
return NULL;
}
$pos = 0;
$max = strlen ($xmlCode);
$elementLength = strlen ($element);
$contentStart = 0;
$contentEnd = 0;
// Search the opening tag first.
while ($pos < $max) {
$start = strpos ($xmlCode, '<'.$element, $pos);
if ($start === false) {
// Nothing found.
return NULL;
}
$next = $xmlCode [$start+$elementLength+1];
if ($next == '/' || $next == '>' || ctype_space($next)) {
// Found it.
break;
}
$pos = $start+$elementLength;
}
$pos = $start+$elementLength;
// Search next '>'.
$angle = strpos ($xmlCode, '>', $pos);
if ($angle === false) {
// Opening tag is not terminated.
return NULL;
}
$pos = $angle + 1;
// Is this already the end?
if ($xmlCode [$angle-1] == '/') {
// Yes. No content in this case!
$endPos = $angle+1;
return NULL;
}
$contentStart = $angle+1;
// Now, search closing tag.
// (Simple solution which expects there are no child elements
// with the same name. This means we assume the element can not
// be nested in itself!)
$end = strpos ($xmlCode, '</'.$element.'>', $pos);
if ($end === false) {
return NULL;
}
$contentEnd = $end - 1;
$end += 3 + $elementLength;
// Found closing tag.
$endPos = $end;
if ($contentEnd <= $contentStart) {
return NULL;
}
return substr ($xmlCode, $contentStart, $contentEnd-$contentStart+1);
}
/**
* Helper function to find the next element and return its
* content only without the opening and closing tag of $element itself.
*
* THIS FUNCTION DOES NOT HANDLE ELEMENTS WHICH CAN BE NESTED IN THEMSELVES!!!
*
* @param $element On success $element carries the name of the found element
* @param $xmlCode The XML code to search through
* @return string Found element or NULL
*/
public static function getNextElementContent (&$element, $xmlCode, &$endPos=NULL) {
if(empty($xmlCode)) {
return NULL;
}
$pos = 0;
$max = strlen ($xmlCode);
$contentStart = 0;
$contentEnd = 0;
// Search the opening tag first.
while ($pos < $max) {
$start = strpos ($xmlCode, '<', $pos);
if ($start === false) {
// Nothing found.
return NULL;
}
if (XMLUtil::isValidXMLName ($xmlCode [$start+1])) {
// Extract element name.
$read = $start+1;
$found_element = '';
while (XMLUtil::isValidXMLName ($xmlCode [$read])) {
$found_element .= $xmlCode [$read];
$read++;
if ($read >= $max) {
return NULL;
}
}
$elementLength = strlen ($found_element);
$next = $xmlCode [$start+$elementLength+1];
if ($next == '/' || $next == '>' || ctype_space($next)) {
// Found it.
break;
}
$pos = $start+$elementLength;
} else {
// Skip this one.
$pos = $start+2;
}
}
$pos = $start+$elementLength;
// Search next '>'.
$angle = strpos ($xmlCode, '>', $pos);
if ($angle === false) {
// Opening tag is not terminated.
return NULL;
}
$pos = $angle + 1;
// Is this already the end?
if ($xmlCode [$angle-1] == '/') {
// Yes. No content in this case!
$endPos = $angle+1;
$element = $found_element;
return NULL;
}
$contentStart = $angle+1;
// Now, search closing tag.
// (Simple solution which expects there are no child elements
// with the same name. This means we assume the element can not
// be nested in itself!)
$end = strpos ($xmlCode, '</'.$found_element.'>', $pos);
if ($end === false) {
return NULL;
}
$contentEnd = $end - 1;
$end += 3 + $elementLength;
// Found closing tag.
$endPos = $end;
if ($contentEnd <= $contentStart) {
return NULL;
}
$element = $found_element;
return substr ($xmlCode, $contentStart, $contentEnd-$contentStart+1);
}
/**
* Helper function to find the next element and return its
* complete definition including opening and closing tag.
*
* THIS FUNCTION DOES NOT HANDLE ELEMENTS WHICH CAN BE NESTED IN THEMSELVES!!!
*
* @param $element On success $element carries the name of the found element
* @param $xmlCode The XML code to search through
* @return string Found element or NULL
*/
public static function getNextElement (&$element, $xmlCode, &$endPos=NULL) {
if(empty($xmlCode)) {
return NULL;
}
$pos = 0;
$max = strlen ($xmlCode);
// Search the opening tag first.
while ($pos < $max) {
$start = strpos ($xmlCode, '<', $pos);
if ($start === false) {
// Nothing found.
return NULL;
}
if (XMLUtil::isValidXMLName ($xmlCode [$start+1])) {
// Extract element name.
$read = $start+1;
$found_element = '';
while (XMLUtil::isValidXMLName ($xmlCode [$read])) {
$found_element .= $xmlCode [$read];
$read++;
if ($read >= $max) {
return NULL;
}
}
$elementLength = strlen ($found_element);
$next = $xmlCode [$start+$elementLength+1];
if ($next == '/' || $next == '>' || ctype_space($next)) {
// Found it.
break;
}
$pos = $start+$elementLength;
} else {
// Skip this one.
$pos = $start+2;
}
}
$pos = $start+$elementLength;
// Search next '>'.
$angle = strpos ($xmlCode, '>', $pos);
if ($angle === false) {
// Opening tag is not terminated.
return NULL;
}
$pos = $angle + 1;
// Is this already the end?
if ($xmlCode [$angle-1] == '/') {
// Yes.
$endPos = $angle+1;
$element = $found_element;
return substr ($xmlCode, $start, $angle-$start+1);
}
// Now, search closing tag.
// (Simple solution which expects there are no child elements
// with the same name. This means we assume the element can not
// be nested in itself!)
$end = strpos ($xmlCode, '</'.$found_element.'>', $pos);
if ($end === false) {
return NULL;
}
$end += 3 + $elementLength;
// Found closing tag.
$endPos = $end;
$element = $found_element;
return substr ($xmlCode, $start, $end-$start);
}
/**
* Helper function to replace an XML element with a string.
*
* @param $element Name of the element ot be replaced.
* @param $xmlCode The XML code to search through
* @param $replacement The string which shall be inserted
* @return string $xmlCode with replaced element
*/
public static function elementReplace ($element, $xmlCode, $replacement) {
$start = strpos ($xmlCode, '<'.$element);
$empty = false;
if ($start === false) {
$empty = strpos ($xmlCode, '<'.$element.'/>');
if ($empty === false) {
return $xmlCode;
}
}
if ($empty !== false) {
// Element has the form '<element/>'. Do a simple string replace.
return str_replace('<'.$element.'/>', $replacement, $xmlCode);
}
$end = strpos ($xmlCode, '</'.$element.'>');
if ($end === false) {
// $xmlCode not well formed???
return $xmlCode;
}
$end_length = strlen ('</'.$element.'>');
return substr_replace ($xmlCode, $replacement, $start, $end-$start+$end_length);
}
/**
* Helper function which returns the value of $attribute
* if found in $xml_code. Otherwise it returns NULL.
*
* @param $attribute The name of the attribute
* @param $xmlCode The XML code to search through
* @return string Found value or NULL
*/
public static function getAttributeValue ($attribute, $xmlCode) {
$pattern = '/\s'.$attribute.'="[^"]*"/';
if (preg_match ($pattern, $xmlCode, $matches) === 1) {
$value = substr($matches [0], strlen($attribute)+2);
$value = trim($value, '"');
return $value;
}
return NULL;
}
/**
* Helper function which stores all attributes
* in the array $attributes as name => value pairs.
*
* @param $attributes Array to store the attributes in
* @param $xmlCode The XML code to search through
* @return integer Number of found attributes or 0
*/
public static function getAttributes (&$attributes, $xmlCode) {
$pattern = '/\s[-:_.a-zA-Z0-9]+="[^"]*"/';
if (preg_match_all ($pattern, $xmlCode, $matches, PREG_SET_ORDER) > 0) {
foreach ($matches as $match) {
$equal_pos = strpos($match [0], '=');
$name = substr($match [0], 0, $equal_pos);
$name = trim($name);
$value = substr($match [0], $equal_pos+1);
$value = trim($value, '"');
$attributes [$name] = $value;
}
return count($attributes);
}
return 0;
}
}

View File

@@ -0,0 +1,293 @@
<?php
/**
* Utility class for handling border properties.
* Only works with properties stored in an array as delivered from
* class cssimportnew, e.g. from method getPropertiesForElement().
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
/**
* Class cssborder.
*
* @package CSS\CSSAttributeSelector
*/
class cssborder {
static public function getWidthShorthandValues ($value, &$top, &$right, &$bottom, &$left) {
$top = NULL;
$right = NULL;
$bottom = NULL;
$left = NULL;
$values = preg_split ('/\s+/', $value);
switch (count($values)) {
case 1:
$top = $values [0];
$bottom = $values [0];
$right = $values [0];
$left = $values [0];
break;
case 2:
$top = $values [0];
$bottom = $values [0];
$right = $values [1];
$left = $values [1];
break;
case 3:
$top = $values [0];
$right = $values [1];
$left = $values [1];
$bottom = $values [2];
break;
case 4:
default:
$top = $values [0];
$right = $values [1];
$bottom = $values [2];
$left = $values [3];
break;
}
}
static public function getShorthandValues ($value, &$width, &$style, &$color) {
$width = NULL;
$style = NULL;
$color = NULL;
if (empty($value)) {
return;
}
if ($value == 'initial' || $value == 'inherit') {
$width = $value;
$style = $value;
$color = $value;
return;
}
$values = preg_split ('/\s+/', $value);
$index = 0;
$border_width_set = false;
$border_style_set = false;
$border_color_set = false;
while ( $index < 3 ) {
if ( $border_width_set === false ) {
switch ($values [$index]) {
case 'thin':
case 'medium':
case 'thick':
$width = $values [$index];
$index++;
break;
default:
$unit = substr ($values [$index], -2);
if ( ctype_digit($values [$index]) ||
$unit == 'px' ||
$unit == 'pt' ||
$unit == 'cm' ) {
$width = $values [$index];
$index++;
} else {
// There is no default value? So leave it unset.
}
break;
}
$border_width_set = true;
continue;
}
if ( $border_style_set === false ) {
switch ($values [$index]) {
case 'none':
case 'hidden':
case 'dotted':
case 'dashed':
case 'solid':
case 'double':
case 'groove':
case 'ridge':
case 'inset':
case 'outset':
case 'initial':
case 'inherit':
$style = $values [$index];
$index++;
break;
}
$border_style_set = true;
continue;
}
if ( $border_color_set === false ) {
if (!empty($values [$index])) {
$color = $values [$index];
}
// This is the last value.
break;
}
}
}
/**
* The function checks if this atrribute selector matches the
* attributes given in $attributes as key - value pairs.
*
* @param string $attributes String containing the selector
* @return boolean
*/
static public function normalize (array &$properties) {
$border_sides = array ('border-left', 'border-right', 'border-top', 'border-bottom');
$bl_width = '0px';
$br_width = '0px';
$bb_width = '0px';
$bt_width = '0px';
$bl_style = 'none';
$br_style = 'none';
$bb_style = 'none';
$bt_style = 'none';
$bl_color = NULL;
$br_color = NULL;
$bb_color = NULL;
$bt_color = NULL;
if (!empty($properties ['border'])) {
$width = NULL;
$style = NULL;
$color = NULL;
self::getShorthandValues ($properties ['border'], $width, $style, $color);
if (!empty($width)) {
$bl_width = $width;
$br_width = $width;
$bb_width = $width;
$bt_width = $width;
}
if (!empty($style)) {
$bl_style = $style;
$br_style = $style;
$bb_style = $style;
$bt_style = $style;
}
if (!empty($color)) {
$bl_color = $color;
$br_color = $color;
$bb_color = $color;
$bt_color = $color;
}
unset ($properties ['border']);
}
if (!empty($properties ['border-left'])) {
$width = NULL;
$style = NULL;
$color = NULL;
self::getShorthandValues ($properties ['border-left'], $width, $style, $color);
if (!empty($width)) {
$bl_width = $width;
}
if (!empty($style)) {
$bl_style = $style;
}
if (!empty($color)) {
$bl_color = $color;
}
unset ($properties ['border-left']);
}
if (!empty($properties ['border-right'])) {
$width = NULL;
$style = NULL;
$color = NULL;
self::getShorthandValues ($properties ['border-right'], $width, $style, $color);
if (!empty($width)) {
$br_width = $width;
}
if (!empty($style)) {
$br_style = $style;
}
if (!empty($color)) {
$br_color = $color;
}
unset ($properties ['border-right']);
}
if (!empty($properties ['border-top'])) {
$width = NULL;
$style = NULL;
$color = NULL;
self::getShorthandValues ($properties ['border-top'], $width, $style, $color);
if (!empty($width)) {
$bt_width = $width;
}
if (!empty($style)) {
$bt_style = $style;
}
if (!empty($color)) {
$bt_color = $color;
}
unset ($properties ['border-top']);
}
if (!empty($properties ['border-bottom'])) {
$width = NULL;
$style = NULL;
$color = NULL;
self::getShorthandValues ($properties ['border-bottom'], $width, $style, $color);
if (!empty($width)) {
$bb_width = $width;
}
if (!empty($style)) {
$bb_style = $style;
}
if (!empty($color)) {
$bb_color = $color;
}
unset ($properties ['border-bottom']);
}
if (!empty($properties ['border-width'])) {
$top = NULL;
$right = NULL;
$bottom = NULL;
$left = NULL;
self::getWidthShorthandValues ($properties ['border-width'], $top, $right, $bottom, $left);
if (!empty($top)) {
$bt_width = $top;
}
if (!empty($right)) {
$br_width = $right;
}
if (!empty($bottom)) {
$bb_width = $bottom;
}
if (!empty($left)) {
$bl_width = $left;
}
unset ($properties ['border-width']);
}
// Now normalize and minimize the collected properties values
// Re-assemble border properties to per side shorthand.
if (!empty($bt_width) || !empty($bt_style) || !empty($bt_color)) {
$properties ['border-top'] = $bt_width.' '.$bt_style.' '.$bt_color;
}
if (!empty($br_width) || !empty($br_style) || !empty($br_color)) {
$properties ['border-right'] = $br_width.' '.$br_style.' '.$br_color;
}
if (!empty($bb_width) || !empty($bb_style) || !empty($bb_color)) {
$properties ['border-bottom'] = $bb_width.' '.$bb_style.' '.$bb_color;
}
if (!empty($bl_width) || !empty($bl_style) || !empty($bl_color)) {
$properties ['border-left'] = $bl_width.' '.$bl_style.' '.$bl_color;
}
// If all sides are the same we can put them all together as a single border shorthand
if ($properties ['border-top'] == $properties ['border-right'] &&
$properties ['border-top'] == $properties ['border-bottom'] &&
$properties ['border-top'] == $properties ['border-left']) {
$properties ['border'] = $properties ['border-top'];
unset ($properties ['border-top']);
unset ($properties ['border-right']);
unset ($properties ['border-bottom']);
unset ($properties ['border-left']);
}
}
}

View File

@@ -0,0 +1,336 @@
<?php
/**
* Simple class to query CSS color values and names.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
/**
* Class csscolors
*/
class csscolors {
protected static $values = array(
'aliceblue' => '#F0F8FF',
'antiquewhite' => '#FAEBD7',
'aqua' => '#00FFFF',
'aquamarine' => '#7FFFD4',
'azure' => '#F0FFFF',
'beige' => '#F5F5DC',
'bisque' => '#FFE4C4',
'black' => '#000000',
'blanchedalmond' => '#FFEBCD',
'blue' => '#0000FF',
'blueviolet' => '#8A2BE2',
'brown' => '#A52A2A',
'burlywood' => '#DEB887',
'cadetblue' => '#5F9EA0',
'chartreuse' => '#7FFF00',
'chocolate' => '#D2691E',
'coral' => '#FF7F50',
'cornflowerblue' => '#6495ED',
'cornsilk' => '#FFF8DC',
'crimson' => '#DC143C',
'cyan' => '#00FFFF',
'darkblue' => '#00008B',
'darkcyan' => '#008B8B',
'darkgoldenrod' => '#B8860B',
'darkgray' => '#A9A9A9',
'darkgreen' => '#006400',
'darkkhaki' => '#BDB76B',
'darkmagenta' => '#8B008B',
'darkolivegreen' => '#556B2F',
'darkorange' => '#FF8C00',
'darkorchid' => '#9932CC',
'darkred' => '#8B0000',
'darksalmon' => '#E9967A',
'darkseagreen' => '#8FBC8F',
'darkslateblue' => '#483D8B',
'darkslategray' => '#2F4F4F',
'darkturquoise' => '#00CED1',
'darkviolet' => '#9400D3',
'deeppink' => '#FF1493',
'deepskyblue' => '#00BFFF',
'dimgray' => '#696969',
'dodgerblue' => '#1E90FF',
'firebrick' => '#B22222',
'floralwhite' => '#FFFAF0',
'forestgreen' => '#228B22',
'fuchsia' => '#FF00FF',
'gainsboro' => '#DCDCDC',
'ghostwhite' => '#F8F8FF',
'gold' => '#FFD700',
'goldenrod' => '#DAA520',
'gray' => '#808080',
'green' => '#008000',
'greenyellow' => '#ADFF2F',
'honeydew' => '#F0FFF0',
'hotpink' => '#FF69B4',
'indianred' => '#CD5C5C',
'indigo' => '#4B0082',
'ivory' => '#FFFFF0',
'khaki' => '#F0E68C',
'lavender' => '#E6E6FA',
'lavenderblush' => '#FFF0F5',
'lawngreen' => '#7CFC00',
'lemonchiffon' => '#FFFACD',
'lightblue' => '#ADD8E6',
'lightcoral' => '#F08080',
'lightcyan' => '#E0FFFF',
'lightgoldenrodyellow' => '#E0FFFF',
'lightgray' => '#D3D3D3',
'lightgreen' => '#90EE90',
'lightpink' => '#FFB6C1',
'lightsalmon' => '#FFA07A',
'lightseagreen' => '#20B2AA',
'lightskyblue' => '#87CEFA',
'lightslategray' => '#778899',
'lightsteelblue' => '#B0C4DE',
'lightyellow' => '#FFFFE0',
'lime' => '#00FF00',
'limegreen' => '#32CD32',
'linen' => '#FAF0E6',
'magenta' => '#FF00FF',
'maroon' => '#800000',
'mediumaquamarine' => '#66CDAA',
'mediumblue' => '#0000CD',
'mediumorchid' => '#BA55D3',
'mediumpurple' => '#9370DB',
'mediumseagreen' => '#3CB371',
'mediumslateblue' => '#7B68EE',
'mediumspringgreen' => '#00FA9A',
'mediumturquoise' => '#48D1CC',
'mediumvioletred' => '#C71585',
'midnightblue' => '#191970',
'mintcream' => '#F5FFFA',
'mistyrose' => '#FFE4E1',
'moccasin' => '#FFE4B5',
'navajowhite' => '#FFDEAD',
'navy' => '#000080',
'oldlace' => '#FDF5E6',
'olive' => '#808000',
'olivedrab' => '#6B8E23',
'orange' => '#FFA500',
'orangered' => '#FF4500',
'orchid' => '#DA70D6',
'palegoldenrod' => '#EEE8AA',
'palegreen' => '#98FB98',
'paleturquoise' => '#AFEEEE',
'palevioletred' => '#DB7093',
'papayawhip' => '#FFEFD5',
'peachpuff' => '#FFDAB9',
'peru' => '#CD853F',
'pink' => '#FFC0CB',
'plum' => '#DDA0DD',
'powderblue' => '#B0E0E6',
'purple' => '#800080',
'red' => '#FF0000',
'rosybrown' => '#BC8F8F',
'royalblue' => '#4169E1',
'saddlebrown' => '#8B4513',
'salmon' => '#FA8072',
'sandybrown' => '#F4A460',
'seagreen' => '#2E8B57',
'seashell' => '#FFF5EE',
'sienna' => '#A0522D',
'silver' => '#C0C0C0',
'skyblue' => '#87CEEB',
'slateblue' => '#6A5ACD',
'slategray' => '#708090',
'snow' => '#FFFAFA',
'springgreen' => '#00FF7F',
'steelblue' => '#4682B4',
'tan' => '#D2B48C',
'teal' => '#008080',
'thistle' => '#D8BFD8',
'tomato' => '#FF6347',
'turquoise' => '#40E0D0',
'violet' => '#EE82EE',
'wheat' => '#F5DEB3',
'white' => '#FFFFFF',
'whitesmoke' => '#F5F5F5',
'yellow' => '#FFFF00',
'yellowgreen' => '#9ACD32',
);
protected static $names = array(
'#F0F8FF' => 'AliceBlue',
'#FAEBD7' => 'AntiqueWhite',
// '#00FFFF' => 'Aqua', //duplicated key with Cyan
'#7FFFD4' => 'Aquamarine',
'#F0FFFF' => 'Azure',
'#F5F5DC' => 'Beige',
'#FFE4C4' => 'Bisque',
'#000000' => 'Black',
'#FFEBCD' => 'BlanchedAlmond',
'#0000FF' => 'Blue',
'#8A2BE2' => 'BlueViolet',
'#A52A2A' => 'Brown',
'#DEB887' => 'BurlyWood',
'#5F9EA0' => 'CadetBlue',
'#7FFF00' => 'Chartreuse',
'#D2691E' => 'Chocolate',
'#FF7F50' => 'Coral',
'#6495ED' => 'CornflowerBlue',
'#FFF8DC' => 'Cornsilk',
'#DC143C' => 'Crimson',
'#00FFFF' => 'Cyan',
'#00008B' => 'DarkBlue',
'#008B8B' => 'DarkCyan',
'#B8860B' => 'DarkGoldenRod',
'#A9A9A9' => 'DarkGray',
'#006400' => 'DarkGreen',
'#BDB76B' => 'DarkKhaki',
'#8B008B' => 'DarkMagenta',
'#556B2F' => 'DarkOliveGreen',
'#FF8C00' => 'DarkOrange',
'#9932CC' => 'DarkOrchid',
'#8B0000' => 'DarkRed',
'#E9967A' => 'DarkSalmon',
'#8FBC8F' => 'DarkSeaGreen',
'#483D8B' => 'DarkSlateBlue',
'#2F4F4F' => 'DarkSlateGray',
'#00CED1' => 'DarkTurquoise',
'#9400D3' => 'DarkViolet',
'#FF1493' => 'DeepPink',
'#00BFFF' => 'DeepSkyBlue',
'#696969' => 'DimGray',
'#1E90FF' => 'DodgerBlue',
'#B22222' => 'FireBrick',
'#FFFAF0' => 'FloralWhite',
'#228B22' => 'ForestGreen',
//'#FF00FF' => 'Fuchsia', //duplicated key with Magenta
'#DCDCDC' => 'Gainsboro',
'#F8F8FF' => 'GhostWhite',
'#FFD700' => 'Gold',
'#DAA520' => 'GoldenRod',
'#808080' => 'Gray',
'#008000' => 'Green',
'#ADFF2F' => 'GreenYellow',
'#F0FFF0' => 'HoneyDew',
'#FF69B4' => 'HotPink',
'#CD5C5C' => 'IndianRed',
'#4B0082' => 'Indigo',
'#FFFFF0' => 'Ivory',
'#F0E68C' => 'Khaki',
'#E6E6FA' => 'Lavender',
'#FFF0F5' => 'LavenderBlush',
'#7CFC00' => 'LawnGreen',
'#FFFACD' => 'LemonChiffon',
'#ADD8E6' => 'LightBlue',
'#F08080' => 'LightCoral',
'#E0FFFF' => 'LightCyan',
//'#E0FFFF' => 'LightGoldenRodYellow', //duplicated key with LightCyan
'#D3D3D3' => 'LightGray',
'#90EE90' => 'LightGreen',
'#FFB6C1' => 'LightPink',
'#FFA07A' => 'LightSalmon',
'#20B2AA' => 'LightSeaGreen',
'#87CEFA' => 'LightSkyBlue',
'#778899' => 'LightSlateGray',
'#B0C4DE' => 'LightSteelBlue',
'#FFFFE0' => 'LightYellow',
'#00FF00' => 'Lime',
'#32CD32' => 'LimeGreen',
'#FAF0E6' => 'Linen',
'#FF00FF' => 'Magenta',
'#800000' => 'Maroon',
'#66CDAA' => 'MediumAquaMarine',
'#0000CD' => 'MediumBlue',
'#BA55D3' => 'MediumOrchid',
'#9370DB' => 'MediumPurple',
'#3CB371' => 'MediumSeaGreen',
'#7B68EE' => 'MediumSlateBlue',
'#00FA9A' => 'MediumSpringGreen',
'#48D1CC' => 'MediumTurquoise',
'#C71585' => 'MediumVioletRed',
'#191970' => 'MidnightBlue',
'#F5FFFA' => 'MintCream',
'#FFE4E1' => 'MistyRose',
'#FFE4B5' => 'Moccasin',
'#FFDEAD' => 'NavajoWhite',
'#000080' => 'Navy',
'#FDF5E6' => 'OldLace',
'#808000' => 'Olive',
'#6B8E23' => 'OliveDrab',
'#FFA500' => 'Orange',
'#FF4500' => 'OrangeRed',
'#DA70D6' => 'Orchid',
'#EEE8AA' => 'PaleGoldenRod',
'#98FB98' => 'PaleGreen',
'#AFEEEE' => 'PaleTurquoise',
'#DB7093' => 'PaleVioletRed',
'#FFEFD5' => 'PapayaWhip',
'#FFDAB9' => 'PeachPuff',
'#CD853F' => 'Peru',
'#FFC0CB' => 'Pink',
'#DDA0DD' => 'Plum',
'#B0E0E6' => 'PowderBlue',
'#800080' => 'Purple',
'#FF0000' => 'Red',
'#BC8F8F' => 'RosyBrown',
'#4169E1' => 'RoyalBlue',
'#8B4513' => 'SaddleBrown',
'#FA8072' => 'Salmon',
'#F4A460' => 'SandyBrown',
'#2E8B57' => 'SeaGreen',
'#FFF5EE' => 'SeaShell',
'#A0522D' => 'Sienna',
'#C0C0C0' => 'Silver',
'#87CEEB' => 'SkyBlue',
'#6A5ACD' => 'SlateBlue',
'#708090' => 'SlateGray',
'#FFFAFA' => 'Snow',
'#00FF7F' => 'SpringGreen',
'#4682B4' => 'SteelBlue',
'#D2B48C' => 'Tan',
'#008080' => 'Teal',
'#D8BFD8' => 'Thistle',
'#FF6347' => 'Tomato',
'#40E0D0' => 'Turquoise',
'#EE82EE' => 'Violet',
'#F5DEB3' => 'Wheat',
'#FFFFFF' => 'White',
'#F5F5F5' => 'WhiteSmoke',
'#FFFF00' => 'Yellow',
'#9ACD32' => 'YellowGreen',
);
/**
* @param string|null $name
* @return string
*/
public static function getColorValue ($name=NULL) {
$value = '#000000';
if ($name != NULL) {
$value = self::$values [strtolower($name)];
if ($value == NULL)
$value = '#000000';
}
return $value;
}
/**
* @param null $value
* @return string
*/
public static function getValueName ($value=NULL) {
$name = 'Black';
if ($value != NULL) {
$name = self::$names [$value];
if ($name == NULL)
$name = 'Black';
}
return $name;
}
/**
* @param string $name
* @return boolean
*/
public static function isKnownColorName ($name=NULL) {
if ($name != NULL) {
return array_key_exists(strtolower($name), self::$values);
}
return false;
}
}

View File

@@ -0,0 +1,436 @@
<?php
/**
* Class to fake a document tree for CSS matching.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
/** Include ecm_interface */
require_once DOKU_INC.'lib/plugins/odt/helper/ecm_interface.php';
/**
* Class css_doc_element
*
* @package CSS\CSSDocElement
*/
class css_doc_element implements iElementCSSMatchable {
/** var Reference to corresponding cssdocument */
public $doc = NULL;
/** var Index of this element in the corresponding cssdocument */
public $index = 0;
/**
* Get the name of this element.
*
* @return string
*/
public function iECSSM_getName() {
return $this->doc->entries [$this->index]['element'];
}
/**
* Get the attributes of this element.
*
* @return array
*/
public function iECSSM_getAttributes() {
return $this->doc->entries [$this->index]['attributes_array'];
}
/**
* Get the parent of this element.
*
* @return css_doc_element
*/
public function iECSSM_getParent() {
$index = $this->doc->findParent($this->index);
if ($index == -1 ) {
return NULL;
}
$element = new css_doc_element();
$element->doc = $this->doc;
$element->index = $index;
return $element;
}
/**
* Get the preceding sibling of this element.
*
* @return css_doc_element
*/
public function iECSSM_getPrecedingSibling() {
$index = $this->doc->getPrecedingSibling($this->index);
if ($index == -1 ) {
return NULL;
}
$element = new css_doc_element();
$element->doc = $this->doc;
$element->index = $index;
return $element;
}
/**
* Does this element belong to pseudo class $class?
*
* @param string $class
* @return boolean
*/
public function iECSSM_has_pseudo_class($class) {
if ($this->doc->entries [$this->index]['pseudo_classes'] == NULL) {
return false;
}
$result = array_search($class,
$this->doc->entries [$this->index]['pseudo_classes']);
if ($result === false) {
return false;
}
return true;
}
/**
* Does this element match the pseudo element $element?
*
* @param string $element
* @return boolean
*/
public function iECSSM_has_pseudo_element($element) {
if ($this->doc->entries [$this->index]['pseudo_elements'] == NULL) {
return false;
}
$result = array_search($element,
$this->doc->entries [$this->index]['pseudo_elements']);
if ($result === false) {
return false;
}
return true;
}
/**
* Return the CSS properties assigned to this element.
* (from extern via setProperties())
*
* @return array
*/
public function getProperties () {
return $this->doc->entries [$this->index]['properties'];
}
/**
* Set/assign the CSS properties for this element.
*
* @param array $properties
*/
public function setProperties (array &$properties) {
$this->doc->entries [$this->index]['properties'] = $properties;
}
}
/**
* Class cssdocument.
*
* @package CSS\CSSDocument
*/
class cssdocument {
/** var Current size, Index for next entry */
public $size = 0;
/** var Current nesting level */
public $level = 0;
/** var Array of entries, see open() */
public $entries = array ();
/** var Root index, see saveRootIndex() */
protected $rootIndex = 0;
/** var Root level, see saveRootIndex() */
protected $rootLevel = 0;
/**
* Internal function to get the value of an attribute.
*
* @param string $value Value of the attribute
* @param string $input Code to parse
* @param integer $pos Current position in $input
* @param integer $max End of $input
* @return integer Position at which the attribute ends
*/
protected function collect_attribute_value (&$value, $input, $pos, $max) {
$value = '';
$in_quotes = false;
$quote = '';
while ($pos < $max) {
$sign = $input [$pos];
$pos++;
if ($in_quotes == false) {
if ($sign == '"' || $sign == "'") {
$quote = $sign;
$in_quotes = true;
}
} else {
if ($sign == $quote) {
break;
}
$value .= $sign;
}
}
if ($in_quotes == false || $sign != $quote) {
// No proper quotes, delete value
$value = NULL;
}
return $pos;
}
/**
* Internal function to parse $attributes for key="value" pairs
* and store the result in an array.
*
* @param string $attributes Code to parse
* @return array Array of attributes
*/
protected function get_attributes_array ($attributes) {
if ($attributes == NULL) {
return NULL;
}
$result = array();
$pos = 0;
$max = strlen($attributes);
while ($pos < $max) {
$equal_sign = strpos ($attributes, '=', $pos);
if ($equal_sign === false) {
break;
}
$att_name = substr ($attributes, $pos, $equal_sign-$pos);
$att_name = trim ($att_name, ' ');
$att_end = $this->collect_attribute_value($att_value, $attributes, $equal_sign+1, $max);
// Add a attribute to array
$result [$att_name] = $att_value;
$pos = $att_end + 1;
}
return $result;
}
/**
* Save the current position as the root index of the document.
* It is guaranteed that elements below the root index will not be
* discarded from the cssdocument.
*/
public function saveRootIndex () {
$this->rootIndex = $this->getIndexLastOpened ();
$this->rootLevel = $this->level-1;
}
/**
* Shrinks/cuts the cssdocument down to its root index.
*/
public function restoreToRoot () {
for ($index = $this->size-1 ; $index > $this->rootIndex ; $index--) {
$this->entries [$index] = NULL;
}
$this->size = $this->rootIndex + 1;
$this->level = $this->rootLevel + 1;
}
/**
* Get the current state of the cssdocument.
*
* @param array $state Returned state information
*/
public function getState (array &$state) {
$state ['index'] = $this->size-1;
$state ['level'] = $this->level;
}
/**
* Shrinks/cuts the cssdocument down to the given $state.
* ($state must be retrieved by calling getState())
*
* @param array $state State information
*/
public function restoreState (array $state) {
for ($index = $this->size-1 ; $index > $state ['index'] ; $index--) {
$this->entries [$index] = NULL;
}
$this->size = $state ['index'] + 1;
$this->level = $state ['level'];
}
/**
* Open a new element in the cssdocument.
*
* @param string $element The element's name
* @param string $attributes The element's attributes
* @param string $pseudo_classes The element's pseudo classes
* @param string $pseudo_elements The element's pseudo elements
*/
public function open ($element, $attributes=NULL, $pseudo_classes=NULL, $pseudo_elements=NULL) {
$this->entries [$this->size]['level'] = $this->level;
$this->entries [$this->size]['state'] = 'open';
$this->entries [$this->size]['element'] = $element;
$this->entries [$this->size]['attributes'] = $attributes;
if (!empty($pseudo_classes)) {
$this->entries [$this->size]['pseudo_classes'] = explode(' ', $pseudo_classes);
}
if (!empty($pseudo_elements)) {
$this->entries [$this->size]['pseudo_elements'] = explode(' ', $pseudo_elements);
}
// Build attribute array/parse attributes
if ($attributes != NULL) {
$this->entries [$this->size]['attributes_array'] =
$this->get_attributes_array ($attributes);
}
$this->size++;
$this->level++;
}
/**
* Close $element in the cssdocument.
*
* @param string $element The element's name
*/
public function close ($element) {
$this->level--;
$this->entries [$this->size]['level'] = $this->level;
$this->entries [$this->size]['state'] = 'close';
$this->entries [$this->size]['element'] = $element;
$this->size++;
}
/**
* Get the current element.
*
* @return css_doc_element
*/
public function getCurrentElement() {
$index = $this->getIndexLastOpened ();
if ($index == -1) {
return NULL;
}
$element = new css_doc_element();
$element->doc = $this;
$element->index = $index;
return $element;
}
/**
* Get the entry of internal array $entries at $index.
*
* @param integer $index
* @return array
*/
public function getEntry ($index) {
if ($index >= $this->size ) {
return NULL;
}
return $this->entries [$index];
}
/**
* Get the current entry of internal array $entries.
*
* @return array
*/
public function getCurrentEntry () {
if ($this->size == 0) {
return NULL;
}
return $this->entries [$this->size-1];
}
/**
* Get the index of the 'open' entry of the latest opened element.
*
* @return integer
*/
public function getIndexLastOpened () {
if ($this->size == 0) {
return -1;
}
for ($index = $this->size-1 ; $index >= 0 ; $index--) {
if ($this->entries [$index]['state'] == 'open') {
return $index;
}
}
return -1;
}
/**
* Find the parent for the entry at index $start.
*
* @param integer $start Starting point
*/
public function findParent ($start) {
if ($this->size == 0 || $start >= $this->size) {
return -1;
}
$start_level = $this->entries [$start]['level'];
if ($start_level == 0) {
return -1;
}
for ($index = $start-1 ; $index >= 0 ; $index--) {
if ($this->entries [$index]['state'] == 'open'
&&
$this->entries [$index]['level'] == $start_level-1) {
return $index;
}
}
return -1;
}
/**
* Find the preceding sibling for the entry at index $current.
*
* @param integer $current Starting point
*/
public function getPrecedingSibling ($current) {
if ($this->size == 0 || $current >= $this->size || $current == 0) {
return -1;
}
$current_level = $this->entries [$current]['level'];
if ($this->entries [$current-1]['level'] == $current_level) {
return ($current-1);
}
return -1;
}
/**
* Dump the current elements/entries in this cssdocument.
* Only for debugging purposes.
*/
public function getDump () {
$dump = '';
$dump .= 'RootLevel: '.$this->rootLevel.', RootIndex: '.$this->rootIndex."\n";
for ($index = 0 ; $index < $this->size ; $index++) {
$element = $this->entries [$index];
$dump .= str_repeat(' ', $element ['level'] * 2);
if ($this->entries [$index]['state'] == 'open') {
$dump .= '<'.$element ['element'];
$dump .= ' '.$element ['attributes'].'>';
} else {
$dump .= '</'.$element ['element'].'>';
}
$dump .= ' (Level: '.$element ['level'].')';
$dump .= "\n";
}
return $dump;
}
/**
* Remove the current entry.
*/
public function removeCurrent () {
$index = $this->size-1;
if ($index <= $this->rootIndex) {
// Do not remove root elements!
return;
}
$this->level = $this->entries [$index]['level'];
$this->entries [$index] = NULL;
$this->size--;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,92 @@
<?php
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTStateElement.php';
/**
* Interface iContainerAccess
*
* To prevent clashes with other interfaces function names all functions
* are prefixed with iCA_.
*
* @package ODT\iContainerAccess
*/
interface iContainerAccess
{
public function isNested ();
public function addNestedContainer (iContainerAccess $nested);
public function getNestedContainers ();
public function determinePositionInContainer (array &$data, ODTStateElement $current);
public function getMaxWidthOfNestedContainer (ODTInternalParams $params, array $data);
}
/**
* ODTContainerElement:
* Class for extra code to support container elements (frame and table).
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
class ODTContainerElement
{
// Container specific state data
protected $owner = NULL;
protected $is_nested = false;
protected $nestedContainers = array();
/**
* Constructor.
*/
public function __construct(ODTStateElement $owner) {
$this->owner = $owner;
}
/**
* Determine and set the parent for this element.
* The parent is the previous element.
*
* If the container is nested in another table or frame,
* then the surrounding table or frame is the parent!
*
* @param ODTStateElement $previous
*/
public function determineParent(ODTStateElement $previous) {
$container = $previous;
while ($container != NULL) {
if ($container->getClass() == 'table-cell') {
$cell = $container;
}
if ($container->getClass() == 'table') {
break;
}
if ($container->getClass() == 'frame') {
break;
}
$container = $container->getParent();
}
if ($container == NULL) {
$this->owner->setParent($previous);
} else {
$this->owner->setParent($container);
$container->addNestedContainer ($this->owner);
$this->is_nested = true;
}
}
/**
* Is this container nested in another container
* (inserted into another table or frame)?
*
* @return boolean
*/
public function isNested () {
return $this->is_nested;
}
public function addNestedContainer (iContainerAccess $nested) {
$this->nestedContainers [] = $nested;
}
public function getNestedContainers () {
return $this->nestedContainers;
}
}

View File

@@ -0,0 +1,299 @@
<?php
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTStateElement.php';
/**
* ODTElementFrame:
* Class for handling the frame element.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
class ODTElementFrame extends ODTStateElement implements iContainerAccess
{
protected $container = NULL;
protected $containerPos = NULL;
protected $attributes = NULL;
protected $own_max_width = NULL;
protected $nameAttr = NULL;
protected $name = NULL;
protected $written = false;
/**
* Constructor.
*/
public function __construct($style_name=NULL) {
parent::__construct();
$this->setClass ('frame');
if ($style_name != NULL) {
$this->setStyleName ($style_name);
}
$this->container = new ODTContainerElement($this);
}
/**
* Return the elements name.
*
* @return string The ODT XML element name.
*/
public function getElementName () {
return ('draw:frame');
}
/**
* Return string with encoded opening tag.
*
* @return string The ODT XML code to open this element.
*/
public function getOpeningTag (ODTInternalParams $params=NULL) {
// Convert width to points
$width = $this->getWidth();
if ($width !== NULL) {
$width = $params->units->toPoints($width);
$this->setWidth($width);
}
$encoded = '<draw:frame draw:style-name="'.$this->getStyleName().'" ';
$encoded .= $this->getAttributes().'>';
$this->written = true;
return $encoded;
}
/**
* Return string with encoded closing tag.
*
* @return string The ODT XML code to close this element.
*/
public function getClosingTag () {
return '</draw:frame>';
}
/**
* Are we in a paragraph or not?
* As a frame we are not.
*
* @return boolean
*/
public function getInParagraph() {
return false;
}
/**
* Determine and set the parent for this element.
* As a frame the previous element is our parent.
*
* @param ODTStateElement $previous
*/
public function determineParent(ODTStateElement $previous) {
$this->container->determineParent($previous);
if ($this->isNested ()) {
$this->containerPos = array();
$this->getParent()->determinePositionInContainer($this->containerPos, $previous);
}
//$this->setParent($previous);
}
/**
* Set frame attributes
*
* @param array $value
*/
public function setAttributes($value) {
// Delete linebreaks and multiple whitespace
$this->attributes = preg_replace( "/\r|\n/", "", $value);
$this->attributes = preg_replace( "/\s+/", " ", $this->attributes);
// Save name for later width rewriting
$this->nameAttr = $this->getNameAttribute();
$this->name = $this->getName();
}
/**
* Get frame attributes
*
* @return array
*/
public function getAttributes() {
return $this->attributes;
}
/**
* Is this frame a nested frame (inserted into another table/frame)?
*
* @return boolean
*/
public function isNested () {
return $this->container->isNested();
}
public function addNestedContainer (iContainerAccess $nested) {
$this->container->addNestedContainer ($nested);
}
public function getNestedContainers () {
return $this->container->getNestedContainers ();
}
public function determinePositionInContainer (array &$data, ODTStateElement $current) {
// Position in frame doesn't mater for width calculation
// So this is a dummy for now
$data ['frame'] = true;
}
public function getMaxWidthOfNestedContainer (ODTInternalParams $params, array $data) {
if ($this->own_max_width === NULL) {
// We do not know our own width yet. Calculate it first.
$this->own_max_width = $this->getMaxWidth($params);
// Re-Write our width if frame already has been written to the document
if ($this->written) {
if (preg_match('/<draw:frame[^<]*'.$this->nameAttr.'[^>]*>/', $params->content, $matches) === 1) {
$frameTag = $matches [0];
$frameTag = preg_replace('/svg:width="[^"]*"/', 'svg:width="'.$this->own_max_width.'"', $frameTag);
// Replace old frame tag in document in
$params->content = str_replace ($matches [0], $frameTag, $params->content);
}
}
}
// Convert to points
if ($this->own_max_width !== NULL) {
$width = $params->units->getDigits ($params->units->toPoints($this->own_max_width));
}
return $width.'pt';
}
public function getMaxWidth (ODTInternalParams $params) {
if ($this->own_max_width !== NULL) {
return $this->own_max_width;
}
$frameStyle = $this->getStyle();
// Get frame left margin
$leftMargin = $frameStyle->getProperty('margin-left');
if ($leftMargin == NULL) {
$leftMarginPt = 0;
} else {
$leftMarginPt = $params->units->getDigits ($params->units->toPoints($leftMargin));
}
// Get frame right margin
$rightMargin = $frameStyle->getProperty('margin-right');
if ($rightMargin == NULL) {
$rightMarginPt = 0;
} else {
$rightMarginPt = $params->units->getDigits ($params->units->toPoints($rightMargin));
}
// Get available max width
if (!$this->isNested ()) {
// Get max page width in points.
$maxWidth = $params->document->getAbsWidthMindMargins ();
$maxWidthPt = $params->units->getDigits ($params->units->toPoints($maxWidth.'cm'));
} else {
// If this frame is nested in another container we have to ask it's parent
// for the allowed max width
$maxWidth = $this->getParent()->getMaxWidthOfNestedContainer($params, $this->containerPos);
$maxWidthPt = $params->units->getDigits ($params->units->toPoints($maxWidth));
}
// Get frame width
$width = $this->getWidth();
if ($width !== NULL) {
if ($width [strlen($width)-1] != '%') {
$widthPt = $params->units->getDigits ($params->units->toPoints($width));
} else {
$percentage = trim ($width, '%');
$widthPt = ($percentage * $maxWidthPt)/100;
}
}
// Calculate final width.
// If no frame width is set or the frame width is greater than
// the calculated max width then use the max width.
$maxWidthPt = $maxWidthPt - $leftMarginPt - $rightMarginPt;
if ($width == NULL || $widthPt > $maxWidthPt) {
$width = $maxWidthPt - $leftMarginPt - $rightMarginPt;
} else {
$width = $widthPt;
}
$width = $width.'pt';
return $width;
}
public function getWidth() {
if ($this->attributes !== NULL) {
if ( preg_match('/svg:width="[^"]+"/', $this->attributes, $matches) === 1 ) {
$width = substr ($matches [0], 11);
$width = trim ($width, '"');
return $width;
}
}
return NULL;
}
public function setWidth($width) {
if ($this->attributes !== NULL) {
if ( preg_match('/svg:width="[^"]+"/', $this->attributes, $matches) === 1 ) {
$widthAttr = 'svg:width="'.$width.'"';
$this->attributes = str_replace($matches [0], $widthAttr, $this->attributes);
return;
}
}
$this->attributes .= ' svg:width="'.$width.'"';
}
public function getNameAttribute() {
if ($this->attributes !== NULL) {
if ( preg_match('/draw:name="[^"]+"/', $this->attributes, $matches) === 1 ) {
return $matches [0];
}
}
return NULL;
}
public function getName() {
if ($this->attributes !== NULL) {
if ( preg_match('/draw:name="[^"]+"/', $this->attributes, $matches) === 1 ) {
$name = substr ($matches [0], 10);
$name = trim ($name, '"');
return $name;
}
}
return NULL;
}
/**
* This function adjust the width of the frame.
* There is not much to do except conversion of relative to absolute values
* and calling the method for all nested elements.
* (table has got more work to do, see ODTElementTable::adjustWidth)
*
* @param ODTInternalParams $params Common ODT params
* @param boolean $allowNested Allow to process call if this frame is nested
*/
public function adjustWidth (ODTInternalParams $params, $allowNested=false) {
if ($this->isNested () && !$allowNested) {
// Do not do anything if this is a nested table.
// Only if the function is called for the parent/root table
// then the width of the nested tables will be calculated.
return;
}
$matches = array ();
$max_width = $this->getMaxWidth($params);
//FIXME: convert % to points
// Now adjust all nested containers too
$nested = $this->getNestedContainers ();
foreach ($nested as $container) {
$container->adjustWidth ($params, true);
}
}
}

View File

@@ -0,0 +1,153 @@
<?php
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTStateElement.php';
/**
* ODTElementList:
* Class for handling the list element.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
class ODTElementList extends ODTStateElement
{
// List state data
protected $continue_numbering;
protected $in_list = false;
protected $list_first_paragraph = true;
protected $list_paragraph_pos = -1;
/**
* Constructor.
*/
public function __construct($style_name=NULL, $continue=false) {
parent::__construct();
$this->setClass ('list');
if ($style_name != NULL) {
$this->setStyleName ($style_name);
}
$this->setContinueNumbering ($continue);
}
/**
* Return the elements name.
*
* @return string The ODT XML element name.
*/
public function getElementName () {
return ('text:list');
}
/**
* Return string with encoded opening tag.
*
* @return string The ODT XML code to open this element.
*/
public function getOpeningTag () {
$encoded = '<text:list text:style-name="'.$this->getStyleName().'"';
if ($this->getContinueNumbering()) {
$encoded .= ' text:continue-numbering="true" ';
} else {
if ($this->in_list === false) {
$encoded .= ' text:continue-numbering="false" ';
}
}
$encoded .= '>';
return $encoded;
}
/**
* Return string with encoded closing tag.
*
* @return string The ODT XML code to close this element.
*/
public function getClosingTag () {
return '</text:list>';
}
/**
* Are we in a paragraph or not?
* As a list we are not.
*
* @return boolean
*/
public function getInParagraph() {
return false;
}
/**
* Determine and set the parent for this element.
* As a list the previous element is our parent.
*
* @param ODTStateElement $previous
*/
public function determineParent(ODTStateElement $previous) {
$this->setParent($previous);
// Check if this is a nested list
while ($previous != NULL) {
if ($previous->getClass() == 'list') {
break;
}
$previous = $previous->getParent();
}
if ($previous != NULL) {
// Yes, nested list.
$this->in_list = true;
}
}
/**
* Set continue numbering to $value
*
* @param bool $value
*/
public function setContinueNumbering($value) {
$this->continue_numbering = $value;
}
/**
* Get continue numbering to $value
*
* @return bool
*/
public function getContinueNumbering() {
return $this->continue_numbering;
}
/**
* Set flag if the next paragraph will be the first in the list
*
* @param boolean $value
*/
public function setListFirstParagraph($value) {
$this->list_first_paragraph = $value;
}
/**
* Get flag if the next paragraph will be the first in the list
*
* @return boolean
*/
public function getListFirstParagraph() {
return $this->list_first_paragraph;
}
/**
* Set position of last opened paragraph in the list
*
* @param integer $value
*/
public function setListLastParagraphPosition($value) {
$this->list_paragraph_pos = $value;
}
/**
* Get position of last opened paragraph in the list
*
* @return integer
*/
public function getListLastParagraphPosition() {
return $this->list_paragraph_pos;
}
}

View File

@@ -0,0 +1,106 @@
<?php
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTStateElement.php';
/**
* ODTElementListHeader:
* Class for handling the list header element.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
class ODTElementListHeader extends ODTStateElement
{
// List item state data
protected $in_list_item = false;
protected $list_item_level = 0;
/**
* Constructor.
*/
public function __construct($level=0) {
parent::__construct();
$this->setClass ('list-item');
$this->setListItemLevel ($level);
}
/**
* Return the elements name.
*
* @return string The ODT XML element name.
*/
public function getElementName () {
return ('text:list-header');
}
/**
* Return string with encoded opening tag.
*
* @return string The ODT XML code to open this element.
*/
public function getOpeningTag () {
return '<text:list-header>';
}
/**
* Return string with encoded closing tag.
*
* @return string The ODT XML code to close this element.
*/
public function getClosingTag () {
return '</text:list-header>';
}
/**
* Are we in a paragraph or not?
* As a list item we are not.
*
* @return boolean
*/
public function getInParagraph() {
return false;
}
/**
* Determine and set the parent for this element.
* As a list item the list element is our parent.
*
* @param ODTStateElement $previous
*/
public function determineParent(ODTStateElement $previous) {
while ($previous != NULL) {
if ($previous->getClass() == 'list') {
break;
}
$previous = $previous->getParent();
}
$this->setParent($previous);
}
/**
* Set the level for an list item
*
* @param integer $value
*/
public function setListItemLevel($value) {
$this->list_item_level = $value;
}
/**
* Get level of a list item
*
* @return integer
*/
public function getListItemLevel() {
return $this->list_item_level;
}
/**
* Return the list to which this list item belongs.
*
* @return ODTStateElement
*/
public function getList () {
return $this->getParent();
}
}

View File

@@ -0,0 +1,106 @@
<?php
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTStateElement.php';
/**
* ODTElementListItem:
* Class for handling the list item element.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
class ODTElementListItem extends ODTStateElement
{
// List item state data
protected $in_list_item = false;
protected $list_item_level = 0;
/**
* Constructor.
*/
public function __construct($level=0) {
parent::__construct();
$this->setClass ('list-item');
$this->setListItemLevel ($level);
}
/**
* Return the elements name.
*
* @return string The ODT XML element name.
*/
public function getElementName () {
return ('text:list-item');
}
/**
* Return string with encoded opening tag.
*
* @return string The ODT XML code to open this element.
*/
public function getOpeningTag () {
return '<text:list-item>';
}
/**
* Return string with encoded closing tag.
*
* @return string The ODT XML code to close this element.
*/
public function getClosingTag () {
return '</text:list-item>';
}
/**
* Are we in a paragraph or not?
* As a list item we are not.
*
* @return boolean
*/
public function getInParagraph() {
return false;
}
/**
* Determine and set the parent for this element.
* As a list item the list element is our parent.
*
* @param ODTStateElement $previous
*/
public function determineParent(ODTStateElement $previous) {
while ($previous != NULL) {
if ($previous->getClass() == 'list') {
break;
}
$previous = $previous->getParent();
}
$this->setParent($previous);
}
/**
* Set the level for an list item
*
* @param integer $value
*/
public function setListItemLevel($value) {
$this->list_item_level = $value;
}
/**
* Get level of a list item
*
* @return integer
*/
public function getListItemLevel() {
return $this->list_item_level;
}
/**
* Return the list to which this list item belongs.
*
* @return ODTStateElement
*/
public function getList () {
return $this->getParent();
}
}

View File

@@ -0,0 +1,81 @@
<?php
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTStateElement.php';
/**
* ODTElementNote:
* Class for handling the text note element (e.g. for footnotes).
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
class ODTElementNote extends ODTStateElement
{
protected $value_type = 'string';
/**
* Constructor.
*/
public function __construct() {
parent::__construct();
$this->setClass ('text-note');
}
/**
* Return the elements name.
*
* @return string The ODT XML element name.
*/
public function getElementName () {
return ('text:note');
}
/**
* Return string with encoded opening tag.
*
* @return string The ODT XML code to open this element.
*/
public function getOpeningTag () {
// Intentionally return an empty string!
return '';
}
/**
* Return string with encoded closing tag.
*
* @return string The ODT XML code to close this element.
*/
public function getClosingTag () {
// Intentionally return an empty string!
return '';
}
/**
* Are we in a paragraph or not?
* As a text note we are not.
*
* @return boolean
*/
public function getInParagraph() {
return false;
}
/**
* Determine and set the parent for this element.
* As a table cell our parent is the table element.
*
* @param ODTStateElement $previous
*/
public function determineParent(ODTStateElement $previous) {
$this->setParent($previous);
}
/**
* Return the table to which this column belongs.
*
* @return ODTStateElement
*/
public function getTable () {
return $this->getParent();
}
}

View File

@@ -0,0 +1,71 @@
<?php
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTStateElement.php';
/**
* ODTElementParagraph:
* Class for handling the paragraph element.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
class ODTElementParagraph extends ODTStateElement
{
/**
* Constructor.
*/
public function __construct($style_name=NULL) {
parent::__construct();
$this->setClass ('paragraph');
if ($style_name != NULL) {
$this->setStyleName ($style_name);
}
}
/**
* Return the elements name.
*
* @return string The ODT XML element name.
*/
public function getElementName () {
return ('text:p');
}
/**
* Return string with encoded opening tag.
*
* @return string The ODT XML code to open this element.
*/
public function getOpeningTag () {
return '<text:p text:style-name="'.$this->getStyleName().'">';
}
/**
* Return string with encoded closing tag.
*
* @return string The ODT XML code to close this element.
*/
public function getClosingTag () {
return '</text:p>';
}
/**
* Are we in a paragraph or not?
* As a paragraph we are of course.
*
* @return boolean
*/
public function getInParagraph() {
return true;
}
/**
* Determine and set the parent for this element.
* As a paragraph the previous element is our parent.
*
* @param ODTStateElement $previous
*/
public function determineParent(ODTStateElement $previous) {
$this->setParent($previous);
}
}

View File

@@ -0,0 +1,75 @@
<?php
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTStateElement.php';
/**
* ODTElementSpan:
* Class for handling the span element.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
class ODTElementSpan extends ODTStateElement
{
/**
* Constructor.
*/
public function __construct($style_name=NULL) {
parent::__construct();
$this->setClass ('span');
if ($style_name != NULL) {
$this->setStyleName ($style_name);
}
}
/**
* Return the elements name.
*
* @return string The ODT XML element name.
*/
public function getElementName () {
return ('text:span');
}
/**
* Return string with encoded opening tag.
*
* @return string The ODT XML code to open this element.
*/
public function getOpeningTag () {
return '<text:span text:style-name="'.$this->getStyleName().'">';
}
/**
* Return string with encoded closing tag.
*
* @return string The ODT XML code to close this element.
*/
public function getClosingTag () {
return '</text:span>';
}
/**
* Are we in a paragraph or not?
* As a span we ask our parent.
*
* @return boolean
*/
public function getInParagraph() {
$parent = $this->getParent();
if ($parent != NULL) {
return $parent->getInParagraph();
}
return NULL;
}
/**
* Determine and set the parent for this element.
* As a span the previous element is our parent.
*
* @param ODTStateElement $previous
*/
public function determineParent(ODTStateElement $previous) {
$this->setParent($previous);
}
}

View File

@@ -0,0 +1,541 @@
<?php
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTStateElement.php';
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTContainerElement.php';
/**
* ODTElementTable:
* Class for handling the table element.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
class ODTElementTable extends ODTStateElement implements iContainerAccess
{
// Table specific state data
protected $container = NULL;
protected $containerPos = NULL;
protected $table_column_styles = array ();
protected $table_style = NULL;
protected $table_autocols = false;
protected $table_maxcols = 0;
protected $table_curr_column = 0;
protected $table_row_count = 0;
protected $own_max_width = NULL;
// Flag indicating that a table was created inside of a list
protected $list_interrupted = false;
/**
* Constructor.
* ($numrows is currently unused)
*/
public function __construct($style_name=NULL, $maxcols = 0, $numrows = 0) {
parent::__construct();
$this->setClass ('table');
if ($style_name != NULL) {
$this->setStyleName ($style_name);
}
$this->setTableMaxColumns($maxcols);
if ($maxcols == 0) {
$this->setTableAutoColumns(true);
}
$this->container = new ODTContainerElement($this);
}
/**
* Return the elements name.
*
* @return string The ODT XML element name.
*/
public function getElementName () {
return ('table:table');
}
/**
* Return string with encoded opening tag.
*
* @return string The ODT XML code to open this element.
*/
public function getOpeningTag () {
$style_name = $this->getStyleName();
if ($style_name == NULL) {
$encoded = '<table:table>';
} else {
$encoded .= '<table:table table:style-name="'.$style_name.'">';
}
$maxcols = $this->getTableMaxColumns();
$count = $this->getCount();
if ($maxcols == 0) {
// Try to automatically detect the number of columns.
$this->setTableAutoColumns(true);
} else {
$this->setTableAutoColumns(false);
}
// Add column definitions placeholder.
// This will be replaced on tabelClose()/getClosingTag()
$encoded .= '<ColumnsPlaceholder'.$count.'>';
// We start with the first column
$this->setTableCurrentColumn(0);
return $encoded;
}
/**
* Return string with encoded closing tag.
*
* @return string The ODT XML code to close this element.
*/
public function getClosingTag (&$content = NULL) {
// Generate table column definitions and replace the placeholder with it
$count = $this->getCount();
$max = $this->getTableMaxColumns();
if ($max > 0 && $content != NULL) {
$column_defs = '';
for ($index = 0 ; $index < $max ; $index++) {
$styleName = $this->getTableColumnStyleName($index);
if (!empty($styleName)) {
$column_defs .= '<table:table-column table:style-name="'.$styleName.'"/>';
} else {
$column_defs .= '<table:table-column/>';
}
}
$content =
str_replace ('<ColumnsPlaceholder'.$count.'>', $column_defs, $content);
$content =
str_replace ('<MaxColsPlaceholder'.$count.'>', $max, $content);
}
return '</table:table>';
}
/**
* Are we in a paragraph or not?
* As a table we are not.
*
* @return boolean
*/
public function getInParagraph() {
return false;
}
/**
* Determine and set the parent for this element.
* As a table the previous element is our parent.
*
* If the table is nested in another table, then the surrounding
* table is the parent!
*
* @param ODTStateElement $previous
*/
public function determineParent(ODTStateElement $previous) {
$this->container->determineParent($previous);
if ($this->isNested ()) {
$this->containerPos = array();
$this->getParent()->determinePositionInContainer($this->containerPos, $previous);
}
}
/**
* Set table column styles
*
* @param array $value
*/
public function setTableColumnStyles($value) {
$this->table_column_styles = $value;
}
/**
* Set table column style for $column
*
* @param array $value
*/
public function setTableColumnStyleName($column, $style_name) {
$this->table_column_styles [$column] = $style_name;
}
/**
* Get table column styles
*
* @return array
*/
public function getTableColumnStyles() {
return $this->table_column_styles;
}
/**
* Set table column style for $column
*
* @param array $value
*/
public function getTableColumnStyleName($column) {
return $this->table_column_styles [$column];
}
/**
* Set flag if table columns shall be generated automatically.
* (automatically detect the number of columns)
*
* @param boolean $value
*/
public function setTableAutoColumns($value) {
$this->table_autocols = $value;
}
/**
* Get flag if table columns shall be generated automatically.
* (automatically detect the number of columns)
*
* @return boolean
*/
public function getTableAutoColumns() {
return $this->table_autocols;
}
/**
* Set maximal number of columns.
*
* @param integer $value
*/
public function setTableMaxColumns($value) {
$this->table_maxcols = $value;
}
/**
* Get maximal number of columns.
*
* @return integer
*/
public function getTableMaxColumns() {
return $this->table_maxcols;
}
/**
* Set current column.
*
* @param integer $value
*/
public function setTableCurrentColumn($value) {
$this->table_curr_column = $value;
}
/**
* Get current column.
*
* @return integer
*/
public function getTableCurrentColumn() {
return $this->table_curr_column;
}
/**
* Get the predefined style name for the current
* table column.
*
* @return string
*/
public function getCurrentTableColumnStyleName() {
$table_column_styles = $this->getTableColumnStyles();
$curr_column = $this->getTableCurrentColumn();
return $table_column_styles [$curr_column];
}
/**
* Set flag if current list is interrupted (by a table) or not.
*
* @param boolean $value
*/
public function setListInterrupted($value) {
$this->list_interrupted = $value;
}
/**
* Get flag if current list is interrupted (by a table) or not.
*
* @return boolean
*/
public function getListInterrupted() {
return $this->list_interrupted;
}
/**
* Increae the number of rows
*
* @param boolean $value
*/
public function increaseRowCount() {
$this->table_row_count++;
}
public function getRowCount() {
return $this->table_row_count;
}
/**
* Is this table a nested table (inserted into another table)?
*
* @return boolean
*/
public function isNested () {
return $this->container->isNested();
}
public function addNestedContainer (iContainerAccess $nested) {
$this->container->addNestedContainer ($nested);
}
public function getNestedContainers () {
return $this->container->getNestedContainers ();
}
public function determinePositionInContainer (array &$data, ODTStateElement $current) {
$data ['column'] = $this->getTableCurrentColumn();
$cell = NULL;
while ($current != NULL) {
if ($current->getClass() == 'table-cell') {
$cell = $current;
break;
}
if ($current->getClass() == 'table') {
break;
}
$current = $current->getParent();
}
if ($cell !== NULL) {
$data ['cell'] = $cell;
}
}
public function getMaxWidthOfNestedContainer (ODTInternalParams $params, array $data) {
if ($this->own_max_width === NULL) {
// We do not know our own width yet. Calculate it first.
$this->own_max_width = $this->getMaxWidth($params);
}
$column = $data ['column'];
$cell = $data ['cell'];
$cell_style = $cell->getStyle();
$padding = 0;
if ($cell_style->getProperty('padding-left') != NULL
||
$cell_style->getProperty('padding-right') != NULL) {
$value = $cell_style->getProperty('padding-left');
$value = $params->document->toPoints($value, 'y');
$padding += $value;
$value = $cell_style->getProperty('padding-right');
$value = $params->document->toPoints($value, 'y');
$padding += $value;
} else if ($cell_style->getProperty('padding') != NULL) {
$value = $cell_style->getProperty('padding');
$value = $params->document->toPoints($value, 'y');
$padding += 2 * $value;
}
$table_column_styles = $this->getTableColumnStyles();
$style_name = $table_column_styles [$column-1];
$style_obj = $params->document->getStyle($style_name);
if ($style_obj !== NULL) {
$width = $style_obj->getProperty('column-width');
$width = trim ($width, 'pt');
$width -= $padding;
}
// Compare with total table width
if ($this->own_max_width !== NULL) {
$table_width = $params->units->getDigits ($params->units->toPoints($this->own_max_width));
if ($table_width < $width) {
$width = $table_width;
}
}
return $width.'pt';
}
public function getMaxWidth (ODTInternalParams $params) {
$tableStyle = $this->getStyle();
if (!$this->isNested ()) {
// Get max page width in points.
$maxPageWidth = $params->document->getAbsWidthMindMargins ();
$maxPageWidthPt = $params->units->getDigits ($params->units->toPoints($maxPageWidth.'cm'));
// Get table left margin
$leftMargin = $tableStyle->getProperty('margin-left');
if ($leftMargin === NULL) {
$leftMarginPt = 0;
} else {
$leftMarginPt = $params->units->getDigits ($params->units->toPoints($leftMargin));
}
// Get table right margin
$rightMargin = $tableStyle->getProperty('margin-right');
if ($rightMargin === NULL) {
$rightMarginPt = 0;
} else {
$rightMarginPt = $params->units->getDigits ($params->units->toPoints($rightMargin));
}
// Get table width
$width = $tableStyle->getProperty('width');
if ($width !== NULL) {
$widthPt = $params->units->getDigits ($params->units->toPoints($width));
}
if ($width === NULL) {
$width = $maxPageWidthPt - $leftMarginPt - $rightMarginPt;
} else {
$width = $widthPt;
}
$width = $width.'pt';
} else {
// If this table is nested in another container we have to ask it's parent
// for the allowed max width
$width = $this->getParent()->getMaxWidthOfNestedContainer($params, $this->containerPos);
}
return $width;
}
/**
* This function replaces the width of $table with the
* value of all column width added together. If a column has
* no width set then the function will abort and change nothing.
*
* @param ODTDocument $doc The current document
* @param ODTElementTable $table The table to be adjusted
*/
public function adjustWidth (ODTInternalParams $params, $allowNested=false) {
if ($this->isNested () && !$allowNested) {
// Do not do anything if this is a nested table.
// Only if the function is called for the parent/root table
// then the width of the nested tables will be calculated.
return;
}
$matches = array ();
$table_style_name = $this->getStyleName();
if (empty($table_style_name)) {
return;
}
$max_width = $this->getMaxWidth($params);
$width = $this->adjustWidthInternal ($params, $max_width);
$style_obj = $params->document->getStyle($table_style_name);
if ($style_obj != NULL) {
$style_obj->setProperty('width', $width.'pt');
if (!$this->isNested ()) {
// Calculate rel width in relation to maximum page width
$maxPageWidth = $params->document->getAbsWidthMindMargins ();
$maxPageWidth = $params->units->getDigits ($params->units->toPoints($maxPageWidth.'cm'));
if ($maxPageWidth != 0) {
$rel_width = round(($width * 100)/$maxPageWidth);
}
} else {
// Calculate rel width in relation to maximum table width
if ($max_width != 0) {
$rel_width = round(($width * 100)/$max_width);
}
}
$style_obj->setProperty('rel-width', $rel_width.'%');
}
// Now adjust all nested containers too
$nested = $this->getNestedContainers ();
foreach ($nested as $container) {
$container->adjustWidth ($params, true);
}
}
public function adjustWidthInternal (ODTInternalParams $params, $maxWidth) {
$empty = array();
$relative = array();
$anyWidthFound = false;
$onlyAbsWidthFound = true;
$tableStyle = $this->getStyle();
// First step:
// - convert all absolute widths to points
// - build the sum of all absolute column width values (if any)
// - build the sum of all relative column width values (if any)
$abs_sum = 0;
$table_column_styles = $this->getTableColumnStyles();
$replace = true;
for ($index = 0 ; $index < $this->getTableMaxColumns() ; $index++ ) {
$style_name = $table_column_styles [$index];
$style_obj = $params->document->getStyle($style_name);
if ($style_obj != NULL) {
if ($style_obj->getProperty('rel-column-width') != NULL) {
$width = $style_obj->getProperty('rel-column-width');
$length = strlen ($width);
$width = trim ($width, '*');
// Add column style object to relative array
// We need convert it to an absolute width
$entry = array();
$entry ['width'] = $width;
$entry ['obj'] = $style_obj;
$relative [] = $entry;
$abs_sum += (($width/10)/100) * $maxWidth;
$onlyAbsWidthFound = false;
$anyWidthFound = true;
} else if ($style_obj->getProperty('column-width') != NULL) {
$width = $style_obj->getProperty('column-width');
$length = strlen ($width);
$width = $params->document->toPoints($width, 'x');
$abs_sum += (float) trim ($width, 'pt');
$anyWidthFound = true;
} else {
// Add column style object to empty array
// We need to assign a width to this column
$empty [] = $style_obj;
$onlyAbsWidthFound = false;
}
}
}
// Convert max width to points
$maxWidth = $params->units->toPoints($maxWidth);
$maxWidth = $params->units->getDigits($maxWidth);
// The remaining absolute width is the max width minus the sum of
// all absolute width values
$absWidthLeft = $maxWidth - $abs_sum;
// Calculate the relative width left
// (e.g. if the absolute width is the half of the max width
// then the relative width left if 50%)
if ($maxWidth != 0) {
$relWidthLeft = 100-(($absWidthLeft/$maxWidth)*100);
}
// Give all empty columns a width
$maxEmpty = count($empty);
foreach ($empty as $column) {
//$width = ($relWidthLeft/$maxEmpty) * $absWidthLeft;
$width = $absWidthLeft/$maxEmpty;
$column->setProperty('column-width', $width.'pt');
$column->setProperty('rel-column-width', NULL);
}
// Convert all relative width to absolute
foreach ($relative as $column) {
$width = (($column ['width']/10)/100) * $maxWidth;
$column ['obj']->setProperty('column-width', $width.'pt');
$column ['obj']->setProperty('rel-column-width', NULL);
}
// If all columns have a fixed absolute width set then that means
// the table shall have the width of all comuns added together
// and not the maximum available width. Return $abs_sum.
if ($onlyAbsWidthFound && $anyWidthFound) {
return $abs_sum;
}
return $maxWidth;
}
}

View File

@@ -0,0 +1,173 @@
<?php
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTStateElement.php';
/**
* ODTElementTableCell:
* Class for handling the table cell element.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
class ODTElementTableCell extends ODTStateElement
{
protected $colspan;
protected $rowspan;
protected $value_type = 'string';
/**
* Constructor.
*/
public function __construct($style_name=NULL, $colspan = 1, $rowspan = 1) {
parent::__construct();
$this->setClass ('table-cell');
if ($style_name != NULL) {
$this->setStyleName ($style_name);
}
$this->setColumnSpan($colspan);
$this->setRowSpan($rowspan);
}
/**
* Return the elements name.
*
* @return string The ODT XML element name.
*/
public function getElementName () {
return ('table:table-cell');
}
/**
* Return string with encoded opening tag.
*
* @return string The ODT XML code to open this element.
*/
public function getOpeningTag () {
$colspan = $this->getColumnSpan();
$rowspan = $this->getRowSpan();
$encoded = '<table:table-cell office:value-type="'.$this->getValueType().'"';
$encoded .= ' table:style-name="'.$this->getStyleName().'"';
if ( $colspan > 1 ) {
$encoded .= ' table:number-columns-spanned="'.$colspan.'"';
}
if ($rowspan > 1) {
$encoded .= ' table:number-rows-spanned="'.$rowspan.'"';
}
$encoded .= '>';
return $encoded;
}
/**
* Return string with encoded closing tag.
*
* @return string The ODT XML code to close this element.
*/
public function getClosingTag () {
$content = '</table:table-cell>';
$colspan = $this->getColumnSpan();
for ($i = 1 ; $i < $colspan ; $i++) {
$content .= '<table:covered-table-cell/>';
}
return $content;
}
/**
* Are we in a paragraph or not?
* As a table cell we are not.
*
* @return boolean
*/
public function getInParagraph() {
return false;
}
/**
* Determine and set the parent for this element.
* As a table cell our parent is the table element.
*
* @param ODTStateElement $previous
*/
public function determineParent(ODTStateElement $previous) {
while ($previous != NULL) {
if ($previous->getClass() == 'table') {
break;
}
$previous = $previous->getParent();
}
$this->setParent($previous);
$curr_column = $previous->getTableCurrentColumn();
$curr_column++;
$previous->setTableCurrentColumn($curr_column);
// Eventually increase max columns if out range
$max_columns = $previous->getTableMaxColumns();
if ( $curr_column > $max_columns ) {
$previous->setTableMaxColumns($max_columns + 1);
}
}
/**
* Return the table to which this column belongs.
*
* @return ODTStateElement
*/
public function getTable () {
return $this->getParent();
}
/**
* Set the number of columns spanned by this cell.
*
* @param integer $value
*/
public function setColumnSpan($value) {
$this->colspan = $value;
}
/**
* Get the number of columns spanned by this cell.
*
* @return integer
*/
public function getColumnSpan() {
return $this->colspan;
}
/**
* Set the number of rows spanned by this cell.
*
* @param integer $value
*/
public function setRowSpan($value) {
$this->rowspan = $value;
}
/**
* Get the number of rows spanned by this cell.
*
* @return integer
*/
public function getRowSpan() {
return $this->rowspan;
}
/**
* Set the office value type for this cell.
*
* @param string $value
*/
public function setValueType($value) {
$this->value_type = $value;
}
/**
* Get the office value type for this cell.
*
* @return string
*/
public function getValueType() {
return $this->value_type;
}
}

View File

@@ -0,0 +1,148 @@
<?php
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTStateElement.php';
/**
* ODTElementTableColumn:
* Class for handling the table column element.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
class ODTElementTableColumn extends ODTStateElement
{
// Which column number in the corresponding table is this?
// [Is set on enter() ==> determineParent()]
protected $columnNumber = 0;
/**
* Constructor.
*/
public function __construct() {
parent::__construct();
$this->setClass ('table-column');
}
/**
* Return the elements name.
*
* @return string The ODT XML element name.
*/
public function getElementName () {
return ('table:table-column');
}
/**
* Return string with encoded opening tag.
*
* @return string The ODT XML code to open this element.
*/
public function getOpeningTag () {
return '<table:table-column table:style-name="'.$this->getStyleName().'"/>';
}
/**
* Return string with encoded closing tag.
*
* @return string The ODT XML code to close this element.
*/
public function getClosingTag () {
return '</table:table-column>';
}
/**
* Are we in a paragraph or not?
* As a table column we are not.
*
* @return boolean
*/
public function getInParagraph() {
return false;
}
/**
* Determine and set the parent for this element.
* As a table column our parent is the table element.
*
* @param ODTStateElement $previous
*/
public function determineParent(ODTStateElement $previous) {
$table = $previous;
while ($table != NULL) {
if ($table->getClass() == 'table') {
break;
}
$table = $table->getParent();
}
$this->setParent($table);
if ($table == NULL) {
// ??? Should not be...
return;
}
// Overwrite/Create column style for actual column if $properties has any
// meaningful params for a column-style (e.g. width).
$table_column_styles = $table->getTableColumnStyles();
$auto_columns = $table->getTableAutoColumns();
$max_columns = $table->getTableMaxColumns();
$row_count = $table->getRowCount();
$curr_column = $table->getTableCurrentColumn();
if ($row_count > 0) {
$curr_column--;
}
// Set our column number.
$this->columnNumber = $curr_column;
// Set our style name to a predefined name
// and also set it in the table (if not done yet)
$style_name = $table->getTableColumnStyleName($curr_column);
if (empty($style_name)) {
$style_name = 'odt_auto_style_table_column_'.$table->getCount().'_'.($curr_column+1);
$table->setTableColumnStyleName($curr_column, $style_name);
}
$this->setStyleName ($style_name);
if ($row_count == 0) {
// Only count columns here if not already a row has been opened.
// Otherwise counting will be done in ODTElementTableCell!
$curr_column++;
$table->setTableCurrentColumn($curr_column);
// Eventually increase max columns if out range
if ( $curr_column > $max_columns ) {
$table->setTableMaxColumns($max_columns + 1);
}
}
}
/**
* Set the style name.
* The method is overwritten to make the column also set the new
* column style name in its corresponding table.
*
* FIXME: it would be better to just have an array of object
* pointers in the table and use them to query the names.
*
* @param string $value Style name, e.g. 'body'
*/
public function setStyleName($value) {
parent::setStyleName($value);
$table = $this->getParent();
if ($table != NULL) {
$table->setTableColumnStyleName($this->columnNumber, $value);
} else {
// FIXME: some error logging would be nice...
}
}
/**
* Return the table to which this column belongs.
*
* @return ODTStateElement
*/
public function getTable () {
return $this->getParent();
}
}

View File

@@ -0,0 +1,57 @@
<?php
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTStateElement.php';
/**
* ODTElementTableHeaderCell:
* Class for handling the table "header" cell element.
*
* In ODT there is no header cell element so this is just a normal
* table cell with some extra handling for the automatic column
* count mode.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
class ODTElementTableHeaderCell extends ODTElementTableCell
{
/**
* Constructor.
*/
public function __construct($style_name=NULL, $colspan = 0, $rowspan = 0) {
parent::__construct($style_name, $colspan, $rowspan);
$this->setClass ('table-header');
}
/**
* Return string with encoded opening tag.
*
* @return string The ODT XML code to open this element.
*/
public function getOpeningTag () {
$colspan = $this->getColumnSpan();
$rowspan = $this->getRowSpan();
// Get our corresponding table.
$table = $this->getTable();
$auto_columns = false;
$count = 0;
if ($table != NULL) {
$auto_columns = $table->getTableAutoColumns();
$count = $table->getCount();
}
$encoded = '<table:table-cell office:value-type="'.$this->getValueType().'"';
$encoded .= ' table:style-name="'.$this->getStyleName().'"';
if ( $colspan > 1 ) {
$encoded .= ' table:number-columns-spanned="'.$colspan.'"';
} else if ($auto_columns === true && $colspan == 0) {
$encoded .= ' table:number-columns-spanned="<MaxColsPlaceholder'.$count.'>"';
}
if ( $rowspan > 1 ) {
$encoded .= ' table:number-rows-spanned="'.$rowspan.'"';
}
$encoded .= '>';
return $encoded;
}
}

View File

@@ -0,0 +1,110 @@
<?php
/**
* Handle ODT Table row elements.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
* @package ODT\Elements\ODTElementTableRow
*/
/** Include ODTStateElement */
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTStateElement.php';
/**
* ODTElementTableRow:
* Class for handling the table row element.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
class ODTElementTableRow extends ODTStateElement
{
/**
* Constructor.
*
* @param string $style_name Name of the style
*/
public function __construct($style_name=NULL) {
parent::__construct();
$this->setClass ('table-row');
if ($style_name != NULL) {
$this->setStyleName ($style_name);
}
}
/**
* Return the elements name.
*
* @return string The ODT XML element name.
*/
public function getElementName () {
return ('table:table-row');
}
/**
* Return string with encoded opening tag.
*
* @return string The ODT XML code to open this element.
*/
public function getOpeningTag () {
$style_name = $this->getStyleName();
if ($style_name != NULL) {
return '<table:table-row table:style-name="'.$style_name.'">';
}
return '<table:table-row>';
}
/**
* Return string with encoded closing tag.
*
* @return string The ODT XML code to close this element.
*/
public function getClosingTag () {
return '</table:table-row>';
}
/**
* Are we in a paragraph or not?
* As a table row we are not.
*
* @return boolean
*/
public function getInParagraph() {
return false;
}
/**
* Determine and set the parent for this element.
* As a table row our parent is the table element.
*
* @param ODTStateElement $previous
*/
public function determineParent(ODTStateElement $previous) {
$table = $previous;
while ($table != NULL) {
if ($table->getClass() == 'table') {
break;
}
$table = $table->getParent();
}
$this->setParent($table);
if ($table == NULL) {
// ??? Should not be...
return;
}
// A new row, we are back in the first column again.
$table->increaseRowCount();
$table->setTableCurrentColumn(0);
}
/**
* Return the table to which this column belongs.
*
* @return ODTStateElement
*/
public function getTable () {
return $this->getParent();
}
}

View File

@@ -0,0 +1,89 @@
<?php
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTStateElement.php';
/**
* ODTElementTextBox:
* Class for handling the text box element.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
class ODTElementTextBox extends ODTStateElement
{
protected $attributes = NULL;
/**
* Constructor.
*/
public function __construct() {
parent::__construct();
$this->setClass ('text-box');
}
/**
* Return the elements name.
*
* @return string The ODT XML element name.
*/
public function getElementName () {
return ('draw:text-box');
}
/**
* Return string with encoded opening tag.
*
* @return string The ODT XML code to open this element.
*/
public function getOpeningTag () {
$encoded = '<draw:text-box '.$this->getAttributes().'>';
return $encoded;
}
/**
* Return string with encoded closing tag.
*
* @return string The ODT XML code to close this element.
*/
public function getClosingTag () {
return '</draw:text-box>';
}
/**
* Are we in a paragraph or not?
* As a text box we are not.
*
* @return boolean
*/
public function getInParagraph() {
return false;
}
/**
* Determine and set the parent for this element.
* As a text box the previous element is our parent.
*
* @param ODTStateElement $previous
*/
public function determineParent(ODTStateElement $previous) {
$this->setParent($previous);
}
/**
* Set text box attributes
*
* @param array $value
*/
public function setAttributes($value) {
$this->attributes = $value;
}
/**
* Get text box attributes
*
* @return array
*/
public function getAttributes() {
return $this->attributes;
}
}

View File

@@ -0,0 +1,80 @@
<?php
require_once DOKU_PLUGIN.'odt/ODT/elements/ODTStateElement.php';
/**
* ODTElementRoot:
* Root-Element to make things easier. Always needs to be on top of ODTState.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
class ODTElementRoot extends ODTStateElement
{
/**
* Constructor.
*/
public function __construct() {
parent::__construct();
$this->setClass ('root');
$this->setCount (1);
}
/**
* Return the elements name.
*
* @return string The ODT XML element name.
*/
public function getElementName () {
// Dummy.
return 'root';
}
/**
* Return string with encoded opening tag.
*
* @return string The ODT XML code to open this element.
*/
public function getOpeningTag () {
// Dummy.
return '';
}
/**
* Return string with encoded closing tag.
*
* @return string The ODT XML code to close this element.
*/
public function getClosingTag () {
// Dummy.
return '';
}
/**
* Set parent dummy function for preventing that anyone
* accidentally sets a parent for the root.
*
* @param ODTStateElement $parent_element
*/
public function setParent(ODTStateElement $parent_element) {
// Intentionally do nothing!
}
/**
* Are we in a paragraph or not?
* This is the root - so we are not.
*
* @return boolean
*/
public function getInParagraph() {
return false;
}
/**
* Determine and set the parent for this element.
* Nothing to do for the root.
*/
public function determineParent(ODTStateElement $previous) {
// Intentionally do nothing!
}
}

View File

@@ -0,0 +1,193 @@
<?php
/**
* ODTStateElement:
* Base class for all elements which are added/used with class ODTState.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
abstract class ODTStateElement
{
// General state information
protected $clazz = NULL;
protected $style_name = NULL;
protected $count = 0;
protected $parent_element = NULL;
protected $style_obj = NULL;
protected $htmlElement = NULL;
// Temp pointer for various use! Can point to different things!
protected $temp = NULL;
/**
* Constructor.
*/
public function __construct($style_name=NULL) {
// Empty for now.
// All elements call the parent constructor so it might be
// of use in the future...
}
/**
* Set the class to $value.
*
* @param string $value Class, e.g. 'paragraph'
*/
public function setClass($value) {
$this->clazz = $value;
}
/**
* Get the class.
*
* @return string Class.
*/
public function getClass() {
return $this->clazz;
}
/**
* Set the element count to $value.
* If e.g. the element is 'table', then the count specifies
* that this element is table number '$value'.
*
* @param string $value Count
*/
public function setCount($value) {
$this->count = $value;
}
/**
* Get the element count.
*
* @return integer Count.
*/
public function getCount() {
return $this->count;
}
/**
* Set the style name.
*
* @param string $value Style name, e.g. 'body'
*/
public function setStyleName($value) {
$this->style_name = $value;
}
/**
* Get the style name.
*
* @return string Style name.
*/
public function getStyleName() {
return $this->style_name;
}
/**
* Set the style object.
*
* @param ODTStyle $object
*/
public function setStyle($object) {
$this->style_obj = $object;
}
/**
* Get the style object.
*
* @return ODTStyle Style object.
*/
public function getStyle() {
return $this->style_obj;
}
/**
* Set temporary data for various use.
*
* @param mixed $value
*/
public function setTemp($value) {
$this->temp = $value;
}
/**
* Get temporary data for various use.
*
* @return mixed
*/
public function getTemp() {
return $this->temp;
}
/**
* Set parent of this element.
*
* @param ODTStateElement $parent_element
*/
public function setParent(ODTStateElement $parent_element) {
$this->parent_element = $parent_element;
}
/**
* Get parent of this element.
*
* @return ODTStateElement
*/
public function getParent() {
return $this->parent_element;
}
/**
* Return the elements name.
*
* @return string The ODT XML element name.
*/
abstract public function getElementName ();
/**
* Return string with encoded opening tag.
*
* @return string The ODT XML code to open this element.
*/
abstract public function getOpeningTag ();
/**
* Return string with encoded closing tag.
*
* @return string The ODT XML code to close this element.
*/
abstract public function getClosingTag ();
/**
* Are we in a paragraph or not?
*
* @return boolean
*/
abstract public function getInParagraph();
/**
* Determine and set the parent for this element.
* The search starts at element $previous.
*/
abstract public function determineParent(ODTStateElement $previous);
/**
* Set the HTML element name pushed to the HTML stack for this ODT element.
*
* @param string $value HTML element name e.g. 'u'
*/
public function setHTMLElement($value) {
$this->htmlElement = $value;
}
/**
* Get the HTML element name pushed to the HTML stack for this ODT element.
*
* @return string HTML element name.
*/
public function getHTMLElement() {
return $this->htmlElement;
}
}

View File

@@ -0,0 +1,373 @@
<?php
/**
* pageFormat: class for handling page formats.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
// must be run within Dokuwiki
if(!defined('DOKU_INC')) die();
/**
* The pageFormat class
*/
class pageFormat
{
var $format = 'A4';
var $orientation = 'portrait';
// Page parameters.
// All values are assumed to be in 'cm' units.
var $width = 21;
var $height = 29.7;
var $margin_top = 2;
var $margin_bottom = 2;
var $margin_left = 2;
var $margin_right = 2;
/**
* Return given page format parameters as a single string.
*
* @param string $format
* @param string $orientation
* @param string $margin_top
* @param string $margin_right
* @param string $margin_bottom
* @param string $margin_left
* @return string
*/
public static function formatToString ($format, $orientation, $margin_top=2, $margin_right=2, $margin_bottom=2, $margin_left=2) {
$margins = $margin_top.'-'.$margin_right.'-'.$margin_bottom.'-'.$margin_left;
$margins = str_replace (',', '', $margins);
$margins = str_replace ('.', '', $margins);
return $format.'-'.$orientation.'-'.$margins;
}
/**
* Return currently set format parameters as a single string.
*
* @return string
*/
public function toString () {
$margins = $this->margin_top.'-'.$this->margin_right.'-'.$this->margin_bottom.'-'.$this->margin_left;
$margins = str_replace (',', '', $margins);
$margins = str_replace ('.', '', $margins);
return $this->format.'-'.$this->orientation.'-'.$margins;
}
/**
* Query format data. Returns data in assoziative array $dest.
* Returned fields are 'format', 'orientation', 'width', 'height',
* 'margin-top', 'margin-bottom', 'margin-left' and 'margin-right'.
* If $format is unknown, then format 'A4' will be assumed.
*
* @param string $format
* @param string $orientation
*/
public static function queryFormat (&$dest, $format, $orientation='portrait', $margin_top=2, $margin_right=2, $margin_bottom=2, $margin_left=2) {
switch ($format) {
case 'A6':
$width = 10.5;
$height = 14.8;
break;
case 'A5':
$width = 14.8;
$height = 21;
break;
case 'A3':
$width = 29.7;
$height = 42;
break;
case 'B6 (ISO)':
$width = 12.5;
$height = 17.6;
break;
case 'B5 (ISO)':
$width = 17.6;
$height = 25;
break;
case 'B4 (ISO)':
$width = 25;
$height = 35.3;
break;
case 'Letter':
$width = 21.59;
$height = 27.94;
break;
case 'Legal':
$width = 21.59;
$height = 35.56;
break;
case 'Long Bond':
$width = 21.59;
$height = 33.02;
break;
case 'Tabloid':
$width = 27.94;
$height = 43.18;
break;
case 'B6 (JIS)':
$width = 12.8;
$height = 18.2;
break;
case 'B5 (JIS)':
$width = 18.2;
$height = 25.7;
break;
case 'B4 (JIS)':
$width = 25.7;
$height = 36.4;
break;
case '16 Kai':
$width = 18.4;
$height = 26;
break;
case '32 Kai':
$width = 13;
$height = 18.4;
break;
case 'Big 32 Kai':
$width = 14;
$height = 20.3;
break;
case 'DL Envelope':
$width = 11;
$height = 22;
break;
case 'C6 Envelope':
$width = 11.4;
$height = 16.2;
break;
case 'C6/5 Envelope':
$width = 11.4;
$height = 22.9;
break;
case 'C5 Envelope':
$width = 16.2;
$height = 22.9;
break;
case 'C4 Envelope':
$width = 22.9;
$height = 32.4;
break;
case '#6 3/4 Envelope':
$width = 9.21;
$height = 16.51;
break;
case '#7 3/4 (Monarch) Envelope':
$width = 9.84;
$height = 19.05;
break;
case '#9 Envelope':
$width = 9.84;
$height = 22.54;
break;
case '#10 Envelope':
$width = 10.48;
$height = 24.13;
break;
case '#11 Envelope':
$width = 11.43;
$height = 26.35;
break;
case '#12 Envelope':
$width = 12.07;
$height = 27.94;
break;
case 'Japanese Postcard':
$width = 10;
$height = 14.8;
break;
case 'A4':
default:
$format = 'A4';
$width = 21;
$height = 29.7;
break;
}
if ( $orientation != 'portrait' ) {
$orientation = 'landscape';
$help = $width;
$width = $height;
$height = $help;
}
// Return format data.
$dest ['format'] = $format;
$dest ['orientation'] = $orientation;
$dest ['width'] = $width;
$dest ['height'] = $height;
// Margins are currently accepted 'as is'
// but could be subject to further checks/adjustments in the future.
$dest ['margin-top'] = $margin_top;
$dest ['margin-bottom'] = $margin_bottom;
$dest ['margin-left'] = $margin_left;
$dest ['margin-right'] = $margin_right;
}
/**
* Set format. Sets all values according to $format.
*
* @param string $format
* @param string $orientation
*/
public function setFormat($format, $orientation='portrait', $margin_top=2, $margin_right=2, $margin_bottom=2, $margin_left=2) {
$data = array();
// Query format data
$this->queryFormat ($data, $format, $orientation, $margin_top, $margin_right, $margin_bottom, $margin_left);
// Save as page settings
$this->format = $data ['format'];
$this->orientation = $data ['orientation'];
$this->width = $data ['width'];
$this->height = $data ['height'];
$this->margin_top = $data ['margin-top'];
$this->margin_bottom = $data ['margin-bottom'];
$this->margin_left = $data ['margin-left'];
$this->margin_right = $data ['margin-right'];
}
/**
* @return string
*/
public function getFormat() {
return $this->format;
}
/**
* @return string
*/
public function getOrientation() {
return $this->orientation;
}
/**
* @return float
*/
public function getWidth() {
return $this->width;
}
/**
* @return float
*/
public function getHeight() {
return $this->height;
}
/**
* @return int
*/
public function getMarginTop() {
return $this->margin_top;
}
/**
* @return int
*/
public function getMarginBottom() {
return $this->margin_bottom;
}
/**
* @return int
*/
public function getMarginLeft() {
return $this->margin_left;
}
/**
* @return int
*/
public function getMarginRight() {
return $this->margin_right;
}
/**
* Return width percentage value if margins are taken into account.
* Usually "100%" means 21cm in case of A4 format.
* But usually you like to take care of margins. This function
* adjusts the percentage to the value which should be used for margins.
* So 100% == 21cm e.g. becomes 80.9% == 17cm (assuming a margin of 2 cm on both sides).
*
* @param string $percentage
* @return int|string
*/
function getRelWidthMindMargins ($percentage = '100'){
$percentage *= $this->width - $this->margin_left - $this->margin_right;
$percentage /= $this->width;
return $percentage;
}
/**
* Like getRelWidthMindMargins but returns the absulute width
* in centimeters.
*
* @param string $percentage
* @return float
*/
function getAbsWidthMindMargins ($percentage = '100'){
$percentage *= $this->width - $this->margin_left - $this->margin_right;
return ($percentage/100);
}
/**
* Return height percentage value if margins are taken into account.
* Usually "100%" means 29.7cm in case of A4 format.
* But usually you like to take care of margins. This function
* adjusts the percentage to the value which should be used for margins.
* So 100% == 29.7cm e.g. becomes 86.5% == 25.7cm (assuming a margin of 2 cm on top and bottom).
*
* @param string $percentage
* @return float|string
*/
function getRelHeightMindMargins ($percentage = '100'){
$percentage *= $this->height - $this->margin_top - $this->margin_bottom;
$percentage /= $this->height;
return $percentage;
}
/**
* Like getRelHeightMindMargins but returns the absulute width
* in centimeters.
*
* @param string $percentage
* @return float
*/
function getAbsHeightMindMargins ($percentage = '100'){
$percentage *= $this->height - $this->margin_left - $this->margin_right;
return ($percentage/100);
}
}

View File

@@ -0,0 +1,208 @@
<?php
/**
* ODTMasterPageStyle: class for ODT text list styles.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
require_once DOKU_INC.'lib/plugins/odt/ODT/XMLUtil.php';
require_once DOKU_INC.'lib/plugins/odt/ODT/styles/ODTStyle.php';
/**
* The ODTMasterPageStyle class
*/
class ODTMasterPageStyle extends ODTStyle
{
static $master_fields = array(
// Fields belonging to "style:master-page"
'style-name' => array ('style:name', 'style', false),
'style-display-name' => array ('style:display-name', 'style', false),
'style-page-layout-name' => array ('style:page-layout-name', 'style', false),
'draw-style-name' => array ('draw:style-name', 'style', false),
'style-next' => array ('style:next-style-name', 'style', true),
);
static $header_footer_fields = array(
// Fields belonging to "style:header", "style:footer",
// "style:header-left" and "style:footer-left"
// The content/child-elements of "style:header" are saved as is
'style-display' => array ('style:display', 'header', true),
);
protected $master_style = array();
protected $style_header = array();
protected $style_footer = array();
protected $style_header_left = array();
protected $style_footer_left = array();
protected $content_header = NULL;
protected $content_footer = NULL;
protected $content_header_left = NULL;
protected $content_footer_left = NULL;
/**
* Get the element name for the ODT XML encoding of the style.
*/
public function getElementName() {
return 'style:master-page';
}
/**
* Set style properties by importing values from a properties array.
* Properties might be disabled by setting them in $disabled.
* The style must have been previously created.
*
* @param $properties Properties to be imported
* @param $disabled Properties to be ignored
*/
public function importProperties($properties, $disabled=array()) {
$this->importPropertiesInternal(self::$master_fields, $properties, $disabled, $this->master_style);
}
/**
* Check if a style is a common style.
*
* @return bool Is common style
*/
public function mustBeCommonStyle() {
return false;
}
/**
* Set a property.
*
* @param $property The name of the property to set
* @param $value New value to set
*/
public function setProperty($property, $value) {
if (array_key_exists ($property, self::$master_fields)) {
$this->setPropertyInternal
($property, self::$master_fields [$property][0], $value, self::$master_fields [$property][1], $this->master_style);
return;
}
}
/**
* Get the value of a property.
*
* @param $property The property name
* @return string The current value of the property
*/
public function getProperty($property) {
if (array_key_exists ($property, self::$master_fields)) {
return $this->master_style [$property]['value'];
}
return NULL;
}
/**
* Create new style by importing ODT style definition.
*
* @param $xmlCode Style definition in ODT XML format
* @return ODTStyle New specific style
*/
static public function importODTStyle($xmlCode) {
$style = new ODTMasterPageStyle();
// Get attributes for element 'style:master-page'
$open = XMLUtil::getElementOpenTag('style:master-page', $xmlCode);
if (!empty($open)) {
$style->importODTStyleInternal(self::$master_fields, $open, $style->master_style);
}
// Get attributes for element 'style:header'
$open = XMLUtil::getElementOpenTag('style:header', $xmlCode);
if (!empty($open)) {
$style->importODTStyleInternal(self::$header_footer_fields, $open, $style->style_header);
$content_header = XMLUtil::getElementContent ('style:header', $xmlCode);
}
// Get attributes for element 'style:footer'
$open = XMLUtil::getElementOpenTag('style:footer', $xmlCode);
if (!empty($open)) {
$style->importODTStyleInternal(self::$header_footer_fields, $open, $style->style_footer);
$content_footer = XMLUtil::getElementContent ('style:footer', $xmlCode);
}
// Get attributes for element 'style:header-left'
$open = XMLUtil::getElementOpenTag('style:header-left', $xmlCode);
if (!empty($open)) {
$style->importODTStyleInternal(self::$header_footer_fields, $open, $style->style_header_left);
$content_header_left = XMLUtil::getElementContent ('style:header-left', $xmlCode);
}
// Get attributes for element 'style:footer-left'
$open = XMLUtil::getElementOpenTag('style:footer-left', $xmlCode);
if (!empty($open)) {
$style->importODTStyleInternal(self::$header_footer_fields, $open, $style->style_footer_left);
$content_footer_left = XMLUtil::getElementContent ('style:footer-left', $xmlCode);
}
return $style;
}
/**
* Encode current style values in a string and return it.
*
* @return string ODT XML encoded style
*/
public function toString() {
$style = '';
$master = '';
$header = '';
$footer = '';
$header_left = '';
$footer_left = '';
// Get master style ODT properties
foreach ($this->master_style as $property => $items) {
$master .= $items ['odt_property'].'="'.$items ['value'].'" ';
}
// Get header ODT properties
foreach ($this->style_header as $property => $items) {
$header .= $items ['odt_property'].'="'.$items ['value'].'" ';
}
// Get footer ODT properties
foreach ($this->style_footer as $property => $items) {
$footer .= $items ['odt_property'].'="'.$items ['value'].'" ';
}
// Get header-left ODT properties
foreach ($this->style_header_left as $property => $items) {
$header_left .= $items ['odt_property'].'="'.$items ['value'].'" ';
}
// Get footer-left ODT properties
foreach ($this->style_footer_left as $property => $items) {
$footer_left .= $items ['odt_property'].'="'.$items ['value'].'" ';
}
// Build style.
$style = '<style:master-page '.$master.">\n";
if ( !empty($header) || !empty($content_header) ) {
$style .= '<style:header '.$header.">\n";
$style .= $content_header;
$style .= '</style:header>'."\n";
}
if ( !empty($footer) || !empty($content_footer) ) {
$style .= '<style:footer '.$footer.">\n";
$style .= $content_footer;
$style .= '</style:footer>'."\n";
}
if ( !empty($header_left) || !empty($content_header_left) ) {
$style .= '<style:header-left '.$header_left.">\n";
$style .= $content_header_left;
$style .= '</style:header-left>'."\n";
}
if ( !empty($footer_left) || !empty($content_footer_left) ) {
$style .= '<style:footer-left '.$footer_left.">\n";
$style .= $content_footer_left;
$style .= '</style:footer-left>'."\n";
}
$style .= '</style:master-page'.">\n";
return $style;
}
}

View File

@@ -0,0 +1,363 @@
<?php
/**
* ODTPageLayoutStyle: class for ODT page layout styles.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
require_once DOKU_INC.'lib/plugins/odt/ODT/XMLUtil.php';
require_once DOKU_INC.'lib/plugins/odt/ODT/styles/ODTStyle.php';
/**
* The ODTPageLayoutStyle class
*/
class ODTPageLayoutStyle extends ODTStyle
{
static $page_layout_fields = array(
// Fields belonging to "style:master-page"
'style-name' => array ('style:name', 'style', false),
'style-page-usage' => array ('style:page-usage', 'style', false),
);
static $layout_props_fields = array(
// Fields belonging to "style:page-layout-properties"
'width' => array ('fo:page-width', 'props', true),
'height' => array ('fo:page-height', 'props', true),
'num-format' => array ('style:num-format', 'props', true),
'num-letter-sync' => array ('style:num-letter-sync', 'props', true),
'num-prefix' => array ('style:num-prefix', 'props', true),
'num-suffix' => array ('style:num-suffix', 'props', true),
'paper-tray-name' => array ('style:paper-tray-name', 'props', true),
'print-orientation' => array ('style:print-orientation', 'props', true),
'margin-left' => array ('fo:margin-left', 'props', true),
'margin-right' => array ('fo:margin-right', 'props', true),
'margin-top' => array ('fo:margin-top', 'props', true),
'margin-bottom' => array ('fo:margin-bottom', 'props', true),
'margin' => array ('fo:margin', 'props', true),
'border' => array ('fo:border', 'props', true),
'border-top' => array ('fo:border-top', 'props', true),
'border-right' => array ('fo:border-right', 'props', true),
'border-bottom' => array ('fo:border-bottom', 'props', true),
'border-left' => array ('fo:border-left', 'props', true),
'border-line-width' => array ('style:border-line-width', 'props', true),
'border-line-width-top' => array ('style:border-line-width-top', 'props', true),
'border-line-width-bottom' => array ('style:border-line-width-bottom', 'props', true),
'border-line-width-left' => array ('style:border-line-width-left', 'props', true),
'border-line-width-right' => array ('style:border-line-width-right', 'props', true),
'padding' => array ('fo:padding', 'props', true),
'padding-top' => array ('fo:padding-top', 'props', true),
'padding-bottom' => array ('fo:padding-bottom', 'props', true),
'padding-left' => array ('fo:padding-left', 'props', true),
'padding-right' => array ('fo:padding-right', 'props', true),
'shadow' => array ('style:shadow', 'props', true),
'background-color' => array ('fo:background-color', 'props', true),
'register-truth-ref-style-name' => array ('style:register-truth-ref-style-name', 'props', true),
'print' => array ('style:print', 'props', true),
'print-page-order' => array ('style:print-page-order', 'props', true),
'first-page-number' => array ('style:first-page-number', 'props', true),
'scale-to' => array ('style:scale-to', 'props', true),
'scale-to-pages' => array ('style:scale-to-pages', 'props', true),
'table-centering' => array ('style:table-centering', 'props', true),
'footnote-max-height' => array ('style:footnote-max-height', 'props', true),
'writing-mode' => array ('style:writing-mode', 'props', true),
'layout-grid-mode' => array ('style:layout-grid-mode', 'props', true),
'layout-grid-standard-mode' => array ('style:layout-grid-standard-mode', 'props', true),
'layout-grid-base-height' => array ('style:layout-grid-base-height', 'props', true),
'layout-grid-ruby-height' => array ('style:layout-grid-ruby-height', 'props', true),
'layout-grid-lines' => array ('style:layout-grid-lines', 'props', true),
'layout-grid-base-width' => array ('style:layout-grid-base-width', 'props', true),
'layout-grid-color' => array ('style:layout-grid-color', 'props', true),
'layout-grid-ruby-below' => array ('style:layout-grid-ruby-below', 'props', true),
'layout-grid-print' => array ('style:layout-grid-print', 'props', true),
'layout-grid-display' => array ('style:layout-grid-display', 'props', true),
'layout-grid-snap-to' => array ('style:layout-grid-snap-to', 'props', true),
);
static $bgi_fields = array(
// Fields belonging to "style:background-image"
// The content of element "style:background-image" will be saved as is
'repeat' => array ('style:repeat', 'bgi', true),
'position' => array ('style:position', 'bgi', true),
'filter-name' => array ('style:filter-name', 'bgi', true),
'opacity' => array ('draw:opacity', 'bgi', true),
'xlink-type' => array ('xlink:type', 'bgi', true),
'xlink-href' => array ('xlink:href', 'bgi', true),
'xlink-show' => array ('xlink:show', 'bgi', true),
'xlink-actuate' => array ('xlink:actuate', 'bgi', true),
);
static $columns_fields = array(
// Fields belonging to "style:columns"
// The content of element "style:columns" will be saved as is
'column-count' => array ('fo:column-count', 'columns', true),
'column-gap' => array ('fo:column-gap', 'columns', true),
'column-gap' => array ('fo:column-gap', 'columns', true),
);
static $footnote_fields = array(
// Fields belonging to "style:footnote-sep"
'ftsep-width' => array ('style:width', 'ftsep', true),
'ftsep-rel-width' => array ('style:rel-width', 'ftsep', true),
'ftsep-color' => array ('style:color', 'ftsep', true),
'ftsep-line-style' => array ('style:line-style', 'ftsep', true),
'ftsep-adjustment' => array ('style:adjustment', 'ftsep', true),
'ftsep-distance-before-sep' => array ('style:distance-before-sep', 'ftsep', true),
'ftsep-distance-after-sep' => array ('style:distance-after-sep', 'ftsep', true),
);
protected $page_layout_style = array();
protected $layout_props = array();
protected $bgi_props = array();
protected $columns_props = array();
protected $footnote_props = array();
protected $content_bgi = NULL;
protected $content_columns = NULL;
protected $content_header = NULL;
protected $content_footer = NULL;
/**
* Get the element name for the ODT XML encoding of the style.
*/
public function getElementName() {
return 'style:page-layout';
}
/**
* Set style properties by importing values from a properties array.
* Properties might be disabled by setting them in $disabled.
* The style must have been previously created.
*
* @param $properties Properties to be imported
* @param $disabled Properties to be ignored
*/
public function importProperties($properties, $disabled=array()) {
$this->importPropertiesInternal(self::$page_layout_fields, $properties, $disabled, $this->page_layout_style);
$this->importPropertiesInternal(self::$layout_props_fields, $properties, $disabled, $this->layout_props);
$this->importPropertiesInternal(self::$bgi_fields, $properties, $disabled, $this->bgi_props);
$this->importPropertiesInternal(self::$columns_fields, $properties, $disabled, $this->columns_props);
$this->importPropertiesInternal(self::$footnote_props, $properties, $disabled, $this->footnote_fields);
}
/**
* Check if a style is a common style.
*
* @return bool Is common style
*/
public function mustBeCommonStyle() {
return false;
}
/**
* Set a property.
*
* @param $property The name of the property to set
* @param $value New value to set
*/
public function setProperty($property, $value) {
if (array_key_exists ($property, self::$page_layout_fields)) {
$this->setPropertyInternal
($property, self::$page_layout_fields [$property][0], $value, self::$page_layout_fields [$property][1], $this->page_layout_style);
return;
}
if (array_key_exists ($property, self::$layout_props_fields)) {
$this->setPropertyInternal
($property, self::$layout_props_fields [$property][0], $value, self::$layout_props_fields [$property][1], $this->layout_props);
return;
}
if (array_key_exists ($property, self::$bgi_fields)) {
$this->setPropertyInternal
($property, self::$bgi_fields [$property][0], $value, self::$bgi_fields [$property][1], $this->bgi_props);
return;
}
if (array_key_exists ($property, self::$columns_fields)) {
$this->setPropertyInternal
($property, self::$columns_fields [$property][0], $value, self::$columns_fields [$property][1], $this->columns_props);
return;
}
if (array_key_exists ($property, self::$footnote_fields)) {
$this->setPropertyInternal
($property, self::$footnote_fields [$property][0], $value, self::$footnote_fields [$property][1], $this->footnote_props);
return;
}
}
/**
* Get the value of a property.
*
* @param $property The property name
* @return string The current value of the property
*/
public function getProperty($property) {
if (array_key_exists ($property, self::$page_layout_fields)) {
return $this->page_layout_style [$property]['value'];
}
if (array_key_exists ($property, self::$layout_props_fields)) {
return $this->layout_props [$property]['value'];
}
if (array_key_exists ($property, self::$bgi_fields)) {
return $this->bgi_props [$property]['value'];
}
if (array_key_exists ($property, self::$columns_fields)) {
return $this->columns_props [$property]['value'];
}
if (array_key_exists ($property, self::$footnote_fields)) {
return $this->footnote_props [$property]['value'];
}
return NULL;
}
/**
* Create new style by importing ODT style definition.
*
* @param $xmlCode Style definition in ODT XML format
* @return ODTStyle New specific style
*/
static public function importODTStyle($xmlCode) {
$style = new ODTPageLayoutStyle();
// Get attributes for element 'style:master-page'
$open = XMLUtil::getElementOpenTag('style:page-layout', $xmlCode);
if (!empty($open)) {
$style->importODTStyleInternal(self::$page_layout_fields, $open, $style->page_layout_style);
$childs = XMLUtil::getElementContent ('style:page-layout', $xmlCode);
if (!empty($childs)) {
// Get attributes for element 'style:page-layout-properties'
$open = XMLUtil::getElementOpenTag('style:page-layout-properties', $childs);
$style->content_header = XMLUtil::getElement ('style:header-style', $childs);
$style->content_footer = XMLUtil::getElement ('style:footer-style', $childs);
if (!empty($open)) {
$style->importODTStyleInternal(self::$layout_props_fields, $open, $style->layout_props);
$childs = XMLUtil::getElementContent ('style:page-layout-properties', $xmlCode);
if (!empty($childs)) {
// Get 'style:background-image'
$open = XMLUtil::getElementOpenTag('style:background-image', $childs);
if (!empty($open)) {
$style->importODTStyleInternal(self::$bgi_fields, $open, $style->bgi_props);
$style->content_bgi = XMLUtil::getElementContent ('style:background-image', $childs);
}
// Get 'style:columns'
$open = XMLUtil::getElementOpenTag('style:columns', $childs);
if (!empty($open)) {
$style->importODTStyleInternal(self::$columns_fields, $open, $style->columns_props);
$style->content_columns = XMLUtil::getElementContent ('style:columns', $childs);
}
// Get 'style:footnote-sep'
$open = XMLUtil::getElementOpenTag('style:footnote-sep', $childs);
if (!empty($open)) {
$style->importODTStyleInternal(self::$footnote_fields, $open, $style->footnote_props);
}
}
}
}
}
return $style;
}
/**
* Encode current style values in a string and return it.
*
* @return string ODT XML encoded style
*/
public function toString() {
$layout_style = '';
$layout = '';
$bgi = '';
$columns = '';
$footnote = '';
// Get page layout style ODT properties
foreach ($this->page_layout_style as $property => $items) {
$layout_style .= $items ['odt_property'].'="'.$items ['value'].'" ';
}
// Get page layout properties ODT properties
foreach ($this->layout_props as $property => $items) {
$layout .= $items ['odt_property'].'="'.$items ['value'].'" ';
}
// Get background-image ODT properties
foreach ($this->bgi_props as $property => $items) {
$bgi .= $items ['odt_property'].'="'.$items ['value'].'" ';
}
// Get columns ODT properties
foreach ($this->columns_props as $property => $items) {
$columns .= $items ['odt_property'].'="'.$items ['value'].'" ';
}
// Get footnote-sep ODT properties
foreach ($this->footnote_props as $property => $items) {
$footnote .= $items ['odt_property'].'="'.$items ['value'].'" ';
}
// Build style.
$style = '<style:page-layout '.$layout_style.">\n";
if ( !empty($layout) || !empty($bgi) || !empty($columns) || !empty($footnote) ||
!empty($this->content_bgi) || !empty($this->content_columns) ) {
$style .= '<style:page-layout-properties '.$layout.">\n";
if ( !empty($bgi) || !empty($this->content_bgi) ) {
$style .= '<style:background-image '.$bgi.">\n";
$style .= $this->content_bgi;
$style .= '</style:background-image>'."\n";
}
if ( !empty($columns) || !empty($content_columns) ) {
$style .= '<style:columns '.$columns.">\n";
$style .= $this->content_columns;
$style .= '</style:columns>'."\n";
}
if ( !empty($footnote) ) {
$style .= '<style:footnote-sep '.$footnote."/>\n";
}
$style .= '</style:page-layout-properties>'."\n";
}
$style .= $this->content_header;
$style .= $this->content_footer;
$style .= '</style:page-layout'.">\n";
return $style;
}
/**
* This function creates a page layout style with the parameters given in $properies.
*
* The currently supported properties are:
* style-name, width, height, margin-top, margin-bottom, margin-right and margin-left.
* All properties except the style-name are expected to be numeric values.
* The function will add 'cm' itself, so do not add any units.
*
* The function returns the name of the new style or NULL if all relevant properties are empty.
*
* @author LarsDW223
*
* @param $properties
* @param null $disabled_props
* @return ODTUnknownStyle or NULL
*/
public static function createPageLayoutStyle(array $properties, array $disabled_props = NULL) {
// Create style name (if not given).
$style_name = $properties ['style-name'];
if ( empty($style_name) ) {
$style_name = self::getNewStylename ('Page');
$properties ['style-name'] = $style_name;
}
$style = '<style:page-layout style:name="'.$style_name.'">
<style:page-layout-properties fo:page-width="'.$properties ['width'].'cm" fo:page-height="'.$properties ['height'].'cm" style:num-format="1" style:print-orientation="landscape" fo:margin-top="'.$properties ['margin-top'].'cm" fo:margin-bottom="'.$properties ['margin-bottom'].'cm" fo:margin-left="'.$properties ['margin-left'].'cm" fo:margin-right="'.$properties ['margin-right'].'cm" style:writing-mode="lr-tb" style:footnote-max-height="0cm">
<style:footnote-sep style:width="0.018cm" style:distance-before-sep="0.1cm" style:distance-after-sep="0.1cm" style:adjustment="left" style:rel-width="25%" style:color="#000000"/>
</style:page-layout-properties>
<style:header-style/>
<style:footer-style/>
</style:page-layout>';
return self::importODTStyle($style);
}
}

View File

@@ -0,0 +1,497 @@
<?php
/**
* ODTParagraphStyle: class for ODT paragraph styles.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
require_once DOKU_PLUGIN.'odt/ODT/XMLUtil.php';
require_once 'ODTStyle.php';
ODTStyleStyle::register('ODTParagraphStyle');
/**
* The ODTParagraphStyle class
*/
class ODTParagraphStyle extends ODTStyleStyle
{
static $paragraph_fields = array(
'line-height' => array ('fo:line-height', 'paragraph', true),
'line-height-at-least' => array ('style:line-height-at-least', 'paragraph', true),
'line-spacing' => array ('style:line-spacing', 'paragraph', true),
'font-independent-line-spacing' => array ('style:font-independent-line-spacing', 'paragraph', true),
'text-align' => array ('fo:text-align', 'paragraph', true),
'text-align-last' => array ('fo:text-align-last', 'paragraph', true),
'justify-single-word' => array ('style:justify-single-word', 'paragraph', true),
'keep-together' => array ('fo:keep-together', 'paragraph', true),
'widows' => array ('fo:widows', 'paragraph', true),
'orphans' => array ('fo:orphans', 'paragraph', true),
'tab-stop-distance' => array ('style:tab-stop-distance', 'paragraph', true),
'hyphenation-keep' => array ('fo:hyphenation-keep', 'paragraph', true),
'hyphenation-ladder-count' => array ('fo:hyphenation-ladder-count', 'paragraph', true),
'register-true' => array ('style:register-true', 'paragraph', true),
'text-indent' => array ('fo:text-indent', 'paragraph', true),
'auto-text-indent' => array ('style:auto-text-indent', 'paragraph', true),
'margin' => array ('fo:margin', 'paragraph', true),
'margin-top' => array ('fo:margin-top', 'paragraph', true),
'margin-right' => array ('fo:margin-right', 'paragraph', true),
'margin-bottom' => array ('fo:margin-bottom', 'paragraph', true),
'margin-left' => array ('fo:margin-left', 'paragraph', true),
'break-before' => array ('fo:break-before', 'paragraph', true),
'break-after' => array ('fo:break-after', 'paragraph', true),
'background-color' => array ('fo:background-color', 'paragraph', true),
'border' => array ('fo:border', 'paragraph', true),
'border-top' => array ('fo:border-top', 'paragraph', true),
'border-right' => array ('fo:border-right', 'paragraph', true),
'border-bottom' => array ('fo:border-bottom', 'paragraph', true),
'border-left' => array ('fo:border-left', 'paragraph', true),
'border-line-width' => array ('style:border-line-width', 'paragraph', true),
'border-line-width-top' => array ('style:border-line-width-top', 'paragraph', true),
'border-line-width-bottom' => array ('style:border-line-width-bottom', 'paragraph', true),
'border-line-width-left' => array ('style:border-line-width-left', 'paragraph', true),
'border-line-width-right' => array ('style:border-line-width-right', 'paragraph', true),
'join-border' => array ('style:join-border', 'paragraph', true),
'padding' => array ('fo:padding', 'paragraph', true),
'padding-top' => array ('fo:padding-top', 'paragraph', true),
'padding-bottom' => array ('fo:padding-bottom', 'paragraph', true),
'padding-left' => array ('fo:padding-left', 'paragraph', true),
'padding-right' => array ('fo:padding-right', 'paragraph', true),
'shadow' => array ('style:shadow', 'paragraph', true),
'keep-with-next' => array ('fo:keep-with-next', 'paragraph', true),
'number-lines' => array ('text:number-lines', 'paragraph', true),
'line-number' => array ('text:line-number', 'paragraph', true),
'text-autospace' => array ('style:text-autospace', 'paragraph', true),
'punctuation-wrap' => array ('style:punctuation-wrap', 'paragraph', true),
'line-break' => array ('style:line-break', 'paragraph', true),
'vertical-align' => array ('style:vertical-align', 'paragraph', true),
'writing-mode' => array ('style:writing-mode', 'paragraph', true),
'writing-mode-automatic' => array ('style:writing-mode-automatic', 'paragraph', true),
'snap-to-layout-grid' => array ('style:snap-to-layout-grid', 'paragraph', true),
'page-number' => array ('style:page-number', 'paragraph', true),
'background-transparency' => array ('style:background-transparency', 'paragraph', true),
);
// Additional fields for child element tab-stop.
static $tab_stop_fields = array(
'style-position' => array ('style:position', 'tab-stop', true),
'style-type' => array ('style:type', 'tab-stop', true),
'style-leader-type' => array ('style:leader-type', 'tab-stop', true),
'style-leader-style' => array ('style:leader-style', 'tab-stop', true),
'style-leader-width' => array ('style:leader-width', 'tab-stop', true),
'style-leader-color' => array ('style:leader-color', 'tab-stop', true),
'style-leader-text' => array ('style:leader-text', 'tab-stop', true),
);
protected $style_properties = array();
protected $text_properties = array();
protected $tab_stops = array();
/**
* Constructor.
*/
public function __construct() {
parent::__construct();
}
/**
* Set style properties by importing values from a properties array.
* Properties might be disabled by setting them in $disabled.
* The style must have been previously created.
*
* @param $properties Properties to be imported
* @param $disabled Properties to be ignored
*/
public function importProperties($properties, $disabled=array()) {
foreach ($properties as $property => $value) {
if ($disabled [$property] == 0) {
$this->setProperty($property, $value);
}
}
}
/**
* Check if a style is a common style.
*
* @return bool Is common style
*/
public function mustBeCommonStyle() {
return false;
}
/**
* Get the style family of a style.
*
* @return string Style family
*/
static public function getFamily() {
return 'paragraph';
}
/**
* Set a property.
*
* @param $property The name of the property to set
* @param $value New value to set
*/
public function setProperty($property, $value) {
$style_fields = ODTStyleStyle::getStyleProperties ();
if (array_key_exists ($property, $style_fields)) {
$this->setPropertyInternal
($property, $style_fields [$property][0], $value, $style_fields [$property][1], $this->style_properties);
return;
}
// FIXME: currently with setProperty there always will only be one tab-stop.
// Maybe in the future supply a function "add tab stop" or something.
if (array_key_exists ($property, self::$tab_stop_fields)) {
if ($this->tab_stops [0] == NULL) {
$this->tab_stops [0] = array();
}
$this->setPropertyInternal
($property, self::$tab_stop_fields [$property][0], $value, self::$tab_stop_fields [$property][1], $this->tab_stops[0]);
return;
}
// Compare with paragraph fields before text fields first!
// So, paragraph properties get precedence.
if (array_key_exists ($property, self::$paragraph_fields)) {
$this->setPropertyInternal
($property, self::$paragraph_fields [$property][0], $value, self::$paragraph_fields [$property][1]);
return;
}
$text_fields = ODTTextStyle::getTextProperties ();
if (array_key_exists ($property, $text_fields)) {
$this->setPropertyInternal
($property, $text_fields [$property][0], $value, $text_fields [$property][1], $this->text_properties);
return;
}
}
/**
* Get the value of a property.
*
* @param $property The property name
* @return string The current value of the property
*/
public function getProperty($property) {
$style_fields = ODTStyleStyle::getStyleProperties ();
if (array_key_exists ($property, $style_fields)) {
return $this->style_properties [$property]['value'];
}
$paragraph_fields = self::$paragraph_fields;
if (array_key_exists ($property, $paragraph_fields)) {
return parent::getProperty($property);
}
$text_fields = ODTTextStyle::getTextProperties ();
if (array_key_exists ($property, $text_fields)) {
return $this->text_properties [$property]['value'];
}
return parent::getProperty($property);
}
/**
* Create new style by importing ODT style definition.
*
* @param $xmlCode Style definition in ODT XML format
* @return ODTStyle New specific style
*/
static public function importODTStyle($xmlCode) {
$style = new ODTParagraphStyle();
$attrs = 0;
$open = XMLUtil::getElementOpenTag('style:style', $xmlCode);
if (!empty($open)) {
$attrs += $style->importODTStyleInternal(ODTStyleStyle::getStyleProperties (), $open, $style->style_properties);
} else {
$open = XMLUtil::getElementOpenTag('style:default-style', $xmlCode);
if (!empty($open)) {
$style->setDefault(true);
$attrs += $style->importODTStyleInternal(ODTStyleStyle::getStyleProperties (), $open, $style->style_properties);
}
}
$open = XMLUtil::getElementOpenTag('style:paragraph-properties', $xmlCode);
if (!empty($open)) {
$attrs += $style->importODTStyleInternal(self::$paragraph_fields, $xmlCode);
}
$open = XMLUtil::getElementOpenTag('style:text-properties', $xmlCode);
if (!empty($open)) {
$attrs += $style->importODTStyleInternal(ODTTextStyle::getTextProperties (), $open, $style->text_properties);
}
// Get all tab-stops.
$tabs = XMLUtil::getElementContent('style:tab-stops', $xmlCode);
if ($tabs != NULL) {
$max = strlen($tabs);
$pos = 0;
$index = 0;
$tab = XMLUtil::getElement('style:tab-stop', $tabs, $end);
$pos = $end;
while ($tab != NULL) {
$style->tab_stops [$index] = array();
$attrs += $style->importODTStyleInternal(self::$tab_stop_fields, $tab, $style->tab_stops [$index]);
$index++;
$tab = XMLUtil::getElement('style:tab-stop', substr ($tabs, $pos), $end);
$pos += $end;
}
}
// If style has no meaningfull content then throw it away
if ( $attrs == 0 ) {
return NULL;
}
return $style;
}
static public function getParagraphProperties () {
return self::$paragraph_fields;
}
/**
* Encode current style values in a string and return it.
*
* @return string ODT XML encoded style
*/
public function toString() {
$style = '';
$para_props = '';
$text_props = '';
$tab_stops_props = '';
// Get style contents
foreach ($this->style_properties as $property => $items) {
if ($items ['odt_property'] != 'style:family') {
$style .= $items ['odt_property'].'="'.$items ['value'].'" ';
}
}
$style .= 'style:family="'.$this->getFamily().'" ';
// Get paragraph properties ODT properties
foreach ($this->properties as $property => $items) {
$para_props .= $items ['odt_property'].'="'.$items ['value'].'" ';
}
// Get text properties
foreach ($this->text_properties as $property => $items) {
$text_props .= $items ['odt_property'].'="'.$items ['value'].'" ';
}
// Get tab-stops properties
for ($index = 0 ; $index < count($this->tab_stops) ; $index++) {
$tab_stops_props .= '<style:tab-stop ';
foreach ($this->tab_stops[$index] as $property => $items) {
$tab_stops_props .= $items ['odt_property'].'="'.$items ['value'].'" ';
}
$tab_stops_props .= '/>';
}
// Build style.
if (!$this->isDefault()) {
$style = '<style:style '.$style.">\n";
} else {
$style = '<style:default-style '.$style.">\n";
}
if (!empty($para_props) || !empty($tab_stops_props)) {
if (empty($tab_stops_props)) {
$style .= '<style:paragraph-properties '.$para_props."/>\n";
} else {
$style .= '<style:paragraph-properties '.$para_props.">\n";
$style .= '<style:tab-stops>'."\n";
$style .= $tab_stops_props."\n";
$style .= '</style:tab-stops>'."\n";
$style .= '</style:paragraph-properties>'."\n";
}
}
if (!empty($text_props)) {
$style .= '<style:text-properties '.$text_props."/>\n";
}
if (!$this->isDefault()) {
$style .= '</style:style>'."\n";
} else {
$style .= '</style:default-style>'."\n";
}
return $style;
}
/**
* This function creates a paragraph style using the style as set in the assoziative array $properties.
* The parameters in the array should be named as the CSS property names e.g. 'color' or 'background-color'.
* Properties which shall not be used in the style can be disabled by setting the value in disabled_props
* to 1 e.g. $disabled_props ['color'] = 1 would block the usage of the color property.
*
* The currently supported properties are:
* background-color, color, font-style, font-weight, font-size, border, font-family, font-variant, letter-spacing,
* vertical-align, line-height, background-image
*
* The function returns the name of the new style or NULL if all relevant properties are empty.
*
* @author LarsDW223
* @param $properties
* @param null $disabled_props
* @return ODTParagraphStyle or NULL
*/
public static function createParagraphStyle(array $properties, array $disabled_props = NULL, ODTDocument $doc=NULL){
// Convert 'text-decoration'.
if ( $properties ['text-decoration'] == 'line-through' ) {
$properties ['text-line-through-style'] = 'solid';
}
if ( $properties ['text-decoration'] == 'underline' ) {
$properties ['text-underline-style'] = 'solid';
}
if ( $properties ['text-decoration'] == 'overline' ) {
$properties ['text-overline-style'] = 'solid';
}
// If the property 'vertical-align' has the value 'sub' or 'super'
// then for ODT it needs to be converted to the corresponding 'text-position' property.
// Replace sub and super with text-position.
$valign = $properties ['vertical-align'];
if (!empty($valign)) {
if ( $valign == 'sub' ) {
$properties ['text-position'] = '-33% 100%';
unset($properties ['vertical-align']);
} elseif ( $valign == 'super' ) {
$properties ['text-position'] = '33% 100%';
unset($properties ['vertical-align']);
}
}
// Separate country from language
$lang = $properties ['lang'];
$country = $properties ['country'];
if ( !empty($lang) ) {
$parts = preg_split ('/-/', $lang);
$lang = $parts [0];
$country = $parts [1];
$properties ['country'] = trim($country);
$properties ['lang'] = trim($lang);
}
if (!empty($properties ['country'])) {
if (empty($properties ['country-asian'])) {
$properties ['country-asian'] = $properties ['country'];
}
if (empty($properties ['country-complex'])) {
$properties ['country-complex'] = $properties ['country'];
}
}
// Always set 'auto-text-indent = false' if 'text-indent' is set.
if (!empty($properties ['text-indent'])) {
$properties ['auto-text-indent'] = 'false';
$length = strlen ($properties ['text-indent']);
if ( $length > 0 && $properties ['text-indent'] [$length-1] == '%' && $doc != NULL ) {
// Percentage value needs to be converted to absolute value.
// ODT standard says that percentage value should work if used in a common style.
// This did not work with LibreOffice 4.4.3.2.
$value = trim ($properties ['text-indent'], '%');
$properties ['text-indent'] = $doc->getAbsWidthMindMargins ($value).'cm';
}
}
// Eventually create parent for font-size
$save = $disabled_props ['font-size'];
$odt_fo_size = '';
if ( empty ($disabled_props ['font-size']) ) {
$odt_fo_size = $properties ['font-size'];
}
$parent = '';
$length = strlen ($odt_fo_size);
if ( $length > 0 && $odt_fo_size [$length-1] == '%' && $doc != NULL) {
// A font-size in percent is only supported in common style definitions, not in automatic
// styles. Create a common style and set it as parent for this automatic style.
$name = 'Size'.trim ($odt_fo_size, '%').'pc';
$style_obj = ODTTextStyle::createSizeOnlyTextStyle ($name, $odt_fo_size);
$doc->addStyle($style_obj);
$parent = $style_obj->getProperty('style-name');
if (!empty($parent)) {
$properties ['style-parent'] = $parent;
}
}
// Create style name (if not given).
$style_name = $properties ['style-name'];
if ( empty($style_name) ) {
$style_name = self::getNewStylename ('Paragraph');
$properties ['style-name'] = $style_name;
}
// FIXME: fix missing tab stop handling...
//case 'tab-stop':
// $tab .= $params [$property]['name'].'="'.$value.'" ';
// $tab .= self::writeExtensionNames ($params [$property]['name'], $value);
// break;
// Create empty paragraph style.
$object = new ODTParagraphStyle();
if ($object == NULL) {
return NULL;
}
// Import our properties
$object->importProperties($properties, $disabled_props);
// Restore $disabled_props
$disabled_props ['font-size'] = $save;
return $object;
}
/**
* Simple helper function for creating a paragrapg style for a pagebreak.
*
* @author LarsDW223
*
* @param string $parent Name of the parent style to set
* @param string $before Pagebreak before or after?
* @return ODTParagraphStyle
*/
public static function createPagebreakStyle($style_name, $parent=NULL,$before=true) {
$properties = array();
$properties ['style-name'] = $style_name;
if ( !empty($parent) ) {
$properties ['style-parent'] = $parent;
}
if ($before == true ) {
$properties ['break-before'] = 'page';
} else {
$properties ['break-after'] = 'page';
}
return self::createParagraphStyle($properties);
}
/**
* Set a property.
*
* @param $property The name of the property to set
* @param $value New value to set
*/
public static function copyLayoutProperties(ODTParagraphStyle $source, ODTParagraphStyle $dest, array $disabled=NULL) {
// DO NOT COPY STYLE FIELDS/PROPERTIES
// Copy $tab_stop_fields
foreach (self::$tab_stop_fields as $property => $fields) {
$value = $source->getProperty($property);
if ($value != NULL && $disabled [$property] == 0) {
$dest -> setProperty($property, $value);
}
}
// Copy $paragraph_fields
foreach (self::$paragraph_fields as $property => $fields) {
$value = $source->getProperty($property);
if ($value != NULL && $disabled [$property] == 0) {
$dest -> setProperty($property, $value);
}
}
// Copy $text_fields
$text_fields = ODTTextStyle::getTextProperties ();
foreach ($text_fields as $property => $fields) {
$value = $source->getProperty($property);
if ($value != NULL && $disabled [$property] == 0) {
$dest -> setProperty($property, $value);
}
}
}
}

View File

@@ -0,0 +1,248 @@
<?php
/**
* ODTStyle: base class for ODT styles.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
require_once 'ODTUnknownStyle.php';
require_once 'ODTStyleStyle.php';
require_once 'ODTTextOutlineStyle.php';
require_once 'ODTTextListStyle.php';
require_once 'ODTMasterPageStyle.php';
require_once 'ODTPageLayoutStyle.php';
require_once 'ODTTextStyle.php';
require_once 'ODTParagraphStyle.php';
require_once 'ODTTableStyle.php';
require_once 'ODTTableRowStyle.php';
require_once 'ODTTableColumnStyle.php';
require_once 'ODTTableCellStyle.php';
/**
* The ODTStyle class
*/
abstract class ODTStyle
{
protected static $style_base_name = 'PluginODTAutoStyle_';
protected static $style_count = 0;
protected $properties = array();
/**
* Get the element name for the ODT XML encoding of the style.
*
* @param $properties Properties to be imported
* @param $disabled Properties to be ignored
*/
abstract public function getElementName();
/**
* Set style properties by importing values from a properties array.
* Properties might be disabled by setting them in $disabled.
* The style must have been previously created.
*
* @param $properties Properties to be imported
* @param $disabled Properties to be ignored
*/
abstract public function importProperties($properties, $disabled=array());
/**
* Check if a style is a common style.
*
* @return bool Is common style
*/
abstract public function mustBeCommonStyle();
/**
* Encode current style values in a string and return it.
*
* @return string ODT XML encoded style
*/
abstract public function toString();
/**
* Set a property.
*
* @param $property The name of the property to set
* @param $value New value to set
*/
abstract public function setProperty($property, $value);
/**
* Get the value of a property.
*
* @param $property The property name
* @return string The current value of the property
*/
public function getProperty($property) {
return $this->properties [$property]['value'];
}
/**
* Get the value of a property.
*
* @param $property The property name
* @param $properties Properties array to query the value from,
* or NULL for using ours.
* @return string The current value of the property
*/
public function getPropertyInternal($property, $properties=NULL) {
if ( $properties === NULL ) {
return $this->properties [$property]['value'];
} else {
return $properties [$property]['value'];
}
}
/**
* Get the value of a property.
*
* @param $property The property name
* @return string The current value of the property
*/
public function getPropertySection($property) {
return $this->properties [$property]['section'];
}
/**
* Create new style by importing ODT style definition.
*
* @param $xmlCode Style definition in ODT XML format
* @return ODTStyle New specific style
*/
static public function importODTStyle($xmlCode) {
$matches = array();
$pattern = '/<(\w)+[^\s\/>]+/';
if (preg_match ($pattern, $xmlCode, $matches) !== 1) {
return NULL;
}
$element = trim ($matches [0], '"<>');
$style = NULL;
switch ($element) {
case 'style:style':
case 'style:default-style':
$style = ODTStyleStyle::importODTStyle($xmlCode);
break;
case 'text:outline-style':
$style = ODTTextOutlineStyle::importODTStyle($xmlCode);
break;
case 'text:list-style':
$style = ODTTextListStyle::importODTStyle($xmlCode);
break;
case 'style:master-page':
$style = ODTMasterPageStyle::importODTStyle($xmlCode);
break;
case 'style:page-layout':
$style = ODTPageLayoutStyle::importODTStyle($xmlCode);
break;
default:
break;
}
if ($style != NULL ) {
return $style;
}
// Unknown/not implemented style.
// Create generic style which can not be changed.
$unknown = ODTUnknownStyle::importODTStyle($xmlCode);
$unknown->setElementName($element);
return $unknown;
}
/**
* Set a property.
*
* @param $property The name of the property to set
* @param $value New value to set
*/
protected function setPropertyInternal($property, $odt_property, $value, $section, &$dest=NULL) {
if ($value !== NULL) {
if ( $dest === NULL ) {
$this->properties [$property] = array ('odt_property' => $odt_property,
'value' => $value,
'section' => $section);
} else {
$dest [$property] = array ('odt_property' => $odt_property,
'value' => $value,
'section' => $section);
}
} else {
if ( $dest === NULL ) {
unset ($this->properties [$property]);
} else {
unset ($dest [$property]);
}
}
}
/**
* Import ODT style definition according to given fields into given style.
*
* @param $style ODTStyle object for storing the properties
* @param $fields Properties accepted by the object/class
* @param $xmlCode Style definition in ODT XML format
* @return integer Number of meaningful properties found
*/
protected function importODTStyleInternal(array $fields, $xmlCode, &$properties=NULL) {
$attrs = 0;
foreach ($fields as $property => $field) {
// The pattern is specified in that way that it also reads in empty attributes.
// Sometimes an empty attribute is not the same as an not existing one. E.g.
// in ODT XML '<text:outline-level-style text:level="3" style:num-format="" >'
// has NOT the same meaning as '<text:outline-level-style text:level="3" >'!!!
// So DO NOT change the '*' in the pattern to '+'!
if (preg_match ('/'.$field[0].'="[^"]*"/', $xmlCode, $matches) === 1) {
$value = substr ($matches [0], strlen($field[0].'="'));
$value = trim ($value, '"<>');
$this->setPropertyInternal($property, $field[0], $value, $field[1], $properties);
if ( $field[2] == true ) {
$attrs++;
}
}
}
return $attrs;
}
/**
* Set style properties by importing values from a properties array.
* Properties might be disabled by setting them in $disabled.
* The style must have been previously created. Only those properties
* will be accepted that are mentioned in the fields array.
*
* @param $style ODTStyle object for storing the properties
* @param $fields Properties accepted by the object/class
* @param $properties Properties to be imported
* @param $disabled Properties to be ignored
*/
protected function importPropertiesInternal(array $fields, $properties, $disabled, &$dest=NULL) {
foreach ($properties as $property => $value) {
if ($disabled [$property] == 0 && array_key_exists ($property, $fields)) {
$this->setPropertyInternal($property, $fields[$property][0], $value, $fields[$property][1], $dest);
}
}
}
/**
* Is this style a default style?
* Needs to be overwritten if a style could also be a default style.
*
* @return boolean Always false.
*/
public function isDefault() {
return false;
}
/**
* This function creates a new style name. All functions of this class which create a new
* style/style name shall use this function to create the style name. By doing so it is
* guaranteed that all style names created by this class are unique.
*
* The function returns the name of the new style or NULL if all relevant properties are empty.
*/
public static function getNewStylename ($type = '') {
self::$style_count++;
$style_name = self::$style_base_name.$type.'_'.self::$style_count;
return $style_name;
}
}

View File

@@ -0,0 +1,242 @@
<?php
/**
* ODTStyleStyle: class for ODT style styles.
* (Elements style:style and style:default-style)
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
require_once DOKU_PLUGIN.'odt/ODT/styles/ODTStyle.php';
//require_once 'ODTTextStyle.php';
//require_once 'ODTParagraphStyle.php';
//require_once 'ODTTableStyle.php';
//require_once 'ODTTableRowStyle.php';
//require_once 'ODTTableColumnStyle.php';
//require_once 'ODTTableCellStyle.php';
//require_once DOKU_PLUGIN.'odt/ODT/styles/ODTParagraphStyle.php';
//require_once DOKU_PLUGIN.'odt/ODT/styles/ODTTableStyle.php';
//require_once DOKU_PLUGIN.'odt/ODT/styles/ODTTableRowStyle.php';
//require_once DOKU_PLUGIN.'odt/ODT/styles/ODTTableCellStyle.php';
/**
* The ODTStyleStyle class
*/
abstract class ODTStyleStyle extends ODTStyle
{
// Style properties/attributes common to each
// style:style and style:default-style element
static $style_fields = array(
'style-name' => array ('style:name', 'style', false),
'style-display-name' => array ('style:display-name', 'style', false),
'style-parent' => array ('style:parent-style-name', 'style', false),
'style-class' => array ('style:class', 'style', true),
'style-family' => array ('style:family', 'style', true),
'style-next' => array ('style:next-style-name', 'style', true),
'style-list-level' => array ('style:list-level', 'style', true),
'style-list-style-name' => array ('style:list-style-name', 'style', true),
'style-master-page-name' => array ('style:master-page-name', 'style', true),
'style-auto-update' => array ('style:auto-update', 'style', true),
'style-data-style-name' => array ('style:data-style-name', 'style', true),
'style-percentage-data-style-name' => array ('style:percentage-data-style-name', 'style', true),
'style-default-outline-level' => array ('style:default-outline-level', 'style', true),
);
static $get_family_callbacks = NULL;
static $import_odt_callbacks = NULL;
protected $is_default = false;
/**
* Constructor.
*/
public function __construct() {
if (self::$get_family_callbacks === NULL)
self::$get_family_callbacks = array();
if (self::$import_odt_callbacks === NULL)
self::$import_odt_callbacks = array();
}
static public function register ($classname) {
self::$get_family_callbacks [] = array($classname, 'getFamily');
self::$import_odt_callbacks [] = array($classname, 'importODTStyle');
}
/**
* Get the element name for the ODT XML encoding of the style.
*
* @param $properties Properties to be imported
* @param $disabled Properties to be ignored
*/
public function getElementName() {
if ($this->isDefault() == true) {
return 'style:default-style';
}
return 'style:style';
}
/**
* Mark style as default style or not.
*
* @param $is_default
*/
public function setDefault($is_default) {
$this->is_default = $is_default;
}
/**
* Is this style a default style?
*
* @return boolean Is this a default style?
*/
public function isDefault() {
return $this->is_default;
}
/**
* Encode current style values in a string and return it.
*
* @return string ODT XML encoded style
*/
public function toString() {
//FIXME: Handling for background-image-section
$style = '';
$text = '';
$paragraph = '';
$table = '';
$table_column = '';
$table_row = '';
$table_cell = '';
$tab_stop = '';
$image = '';
foreach ($this->properties as $property => $items) {
switch ($items ['section']) {
case 'style':
$style .= $items ['odt_property'].'="'.$items ['value'].'" ';
break;
case 'text':
$text .= $items ['odt_property'].'="'.$items ['value'].'" ';
break;
case 'paragraph':
$paragraph .= $items ['odt_property'].'="'.$items ['value'].'" ';
break;
case 'table':
$table .= $items ['odt_property'].'="'.$items ['value'].'" ';
break;
case 'table-column':
$table_column .= $items ['odt_property'].'="'.$items ['value'].'" ';
break;
case 'table-row':
$table_row .= $items ['odt_property'].'="'.$items ['value'].'" ';
break;
case 'table-cell':
$table_cell .= $items ['odt_property'].'="'.$items ['value'].'" ';
break;
case 'tab-stop':
$tab_stop .= $items ['odt_property'].'="'.$items ['value'].'" ';
break;
case 'table-cell-background-image':
$image .= $items ['odt_property'].'="'.$items ['value'].'" ';
break;
}
}
// Build style.
$element = $this->getElementName();
$style = '<'.$element.' '.$style.'>'."\n";
if ( !empty($paragraph) ) {
if ( empty($tab_stop) ) {
$style .= ' <style:paragraph-properties '.$paragraph.'/>'."\n";
} else {
$style .= ' <style:paragraph-properties '.$paragraph.'>'."\n";
$style .= ' <style:tab-stops><style:tab-stop '.$tab_stop.'/></style:tab-stops>'."\n";
$style .= ' </style:paragraph-properties>'."\n";
}
}
if ( !empty($text) ) {
$style .= ' <style:text-properties '.$text.'/>'."\n";
}
if ( !empty($table) ) {
$style .= ' <style:table-properties '.$table.'/>'."\n";
}
if ( !empty($table_column) ) {
$style .= ' <style:table-column-properties '.$table_column.'/>'."\n";
}
if ( !empty($table_row) ) {
$style .= ' <style:table-row-properties '.$table_row.'/>'."\n";
}
if ( !empty($table_cell) ) {
if (empty($image)) {
$style .= ' <style:table-cell-properties '.$table_cell.'/>'."\n";
} else {
$style .= ' <style:table-cell-properties '.$table_cell.'>'."\n";
$style .=' <style:background-image '.$image.'/>'."\n";
$style .= ' </style:table-cell-properties>';
}
}
$style .= '</'.$element.'>'."\n";
return $style;
}
/**
* Create new style by importing ODT style definition.
*
* @param $xmlCode Style definition in ODT XML format
* @return ODTStyle New specific style
*/
static public function importODTStyle($xmlCode) {
$matches = array();
if (preg_match ('/style:family="[^"]+"/', $xmlCode, $matches) !== 1) {
return NULL;
}
$family = substr ($matches [0], strlen('style:family='));
$family = trim ($family, '"<>');
for ($index = 0 ; $index < count(self::$get_family_callbacks) ; $index++ ) {
$curr_family = call_user_func(self::$get_family_callbacks [$index]);
if ($curr_family == $family) {
return call_user_func(self::$import_odt_callbacks [$index], $xmlCode);
}
}
// Unknown/not implemented style family.
// Return NULL, in this case ODTStyle will create a generic unknown style.
return NULL;
}
/**
* Set style properties by importing values from a properties array.
* Properties might be disabled by setting them in $disabled.
* The style must have been previously created. Only those properties
* will be accepted that are mentioned in the fields array.
*
* @param $style ODTStyle object for storing the properties
* @param $fields Properties accepted by the object/class
* @param $properties Properties to be imported
* @param $disabled Properties to be ignored
*/
protected function importPropertiesInternal(array $fields, $properties, $disabled, &$dest=NULL) {
parent::importPropertiesInternal($fields, $properties, $disabled, $dest);
}
/**
* The function deletes all properties that do not belong to the styles section,
* e.g. text properties or paragraph properties.
*/
public function clearLayoutProperties() {
foreach ($this->properties as $property => $items) {
switch ($items ['section']) {
case 'style':
// Keep it.
break;
default:
// Delete everything that does not belong to the styles section.
$this->properties [$property] = NULL;
unset ($this->properties [$property]);
break;
}
}
}
static public function getStyleProperties () {
return self::$style_fields;
}
}

View File

@@ -0,0 +1,224 @@
<?php
/**
* ODTTableCellStyle: class for ODT table cell styles.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
require_once DOKU_INC.'lib/plugins/odt/ODT/XMLUtil.php';
require_once 'ODTStyle.php';
ODTStyleStyle::register('ODTTableCellStyle');
/**
* The ODTTableCellStyle class
*/
class ODTTableCellStyle extends ODTStyleStyle
{
static $table_cell_fields = array(
'vertical-align' => array ('style:vertical-align', 'table-cell', true),
'text-align-source' => array ('style:text-align-source', 'table-cell', true),
'direction' => array ('style:direction', 'table-cell', true),
'glyph-orientation-vertical' => array ('style:glyph-orientation-vertical', 'table-cell', true),
'writing-mode' => array ('style:writing-mode', 'table-cell', true),
'shadow' => array ('style:shadow', 'table-cell', true),
'background-color' => array ('fo:background-color', 'table-cell', true),
'border' => array ('fo:border', 'table-cell', true),
'border-top' => array ('fo:border-top', 'table-cell', true),
'border-right' => array ('fo:border-right', 'table-cell', true),
'border-bottom' => array ('fo:border-bottom', 'table-cell', true),
'border-left' => array ('fo:border-left', 'table-cell', true),
'diagonal-tl-br' => array ('style:diagonal-tl-br', 'table-cell', true),
'diagonal-tl-br-widths' => array ('style:diagonal-tl-br-widths', 'table-cell', true),
'diagonal-bl-tr' => array ('style:diagonal-bl-tr', 'table-cell', true),
'diagonal-bl-tr-widths' => array ('style:diagonal-bl-tr-widths', 'table-cell', true),
'border-line-width' => array ('style:border-line-width', 'table-cell', true),
'border-line-width-top' => array ('style:border-line-width-top', 'table-cell', true),
'border-line-width-bottom' => array ('style:border-line-width-bottom', 'table-cell', true),
'border-line-width-left' => array ('style:border-line-width-left', 'table-cell', true),
'border-line-width-right' => array ('style:border-line-width-right', 'table-cell', true),
'padding' => array ('fo:padding', 'table-cell', true),
'padding-top' => array ('fo:padding-top', 'table-cell', true),
'padding-right' => array ('fo:padding-right', 'table-cell', true),
'padding-bottom' => array ('fo:padding-bottom', 'table-cell', true),
'padding-left' => array ('fo:padding-left', 'table-cell', true),
'wrap-option' => array ('fo:wrap-option', 'table-cell', true),
'rotation-angle' => array ('style:rotation-angle', 'table-cell', true),
'rotation-align' => array ('style:rotation-align', 'table-cell', true),
'cell-protect' => array ('style:cell-protect', 'table-cell', true),
'print-content' => array ('style:print-content', 'table-cell', true),
'decimal-places' => array ('style:decimal-places', 'table-cell', true),
'repeat-content' => array ('style:repeat-content', 'table-cell', true),
'shrink-to-fit' => array ('style:shrink-to-fit', 'table-cell', true),
// Fields for background-image
// (see '<define name="style-background-image"> in relax-ng schema)'
'repeat' => array ('style:repeat', 'table-cell-background-image', true),
'position' => array ('style:position', 'table-cell-background-image', true),
'style:filter-name' => array ('style:filter-name', 'table-cell-background-image', true),
'opacity' => array ('draw:opacity', 'table-cell-background-image', true),
'type' => array ('xlink:type', 'table-cell-background-image', true),
'href' => array ('xlink:href', 'table-cell-background-image', true),
'show' => array ('xlink:show', 'table-cell-background-image', true),
'actuate' => array ('xlink:actuate', 'table-cell-background-image', true),
'binary-data' => array ('office:binary-data', 'table-cell-background-image', true),
'base64Binary' => array ('base64Binary', 'table-cell-background-image', true),
);
/**
* Constructor.
*/
public function __construct() {
parent::__construct();
}
/**
* Set style properties by importing values from a properties array.
* Properties might be disabled by setting them in $disabled.
* The style must have been previously created.
*
* @param $properties Properties to be imported
* @param $disabled Properties to be ignored
*/
public function importProperties($properties, $disabled=array()) {
$this->importPropertiesInternal(ODTStyleStyle::getStyleProperties (), $properties, $disabled);
$this->importPropertiesInternal(ODTTextStyle::getTextProperties (), $properties, $disabled);
$this->importPropertiesInternal(ODTParagraphStyle::getParagraphProperties (), $properties, $disabled);
$this->importPropertiesInternal(self::$table_cell_fields, $properties, $disabled);
$this->setProperty('style-family', $this->getFamily());
}
/**
* Check if a style is a common style.
*
* @return bool Is common style
*/
public function mustBeCommonStyle() {
return false;
}
/**
* Get the style family of a style.
*
* @return string Style family
*/
static public function getFamily() {
return 'table-cell';
}
/**
* Set a property.
*
* @param $property The name of the property to set
* @param $value New value to set
*/
public function setProperty($property, $value) {
$style_fields = ODTStyleStyle::getStyleProperties ();
if (array_key_exists ($property, $style_fields)) {
$this->setPropertyInternal
($property, $style_fields [$property][0], $value, $style_fields [$property][1]);
return;
}
if (array_key_exists ($property, self::$table_cell_fields)) {
$this->setPropertyInternal
($property, self::$table_cell_fields [$property][0], $value, self::$table_cell_fields [$property][1]);
return;
}
$paragraph_fields = ODTParagraphStyle::getParagraphProperties ();
if (array_key_exists ($property, $paragraph_fields)) {
$this->setPropertyInternal
($property, $paragraph_fields [$property][0], $value, $paragraph_fields [$property][1]);
return;
}
$text_fields = ODTTextStyle::getTextProperties ();
if (array_key_exists ($property, $text_fields)) {
$this->setPropertyInternal
($property, $text_fields [$property][0], $value, $text_fields [$property][1]);
return;
}
}
/**
* Create new style by importing ODT style definition.
*
* @param $xmlCode Style definition in ODT XML format
* @return ODTStyle New specific style
*/
static public function importODTStyle($xmlCode) {
$style = new ODTTableCellStyle();
$attrs = 0;
$open = XMLUtil::getElementOpenTag('style:style', $xmlCode);
if (!empty($open)) {
$attrs += $style->importODTStyleInternal(ODTStyleStyle::getStyleProperties (), $open);
} else {
$open = XMLUtil::getElementOpenTag('style:default-style', $xmlCode);
if (!empty($open)) {
$style->setDefault(true);
$attrs += $style->importODTStyleInternal(ODTStyleStyle::getStyleProperties (), $open);
}
}
$open = XMLUtil::getElementOpenTag('style:paragraph-properties', $xmlCode);
if (!empty($open)) {
$attrs += $style->importODTStyleInternal(ODTParagraphStyle::getParagraphProperties (), $xmlCode);
}
$open = XMLUtil::getElementOpenTag('style:text-properties', $xmlCode);
if (!empty($open)) {
$attrs += $style->importODTStyleInternal(ODTTextStyle::getTextProperties (), $open);
}
$open = XMLUtil::getElementOpenTag('style:table-cell-properties', $xmlCode);
if (!empty($open)) {
$attrs += $style->importODTStyleInternal(self::$table_cell_fields, $open);
}
// If style has no meaningfull content then throw it away
if ( $attrs == 0 ) {
return NULL;
}
return $style;
}
static public function getTableCellProperties () {
return self::$table_cell_fields;
}
/**
* This function creates a table cell style using the style as set in the assoziative array $properties.
* The parameters in the array should be named as the CSS property names e.g. 'color' or 'background-color'.
* Properties which shall not be used in the style can be disabled by setting the value in disabled_props
* to 1 e.g. $disabled_props ['color'] = 1 would block the usage of the color property.
*
* The currently supported properties are:
* background-color, vertical-align
*
* The function returns the name of the new style or NULL if all relevant properties are empty.
*
* @author LarsDW223
* @param $properties
* @param null $disabled_props
* @return ODTTableCellStyle or NULL
*/
public static function createTableCellStyle(array $properties, array $disabled_props = NULL){
// Create style name (if not given).
$style_name = $properties ['style-name'];
if ( empty($style_name) ) {
$style_name = self::getNewStylename ('TableCell');
$properties ['style-name'] = $style_name;
}
// Create empty table cell style.
$object = new ODTTableCellStyle();
if ($object == NULL) {
return NULL;
}
// Import our properties
$object->importProperties($properties, $disabled_props);
return $object;
}
}

View File

@@ -0,0 +1,179 @@
<?php
/**
* ODTTableColumnStyle: class for ODT table column styles.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
require_once DOKU_INC.'lib/plugins/odt/ODT/XMLUtil.php';
require_once 'ODTStyle.php';
ODTStyleStyle::register('ODTTableColumnStyle');
/**
* The ODTTableColumnStyle class
*/
class ODTTableColumnStyle extends ODTStyleStyle
{
static $table_column_fields = array(
'column-width' => array ('style:column-width', 'table-column', true),
'rel-column-width' => array ('style:rel-column-width', 'table-column', true),
'use-optimal-column-width' => array ('style:use-optimal-column-width', 'table-column', true),
'break-before' => array ('fo:break-before', 'table-column', true),
'break-after' => array ('fo:break-after', 'table-column', true),
);
/**
* Constructor.
*/
public function __construct() {
parent::__construct();
}
/**
* Set style properties by importing values from a properties array.
* Properties might be disabled by setting them in $disabled.
* The style must have been previously created.
*
* @param $properties Properties to be imported
* @param $disabled Properties to be ignored
*/
public function importProperties($properties, $disabled=array()) {
$this->importPropertiesInternal(ODTStyleStyle::getStyleProperties (), $properties, $disabled);
$this->importPropertiesInternal(self::$table_column_fields, $properties, $disabled);
$this->setProperty('style-family', $this->getFamily());
}
/**
* Check if a style is a common style.
*
* @return bool Is common style
*/
public function mustBeCommonStyle() {
return false;
}
/**
* Get the style family of a style.
*
* @return string Style family
*/
static public function getFamily() {
return 'table-column';
}
/**
* Set a property.
*
* @param $property The name of the property to set
* @param $value New value to set
*/
public function setProperty($property, $value) {
$style_fields = ODTStyleStyle::getStyleProperties ();
if (array_key_exists ($property, $style_fields)) {
$this->setPropertyInternal
($property, $style_fields [$property][0], $value, $style_fields [$property][1]);
return;
}
if (array_key_exists ($property, self::$table_column_fields)) {
$this->setPropertyInternal
($property, self::$table_column_fields [$property][0], $value, self::$table_column_fields [$property][1]);
return;
}
}
/**
* Create new style by importing ODT style definition.
*
* @param $xmlCode Style definition in ODT XML format
* @return ODTStyle New specific style
*/
static public function importODTStyle($xmlCode) {
$style = new ODTTableColumnStyle();
$attrs = 0;
$open = XMLUtil::getElementOpenTag('style:style', $xmlCode);
if (!empty($open)) {
$attrs += $style->importODTStyleInternal(ODTStyleStyle::getStyleProperties (), $open);
} else {
$open = XMLUtil::getElementOpenTag('style:default-style', $xmlCode);
if (!empty($open)) {
$style->setDefault(true);
$attrs += $style->importODTStyleInternal(ODTStyleStyle::getStyleProperties (), $open);
}
}
$open = XMLUtil::getElementOpenTag('style:table-column-properties', $xmlCode);
if (!empty($open)) {
$attrs += $style->importODTStyleInternal(self::$table_column_fields, $open);
}
// If style has no meaningfull content then throw it away
if ( $attrs == 0 ) {
return NULL;
}
return $style;
}
static public function getTableColumnProperties () {
return self::$table_column_fields;
}
/**
* This function creates a table column style using the style as set in the assoziative array $properties.
* The parameters in the array should be named as the CSS property names e.g. 'color' or 'background-color'.
* Properties which shall not be used in the style can be disabled by setting the value in disabled_props
* to 1 e.g. $disabled_props ['color'] = 1 would block the usage of the color property.
*
* The currently supported properties are:
* width
*
* The function returns the name of the new style or NULL if all relevant properties are empty.
*
* @author LarsDW223
*
* @param $style
* @param $properties
* @param null $disabled_props
* @param null $style_name
* @return ODTUnknownStyle or NULL
*/
public static function createTableColumnStyle(array $properties, array $disabled_props = NULL){
// Create style name (if not given).
$style_name = $properties ['style-name'];
if ( empty($style_name) ) {
$style_name = self::getNewStylename ('TableColumn');
$properties ['style-name'] = $style_name;
}
// Convert width to ODT format
$table_co_width = $properties ['width'];
if ( !empty ($table_co_width) ) {
$length = strlen ($table_co_width);
if ( $table_co_width [$length-1] != '%' ) {
$properties ['column-width'] = $table_co_width;
} else {
// Columns have a specific syntax for relative width in %!
// Change % to *.
// Also mutiply with 10:
// For some reason values for two columns like 10* and 90* do not give the result 10% and 90%.
// But 100* and 900* do give the wanted reuslt! (weird)
$table_co_width = trim ($table_co_width, '%');
$properties ['rel-column-width'] = ($table_co_width*10).'*';
}
}
// Create empty table column style.
$object = new ODTTableColumnStyle();
if ($object == NULL) {
return NULL;
}
// Import our properties
$object->importProperties($properties, $disabled_props);
return $object;
}
}

View File

@@ -0,0 +1,182 @@
<?php
/**
* ODTTableRowStyle: class for ODT table row styles.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
* @package ODT\Styles\ODTTableRowStyle
*/
/** Include XMLUtil and ODTStyle */
require_once DOKU_INC.'lib/plugins/odt/ODT/XMLUtil.php';
require_once 'ODTStyle.php';
ODTStyleStyle::register('ODTTableRowStyle');
/**
* The ODTTableRowStyle class.
*/
class ODTTableRowStyle extends ODTStyleStyle
{
/** var array List of properties belonging to an ODT table. */
static $table_row_fields = array(
'row-height' => array ('style:row-height', 'table-row', true),
'min-row-height' => array ('style:min-row-height', 'table-row', true),
'use-optimal-row-height' => array ('style:use-optimal-row-height', 'table-row', true),
'background-color' => array ('fo:background-color', 'table-row', true),
'background-color' => array ('fo:background-color', 'table-row', true),
'break-before' => array ('fo:break-before', 'table-row', true),
'break-after' => array ('fo:break-after', 'table-row', true),
'keep-together' => array ('fo:keep-together', 'table-row', true),
// Fields for background-image
// (see '<define name="style-background-image"> in relax-ng schema)'
'repeat' => array ('style:repeat', 'table-row-background-image', true),
'position' => array ('style:position', 'table-row-background-image', true),
'style:filter-name' => array ('style:filter-name', 'table-row-background-image', true),
'opacity' => array ('draw:opacity', 'table-row-background-image', true),
'type' => array ('xlink:type', 'table-row-background-image', true),
'href' => array ('xlink:href', 'table-row-background-image', true),
'show' => array ('xlink:show', 'table-row-background-image', true),
'actuate' => array ('xlink:actuate', 'table-row-background-image', true),
'binary-data' => array ('office:binary-data', 'table-row-background-image', true),
'base64Binary' => array ('base64Binary', 'table-row-background-image', true),
);
/**
* Constructor.
*/
public function __construct() {
parent::__construct();
}
/**
* Set style properties by importing values from a properties array.
* Properties might be disabled by setting them in $disabled.
* The style must have been previously created.
*
* @param $properties Properties to be imported
* @param $disabled Properties to be ignored
*/
public function importProperties($properties, $disabled=array()) {
$this->importPropertiesInternal(ODTStyleStyle::getStyleProperties (), $properties, $disabled);
$this->importPropertiesInternal(self::$table_row_fields, $properties, $disabled);
$this->setProperty('style-family', $this->getFamily());
}
/**
* Check if a style is a common style.
*
* @return bool Is common style
*/
public function mustBeCommonStyle() {
return false;
}
/**
* Get the style family of a style.
*
* @return string Style family
*/
static public function getFamily() {
return 'table-row';
}
/**
* Set a property.
*
* @param $property The name of the property to set
* @param $value New value to set
*/
public function setProperty($property, $value) {
$style_fields = ODTStyleStyle::getStyleProperties ();
if (array_key_exists ($property, $style_fields)) {
$this->setPropertyInternal
($property, $style_fields [$property][0], $value, $style_fields [$property][1]);
return;
}
if (array_key_exists ($property, self::$table_row_fields)) {
$this->setPropertyInternal
($property, self::$table_row_fields [$property][0], $value, self::$table_row_fields [$property][1]);
return;
}
}
/**
* Create new style by importing ODT style definition.
*
* @param $xmlCode Style definition in ODT XML format
* @return ODTStyle New specific style
*/
static public function importODTStyle($xmlCode) {
$style = new ODTTableRowStyle();
$attrs = 0;
$open = XMLUtil::getElementOpenTag('style:style', $xmlCode);
if (!empty($open)) {
$attrs += $style->importODTStyleInternal(ODTStyleStyle::getStyleProperties (), $open);
} else {
$open = XMLUtil::getElementOpenTag('style:default-style', $xmlCode);
if (!empty($open)) {
$style->setDefault(true);
$attrs += $style->importODTStyleInternal(ODTStyleStyle::getStyleProperties (), $open);
}
}
$open = XMLUtil::getElementOpenTag('style:table-row-properties', $xmlCode);
if (!empty($open)) {
$attrs += $style->importODTStyleInternal(self::$table_row_fields, $open);
}
// If style has no meaningfull content then throw it away
if ( $attrs == 0 ) {
return NULL;
}
return $style;
}
/**
* Return an array listing the properties belonging to an ODT table row.
*
* @return array Properties
*/
static public function getTableRowProperties () {
return self::$table_row_fields;
}
/**
* This function creates a table row style using the style as set in the assoziative array $properties.
* The parameters in the array should be named as the CSS property names e.g. 'color' or 'background-color'.
* Properties which shall not be used in the style can be disabled by setting the value in disabled_props
* to 1 e.g. $disabled_props ['color'] = 1 would block the usage of the color property.
*
* The currently supported properties are:
* height, background-color
*
* The function returns the name of the new style or NULL if all relevant properties are empty.
*
* @author LarsDW223
* @param array $properties
* @param array|null $disabled_props
* @return ODTTableRowStyle
*/
public static function createTableRowStyle(array $properties, array $disabled_props = NULL){
// Create style name (if not given).
$style_name = $properties ['style-name'];
if ( empty($style_name) ) {
$style_name = self::getNewStylename ('TableRow');
$properties ['style-name'] = $style_name;
}
// Create empty table row style.
$object = new ODTTableRowStyle();
if ($object == NULL) {
return NULL;
}
// Import our properties
$object->importProperties($properties, $disabled_props);
return $object;
}
}

View File

@@ -0,0 +1,226 @@
<?php
/**
* ODTTableStyle: class for ODT table styles.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
* @package ODT\Styles\ODTTableStyle
*/
/** Include XMLUtil and ODTStyle */
require_once DOKU_INC.'lib/plugins/odt/ODT/XMLUtil.php';
require_once 'ODTStyle.php';
ODTStyleStyle::register('ODTTableStyle');
/**
* The ODTTableStyle class.
*/
class ODTTableStyle extends ODTStyleStyle
{
/** var array List of properties belonging to an ODT table. */
static $table_fields = array(
'width' => array ('style:width', 'table', true),
'rel-width' => array ('style:rel-width', 'table', true),
'align' => array ('table:align', 'table', true),
'margin-left' => array ('fo:margin-left', 'table', true),
'margin-right' => array ('fo:margin-right', 'table', true),
'margin-top' => array ('fo:margin-top', 'table', true),
'margin-bottom' => array ('fo:margin-bottom', 'table', true),
'margin' => array ('fo:margin', 'table', true),
'page-number' => array ('style:page-number', 'table', true),
'break-before' => array ('fo:break-before', 'table', true),
'break-after' => array ('fo:break-after', 'table', true),
'background-color' => array ('fo:background-color', 'table', true),
'shadow' => array ('style:shadow', 'table', true),
'keep-with-next' => array ('fo:keep-with-next', 'table', true),
'may-break-between-rows' => array ('style:may-break-between-rows', 'table', true),
'border-model' => array ('table:border-model', 'table', true),
'writing-mode' => array ('style:writing-mode', 'table', true),
'display' => array ('table:display', 'table', true),
// Fields for background-image
// (see '<define name="style-background-image"> in relax-ng schema)'
'repeat' => array ('style:repeat', 'table-background-image', true),
'position' => array ('style:position', 'table-background-image', true),
'style:filter-name' => array ('style:filter-name', 'table-background-image', true),
'opacity' => array ('draw:opacity', 'table-background-image', true),
'type' => array ('xlink:type', 'table-background-image', true),
'href' => array ('xlink:href', 'table-background-image', true),
'show' => array ('xlink:show', 'table-background-image', true),
'actuate' => array ('xlink:actuate', 'table-background-image', true),
'binary-data' => array ('office:binary-data', 'table-background-image', true),
'base64Binary' => array ('base64Binary', 'table-background-image', true),
);
/**
* Constructor.
*/
public function __construct() {
parent::__construct();
}
/**
* Set style properties by importing values from a properties array.
* Properties might be disabled by setting them in $disabled.
* The style must have been previously created.
*
* @param $properties Properties to be imported
* @param $disabled Properties to be ignored
*/
public function importProperties($properties, $disabled=array()) {
$this->importPropertiesInternal(ODTStyleStyle::getStyleProperties (), $properties, $disabled);
$this->importPropertiesInternal(self::$table_fields, $properties, $disabled);
$this->setProperty('style-family', $this->getFamily());
}
/**
* Check if a style is a common style.
*
* @return bool Is common style
*/
public function mustBeCommonStyle() {
return false;
}
/**
* Get the style family of a style.
*
* @return string Style family
*/
static public function getFamily() {
return 'table';
}
/**
* Set a property.
*
* @param $property The name of the property to set
* @param $value New value to set
*/
public function setProperty($property, $value) {
$style_fields = ODTStyleStyle::getStyleProperties ();
if (array_key_exists ($property, $style_fields)) {
$this->setPropertyInternal
($property, $style_fields [$property][0], $value, $style_fields [$property][1]);
return;
}
if (array_key_exists ($property, self::$table_fields)) {
$this->setPropertyInternal
($property, self::$table_fields [$property][0], $value, self::$table_fields [$property][1]);
return;
}
}
/**
* Create new style by importing ODT style definition.
*
* @param $xmlCode Style definition in ODT XML format
* @return ODTStyle New specific style
*/
static public function importODTStyle($xmlCode) {
$style = new ODTTableStyle();
$attrs = 0;
$open = XMLUtil::getElementOpenTag('style:style', $xmlCode);
if (!empty($open)) {
$attrs += $style->importODTStyleInternal(ODTStyleStyle::getStyleProperties (), $open);
} else {
$open = XMLUtil::getElementOpenTag('style:default-style', $xmlCode);
if (!empty($open)) {
$style->setDefault(true);
$attrs += $style->importODTStyleInternal(ODTStyleStyle::getStyleProperties (), $open);
}
}
$open = XMLUtil::getElementOpenTag('style:table-properties', $xmlCode);
if (!empty($open)) {
$attrs += $style->importODTStyleInternal(self::$table_fields, $open);
}
// If style has no meaningfull content then throw it away
if ( $attrs == 0 ) {
return NULL;
}
return $style;
}
/**
* Return an array listing the properties belonging to an ODT table.
*
* @return array Properties
*/
static public function getTableProperties () {
return self::$table_fields;
}
/**
* This function creates a table table style using the style as set in the assoziative array $properties.
* The parameters in the array should be named as the CSS property names e.g. 'color' or 'background-color'.
* Properties which shall not be used in the style can be disabled by setting the value in disabled_props
* to 1 e.g. $disabled_props ['color'] = 1 would block the usage of the color property.
*
* The currently supported properties are:
* width, border-collapse, background-color
*
* The function returns the name of the new style or NULL if all relevant properties are empty.
*
* @author LarsDW223
* @param array $properties Properties for the table style
* @param array|null $disabled_props Ignored properties.
* @param int $max_width_cm Max. allowed table width.
* @return ODTTableStyle|NULL
*/
public static function createTableTableStyle(array $properties, array $disabled_props = NULL, $max_width_cm = 17){
// If we want to change the table width we must set table:align to something else
// than "margins". Otherwise the width will not be changed.
if (empty($properties ['align'])) {
$properties ['align'] = 'center';
}
if ($properties ['margin-left'] == '0') {
unset($properties ['margin-left']);
}
if ($properties ['margin-right'] == '0') {
unset($properties ['margin-right']);
}
// If no width specified always set 100%
if (empty ($properties ['width'])) {
$properties ['width'] = '100%';
}
// If relative width set, then move value to property 'rel-width'!
if ( $properties ['width'] [strlen($properties ['width'])-1] == '%' ) {
$properties ['rel-width'] = $properties ['width'];
unset($properties ['width']);
}
// Convert property 'border-model' to ODT
if ( !empty ($properties ['border-model']) ) {
if ( $properties ['border-model'] == 'collapse' ) {
$properties ['border-model'] = 'collapsing';
} else {
$properties ['border-model'] = 'separating';
}
}
// Create style name (if not given).
$style_name = $properties ['style-name'];
if ( empty($style_name) ) {
$style_name = self::getNewStylename ('Table');
$properties ['style-name'] = $style_name;
}
// Create empty table style.
$object = new ODTTableStyle();
if ($object == NULL) {
return NULL;
}
// Import our properties
$object->importProperties($properties, $disabled_props);
return $object;
}
}

View File

@@ -0,0 +1,376 @@
<?php
/**
* ODTTextListStyle: class for ODT text list styles.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
require_once DOKU_INC.'lib/plugins/odt/ODT/XMLUtil.php';
require_once DOKU_INC.'lib/plugins/odt/ODT/styles/ODTStyle.php';
require_once DOKU_INC.'lib/plugins/odt/ODT/styles/ODTTextStyle.php';
/**
* The ODTTextListStyle class
*/
class ODTTextListStyle extends ODTStyle
{
static $list_fields = array(
// Fields belonging to "text:list-style"
'style-name' => array ('style:name', 'style', false),
'style-display-name' => array ('style:display-name', 'style', false),
'consecutive-numbering' => array ('text:consecutive-numbering', 'style', true),
);
static $style_number_fields = array(
// Fields belonging to "text:list-level-style-number"
'level' => array ('text:level', 'style-attr', true),
'text-style-name' => array ('text:style-name', 'style-attr', true),
'num-format' => array ('style:num-format', 'style-attr', true),
'num-letter-sync' => array ('style:num-letter-sync', 'style-attr', true),
'num-prefix' => array ('style:num-prefix', 'style-attr', true),
'num-suffix' => array ('style:num-suffix', 'style-attr', true),
'display-levels' => array ('text:display-levels', 'style-attr', true),
'start-value' => array ('text:start-value', 'style-attr', true),
);
static $style_bullet_fields = array(
// Fields belonging to "text:list-level-style-bullet"
'level' => array ('text:level', 'style-attr', true),
'text-style-name' => array ('text:style-name', 'style-attr', true),
'text-bullet-char' => array ('text:bullet-char', 'style-attr', true),
'num-prefix' => array ('style:num-prefix', 'style-attr', true),
'num-suffix' => array ('style:num-suffix', 'style-attr', true),
'text-bullet-relative-size' => array ('text:bullet-relative-size', 'style-attr', true),
);
static $style_image_fields = array(
// Fields belonging to "text:list-level-style-image"
'level' => array ('text:level', 'style-attr', true),
'type' => array ('xlink:type', 'style-attr', true),
'href' => array ('xlink:href', 'style-attr', true),
'show' => array ('xlink:show', 'style-attr', true),
'actuate' => array ('xlink:actuate', 'style-attr', true),
'binary-data' => array ('office:binary-data', 'style-attr', true),
'base64Binary' => array ('base64Binary', 'style-attr', true),
);
static $list_level_props_fields = array(
// Fields belonging to "style-list-level-properties"
'text-align' => array ('fo:text-align', 'level-list', true),
'text-space-before' => array ('text:space-before', 'level-list', true),
'text-min-label-width' => array ('text:min-label-width', 'level-list', true),
'text-min-label-distance' => array ('text:min-label-distance', 'level-list', true),
'font-name' => array ('style:font-name', 'level-list', true),
'width' => array ('fo:width', 'level-list', true),
'height' => array ('fo:height', 'level-list', true),
'vertical-rel' => array ('style:vertical-rel', 'level-list', true),
'vertical-pos' => array ('style:vertical-pos', 'level-list', true),
'list-level-position-and-space-mode' => array ('text:list-level-position-and-space-mode', 'level-list', true),
);
static $label_align_fields = array(
// Fields belonging to "style:list-level-label-alignment"
'label-followed-by' => array ('text:label-followed-by', 'level-label', true),
'list-tab-stop-position' => array ('text:list-tab-stop-position', 'level-label', true),
'text-indent' => array ('fo:text-indent', 'level-label', true),
'margin-left' => array ('fo:margin-left', 'level-label', true),
);
protected $list_level_styles = array();
/**
* Get the element name for the ODT XML encoding of the style.
*/
public function getElementName() {
return 'text:list-style';
}
/**
* Set style properties by importing values from a properties array.
* Properties might be disabled by setting them in $disabled.
* The style must have been previously created.
*
* @param $properties Properties to be imported
* @param $disabled Properties to be ignored
*/
public function importProperties($properties, $disabled=array()) {
$this->importPropertiesInternal(ODTTextStyle::getTextProperties (), $properties, $disabled);
$this->importPropertiesInternal(self::$list_fields, $properties, $disabled);
}
/**
* Check if a style is a common style.
*
* @return bool Is common style
*/
public function mustBeCommonStyle() {
return false;
}
/**
* Set a property.
* For a TextListStyle we can only set the style main properties here.
* All properties specific for a level need to be set using setPropertyForLevel().
*
* @param $property The name of the property to set
* @param $value New value to set
*/
public function setProperty($property, $value) {
if (array_key_exists ($property, self::$list_fields)) {
$this->setPropertyInternal
($property, self::$list_fields [$property][0], $value, self::$list_fields [$property][1]);
return;
}
}
/**
* Create new style by importing ODT style definition.
*
* @param $xmlCode Style definition in ODT XML format
* @return ODTStyle New specific style
*/
static public function importODTStyle($xmlCode) {
$style = new ODTTextListStyle();
$attrs = 0;
$open = XMLUtil::getElementOpenTag('text:list-style', $xmlCode);
if (!empty($open)) {
// This properties are stored in the properties of ODTStyle
$attrs += $style->importODTStyleInternal(self::$list_fields, $open);
$content = XMLUtil::getElementContent('text:list-style', $xmlCode);
}
$pos = 0;
$end = 0;
$max = strlen ($content);
$text_fields = ODTTextStyle::getTextProperties ();
while ($pos < $max)
{
// Get XML code for next level.
$level = XMLUtil::getNextElement($element, substr($content, $pos), $end);
$level_content = XMLUtil::getNextElementContent($element, $level, $ignore);
if (!empty($level)) {
$list_style_properties = array();
$list_level_properties = array();
$label_properties = array();
$text_properties = array();
$properties = array();
switch ($element) {
case 'text:list-level-style-number':
$attrs += $style->importODTStyleInternal(self::$style_number_fields, $level, $list_style_properties);
$list_level_style = 'number';
break;
case 'text:list-level-style-bullet':
$attrs += $style->importODTStyleInternal(self::$style_bullet_fields, $level, $list_style_properties);
$list_level_style = 'bullet';
break;
case 'text:list-level-style-image':
$attrs += $style->importODTStyleInternal(self::$style_image_fields, $level, $list_style_properties);
$list_level_style = 'image';
break;
}
$temp_content = XMLUtil::getElement('style:text-properties', $level_content);
$attrs += $style->importODTStyleInternal($text_fields, $temp_content, $text_properties);
$temp_content = XMLUtil::getElementOpenTag('style:list-level-properties', $level_content);
$attrs += $style->importODTStyleInternal(self::$list_level_props_fields, $temp_content, $list_level_properties);
$temp_content = XMLUtil::getElement('style:list-level-label-alignment', $level_content);
$attrs += $style->importODTStyleInternal(self::$label_align_fields, $temp_content, $label_properties);
// Assign properties array to our level array
$level_number = $style->getPropertyInternal('level', $list_style_properties);
$properties ['list-style'] = $list_style_properties;
$properties ['list-level'] = $list_level_properties;
$properties ['label'] = $label_properties;
$properties ['text'] = $text_properties;
$style->list_level_styles [$level_number] = $properties;
// Set special property 'list-level-style' to remember element to encode
// on call to toString()!
$style->setPropertyForLevel($level_number, 'list-level-style', $list_level_style);
}
$pos += $end;
}
// If style has no meaningfull content then throw it away
if ( $attrs == 0 ) {
return NULL;
}
return $style;
}
/**
* Encode current style values in a string and return it.
*
* @return string ODT XML encoded style
*/
public function toString() {
$style = '';
$levels = '';
// The style properties are stored in the properties of ODTStyle
foreach ($this->properties as $property => $items) {
$style .= $items ['odt_property'].'="'.$items ['value'].'" ';
}
// The level properties are stored in our level properties
$level_number = 0;
foreach ($this->list_level_styles as $key => $properties) {
$level_number++;
$element = $this->getPropertyFromLevel($level_number, 'list-level-style');
switch ($element) {
case 'number':
$fields = self::$style_number_fields;
break;
case 'bullet':
$fields = self::$style_bullet_fields;
break;
case 'image':
$fields = self::$style_image_fields;
break;
}
$element = 'text:list-level-style-'.$element;
$style_attr = '';
foreach ($this->list_level_styles [$level_number]['list-style'] as $property => $items) {
// Only add fields/properties which are allowed for the specific list-level-style
if ($property != 'list-level-style' && array_key_exists ($property, $fields)) {
$style_attr .= $items ['odt_property'].'="'.$items ['value'].'" ';
}
}
$level_list = '';
foreach ($this->list_level_styles [$level_number]['list-level'] as $property => $items) {
$level_list .= $items ['odt_property'].'="'.$items ['value'].'" ';
}
$level_label = '';
foreach ($this->list_level_styles [$level_number]['label'] as $property => $items) {
$level_label .= $items ['odt_property'].'="'.$items ['value'].'" ';
}
$text = '';
foreach ($this->list_level_styles [$level_number]['text'] as $property => $items) {
$text .= $items ['odt_property'].'="'.$items ['value'].'" ';
}
$levels .= ' <'.$element.' '.$style_attr.">\n";
if (!empty($level_list)) {
if (empty($level_label)) {
$levels .= ' <style:list-level-properties '.$level_list."/>\n";
} else {
$levels .= ' <style:list-level-properties '.$level_list.">\n";
$levels .= ' <style:list-level-label-alignment '.$level_label."/>\n";
$levels .= " </style:list-level-properties>\n";
}
}
if (!empty($text)) {
$levels .= ' <style:text-properties '.$text.'/>'."\n";
}
$levels .= " </".$element.">\n";
}
// Build style.
$element = $this->getElementName();
$style = '<'.$element.' '.$style.">\n";
if ( !empty($levels) ) {
$style .= $levels;
}
$style .= '</'.$element.">\n";
return $style;
}
/**
* Get the value of a property for text outline level $level.
*
* @param $level The text outline level (usually 1 to 10)
* @param $property The property name
* @return string The current value of the property
*/
public function getPropertyFromLevel($level, $property) {
if ($property == 'list-level-style') {
// Property 'list-level-style' is a special property just to remember
// which element needs to be encoded on a call to toString().
// It may not be included in the output of toString()!!!
return $this->getPropertyInternal('list-level-style', $this->list_level_styles [$level]['list-style']);
}
$text_fields = ODTTextStyle::getTextProperties ();
if (array_key_exists ($property, $text_fields)) {
return $this->getPropertyInternal($property, $this->list_level_styles [$level]['text']);
}
$element = $this->getPropertyInternal('list-level-style', $this->list_level_styles [$level]['list-style']);
switch ($element) {
case 'number':
$fields = self::$style_number_fields;
break;
case 'bullet':
$fields = self::$style_bullet_fields;
break;
case 'image':
$fields = self::$style_image_fields;
break;
}
if (array_key_exists ($property, $fields)) {
return $this->getPropertyInternal($property, $this->list_level_styles [$level]['list-style']);
}
if (array_key_exists ($property, self::$list_level_props_fields)) {
return $this->getPropertyInternal($property, $this->list_level_styles [$level]['list-level']);
}
if (array_key_exists ($property, self::$label_align_fields)) {
return $this->getPropertyInternal($property, $this->list_level_styles [$level]['label']);
}
}
/**
* Set a property for a specific level.
*
* @param $level The level for which to set the property (1 to 10)
* @param $property The name of the property to set
* @param $value New value to set
*/
public function setPropertyForLevel($level, $property, $value) {
if ($property == 'list-level-style') {
// Property 'list-level-style' is a special property just to remember
// which element needs to be encoded on a call to toString().
// It may not be included in the output of toString()!!!
$this->setPropertyInternal
($property, 'list-level-style', $value, 'list-level-style', $this->list_level_styles [$level]['list-style']);
} else {
// First check fields/properties common to each list-level-style
$text_fields = ODTTextStyle::getTextProperties ();
if (array_key_exists ($property, $text_fields)) {
$this->setPropertyInternal
($property, $text_fields [$property][0], $value, $text_fields [$property][1], $this->list_level_styles [$level]['text']);
return;
}
if (array_key_exists ($property, self::$list_level_props_fields)) {
$this->setPropertyInternal
($property, self::$list_level_props_fields [$property][0], $value, self::$list_level_props_fields [$property][1], $this->list_level_styles [$level]['list-level']);
return;
}
if (array_key_exists ($property, self::$label_align_fields)) {
$this->setPropertyInternal
($property, self::$label_align_fields [$property][0], $value, self::$label_align_fields [$property][1], $this->list_level_styles [$level]['label']);
return;
}
// Now check fields specific to the list-level-style.
$element = $this->getPropertyFromLevel ($level, 'list-level-style');
switch ($element) {
case 'number':
$fields = self::$style_number_fields;
break;
case 'bullet':
$fields = self::$style_bullet_fields;
break;
case 'image':
$fields = self::$style_image_fields;
break;
}
if (array_key_exists ($property, $fields)) {
$this->setPropertyInternal
($property, $fields [$property][0], $value, $fields [$property][1], $this->list_level_styles [$level]['list-style']);
}
}
}
}

View File

@@ -0,0 +1,242 @@
<?php
/**
* ODTTextOutlineStyle: class for ODT text outline styles.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
require_once DOKU_INC.'lib/plugins/odt/ODT/XMLUtil.php';
require_once DOKU_INC.'lib/plugins/odt/ODT/styles/ODTStyle.php';
require_once DOKU_INC.'lib/plugins/odt/ODT/styles/ODTTextStyle.php';
/**
* The ODTTextOutlineStyle class
*/
class ODTTextOutlineStyle extends ODTStyle
{
static $outline_fields = array(
// Fields belonging to "text:outline-style"
'style-name' => array ('style:name', 'style', false),
// Fields belonging to "text:outline-level-style"
'level' => array ('text:level', 'level', true),
'text-style-name' => array ('text:style-name', 'level', true),
'num-format' => array ('style:num-format', 'level', true),
'num-letter-sync' => array ('style:num-letter-sync', 'level', true),
'num-prefix' => array ('style:num-prefix', 'level', true),
'num-suffix' => array ('style:num-suffix', 'level', true),
'display-levels' => array ('text:display-levels', 'level', true),
'start-value' => array ('text:start-value', 'level', true),
// Fields belonging to "style-list-level-properties"
'text-align' => array ('fo:text-align', 'level-list', true),
'text-space-before' => array ('text:space-before', 'level-list', true),
'text-min-label-width' => array ('text:min-label-width', 'level-list', true),
'text-min-label-distance' => array ('text:min-label-distance', 'level-list', true),
'font-name' => array ('style:font-name', 'level-list', true),
'width' => array ('fo:width', 'level-list', true),
'height' => array ('fo:height', 'level-list', true),
'vertical-rel' => array ('style:vertical-rel', 'level-list', true),
'vertical-pos' => array ('style:vertical-pos', 'level-list', true),
'list-level-position-and-space-mode' => array ('text:list-level-position-and-space-mode', 'level-list', true),
// Fields belonging to "style:list-level-label-alignment"
'label-followed-by' => array ('text:label-followed-by', 'level-label', true),
'list-tab-stop-position' => array ('text:list-tab-stop-position', 'level-label', true),
'text-indent' => array ('fo:text-indent', 'level-label', true),
'margin-left' => array ('fo:margin-left', 'level-label', true),
);
protected $outline_level_styles = array();
/**
* Get the element name for the ODT XML encoding of the style.
*/
public function getElementName() {
return 'text:outline-style';
}
/**
* Set style properties by importing values from a properties array.
* Properties might be disabled by setting them in $disabled.
* The style must have been previously created.
*
* @param $properties Properties to be imported
* @param $disabled Properties to be ignored
*/
public function importProperties($properties, $disabled=array()) {
$this->importPropertiesInternal(ODTTextStyle::getTextProperties (), $properties, $disabled);
$this->importPropertiesInternal(self::$outline_fields, $properties, $disabled);
}
/**
* Check if a style is a common style.
*
* @return bool Is common style
*/
public function mustBeCommonStyle() {
return false;
}
/**
* Set a property.
*
* @param $property The name of the property to set
* @param $value New value to set
*/
public function setProperty($property, $value) {
$text_fields = ODTTextStyle::getTextProperties ();
if (array_key_exists ($property, $style_fields)) {
$this->setPropertyInternal
($property, $text_fields [$property][0], $value, $text_fields [$property][1]);
return;
}
if (array_key_exists ($property, self::$outline_fields)) {
$this->setPropertyInternal
($property, self::$outline_fields [$property][0], $value, self::$outline_fields [$property][1]);
return;
}
}
/**
* Create new style by importing ODT style definition.
*
* @param $xmlCode Style definition in ODT XML format
* @return ODTStyle New specific style
*/
static public function importODTStyle($xmlCode) {
$style = new ODTTextOutlineStyle();
$attrs = 0;
$open = XMLUtil::getElementOpenTag('text:outline-style', $xmlCode);
if (!empty($open)) {
// This properties are stored in the properties of ODTStyle
$attrs += $style->importODTStyleInternal(self::$outline_fields, $open);
}
$pos = 0;
$end = 0;
$max = strlen ($xmlCode);
$level = XMLUtil::getElement('text:outline-level-style', substr($xmlCode, $pos), $end);
$pos += $end;
$text_fields = ODTTextStyle::getTextProperties ();
$check = 0;
while ($level != NULL)
{
// We can have multiple level definitons with all the same properties.
// So we store this in our own array. The "text:level" is the array key.
if (!empty($level)) {
$properties = array();
$attrs += $style->importODTStyleInternal($text_fields, $level, $properties);
$attrs += $style->importODTStyleInternal(self::$outline_fields, $level, $properties);
// Assign properties array to our level array
$level_number = $style->getPropertyInternal('level', $properties);
$style->outline_level_styles [$level_number] = $properties;
}
// Get XML code for next level.
$level = XMLUtil::getElement('text:outline-level-style', substr($xmlCode, $pos), $end);
$pos += $end;
if ($pos >= $max) {
break;
}
}
// If style has no meaningfull content then throw it away
if ( $attrs == 0 ) {
return NULL;
}
return $style;
}
/**
* Encode current style values in a string and return it.
*
* @return string ODT XML encoded style
*/
public function toString() {
$style = '';
$levels = '';
// The style properties are stored in the properties of ODTStyle
foreach ($this->properties as $property => $items) {
$style .= $items ['odt_property'].'="'.$items ['value'].'" ';
}
// The level properties are stored in our level properties
foreach ($this->outline_level_styles as $key => $properties) {
$level = '';
$level_list = '';
$level_label = '';
$text = '';
foreach ($properties as $property => $items) {
switch ($items ['section']) {
case 'level':
$level .= $items ['odt_property'].'="'.$items ['value'].'" ';
break;
case 'level-list':
$level_list .= $items ['odt_property'].'="'.$items ['value'].'" ';
break;
case 'level-label':
$level_label .= $items ['odt_property'].'="'.$items ['value'].'" ';
break;
case 'text':
$text .= $items ['odt_property'].'="'.$items ['value'].'" ';
break;
}
}
$levels .= ' <text:outline-level-style '.$level.">\n";
if (!empty($level_list)) {
if (empty($level_label)) {
$levels .= ' <style:list-level-properties '.$level_list."/>\n";
} else {
$levels .= ' <style:list-level-properties '.$level_list.">\n";
$levels .= ' <style:list-level-label-alignment '.$level_label."/>\n";
$levels .= " </style:list-level-properties>\n";
}
}
if (!empty($text)) {
$levels .= ' <style:text-properties '.$text.'/>';
}
$levels .= " </text:outline-level-style>\n";
}
// Build style.
$element = $this->getElementName();
$style = '<'.$element.' '.$style.">\n";
if ( !empty($levels) ) {
$style .= $levels;
}
$style .= '</'.$element.">\n";
return $style;
}
/**
* Get the value of a property for text outline level $level.
*
* @param $level The text outline level (usually 1 to 10)
* @param $property The property name
* @return string The current value of the property
*/
public function getPropertyFromLevel($level, $property) {
return $this->getPropertyInternal($property, $this->outline_level_styles [$level]);
}
/**
* Set a property.
*
* @param $property The name of the property to set
* @param $value New value to set
*/
public function setPropertyForLevel($level, $property, $value) {
if (array_key_exists ($property, self::$outline_fields)) {
$this->setPropertyInternal
($property, self::$outline_fields [$property][0], $value, self::$outline_fields [$property][1], $this->outline_level_styles [$level]);
return;
}
}
}

View File

@@ -0,0 +1,337 @@
<?php
/**
* ODTTextStyle: class for ODT text styles.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
require_once DOKU_PLUGIN . 'odt/ODT/XMLUtil.php';
require_once 'ODTStyle.php';
ODTStyleStyle::register('ODTTextStyle');
/**
* The ODTTextStyle class
*/
class ODTTextStyle extends ODTStyleStyle
{
static $text_fields = array(
'padding' => array ('fo:padding', 'text', true),
'padding-top' => array ('fo:padding-top', 'text', true),
'padding-right' => array ('fo:padding-right', 'text', true),
'padding-bottom' => array ('fo:padding-bottom', 'text', true),
'padding-left' => array ('fo:padding-left', 'text', true),
'border' => array ('fo:border', 'text', true),
'border-top' => array ('fo:border-top', 'text', true),
'border-right' => array ('fo:border-right', 'text', true),
'border-bottom' => array ('fo:border-bottom', 'text', true),
'border-left' => array ('fo:border-left', 'text', true),
'color' => array ('fo:color', 'text', true),
'background-color' => array ('fo:background-color', 'text', true),
'background-image' => array ('fo:background-image', 'text', true),
'font-style' => array ('fo:font-style', 'text', true),
'font-style-asian' => array ('style:font-style-asian', 'text', true),
'font-style-complex' => array ('style:font-style-complex', 'text', true),
'font-weight' => array ('fo:font-weight', 'text', true),
'font-weight-asian' => array ('style:font-weight-asian', 'text', true),
'font-weight-complex' => array ('style:font-weight-complex', 'text', true),
'font-size' => array ('fo:font-size', 'text', true),
'font-size-asian' => array ('style:font-size-asian', 'text', true),
'font-size-complex' => array ('style:font-size-complex', 'text', true),
'font-family' => array ('fo:font-family', 'text', true),
'font-family-asian' => array ('style:font-family-asian', 'text', true),
'font-family-complex' => array ('style:font-family-complex', 'text', true),
'font-variant' => array ('fo:font-variant', 'text', true),
'letter-spacing' => array ('fo:letter-spacing', 'text', true),
'vertical-align' => array ('style:vertical-align', 'text', true),
'display' => array ('text:display', 'text', true),
'lang' => array ('fo:language', 'text', true),
'lang-asian' => array ('style:language-asian', 'text', true),
'lang-complex' => array ('style:language-complex', 'text', true),
'country' => array ('fo:country', 'text', true),
'country-asian' => array ('style:country-asian', 'text', true),
'country-complex' => array ('style:country-complex', 'text', true),
'text-transform' => array ('fo:text-transform', 'text', true),
'use-window-font-color' => array ('style:use-window-font-color', 'text', true),
'text-outline' => array ('style:text-outline', 'text', true),
'text-line-through-type' => array ('style:text-line-through-type', 'text', true),
'text-line-through-style' => array ('style:text-line-through-style', 'text', true),
'text-line-through-width' => array ('style:text-line-through-width', 'text', true),
'text-line-through-color' => array ('style:text-line-through-color', 'text', true),
'text-line-through-text' => array ('style:text-line-through-text', 'text', true),
'text-line-through-text-style' => array ('style:text-line-through-text-style', 'text', true),
'text-position' => array ('style:text-position', 'text', true),
'font-name' => array ('style:font-name', 'text', true),
'font-name-asian' => array ('style:font-name-asian', 'text', true),
'font-name-complex' => array ('style:font-name-complex', 'text', true),
'font-family-generic' => array ('style:font-family-generic', 'text', true),
'font-family-generic-asian' => array ('style:font-family-generic-asian', 'text', true),
'font-family-generic-complex' => array ('style:font-family-generic-complex', 'text', true),
'font-style-name' => array ('style:font-style-name', 'text', true),
'font-style-name-asian' => array ('style:font-style-name-asian', 'text', true),
'font-style-name-complex' => array ('style:font-style-name-complex', 'text', true),
'font-pitch' => array ('style:font-pitch', 'text', true),
'font-pitch-asian' => array ('style:font-pitch-asian', 'text', true),
'font-pitch-complex' => array ('style:font-pitch-complex', 'text', true),
'font-charset' => array ('style:font-charset', 'text', true),
'font-charset-asian' => array ('style:font-charset-asian', 'text', true),
'font-charset-complex' => array ('style:font-charset-complex', 'text', true),
'font-size-rel' => array ('style:font-size-rel', 'text', true),
'font-size-rel-asian' => array ('style:font-size-rel-asian', 'text', true),
'font-size-rel-complex' => array ('style:font-size-rel-complex', 'text', true),
'script-type' => array ('style:script-type', 'text', true),
'script' => array ('fo:script', 'text', true),
'script-asian' => array ('style:script-asian', 'text', true),
'script-complex' => array ('style:script-complex', 'text', true),
'rfc-language-tag' => array ('style:rfc-language-tag', 'text', true),
'rfc-language-tag-asian' => array ('style:rfc-language-tag-asian', 'text', true),
'rfc-language-tag-complex' => array ('style:rfc-language-tag-complex', 'text', true),
'rfc-language-tag-complex' => array ('style:rfc-language-tag-complex', 'text', true),
'font-relief' => array ('style:font-relief', 'text', true),
'text-shadow' => array ('fo:text-shadow', 'text', true),
'text-underline-type' => array ('style:text-underline-type', 'text', true),
'text-underline-style' => array ('style:text-underline-style', 'text', true),
'text-underline-width' => array ('style:text-underline-width', 'text', true),
'text-underline-color' => array ('style:text-underline-color', 'text', true),
'text-overline-type' => array ('style:text-overline-type', 'text', true),
'text-overline-style' => array ('style:text-overline-style', 'text', true),
'text-overline-width' => array ('style:text-overline-width', 'text', true),
'text-overline-color' => array ('style:text-overline-color', 'text', true),
'text-overline-mode' => array ('style:text-overline-mode', 'text', true),
'text-underline-mode' => array ('style:text-underline-mode', 'text', true),
'text-line-through-mode' => array ('style:text-line-through-mode', 'text', true),
'letter-kerning' => array ('style:letter-kerning', 'text', true),
'text-blinking' => array ('style:text-blinking', 'text', true),
'text-combine' => array ('style:text-combine', 'text', true),
'text-combine-start-char' => array ('style:text-combine-start-char', 'text', true),
'text-combine-end-char' => array ('style:text-combine-end-char', 'text', true),
'text-emphasize' => array ('style:text-emphasize', 'text', true),
'text-scale' => array ('style:text-scale', 'text', true),
'text-rotation-angle' => array ('style:text-rotation-angle', 'text', true),
'text-rotation-scale' => array ('style:text-rotation-scale', 'text', true),
'hyphenate' => array ('fo:hyphenate', 'text', true),
'hyphenation-remain-char-count' => array ('fo:hyphenation-remain-char-count', 'text', true),
'hyphenation-push-char-count' => array ('fo:hyphenation-push-char-count', 'text', true),
'condition' => array ('text:condition', 'text', true),
);
/**
* Constructor.
*/
public function __construct() {
parent::__construct();
}
/**
* Set style properties by importing values from a properties array.
* Properties might be disabled by setting them in $disabled.
* The style must have been previously created.
*
* @param $properties Properties to be imported
* @param $disabled Properties to be ignored
*/
public function importProperties($properties, $disabled=array()) {
$this->importPropertiesInternal(ODTStyleStyle::getStyleProperties (), $properties, $disabled);
$this->importPropertiesInternal(self::$text_fields, $properties, $disabled);
$this->setProperty('style-family', $this->getFamily());
}
/**
* Check if a style is a common style.
*
* @return bool Is common style
*/
public function mustBeCommonStyle() {
return false;
}
/**
* Get the style family of a style.
*
* @return string Style family
*/
static public function getFamily() {
return 'text';
}
/**
* Set a property.
*
* @param $property The name of the property to set
* @param $value New value to set
*/
public function setProperty($property, $value) {
$style_fields = ODTStyleStyle::getStyleProperties ();
if (array_key_exists ($property, $style_fields)) {
$this->setPropertyInternal
($property, $style_fields [$property][0], $value, $style_fields [$property][1]);
return;
}
if (array_key_exists ($property, self::$text_fields)) {
$this->setPropertyInternal
($property, self::$text_fields [$property][0], $value, self::$text_fields [$property][1]);
return;
}
}
/**
* Create new style by importing ODT style definition.
*
* @param $xmlCode Style definition in ODT XML format
* @return ODTStyle New specific style
*/
static public function importODTStyle($xmlCode) {
$style = new ODTTextStyle();
$attrs = 0;
$open = XMLUtil::getElementOpenTag('style:style', $xmlCode);
if (!empty($open)) {
$attrs += $style->importODTStyleInternal(ODTStyleStyle::getStyleProperties (), $open);
} else {
$open = XMLUtil::getElementOpenTag('style:default-style', $xmlCode);
if (!empty($open)) {
$style->setDefault(true);
$attrs += $style->importODTStyleInternal(ODTStyleStyle::getStyleProperties (), $open);
}
}
$open = XMLUtil::getElementOpenTag('style:text-properties', $xmlCode);
if (!empty($open)) {
$attrs += $style->importODTStyleInternal(self::$text_fields, $open);
}
// If style has no meaningfull content then throw it away
if ( $attrs == 0 ) {
return NULL;
}
return $style;
}
static public function getTextProperties () {
return self::$text_fields;
}
/**
* This function creates a text style using the style as set in the assoziative array $properties.
* The parameters in the array should be named as the CSS property names e.g. 'color' or 'background-color'.
* Properties which shall not be used in the style can be disabled by setting the value in disabled_props
* to 1 e.g. $disabled_props ['color'] = 1 would block the usage of the color property.
*
* The currently supported properties are:
* background-color, color, font-style, font-weight, font-size, border, font-family, font-variant, letter-spacing,
* vertical-align, background-image
*
* The function returns the name of the new style or NULL if all relevant properties are empty.
*
* @author LarsDW223
* @param $properties
* @param null $disabled_props
* @return ODTTextStyle or NULL
*/
public static function createTextStyle(array $properties, array $disabled_props = NULL, ODTDocument $doc=NULL){
// Convert 'text-decoration'.
if ( $properties ['text-decoration'] == 'line-through' ) {
$properties ['text-line-through-style'] = 'solid';
}
if ( $properties ['text-decoration'] == 'underline' ) {
$properties ['text-underline-style'] = 'solid';
}
if ( $properties ['text-decoration'] == 'overline' ) {
$properties ['text-overline-style'] = 'solid';
}
// If the property 'vertical-align' has the value 'sub' or 'super'
// then for ODT it needs to be converted to the corresponding 'text-position' property.
// Replace sub and super with text-position.
$valign = $properties ['vertical-align'];
if (!empty($valign)) {
if ( $valign == 'sub' ) {
$properties ['text-position'] = '-33% 100%';
unset($properties ['vertical-align']);
} elseif ( $valign == 'super' ) {
$properties ['text-position'] = '33% 100%';
unset($properties ['vertical-align']);
}
}
// Separate country from language
$lang = $properties ['lang'];
$country = $properties ['country'];
if ( !empty($lang) ) {
$parts = preg_split ('/-/', $lang);
$lang = $parts [0];
$country = $parts [1];
$properties ['country'] = trim($country);
$properties ['lang'] = trim($lang);
}
if (!empty($properties ['country'])) {
if (empty($properties ['country-asian'])) {
$properties ['country-asian'] = $properties ['country'];
}
if (empty($properties ['country-complex'])) {
$properties ['country-complex'] = $properties ['country'];
}
}
// Extra handling for font-size in '%'
$save = $disabled_props ['font-size'];
$odt_fo_size = '';
if ( empty ($disabled_props ['font-size']) ) {
$odt_fo_size = $properties ['font-size'];
}
$length = strlen ($odt_fo_size);
if ( $length > 0 && $odt_fo_size [$length-1] == '%' && $doc != NULL) {
// A font-size in percent is only supported in common style definitions, not in automatic
// styles. Create a common style and set it as parent for this automatic style.
$name = 'Size'.trim ($odt_fo_size, '%').'pc';
$style_obj = self::createSizeOnlyTextStyle ($name, $odt_fo_size);
$doc->addStyle($style_obj);
$parent = $style_obj->getProperty('style-name');
if (!empty($parent)) {
$properties ['style-parent'] = $parent;
}
}
// Create style name (if not given).
$style_name = $properties ['style-name'];
if ( empty($style_name) ) {
$style_name = self::getNewStylename ('Text');
$properties ['style-name'] = $style_name;
}
// Create empty text style.
$object = new ODTTextStyle();
if ($object == NULL) {
return NULL;
}
// Import our properties
$object->importProperties($properties, $disabled_props);
// Restore $disabled_props
$disabled_props ['font-size'] = $save;
return $object;
}
/**
* Simple helper function for creating a text style $name setting the specfied font size $size.
*
* @author LarsDW223
*
* @param string $name
* @param string $size
* @return ODTTextStyle
*/
public static function createSizeOnlyTextStyle ($name, $size) {
$properties = array();
$properties ['style-name'] = $name;
$properties ['style-display-name'] = $name;
$properties ['font-size'] = $size;
$properties ['font-size-asian'] = $size;
$properties ['font-size-complex'] = $size;
return self::createTextStyle($properties);
}
}

View File

@@ -0,0 +1,227 @@
<?php
/**
* ODTUnknownStyle: class for unknown/not implemented ODT style families.
* The goal is to at least read in not supported style faimlies and return
* the original content on a call to toString().
*
* The following has to be taken into account:
* - the properties of an ODTUnknownStyle can not be changed.
* - so setProperty() and importProperties() will do nothing.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
require_once DOKU_INC.'lib/plugins/odt/ODT/styles/ODTStyle.php';
/**
* The ODTUnknownStyle class
*/
class ODTUnknownStyle extends ODTStyle
{
// At least try to read in a name
static $unknown_fields = array(
'style-name' => array ('style:name', 'style', false),
'style-family' => array ('style:family', 'style', false),
);
protected $element_name = NULL;
protected $style_content = NULL;
/**
* Get the element name for the ODT XML encoding of the style.
*
* @return string The element name
*/
public function getElementName() {
return ($this::element_name);
}
/**
* Set the element name.
*
* @param $element_name The element name to set
*/
public function setElementName($element_name) {
$this->element_name = $element_name;
}
/**
* Set style properties by importing values from a properties array.
* Properties might be disabled by setting them in $disabled.
* The style must have been previously created.
*
* Not supported, just a dummy!
*
* @param $properties Properties to be imported
* @param $disabled Properties to be ignored
*/
public function importProperties($properties, $disabled=array()) {
}
/**
* Check if a style is a common style.
*
* @return bool Is common style
*/
public function mustBeCommonStyle() {
return false;
}
/**
* Set a property.
*
* Not supported, just a dummy.
*
* @param $property The name of the property to set
* @param $value New value to set
*/
public function setProperty($property, $value) {
}
/**
* Set style content. This will be returned on toString().
*
* @param $style_content The complete ODT XML style definition.
*/
public function setStyleContent($style_content) {
$this->importODTStyleInternal(self::$unknown_fields, $style_content);
$this->style_content = $style_content."\n";
}
/**
* Create new style by importing ODT style definition.
*
* @param $xmlCode Style definition in ODT XML format
* @return ODTStyle New specific style
*/
static public function importODTStyle($xmlCode) {
$style = new ODTUnknownStyle();
$style->setStyleContent($xmlCode);
return $style;
}
/**
* Encode current style values in a string and return it.
*
* @return string ODT XML encoded style
*/
public function toString() {
return $this->style_content;
}
/**
* Is the style a default style?
*
* @return boolean Is default.
*/
public function isDefault() {
if ($this->element_name == 'style:default-style') {
return true;
}
return false;
}
/**
* Get the style family of a style.
*
* @return string|NULL Style family
*/
public function getFamily() {
return $this->getProperty('style-family');
}
/**
* The function deletes all properties that do not belong to the styles section,
* e.g. text properties or paragraph properties.
* For unknown styles this is just a dummy doing nothing.
*/
public function clearLayoutProperties() {
}
/**
* This function creates a frame style for multiple columns, using the style as set in the assoziative array $properties.
* The parameters in the array should be named as the CSS property names e.g. 'color' or 'background-color'.
* Properties which shall not be used in the style can be disabled by setting the value in disabled_props
* to 1 e.g. $disabled_props ['color'] = 1 would block the usage of the color property.
*
* The currently supported properties are:
* column-count, column-rule, column-gap
*
* The function returns the name of the new style or NULL if all relevant properties are empty.
*
* @author LarsDW223
*
* @param $style
* @param $properties
* @param null $disabled_props
* @return ODTUnknownStyle or NULL
*/
public static function createMultiColumnFrameStyle(array $properties, array $disabled_props = NULL) {
$attrs = 0;
$columns = '';
if ( empty ($disabled_props ['column-count']) ) {
$columns = $properties ['column-count'];
$attrs++;
}
$rule_width = '';
if ( empty ($disabled_props ['column-rule-width']) ) {
$rule_width = $properties ['column-rule-width'];
$attrs++;
}
$rule_style = '';
if ( empty ($disabled_props ['column-rule-style']) ) {
$rule_style = $properties ['column-rule-style'];
$attrs++;
}
$rule_color = '';
if ( empty ($disabled_props ['column-rule-color']) ) {
$rule_color = $properties ['column-rule-color'];
$attrs++;
}
$gap = '';
if ( empty ($disabled_props ['column-gap']) ) {
$gap = $properties ['column-gap'];
$attrs++;
}
// If all relevant properties are empty or disabled, then there
// are no attributes for our style. Return NULL to indicate 'no style required'.
if ( $attrs == 0 ) {
return NULL;
}
// Create style name (if not given).
$style_name = $properties ['style-name'];
if ( empty($style_name) ) {
$style_name = self::getNewStylename ('Frame');
$properties ['style-name'] = $style_name;
}
$width = '1000*';
$style = '<style:style style:name="'.$style_name.'" style:family="graphic" style:parent-style-name="Frame">
<style:graphic-properties fo:border="none" style:vertical-pos="top" style:vertical-rel="paragraph-content" style:horizontal-pos="center" style:horizontal-rel="paragraph">
<style:columns fo:column-count="'.$columns.'" fo:column-gap="'.$gap.'">
<style:column-sep style:style="'.$rule_style.'" style:color="'.$rule_color.'" style:width="'.$rule_width.'"/>
<style:column style:rel-width="'.$width.'" fo:start-indent="0cm" fo:end-indent="0cm"/>
<style:column style:rel-width="'.$width.'" fo:start-indent="0cm" fo:end-indent="0cm"/>
<style:column style:rel-width="'.$width.'" fo:start-indent="0cm" fo:end-indent="0cm"/>
</style:columns>
</style:graphic-properties></style:style>';
// Create empty frame style.
// Not supported yet, so we create an "unknown" style
$object = new ODTUnknownStyle();
if ($object == NULL) {
return NULL;
}
$object->setStyleContent($style);
return $object;
}
}

View File

@@ -0,0 +1,336 @@
<?php
require_once DOKU_INC.'lib/plugins/odt/ODT/XMLUtil.php';
require_once DOKU_INC.'lib/plugins/odt/ODT/styles/ODTStyle.php';
/**
* ODTStyleSet: Abstract class defining the interface a style set/template
* needs to implement towards the ODT renderer.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author LarsDW223
*/
abstract class ODTStyleSet
{
protected $styles = array();
protected $styles_by_name = array();
protected $auto_styles = array();
protected $auto_styles_by_name = array();
protected $master_styles = array();
protected $master_styles_by_name = array();
/**
* Read/import style source.
*
* @param $source (optional)
*/
abstract public function import($source);
/**
* Export $element styles (e.g. 'office:styles' or 'office:automatic-styles')
*
* @param $element The style element to export
* @return string The ODT XML encoded $element style
*/
abstract public function export($element);
/**
* The function needs to be able to return a style name
* for the following basic styles used by the renderer:
* - standard
* - body
* - heading1
* - heading2
* - heading3
* - heading4
* - heading5
* - heading6
* - list
* - numbering
* - table content
* - table heading
* - preformatted
* - source code
* - source file
* - horizontal line
* - footnote
* - emphasis
* - strong
* - graphics
* - monospace
* - quotation1
* - quotation2
* - quotation3
* - quotation4
* - quotation5
*
* @param $style
* @return mixed
*/
abstract public function getStyleName($style);
/**
* @param null $source
*/
public function importFromODTFile($sourceFile, $root_element, $overwrite=false) {
if (empty($sourceFile) || empty($root_element)) {
return false;
}
// Get file contents
$styles_xml_content = file_get_contents ($sourceFile);
if (empty($styles_xml_content)) {
return false;
}
return $this->importFromODT($styles_xml_content, $root_element, $overwrite);
}
public function importFromODT($styles_xml_content, $root_element, $overwrite=false) {
if (empty($styles_xml_content) || empty($root_element)) {
return false;
}
// Only import known style elements
switch ($root_element) {
case 'office:styles':
case 'office:automatic-styles':
case 'office:master-styles':
$style_elements = XMLUtil::getElementContent($root_element, $styles_xml_content, $end);
break;
default:
return false;
}
$pos = 0;
$max = strlen($style_elements);
while ($pos < $max) {
$xml_code = XMLUtil::getNextElement($element, substr($style_elements, $pos), $end);
if ($xml_code == NULL) {
break;
}
$pos += $end;
// Create new ODTStyle
$object = ODTStyle::importODTStyle($xml_code);
if ($object != NULL ) {
// Success, add it
switch ($root_element) {
case 'office:styles':
$this->addStyle($object, $overwrite);
break;
case 'office:automatic-styles':
$this->addAutomaticStyle($object, $overwrite);
break;
case 'office:master-styles':
$this->addMasterStyle($object, $overwrite);
break;
}
}
}
return true;
}
/**
* @param null $destination
*/
public function exportToODT($root_element) {
$export = NULL;
switch ($root_element) {
case 'office:styles':
$export = &$this->styles;
break;
case 'office:automatic-styles':
$export = &$this->auto_styles;
break;
case 'office:master-styles':
$export = &$this->master_styles;
break;
}
if ($export != NULL) {
$office_styles = "<".$root_element.">\n";
foreach ($export as $style) {
$office_styles .= $style->toString();
}
$office_styles .= "</".$root_element.">\n";
return $office_styles;
}
return NULL;
}
/**
* @param null $source
*/
public function addStyle(ODTStyle $new, $overwrite=false) {
return $this->addStyleInternal
($this->styles, $this->styles_by_name, $new, $overwrite);
}
/**
* @param null $source
*/
public function addAutomaticStyle(ODTStyle $new, $overwrite=false) {
return $this->addStyleInternal
($this->auto_styles, $this->auto_styles_by_name, $new, $overwrite);
}
/**
* @param null $source
*/
public function addMasterStyle(ODTStyle $new, $overwrite=false) {
return $this->addStyleInternal
($this->master_styles, $this->master_styles_by_name, $new, $overwrite);
}
/**
* @param null $source
*/
public function addStyleInternal(&$dest, &$dest_by_name, ODTStyle $new, $overwrite=false) {
if ($new->isDefault()) {
// The key for a default style is the family.
$family = $new->getFamily();
// Search for default style with same family.
for ($index = 0 ; $index < count($dest) ; $index++) {
if ($dest [$index]->isDefault() &&
$dest [$index]->getFamily() == $family) {
// Only overwrite it if allowed.
if ($overwrite) {
$dest [$index] = $new;
}
return false;
}
}
// Default style for that family does not exist yet, add it.
$dest [] = $new;
} else {
// The key for a normal style is the name.
$name = $new->getProperty('style-name');
if ($dest_by_name [$name] == NULL) {
$dest [] = $new;
if (!empty($name)) {
$dest_by_name [$name] = $new;
}
return true;
} elseif ($overwrite) {
for ($index = 0 ; $index < count($dest) ; $index++) {
if ($dest [$index] == $dest_by_name [$name]) {
$dest [$index] = $new;
break;
}
}
$dest_by_name [$name] = $new;
return true;
}
}
// Do not overwrite an already existing style.
return false;
}
/**
* The function style checks if a style with the given $name already exists.
*
* @param $name Name of the style to check
* @return boolean
*/
public function styleExists ($name) {
if ($this->auto_styles_by_name [$name] != NULL) {
return true;
}
if ($this->styles_by_name [$name] != NULL) {
return true;
}
if ($this->master_styles_by_name [$name] != NULL) {
return true;
}
return false;
}
/**
* The function returns the style with the given name
*
* @param $name Name of the style
* @return ODTStyle or NULL
*/
public function getStyle ($name) {
if ($this->auto_styles_by_name [$name] != NULL) {
return $this->auto_styles_by_name [$name];
}
if ($this->styles_by_name [$name] != NULL) {
return $this->styles_by_name [$name];
}
if ($this->master_styles_by_name [$name] != NULL) {
return $this->master_styles_by_name [$name];
}
return NULL;
}
/**
* The function returns the style at the given index
*
* @param $element Element of the style e.g. 'office:styles'
* @return ODTStyle or NULL
*/
public function getStyleAtIndex($element, $index) {
switch ($element) {
case 'office:styles':
return $this->styles [$index];
case 'office:automatic-styles':
return $this->auto_styles [$index];
case 'office:master-styles':
return $this->master_styles [$index];
}
return NULL;
}
public function getStyleCount($element) {
switch ($element) {
case 'office:styles':
return count($this->styles);
case 'office:automatic-styles':
return count($this->auto_styles);
case 'office:master-styles':
return count($this->master_styles);
}
return -1;
}
/**
* @param null $source
*/
public function getDefaultStyle($family) {
// Search for default style with same family.
for ($index = 0 ; $index < count($this->styles) ; $index++) {
if ($this->styles [$index]->isDefault() &&
$this->styles [$index]->getFamily() == $family) {
return $this->styles [$index];
}
}
return NULL;
}
/**
* Get styles array.
*/
public function getStyles() {
return $this->styles;
}
/**
* Get automatci/common styles array.
*/
public function getAutomaticStyles() {
return $this->auto_styles;
}
/**
* Get master styles array.
*/
public function getMasterStyles() {
return $this->master_styles;
}
}