blob: ac3086657756de3b56feeab835dd0c4cc050fa47 [file] [log] [blame]
<?php
/**
* File containing the ezcDocumentOdtListStyleGenerator class.
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* @package Document
* @version //autogen//
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @access private
*/
/**
* Class to generate styles for lists (<text:list/>).
*
* @package Document
* @access private
* @version //autogen//
*/
class ezcDocumentOdtListStyleGenerator extends ezcDocumentOdtStyleGenerator
{
/**
* Text style generator.
*
* @var ezcDocumentOdtTextStyleGenerator
*/
protected $textStyleGenerator;
/**
* List property generator.
*
* @var ezcDocumentOdtStyleListPropertyGenerator
*/
protected $listPropertyGenerator;
/**
* List IDs.
*
* @var int
*/
protected $id = 0;
/**
* Creates a new style genertaor.
*
* @param ezcDocumentOdtPcssConverterManager $styleConverters
*/
public function __construct( ezcDocumentOdtPcssConverterManager $styleConverters )
{
parent::__construct( $styleConverters );
$this->textStyleGenerator = new ezcDocumentOdtTextStyleGenerator(
$styleConverters
);
$this->listPropertyGenerator = new ezcDocumentOdtStyleListPropertyGenerator(
$styleConverters
);
}
/**
* Returns if the given $odtElement is handled by this generator.
*
* @param DOMElement $odtElement
* @return bool
*/
public function handles( DOMElement $odtElement )
{
return (
$odtElement->localName === 'list'
);
}
/**
* Creates the styles with $styleAttributes for the given $odtElement.
*
* @param ezcDocumentOdtStyleInformation $styleInfo
* @param DOMElement $odtElement
* @param array(string=>ezcDocumentPcssStyleValue) $styleAttributes
*/
public function createStyle( ezcDocumentOdtStyleInformation $styleInfo, DOMElement $odtElement, array $styleAttributes )
{
$baseListDef = $this->getBaseList( $odtElement );
if ( $baseListDef['list'] === null )
{
$listStyle = $this->createNewListStyle( $odtElement, $styleInfo );
$level = 1;
}
else
{
$listStyle = $this->retrieveListStyle( $baseListDef['list'], $styleInfo );
$level = $baseListDef['depth'];
}
$this->createListLevelStyle( $styleInfo, $listStyle, $level, $styleAttributes );
}
/**
* Creates a style for the <text:list /> element.
*
* Checks if the list is nested in a different list. If this is not the
* case, a new list style is generated. Otherwise, the existing list style
* is retrieved and a list definition for the corresponding nesting depth
* is created.
*
* @param ezcDocumentOdtStyleInformation $styleInfo
* @param DOMElement $list
* @param array $styleAttributes
* @return void
*/
protected function createListStyle( ezcDocumentOdtStyleInformation $styleInfo, DOMElement $list, array $styleAttributes )
{
}
/**
* Creates a new <text:list-style/> and applies it to the given
* $odtElement.
*
* This method creates and returns a new list style DOMElement in
* $styleInfo for $odtElement and assigns its name to the $odtElement. The
* list style can then be filled with list properties of different levels.
*
* @param DOMElement $odtElement
* @param ezcDocumentOdtStyleInformation $styleInfo
* @return DOMElement
*/
protected function createNewListStyle( DOMElement $odtElement, ezcDocumentOdtStyleInformation $styleInfo )
{
$listStyle = $styleInfo->automaticStyleSection->appendChild(
$styleInfo->automaticStyleSection->ownerDocument->createElementNS(
ezcDocumentOdt::NS_ODT_TEXT,
'text:list-style'
)
);
$listStyle->setAttributeNS(
ezcDocumentOdt::NS_ODT_STYLE,
'style:name',
( $styleName = $this->getUniqueStyleName( 'l' ) )
);
$odtElement->setAttributeNS(
ezcDocumentOdt::NS_ODT_TEXT,
'text:style-name',
$styleName
);
// OOO attaches IDs to root lists, so do we.
$odtElement->setAttributeNS(
ezcDocumentOdt::NS_XML,
'xml:id',
sprintf( "%s%s", 'list', ++$this->id )
);
return $listStyle;
}
/**
* Creates the <text:list-level-style-* /> element for $styleAttributes.
*
* This method creates a list-level-style in $listStyle for the given list
* $level applying $styleAttributes to this list level.
*
* @param ezcDocumentOdtStyleInformation $styleInfo
* @param DOMElement $listStyle
* @param int $level
* @param array $styleAttributes
*/
protected function createListLevelStyle( ezcDocumentOdtStyleInformation $styleInfo, DOMElement $listStyle, $level, array $styleAttributes )
{
$styleAttributes = $this->calculateListLevelMeasures(
$listStyle,
$level,
$styleAttributes
);
$listLevelStyle = $listStyle->appendChild(
$listStyle->ownerDocument->createElementNS(
ezcDocumentOdt::NS_ODT_TEXT,
'text:list-level-style-' . $styleAttributes['list-type']->value
)
);
$listLevelStyle->setAttributeNS(
ezcDocumentOdt::NS_ODT_TEXT,
'level',
$level
);
// Creates the text:style-name attribute with a new style that is
// applied to the bullet/numbering.
$this->textStyleGenerator->createStyle(
$styleInfo,
$listLevelStyle,
$styleAttributes
);
// Set by OOO no matter if bullet or number list
// @todo: Make styleable
$listLevelStyle->setAttributeNS(
ezcDocumentOdt::NS_ODT_STYLE,
'style:num-suffix',
'.'
);
$this->listPropertyGenerator->createProperty(
$listLevelStyle,
$styleAttributes
);
if ( $styleAttributes['list-type']->value === 'bullet' )
{
$listLevelStyle->setAttributeNS(
ezcDocumentOdt::NS_ODT_TEXT,
'text:bullet-char',
$styleAttributes['list-bullet']->value
);
}
else
{
$listLevelStyle->setAttributeNS(
ezcDocumentOdt::NS_ODT_STYLE,
'style:num-format',
$styleAttributes['list-number']->value
);
}
}
/**
* Calculates the list margin and indent.
*
* Margin and indent are handled in a strange way in ODF. This method
* calculates the margin for a list level, based on the previous level margin
* and the current margin and padding. In addition, the text-indent is set
* to fit the previous list-level. The new $styleAttributes are returned.
*
* @param DOMElement $listStyle
* @param int $level
* @param array $styleAttributes
* @return array(string=>float)
*/
protected function calculateListLevelMeasures( DOMElement $listStyle, $level, $styleAttributes )
{
$previousMargin = 0;
foreach( $listStyle->childNodes as $listStyleChild )
{
if ( $listStyleChild->nodeType === XML_ELEMENT_NODE
&& strpos( $listStyleChild->localName, 'list-level-style-' ) === 0
&& $listStyleChild->hasAttributeNS( ezcDocumentOdt::NS_ODT_TEXT, 'level' )
&& $listStyleChild->getAttributeNS( ezcDocumentOdt::NS_ODT_TEXT, 'level' ) == ( $level - 1 )
)
{
$alignementProps = $listStyleChild->getElementsByTagNameNS(
ezcDocumentOdt::NS_ODT_STYLE,
'list-level-label-alignment'
);
if ( $alignementProps->length === 1 )
{
$previousMargin = (int) $alignementProps->item( 0 )->getAttributeNS(
ezcDocumentOdt::NS_ODT_FO,
'margin-left'
);
}
break;
}
}
$styleAttributes['margin']->value['left'] = $previousMargin
+ ( $margin = $styleAttributes['margin']->value['left'] )
+ ( $padding = $styleAttributes['padding']->value['left'] );
$styleAttributes['text-indent'] = new ezcDocumentPcssStyleMeasureValue(
- ( $margin + $padding )
);
return $styleAttributes;
}
/**
* Returns the <text:list-style> DOMElement assigned to $odtList.
*
* @param DOMElement $odtList
* @param ezcDocumentOdtStyleInformation $styleInfo
* @return DOMElement
*/
protected function retrieveListStyle( $odtList, ezcDocumentOdtStyleInformation $styleInfo )
{
$styleName = $odtList->getAttributeNS(
ezcDocumentOdt::NS_ODT_TEXT,
'style-name'
);
$xpath = new DOMXpath( $styleInfo->automaticStyleSection->ownerDocument );
$xpath->registerNamespace( ezcDocumentOdt::NS_ODT_TEXT, 'text' );
$xpath->registerNamespace( ezcDocumentOdt::NS_ODT_STYLE, 'style' );
$styleList = $xpath->query(
"text:list-style[@style:name='{$styleName}']",
$styleInfo->automaticStyleSection
);
if ( $styleList->length !== 1 )
{
throw new RuntimeException(
"Inconsistent style section. Found {$styleList->length} list styles with name '{$styleName}'"
);
}
return $styleList->item( 0 );
}
/**
* Returns the parent <text:list/> element or null.
*
* This method returns the parent <text:list/> element for the given $list and the nesting depth of $list,
* if it is nested in another list. The returned structure is:
*
* <code>
* <?php
* array(
* 'base' => <DOMElement|null>,
* 'depth' => <int>
* );
* ?>
* </code>
*
* @param DOMElement $list
* @param int $depth
* @return array
*/
protected function getBaseList( DOMElement $list, $depth = 1 )
{
$parent = $list->parentNode;
if ( $parent === null || $parent->nodeType === XML_DOCUMENT_NODE )
{
return array(
'list' => null,
'depth' => $depth
);
}
if ( $parent->nodeType === XML_ELEMENT_NODE && $parent->localName === 'list' )
{
++$depth;
if ( $parent->hasAttributeNs( ezcDocumentOdt::NS_ODT_TEXT, 'style-name' ) )
{
return array(
'list' => $parent,
'depth' => $depth
);
}
}
return $this->getBaseList( $parent, $depth );
}
}
?>