1<?php 2 3require_once DOKU_PLUGIN.'odt/ODT/elements/ODTStateElement.php'; 4 5/** 6 * ODTElementFrame: 7 * Class for handling the frame element. 8 * 9 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 10 * @author LarsDW223 11 */ 12class ODTElementFrame extends ODTStateElement implements iContainerAccess 13{ 14 protected $container = NULL; 15 protected $containerPos = NULL; 16 protected $attributes = NULL; 17 protected $own_max_width = NULL; 18 protected $nameAttr = NULL; 19 protected $name = NULL; 20 protected $written = false; 21 22 /** 23 * Constructor. 24 */ 25 public function __construct($style_name=NULL) { 26 parent::__construct(); 27 $this->setClass ('frame'); 28 if (isset($style_name)) { 29 $this->setStyleName ($style_name); 30 } 31 $this->container = new ODTContainerElement($this); 32 } 33 34 /** 35 * Return the elements name. 36 * 37 * @return string The ODT XML element name. 38 */ 39 public function getElementName () { 40 return ('draw:frame'); 41 } 42 43 /** 44 * Return string with encoded opening tag. 45 * 46 * @return string The ODT XML code to open this element. 47 */ 48 public function getOpeningTag (ODTInternalParams $params=NULL) { 49 // Convert width to points 50 $width = $this->getWidth(); 51 if (isset($width)) { 52 $width = $params->units->toPoints($width); 53 $this->setWidth($width); 54 } 55 56 $encoded = '<draw:frame draw:style-name="'.$this->getStyleName().'" '; 57 $encoded .= $this->getAttributes().'>'; 58 59 $this->written = true; 60 61 return $encoded; 62 } 63 64 /** 65 * Return string with encoded closing tag. 66 * 67 * @return string The ODT XML code to close this element. 68 */ 69 public function getClosingTag () { 70 return '</draw:frame>'; 71 } 72 73 /** 74 * Are we in a paragraph or not? 75 * As a frame we are not. 76 * 77 * @return boolean 78 */ 79 public function getInParagraph() { 80 return false; 81 } 82 83 /** 84 * Determine and set the parent for this element. 85 * As a frame the previous element is our parent. 86 * 87 * @param ODTStateElement $previous 88 */ 89 public function determineParent(ODTStateElement $previous) { 90 $this->container->determineParent($previous); 91 if ($this->isNested ()) { 92 $this->containerPos = array(); 93 $this->getParent()->determinePositionInContainer($this->containerPos, $previous); 94 } 95 96 //$this->setParent($previous); 97 } 98 99 /** 100 * Set frame attributes 101 * 102 * @param array $value 103 */ 104 public function setAttributes($value) { 105 // Delete linebreaks and multiple whitespace 106 $this->attributes = preg_replace( "/\r|\n/", "", $value); 107 $this->attributes = preg_replace( "/\s+/", " ", $this->attributes); 108 109 // Save name for later width rewriting 110 $this->nameAttr = $this->getNameAttribute(); 111 $this->name = $this->getName(); 112 } 113 114 /** 115 * Get frame attributes 116 * 117 * @return array 118 */ 119 public function getAttributes() { 120 return $this->attributes; 121 } 122 123 /** 124 * Is this frame a nested frame (inserted into another table/frame)? 125 * 126 * @return boolean 127 */ 128 public function isNested () { 129 return $this->container->isNested(); 130 } 131 132 public function addNestedContainer (iContainerAccess $nested) { 133 $this->container->addNestedContainer ($nested); 134 } 135 136 public function getNestedContainers () { 137 return $this->container->getNestedContainers (); 138 } 139 140 public function determinePositionInContainer (array &$data, ODTStateElement $current) { 141 // Position in frame doesn't mater for width calculation 142 // So this is a dummy for now 143 $data ['frame'] = true; 144 } 145 146 public function getMaxWidthOfNestedContainer (ODTInternalParams $params, array $data) { 147 if (!isset($this->own_max_width)) { 148 // We do not know our own width yet. Calculate it first. 149 $this->own_max_width = $this->getMaxWidth($params); 150 151 // Re-Write our width if frame already has been written to the document 152 if ($this->written) { 153 if (preg_match('/<draw:frame[^<]*'.$this->nameAttr.'[^>]*>/', $params->content, $matches) === 1) { 154 $frameTag = $matches [0]; 155 $frameTag = preg_replace('/svg:width="[^"]*"/', 'svg:width="'.$this->own_max_width.'"', $frameTag); 156 157 // Replace old frame tag in document in 158 $params->content = str_replace ($matches [0], $frameTag, $params->content); 159 } 160 } 161 } 162 163 // Convert to points 164 if (isset($this->own_max_width)) { 165 $width = $params->units->getDigits ($params->units->toPoints($this->own_max_width)); 166 } 167 168 return $width.'pt'; 169 } 170 171 public function getMaxWidth (ODTInternalParams $params) { 172 if (isset($this->own_max_width)) { 173 return $this->own_max_width; 174 } 175 $frameStyle = $this->getStyle(); 176 177 // Get frame left margin 178 $leftMargin = $frameStyle->getProperty('margin-left'); 179 if (!isset($leftMargin)) { 180 $leftMarginPt = 0; 181 } else { 182 $leftMarginPt = $params->units->getDigits ($params->units->toPoints($leftMargin)); 183 } 184 185 // Get frame right margin 186 $rightMargin = $frameStyle->getProperty('margin-right'); 187 if (!isset($rightMargin)) { 188 $rightMarginPt = 0; 189 } else { 190 $rightMarginPt = $params->units->getDigits ($params->units->toPoints($rightMargin)); 191 } 192 193 // Get available max width 194 if (!$this->isNested ()) { 195 // Get max page width in points. 196 $maxWidth = $params->document->getAbsWidthMindMargins (); 197 $maxWidthPt = $params->units->getDigits ($params->units->toPoints($maxWidth.'cm')); 198 } else { 199 // If this frame is nested in another container we have to ask it's parent 200 // for the allowed max width 201 $maxWidth = $this->getParent()->getMaxWidthOfNestedContainer($params, $this->containerPos); 202 $maxWidthPt = $params->units->getDigits ($params->units->toPoints($maxWidth)); 203 } 204 205 // Get frame width 206 $width = $this->getWidth(); 207 if (isset($width)) { 208 if ($width [strlen($width)-1] != '%') { 209 $widthPt = $params->units->getDigits ($params->units->toPoints($width)); 210 } else { 211 $percentage = trim ($width, '%'); 212 $widthPt = ($percentage * $maxWidthPt)/100; 213 } 214 } 215 216 // Calculate final width. 217 // If no frame width is set or the frame width is greater than 218 // the calculated max width then use the max width. 219 $maxWidthPt = $maxWidthPt - $leftMarginPt - $rightMarginPt; 220 if (!isset($width) || $widthPt > $maxWidthPt) { 221 $width = $maxWidthPt - $leftMarginPt - $rightMarginPt; 222 } else { 223 $width = $widthPt; 224 } 225 $width = $width.'pt'; 226 227 return $width; 228 } 229 230 public function getWidth() { 231 if (isset($this->attributes)) { 232 if ( preg_match('/svg:width="[^"]+"/', $this->attributes, $matches) === 1 ) { 233 $width = substr ($matches [0], 11); 234 $width = trim ($width, '"'); 235 return $width; 236 } 237 } 238 return NULL; 239 } 240 241 public function setWidth($width) { 242 if (isset($this->attributes)) { 243 if ( preg_match('/svg:width="[^"]+"/', $this->attributes, $matches) === 1 ) { 244 $widthAttr = 'svg:width="'.$width.'"'; 245 $this->attributes = str_replace($matches [0], $widthAttr, $this->attributes); 246 return; 247 } 248 } 249 $this->attributes .= ' svg:width="'.$width.'"'; 250 } 251 252 public function getNameAttribute() { 253 if (isset($this->attributes)) { 254 if ( preg_match('/draw:name="[^"]+"/', $this->attributes, $matches) === 1 ) { 255 return $matches [0]; 256 } 257 } 258 return NULL; 259 } 260 261 public function getName() { 262 if (isset($this->attributes)) { 263 if ( preg_match('/draw:name="[^"]+"/', $this->attributes, $matches) === 1 ) { 264 $name = substr ($matches [0], 10); 265 $name = trim ($name, '"'); 266 return $name; 267 } 268 } 269 return NULL; 270 } 271 272 /** 273 * This function adjust the width of the frame. 274 * There is not much to do except conversion of relative to absolute values 275 * and calling the method for all nested elements. 276 * (table has got more work to do, see ODTElementTable::adjustWidth) 277 * 278 * @param ODTInternalParams $params Common ODT params 279 * @param boolean $allowNested Allow to process call if this frame is nested 280 */ 281 public function adjustWidth (ODTInternalParams $params, $allowNested=false) { 282 if ($this->isNested () && !$allowNested) { 283 // Do not do anything if this is a nested table. 284 // Only if the function is called for the parent/root table 285 // then the width of the nested tables will be calculated. 286 return; 287 } 288 $matches = array (); 289 290 $max_width = $this->getMaxWidth($params); 291 //FIXME: convert % to points 292 293 // Now adjust all nested containers too 294 $nested = $this->getNestedContainers (); 295 foreach ($nested as $container) { 296 $container->adjustWidth ($params, true); 297 } 298 } 299} 300