1<?php 2 3namespace dokuwiki\plugin\wordimport\docx; 4 5/** 6 * A paragraph 7 * 8 * This the most common but also most complex type. It can contain multiple text runs each with different formatting 9 */ 10class Paragraph extends AbstractParagraph 11{ 12 /** @var TextRun[] */ 13 protected $texts = []; 14 15 /** @var string */ 16 protected $alignment = ''; 17 18 /** @var array DokuWiki Syntax for the formatting */ 19 protected $fSyntax = [ 20 'bold' => ['**', '**'], 21 'italic' => ['//', '//'], 22 'underline' => ['__', '__'], 23 'strike' => ['<del>', '</del>'], 24 'mono' => ["''", "''"], 25 ]; 26 27 /** @inheritdoc */ 28 public function parse() 29 { 30 $trs = $this->p->xpath('w:r'); 31 foreach ($trs as $tr) { 32 $textRun = new TextRun($this->docx, $tr); 33 $this->texts[] = $textRun; 34 } 35 $this->updateFormattingScores(); 36 37 $alignment = $this->p->xpath('w:pPr/w:jc'); 38 if ($alignment) { 39 $this->alignment = (string)$alignment[0]->attributes('w', true)->val; 40 } 41 } 42 43 /** 44 * This combines the texts of all runs and wraps them in the correct formatting. It tries to create the most 45 * optimal wrapping by closing and opening formatting as needed. 46 * @inheritdoc 47 */ 48 public function __toString(): string 49 { 50 $result = ''; 51 $fStack = []; 52 53 foreach ($this->texts as $text) { 54 // we don't want to wrap whitespace in formatting 55 if ($text->isWhiteSpace()) { 56 $result .= $text->__toString(); 57 continue; 58 } 59 60 $formatting = $text->getFormatting(); 61 $formatting = array_keys(array_filter($formatting)); 62 63 // close all formatting that is not in the current text 64 $toclose = array_diff($fStack, $formatting); 65 foreach ($toclose as $f) { 66 // we need to make sure all formatting is closed, but we close by popping the 67 // stack. This ensures we don't create invalid nesting 68 while (in_array($f, $fStack)) { 69 $this->closeFormatting($result, array_pop($fStack)); 70 } 71 } 72 73 // open formatting that is in the current text 74 $new = array_diff($formatting, $fStack); 75 foreach ($new as $f) { 76 $this->openFormatting($result, $f); 77 $fStack[] = $f; 78 } 79 80 // add the text 81 $result .= $text->__toString(); 82 } 83 84 // close remaining formatting 85 while ($fStack) { 86 $this->closeFormatting($result, array_pop($fStack)); 87 } 88 89 return $result; 90 } 91 92 /** 93 * Update the formatting scores for all texts 94 * 95 * Walks through the texts in reverse order and updates the formatting scores 96 */ 97 protected function updateFormattingScores() 98 { 99 $len = count($this->texts); 100 if ($len < 2) return; 101 for ($i = $len - 2; $i >= 0; $i--) { 102 $this->texts[$i]->updateFormattingScores($this->texts[$i + 1]); 103 } 104 } 105 106 /** 107 * Get the paragraph alignment 108 * 109 * This is only used in images and tables 110 * 111 * @return string 112 */ 113 public function getAlignment() 114 { 115 return $this->alignment; 116 } 117 118 /** 119 * Pad the given text according to the alignment 120 * 121 * @param string $text 122 */ 123 public function alignmentPadding($text) 124 { 125 switch ($this->getAlignment()) { 126 case 'left': 127 $text = "$text "; 128 break; 129 case 'right': 130 $text = " $text"; 131 break; 132 case 'center': 133 $text = " $text "; 134 break; 135 } 136 return $text; 137 } 138 139 /** 140 * Open a formatting 141 * 142 * @param string $text The text currently worked on 143 * @param string $formatting Name of the formatting 144 * @return void 145 */ 146 public function openFormatting(&$text, $formatting) 147 { 148 if (!isset($this->fSyntax[$formatting])) { 149 throw new \RuntimeException("Unknown formatting: $formatting"); 150 } 151 152 $text .= $this->fSyntax[$formatting][0]; 153 } 154 155 /** 156 * Close a formatting 157 * 158 * Handles whitespace at the end of the text 159 * 160 * @param string $text The text currently worked on 161 * @param string $formatting Name of the formatting 162 * @return void 163 */ 164 public function closeFormatting(&$text, $formatting) 165 { 166 preg_match('/^(.+?)(\s*)$/s', $text, $matches); 167 $text = $matches[1]; 168 $suffix = $matches[2]; 169 170 if (!isset($this->fSyntax[$formatting])) { 171 throw new \RuntimeException("Unknown formatting: $formatting"); 172 } 173 174 $text .= $this->fSyntax[$formatting][1]; 175 $text .= $suffix; 176 } 177} 178