1<?php 2/** 3 * ODTTextStyle: class for ODT text styles. 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author LarsDW223 7 */ 8 9require_once DOKU_PLUGIN . 'odt/ODT/XMLUtil.php'; 10require_once 'ODTStyle.php'; 11 12ODTStyleStyle::register('ODTTextStyle'); 13 14/** 15 * The ODTTextStyle class 16 */ 17class ODTTextStyle extends ODTStyleStyle 18{ 19 static $text_fields = array( 20 'padding' => array ('fo:padding', 'text', true), 21 'padding-top' => array ('fo:padding-top', 'text', true), 22 'padding-right' => array ('fo:padding-right', 'text', true), 23 'padding-bottom' => array ('fo:padding-bottom', 'text', true), 24 'padding-left' => array ('fo:padding-left', 'text', true), 25 'border' => array ('fo:border', 'text', true), 26 'border-top' => array ('fo:border-top', 'text', true), 27 'border-right' => array ('fo:border-right', 'text', true), 28 'border-bottom' => array ('fo:border-bottom', 'text', true), 29 'border-left' => array ('fo:border-left', 'text', true), 30 'color' => array ('fo:color', 'text', true), 31 'background-color' => array ('fo:background-color', 'text', true), 32 'background-image' => array ('fo:background-image', 'text', true), 33 'font-style' => array ('fo:font-style', 'text', true), 34 'font-style-asian' => array ('style:font-style-asian', 'text', true), 35 'font-style-complex' => array ('style:font-style-complex', 'text', true), 36 'font-weight' => array ('fo:font-weight', 'text', true), 37 'font-weight-asian' => array ('style:font-weight-asian', 'text', true), 38 'font-weight-complex' => array ('style:font-weight-complex', 'text', true), 39 'font-size' => array ('fo:font-size', 'text', true), 40 'font-size-asian' => array ('style:font-size-asian', 'text', true), 41 'font-size-complex' => array ('style:font-size-complex', 'text', true), 42 'font-family' => array ('fo:font-family', 'text', true), 43 'font-family-asian' => array ('style:font-family-asian', 'text', true), 44 'font-family-complex' => array ('style:font-family-complex', 'text', true), 45 'font-variant' => array ('fo:font-variant', 'text', true), 46 'letter-spacing' => array ('fo:letter-spacing', 'text', true), 47 'vertical-align' => array ('style:vertical-align', 'text', true), 48 'display' => array ('text:display', 'text', true), 49 'lang' => array ('fo:language', 'text', true), 50 'lang-asian' => array ('style:language-asian', 'text', true), 51 'lang-complex' => array ('style:language-complex', 'text', true), 52 'country' => array ('fo:country', 'text', true), 53 'country-asian' => array ('style:country-asian', 'text', true), 54 'country-complex' => array ('style:country-complex', 'text', true), 55 'text-transform' => array ('fo:text-transform', 'text', true), 56 'use-window-font-color' => array ('style:use-window-font-color', 'text', true), 57 'text-outline' => array ('style:text-outline', 'text', true), 58 'text-line-through-type' => array ('style:text-line-through-type', 'text', true), 59 'text-line-through-style' => array ('style:text-line-through-style', 'text', true), 60 'text-line-through-width' => array ('style:text-line-through-width', 'text', true), 61 'text-line-through-color' => array ('style:text-line-through-color', 'text', true), 62 'text-line-through-text' => array ('style:text-line-through-text', 'text', true), 63 'text-line-through-text-style' => array ('style:text-line-through-text-style', 'text', true), 64 'text-position' => array ('style:text-position', 'text', true), 65 'font-name' => array ('style:font-name', 'text', true), 66 'font-name-asian' => array ('style:font-name-asian', 'text', true), 67 'font-name-complex' => array ('style:font-name-complex', 'text', true), 68 'font-family-generic' => array ('style:font-family-generic', 'text', true), 69 'font-family-generic-asian' => array ('style:font-family-generic-asian', 'text', true), 70 'font-family-generic-complex' => array ('style:font-family-generic-complex', 'text', true), 71 'font-style-name' => array ('style:font-style-name', 'text', true), 72 'font-style-name-asian' => array ('style:font-style-name-asian', 'text', true), 73 'font-style-name-complex' => array ('style:font-style-name-complex', 'text', true), 74 'font-pitch' => array ('style:font-pitch', 'text', true), 75 'font-pitch-asian' => array ('style:font-pitch-asian', 'text', true), 76 'font-pitch-complex' => array ('style:font-pitch-complex', 'text', true), 77 'font-charset' => array ('style:font-charset', 'text', true), 78 'font-charset-asian' => array ('style:font-charset-asian', 'text', true), 79 'font-charset-complex' => array ('style:font-charset-complex', 'text', true), 80 'font-size-rel' => array ('style:font-size-rel', 'text', true), 81 'font-size-rel-asian' => array ('style:font-size-rel-asian', 'text', true), 82 'font-size-rel-complex' => array ('style:font-size-rel-complex', 'text', true), 83 'script-type' => array ('style:script-type', 'text', true), 84 'script' => array ('fo:script', 'text', true), 85 'script-asian' => array ('style:script-asian', 'text', true), 86 'script-complex' => array ('style:script-complex', 'text', true), 87 'rfc-language-tag' => array ('style:rfc-language-tag', 'text', true), 88 'rfc-language-tag-asian' => array ('style:rfc-language-tag-asian', 'text', true), 89 'rfc-language-tag-complex' => array ('style:rfc-language-tag-complex', 'text', true), 90 'rfc-language-tag-complex' => array ('style:rfc-language-tag-complex', 'text', true), 91 'font-relief' => array ('style:font-relief', 'text', true), 92 'text-shadow' => array ('fo:text-shadow', 'text', true), 93 'text-underline-type' => array ('style:text-underline-type', 'text', true), 94 'text-underline-style' => array ('style:text-underline-style', 'text', true), 95 'text-underline-width' => array ('style:text-underline-width', 'text', true), 96 'text-underline-color' => array ('style:text-underline-color', 'text', true), 97 'text-overline-type' => array ('style:text-overline-type', 'text', true), 98 'text-overline-style' => array ('style:text-overline-style', 'text', true), 99 'text-overline-width' => array ('style:text-overline-width', 'text', true), 100 'text-overline-color' => array ('style:text-overline-color', 'text', true), 101 'text-overline-mode' => array ('style:text-overline-mode', 'text', true), 102 'text-underline-mode' => array ('style:text-underline-mode', 'text', true), 103 'text-line-through-mode' => array ('style:text-line-through-mode', 'text', true), 104 'letter-kerning' => array ('style:letter-kerning', 'text', true), 105 'text-blinking' => array ('style:text-blinking', 'text', true), 106 'text-combine' => array ('style:text-combine', 'text', true), 107 'text-combine-start-char' => array ('style:text-combine-start-char', 'text', true), 108 'text-combine-end-char' => array ('style:text-combine-end-char', 'text', true), 109 'text-emphasize' => array ('style:text-emphasize', 'text', true), 110 'text-scale' => array ('style:text-scale', 'text', true), 111 'text-rotation-angle' => array ('style:text-rotation-angle', 'text', true), 112 'text-rotation-scale' => array ('style:text-rotation-scale', 'text', true), 113 'hyphenate' => array ('fo:hyphenate', 'text', true), 114 'hyphenation-remain-char-count' => array ('fo:hyphenation-remain-char-count', 'text', true), 115 'hyphenation-push-char-count' => array ('fo:hyphenation-push-char-count', 'text', true), 116 'condition' => array ('text:condition', 'text', true), 117 ); 118 119 /** 120 * Constructor. 121 */ 122 public function __construct() { 123 parent::__construct(); 124 } 125 126 /** 127 * Set style properties by importing values from a properties array. 128 * Properties might be disabled by setting them in $disabled. 129 * The style must have been previously created. 130 * 131 * @param $properties Properties to be imported 132 * @param $disabled Properties to be ignored 133 */ 134 public function importProperties($properties, $disabled=array()) { 135 $this->importPropertiesInternal(ODTStyleStyle::getStyleProperties (), $properties, $disabled); 136 $this->importPropertiesInternal(self::$text_fields, $properties, $disabled); 137 $this->setProperty('style-family', $this->getFamily()); 138 } 139 140 /** 141 * Check if a style is a common style. 142 * 143 * @return bool Is common style 144 */ 145 public function mustBeCommonStyle() { 146 return false; 147 } 148 149 /** 150 * Get the style family of a style. 151 * 152 * @return string Style family 153 */ 154 static public function getFamily() { 155 return 'text'; 156 } 157 158 /** 159 * Set a property. 160 * 161 * @param $property The name of the property to set 162 * @param $value New value to set 163 */ 164 public function setProperty($property, $value) { 165 $style_fields = ODTStyleStyle::getStyleProperties (); 166 if (array_key_exists ($property, $style_fields)) { 167 $this->setPropertyInternal 168 ($property, $style_fields [$property][0], $value, $style_fields [$property][1]); 169 return; 170 } 171 if (array_key_exists ($property, self::$text_fields)) { 172 $this->setPropertyInternal 173 ($property, self::$text_fields [$property][0], $value, self::$text_fields [$property][1]); 174 return; 175 } 176 } 177 178 /** 179 * Create new style by importing ODT style definition. 180 * 181 * @param $xmlCode Style definition in ODT XML format 182 * @return ODTStyle New specific style 183 */ 184 static public function importODTStyle($xmlCode) { 185 $style = new ODTTextStyle(); 186 $attrs = 0; 187 188 $open = XMLUtil::getElementOpenTag('style:style', $xmlCode); 189 if (!empty($open)) { 190 $attrs += $style->importODTStyleInternal(ODTStyleStyle::getStyleProperties (), $open); 191 } else { 192 $open = XMLUtil::getElementOpenTag('style:default-style', $xmlCode); 193 if (!empty($open)) { 194 $style->setDefault(true); 195 $attrs += $style->importODTStyleInternal(ODTStyleStyle::getStyleProperties (), $open); 196 } 197 } 198 199 $open = XMLUtil::getElementOpenTag('style:text-properties', $xmlCode); 200 if (!empty($open)) { 201 $attrs += $style->importODTStyleInternal(self::$text_fields, $open); 202 } 203 204 // If style has no meaningfull content then throw it away 205 if ( $attrs == 0 ) { 206 return NULL; 207 } 208 209 return $style; 210 } 211 212 static public function getTextProperties () { 213 return self::$text_fields; 214 } 215 216 /** 217 * This function creates a text style using the style as set in the assoziative array $properties. 218 * The parameters in the array should be named as the CSS property names e.g. 'color' or 'background-color'. 219 * Properties which shall not be used in the style can be disabled by setting the value in disabled_props 220 * to 1 e.g. $disabled_props ['color'] = 1 would block the usage of the color property. 221 * 222 * The currently supported properties are: 223 * background-color, color, font-style, font-weight, font-size, border, font-family, font-variant, letter-spacing, 224 * vertical-align, background-image 225 * 226 * The function returns the name of the new style or NULL if all relevant properties are empty. 227 * 228 * @author LarsDW223 229 * @param $properties 230 * @param null $disabled_props 231 * @return ODTTextStyle or NULL 232 */ 233 public static function createTextStyle(array $properties, array $disabled_props = NULL, ODTDocument $doc=NULL){ 234 // Convert 'text-decoration'. 235 if ( $properties ['text-decoration'] == 'line-through' ) { 236 $properties ['text-line-through-style'] = 'solid'; 237 } 238 if ( $properties ['text-decoration'] == 'underline' ) { 239 $properties ['text-underline-style'] = 'solid'; 240 } 241 if ( $properties ['text-decoration'] == 'overline' ) { 242 $properties ['text-overline-style'] = 'solid'; 243 } 244 245 // If the property 'vertical-align' has the value 'sub' or 'super' 246 // then for ODT it needs to be converted to the corresponding 'text-position' property. 247 // Replace sub and super with text-position. 248 $valign = $properties ['vertical-align']; 249 if (!empty($valign)) { 250 if ( $valign == 'sub' ) { 251 $properties ['text-position'] = '-33% 100%'; 252 unset($properties ['vertical-align']); 253 } elseif ( $valign == 'super' ) { 254 $properties ['text-position'] = '33% 100%'; 255 unset($properties ['vertical-align']); 256 } 257 } 258 259 // Separate country from language 260 $lang = $properties ['lang']; 261 $country = $properties ['country']; 262 if ( !empty($lang) ) { 263 $parts = preg_split ('/-/', $lang); 264 $lang = $parts [0]; 265 $country = $parts [1]; 266 $properties ['country'] = trim($country); 267 $properties ['lang'] = trim($lang); 268 } 269 if (!empty($properties ['country'])) { 270 if (empty($properties ['country-asian'])) { 271 $properties ['country-asian'] = $properties ['country']; 272 } 273 if (empty($properties ['country-complex'])) { 274 $properties ['country-complex'] = $properties ['country']; 275 } 276 } 277 278 // Extra handling for font-size in '%' 279 $save = $disabled_props ['font-size']; 280 $odt_fo_size = ''; 281 if ( empty ($disabled_props ['font-size']) ) { 282 $odt_fo_size = $properties ['font-size']; 283 } 284 $length = strlen ($odt_fo_size); 285 if ( $length > 0 && $odt_fo_size [$length-1] == '%' && isset($doc)) { 286 // A font-size in percent is only supported in common style definitions, not in automatic 287 // styles. Create a common style and set it as parent for this automatic style. 288 $name = 'Size'.trim ($odt_fo_size, '%').'pc'; 289 $style_obj = self::createSizeOnlyTextStyle ($name, $odt_fo_size); 290 $doc->addStyle($style_obj); 291 $parent = $style_obj->getProperty('style-name'); 292 if (!empty($parent)) { 293 $properties ['style-parent'] = $parent; 294 } 295 } 296 297 // Create style name (if not given). 298 $style_name = $properties ['style-name']; 299 if ( empty($style_name) ) { 300 $style_name = self::getNewStylename ('Text'); 301 $properties ['style-name'] = $style_name; 302 } 303 304 // Create empty text style. 305 $object = new ODTTextStyle(); 306 if (!isset($object)) { 307 return NULL; 308 } 309 310 // Import our properties 311 $object->importProperties($properties, $disabled_props); 312 313 // Restore $disabled_props 314 $disabled_props ['font-size'] = $save; 315 return $object; 316 } 317 318 /** 319 * Simple helper function for creating a text style $name setting the specfied font size $size. 320 * 321 * @author LarsDW223 322 * 323 * @param string $name 324 * @param string $size 325 * @return ODTTextStyle 326 */ 327 public static function createSizeOnlyTextStyle ($name, $size) { 328 $properties = array(); 329 $properties ['style-name'] = $name; 330 $properties ['style-display-name'] = $name; 331 $properties ['font-size'] = $size; 332 $properties ['font-size-asian'] = $size; 333 $properties ['font-size-complex'] = $size; 334 return self::createTextStyle($properties); 335 } 336} 337 338