Styles.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. <?php
  2. /**
  3. * This file is part of PHPWord - A pure PHP library for reading and writing
  4. * word processing documents.
  5. *
  6. * PHPWord is free software distributed under the terms of the GNU Lesser
  7. * General Public License version 3 as published by the Free Software Foundation.
  8. *
  9. * For the full copyright and license information, please read the LICENSE
  10. * file that was distributed with this source code. For the full list of
  11. * contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
  12. *
  13. * @see https://github.com/PHPOffice/PHPWord
  14. * @copyright 2010-2018 PHPWord contributors
  15. * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
  16. */
  17. namespace PhpOffice\PhpWord\Writer\ODText\Part;
  18. use PhpOffice\PhpWord\Settings;
  19. use PhpOffice\PhpWord\Shared\Converter;
  20. use PhpOffice\PhpWord\Shared\XMLWriter;
  21. use PhpOffice\PhpWord\Style;
  22. /**
  23. * ODText styles part writer: styles.xml
  24. */
  25. class Styles extends AbstractPart
  26. {
  27. /**
  28. * Write part
  29. *
  30. * @return string
  31. */
  32. public function write()
  33. {
  34. $xmlWriter = $this->getXmlWriter();
  35. // XML header
  36. $xmlWriter->startDocument('1.0', 'UTF-8');
  37. $xmlWriter->startElement('office:document-styles');
  38. $this->writeCommonRootAttributes($xmlWriter);
  39. // Font declarations
  40. $this->writeFontFaces($xmlWriter);
  41. // Office styles
  42. $xmlWriter->startElement('office:styles');
  43. $this->writeDefault($xmlWriter);
  44. $this->writeNamed($xmlWriter);
  45. $xmlWriter->endElement();
  46. // Automatic styles
  47. $xmlWriter->startElement('office:automatic-styles');
  48. $this->writePageLayout($xmlWriter);
  49. $xmlWriter->endElement(); // office:automatic-styles
  50. // Master style
  51. $this->writeMaster($xmlWriter);
  52. $xmlWriter->endElement(); // office:document-styles
  53. return $xmlWriter->getData();
  54. }
  55. /**
  56. * Write default styles.
  57. *
  58. * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter
  59. */
  60. private function writeDefault(XMLWriter $xmlWriter)
  61. {
  62. $xmlWriter->startElement('style:default-style');
  63. $xmlWriter->writeAttribute('style:family', 'paragraph');
  64. // Paragraph
  65. $xmlWriter->startElement('style:paragraph-properties');
  66. $xmlWriter->writeAttribute('fo:hyphenation-ladder-count', 'no-limit');
  67. $xmlWriter->writeAttribute('style:text-autospace', 'ideograph-alpha');
  68. $xmlWriter->writeAttribute('style:punctuation-wrap', 'hanging');
  69. $xmlWriter->writeAttribute('style:line-break', 'strict');
  70. $xmlWriter->writeAttribute('style:tab-stop-distance', '1.249cm');
  71. $xmlWriter->writeAttribute('style:writing-mode', 'page');
  72. $xmlWriter->endElement(); // style:paragraph-properties
  73. $language = $this->getParentWriter()->getPhpWord()->getSettings()->getThemeFontLang();
  74. $latinLang = $language != null && is_string($language->getLatin()) ? explode('-', $language->getLatin()) : array('fr', 'FR');
  75. $asianLang = $language != null && is_string($language->getEastAsia()) ? explode('-', $language->getEastAsia()) : array('zh', 'CN');
  76. $complexLang = $language != null && is_string($language->getBidirectional()) ? explode('-', $language->getBidirectional()) : array('hi', 'IN');
  77. if ($this->getParentWriter()->getPhpWord()->getSettings()->hasHideGrammaticalErrors()) {
  78. $latinLang = $asianLang = $complexLang = array('zxx', 'none');
  79. }
  80. // Font
  81. $xmlWriter->startElement('style:text-properties');
  82. $xmlWriter->writeAttribute('style:use-window-font-color', 'true');
  83. $xmlWriter->writeAttribute('style:font-name', Settings::getDefaultFontName());
  84. $xmlWriter->writeAttribute('fo:font-size', Settings::getDefaultFontSize() . 'pt');
  85. $xmlWriter->writeAttribute('fo:language', $latinLang[0]);
  86. $xmlWriter->writeAttribute('fo:country', $latinLang[1]);
  87. $xmlWriter->writeAttribute('style:letter-kerning', 'true');
  88. $xmlWriter->writeAttribute('style:font-name-asian', Settings::getDefaultFontName() . '2');
  89. $xmlWriter->writeAttribute('style:font-size-asian', Settings::getDefaultFontSize() . 'pt');
  90. $xmlWriter->writeAttribute('style:language-asian', $asianLang[0]);
  91. $xmlWriter->writeAttribute('style:country-asian', $asianLang[1]);
  92. $xmlWriter->writeAttribute('style:font-name-complex', Settings::getDefaultFontName() . '2');
  93. $xmlWriter->writeAttribute('style:font-size-complex', Settings::getDefaultFontSize() . 'pt');
  94. $xmlWriter->writeAttribute('style:language-complex', $complexLang[0]);
  95. $xmlWriter->writeAttribute('style:country-complex', $complexLang[1]);
  96. $xmlWriter->writeAttribute('fo:hyphenate', 'false');
  97. $xmlWriter->writeAttribute('fo:hyphenation-remain-char-count', '2');
  98. $xmlWriter->writeAttribute('fo:hyphenation-push-char-count', '2');
  99. $xmlWriter->endElement(); // style:text-properties
  100. $xmlWriter->endElement(); // style:default-style
  101. }
  102. /**
  103. * Write named styles.
  104. *
  105. * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter
  106. */
  107. private function writeNamed(XMLWriter $xmlWriter)
  108. {
  109. $styles = Style::getStyles();
  110. if (count($styles) > 0) {
  111. foreach ($styles as $style) {
  112. if ($style->isAuto() === false) {
  113. $styleClass = str_replace('\\Style\\', '\\Writer\\ODText\\Style\\', get_class($style));
  114. if (class_exists($styleClass)) {
  115. /** @var $styleWriter \PhpOffice\PhpWord\Writer\ODText\Style\AbstractStyle Type hint */
  116. $styleWriter = new $styleClass($xmlWriter, $style);
  117. $styleWriter->write();
  118. }
  119. }
  120. }
  121. }
  122. }
  123. /**
  124. * Convert int in twips to inches/cm then to string and append unit
  125. *
  126. * @param int|float $twips
  127. * @param float $factor
  128. * return string
  129. */
  130. private static function cvttwiptostr($twips, $factor = 1.0)
  131. {
  132. $ins = (string) ($twips * $factor / Converter::INCH_TO_TWIP) . 'in';
  133. $cms = (string) ($twips * $factor * Converter::INCH_TO_CM / Converter::INCH_TO_TWIP) . 'cm';
  134. return (strlen($ins) < strlen($cms)) ? $ins : $cms;
  135. }
  136. /**
  137. * call writePageLayoutIndiv to write page layout styles for each page
  138. *
  139. * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter
  140. */
  141. private function writePageLayout(XMLWriter $xmlWriter)
  142. {
  143. $sections = $this->getParentWriter()->getPhpWord()->getSections();
  144. $countsects = count($sections);
  145. for ($i = 0; $i < $countsects; ++$i) {
  146. $this->writePageLayoutIndiv($xmlWriter, $sections[$i], $i + 1);
  147. }
  148. }
  149. /**
  150. * Write page layout styles.
  151. *
  152. * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter
  153. * @param \PhpOffice\PhpWord\Element\Section $section
  154. * @param int $sectionNbr
  155. */
  156. private function writePageLayoutIndiv(XMLWriter $xmlWriter, $section, $sectionNbr)
  157. {
  158. $sty = $section->getStyle();
  159. if (count($section->getHeaders()) > 0) {
  160. $topfactor = 0.5;
  161. } else {
  162. $topfactor = 1.0;
  163. }
  164. if (count($section->getFooters()) > 0) {
  165. $botfactor = 0.5;
  166. } else {
  167. $botfactor = 1.0;
  168. }
  169. $orient = $sty->getOrientation();
  170. $pwidth = self::cvttwiptostr($sty->getPageSizeW());
  171. $pheight = self::cvttwiptostr($sty->getPageSizeH());
  172. $mtop = self::cvttwiptostr($sty->getMarginTop(), $topfactor);
  173. $mbottom = self::cvttwiptostr($sty->getMarginBottom(), $botfactor);
  174. $mleft = self::cvttwiptostr($sty->getMarginRight());
  175. $mright = self::cvttwiptostr($sty->getMarginLeft());
  176. $xmlWriter->startElement('style:page-layout');
  177. $xmlWriter->writeAttribute('style:name', "Mpm$sectionNbr");
  178. $xmlWriter->startElement('style:page-layout-properties');
  179. $xmlWriter->writeAttribute('fo:page-width', $pwidth);
  180. $xmlWriter->writeAttribute('fo:page-height', $pheight);
  181. $xmlWriter->writeAttribute('style:num-format', '1');
  182. $xmlWriter->writeAttribute('style:print-orientation', $orient);
  183. $xmlWriter->writeAttribute('fo:margin-top', $mtop);
  184. $xmlWriter->writeAttribute('fo:margin-bottom', $mbottom);
  185. $xmlWriter->writeAttribute('fo:margin-left', $mleft);
  186. $xmlWriter->writeAttribute('fo:margin-right', $mright);
  187. $xmlWriter->writeAttribute('style:writing-mode', 'lr-tb');
  188. $xmlWriter->writeAttribute('style:layout-grid-color', '#c0c0c0');
  189. $xmlWriter->writeAttribute('style:layout-grid-lines', '25199');
  190. $xmlWriter->writeAttribute('style:layout-grid-base-height', '0.423cm');
  191. $xmlWriter->writeAttribute('style:layout-grid-ruby-height', '0cm');
  192. $xmlWriter->writeAttribute('style:layout-grid-mode', 'none');
  193. $xmlWriter->writeAttribute('style:layout-grid-ruby-below', 'false');
  194. $xmlWriter->writeAttribute('style:layout-grid-print', 'false');
  195. $xmlWriter->writeAttribute('style:layout-grid-display', 'false');
  196. $xmlWriter->writeAttribute('style:layout-grid-base-width', '0.37cm');
  197. $xmlWriter->writeAttribute('style:layout-grid-snap-to', 'true');
  198. $xmlWriter->writeAttribute('style:footnote-max-height', '0cm');
  199. $xmlWriter->startElement('style:footnote-sep');
  200. $xmlWriter->writeAttribute('style:width', '0.018cm');
  201. $xmlWriter->writeAttribute('style:line-style', 'solid');
  202. $xmlWriter->writeAttribute('style:adjustment', 'left');
  203. $xmlWriter->writeAttribute('style:rel-width', '25%');
  204. $xmlWriter->writeAttribute('style:color', '#000000');
  205. $xmlWriter->endElement(); //style:footnote-sep
  206. $xmlWriter->endElement(); // style:page-layout-properties
  207. $xmlWriter->startElement('style:header-style');
  208. if ($topfactor < 1.0) {
  209. $xmlWriter->startElement('style:header-footer-properties');
  210. $xmlWriter->writeAttribute('fo:min-height', $mtop);
  211. $xmlWriter->writeAttribute('fo:margin-bottom', $mtop);
  212. $xmlWriter->writeAttribute('style:dynamic-spacing', 'true');
  213. $xmlWriter->endElement(); // style:header-footer-properties
  214. }
  215. $xmlWriter->endElement(); // style:header-style
  216. $xmlWriter->startElement('style:footer-style');
  217. if ($botfactor < 1.0) {
  218. $xmlWriter->startElement('style:header-footer-properties');
  219. $xmlWriter->writeAttribute('fo:min-height', $mbottom);
  220. $xmlWriter->writeAttribute('fo:margin-top', $mbottom);
  221. $xmlWriter->writeAttribute('style:dynamic-spacing', 'true');
  222. $xmlWriter->endElement(); // style:header-footer-properties
  223. }
  224. $xmlWriter->endElement(); // style:footer-style
  225. $xmlWriter->endElement(); // style:page-layout
  226. }
  227. /**
  228. * Write master style.
  229. *
  230. * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter
  231. */
  232. private function writeMaster(XMLWriter $xmlWriter)
  233. {
  234. $xmlWriter->startElement('office:master-styles');
  235. $sections = $this->getParentWriter()->getPhpWord()->getSections();
  236. $countsects = count($sections);
  237. for ($i = 0; $i < $countsects; ++$i) {
  238. $iplus1 = $i + 1;
  239. $xmlWriter->startElement('style:master-page');
  240. $xmlWriter->writeAttribute('style:name', "Standard$iplus1");
  241. $xmlWriter->writeAttribute('style:page-layout-name', "Mpm$iplus1");
  242. // Multiple headers and footers probably not supported,
  243. // and, even if they are, I'm not sure how,
  244. // so quit after generating one.
  245. foreach ($sections[$i]->getHeaders() as $hdr) {
  246. $xmlWriter->startElement('style:header');
  247. foreach ($hdr->getElements() as $elem) {
  248. $cl1 = get_class($elem);
  249. $cl2 = str_replace('\\Element\\', '\\Writer\\ODText\\Element\\', $cl1);
  250. if (class_exists($cl2)) {
  251. $wtr = new $cl2($xmlWriter, $elem);
  252. $wtr->write();
  253. }
  254. }
  255. $xmlWriter->endElement(); // style:header
  256. break;
  257. }
  258. foreach ($sections[$i]->getFooters() as $hdr) {
  259. $xmlWriter->startElement('style:footer');
  260. foreach ($hdr->getElements() as $elem) {
  261. $cl1 = get_class($elem);
  262. $cl2 = str_replace('\\Element\\', '\\Writer\\ODText\\Element\\', $cl1);
  263. if (class_exists($cl2)) {
  264. $wtr = new $cl2($xmlWriter, $elem);
  265. $wtr->write();
  266. }
  267. }
  268. $xmlWriter->endElement(); // style:footer
  269. break;
  270. }
  271. $xmlWriter->endElement(); // style:master-page
  272. }
  273. $xmlWriter->endElement(); // office:master-styles
  274. }
  275. }