Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
99.12% covered (success)
99.12%
112 / 113
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
Head
99.12% covered (success)
99.12%
112 / 113
66.67% covered (warning)
66.67%
2 / 3
22
0.00% covered (danger)
0.00%
0 / 1
 write
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
1 / 1
5
 writeStyles
100.00% covered (success)
100.00%
78 / 78
100.00% covered (success)
100.00%
1 / 1
14
 getFontFamily
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
1<?php
2
3/**
4 * This file is part of PHPWord - A pure PHP library for reading and writing
5 * word processing documents.
6 *
7 * PHPWord is free software distributed under the terms of the GNU Lesser
8 * General Public License version 3 as published by the Free Software Foundation.
9 *
10 * For the full copyright and license information, please read the LICENSE
11 * file that was distributed with this source code. For the full list of
12 * contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
13 *
14 * @see         https://github.com/PHPOffice/PHPWord
15 *
16 * @license     http://www.gnu.org/licenses/lgpl.txt LGPL version 3
17 */
18
19namespace PhpOffice\PhpWord\Writer\HTML\Part;
20
21use PhpOffice\PhpWord\Settings;
22use PhpOffice\PhpWord\Shared\Converter;
23use PhpOffice\PhpWord\Style;
24use PhpOffice\PhpWord\Style\Font;
25use PhpOffice\PhpWord\Style\Paragraph;
26use PhpOffice\PhpWord\Style\Table;
27use PhpOffice\PhpWord\Writer\HTML\Style\Font as FontStyleWriter;
28use PhpOffice\PhpWord\Writer\HTML\Style\Generic as GenericStyleWriter;
29use PhpOffice\PhpWord\Writer\HTML\Style\Paragraph as ParagraphStyleWriter;
30use PhpOffice\PhpWord\Writer\HTML\Style\Table as TableStyleWriter;
31
32/**
33 * HTML head part writer.
34 *
35 * @since 0.11.0
36 */
37class Head extends AbstractPart
38{
39    /**
40     * Write part.
41     *
42     * @return string
43     */
44    public function write()
45    {
46        $docProps = $this->getParentWriter()->getPhpWord()->getDocInfo();
47        $propertiesMapping = [
48            'creator' => 'author',
49            'title' => '',
50            'description' => '',
51            'subject' => '',
52            'keywords' => '',
53            'category' => '',
54            'company' => '',
55            'manager' => '',
56        ];
57        $title = $docProps->getTitle();
58        $title = ($title != '') ? $title : 'PHPWord';
59
60        $content = '';
61
62        $content .= '<head>' . PHP_EOL;
63        $content .= '<meta charset="UTF-8" />' . PHP_EOL;
64        $content .= '<title>' . $title . '</title>' . PHP_EOL;
65        foreach ($propertiesMapping as $key => $value) {
66            $value = ($value == '') ? $key : $value;
67            $method = 'get' . $key;
68            if ($docProps->$method() != '') {
69                $content .= '<meta name="' . $value . '"'
70                    . ' content="'
71                    . $this->getParentWriter()->escapeHTML($docProps->$method())
72                    . '"'
73                    . ' />' . PHP_EOL;
74            }
75        }
76        $content .= $this->writeStyles();
77        $content .= '</head>' . PHP_EOL;
78
79        return $content;
80    }
81
82    /**
83     * Get styles.
84     */
85    private function writeStyles(): string
86    {
87        $css = '<style>' . PHP_EOL;
88        $defaultFontColor = Settings::getDefaultFontColor();
89        // Default styles
90        $astarray = [
91            'font-family' => $this->getFontFamily(Settings::getDefaultFontName(), $this->getParentWriter()->getDefaultGenericFont()),
92            'font-size' => Settings::getDefaultFontSize() . 'pt',
93            'color' => "#{$defaultFontColor}",
94        ];
95        // Mpdf sometimes needs separate tag for body; doesn't harm others.
96        $bodyarray = $astarray;
97
98        $defaultWhiteSpace = $this->getParentWriter()->getDefaultWhiteSpace();
99        if ($defaultWhiteSpace) {
100            $astarray['white-space'] = $defaultWhiteSpace;
101        }
102
103        foreach ([
104            'body' => $bodyarray,
105            '*' => $astarray,
106            'a.NoteRef' => [
107                'text-decoration' => 'none',
108            ],
109            'hr' => [
110                'height' => '1px',
111                'padding' => '0',
112                'margin' => '1em 0',
113                'border' => '0',
114                'border-top' => '1px solid #CCC',
115            ],
116            'table' => [
117                'border' => '1px solid black',
118                'border-spacing' => '0px',
119                'width ' => '100%',
120            ],
121            'td' => [
122                'border' => '1px solid black',
123            ],
124        ] as $selector => $style) {
125            $styleWriter = new GenericStyleWriter($style);
126            $css .= $selector . ' {' . $styleWriter->write() . '}' . PHP_EOL;
127        }
128
129        // Custom styles
130        $customStyles = Style::getStyles();
131        if (is_array($customStyles)) {
132            foreach ($customStyles as $name => $style) {
133                $styleParagraph = null;
134                if ($style instanceof Font) {
135                    $styleWriter = new FontStyleWriter($style);
136                    if ($style->getStyleType() == 'title') {
137                        $name = str_replace('Heading_', 'h', $name);
138                        $styleParagraph = $style->getParagraph();
139                        $style = $styleParagraph;
140                    } else {
141                        $name = '.' . $name;
142                    }
143                    $css .= "{$name} {" . $styleWriter->write() . '}' . PHP_EOL;
144                }
145                if ($style instanceof Paragraph) {
146                    $styleWriter = new ParagraphStyleWriter($style);
147                    $styleWriter->setParentWriter($this->getParentWriter());
148                    if (!$styleParagraph) {
149                        $name = '.' . $name;
150                    }
151                    if ($name === '.Normal') {
152                        $name = "p, $name";
153                    }
154                    $css .= "{$name} {" . $styleWriter->write() . '}' . PHP_EOL;
155                }
156                if ($style instanceof Table) {
157                    $styleWriter = new TableStyleWriter($style);
158                    $css .= ".{$name} {" . $styleWriter->write() . '}' . PHP_EOL;
159                }
160            }
161        }
162
163        $css .= 'body > div + div {page-break-before: always;}' . PHP_EOL;
164        $css .= 'div > *:first-child {page-break-before: auto;}' . PHP_EOL;
165
166        $sectionNum = 0;
167        foreach ($this->getParentWriter()->getPhpWord()->getSections() as $section) {
168            ++$sectionNum;
169
170            $css .= "@page page$sectionNum {";
171
172            $paperSize = $section->getStyle()->getPaperSize();
173            $orientation = $section->getStyle()->getOrientation();
174            if ($this->getParentWriter()->isPdf()) {
175                if ($orientation === 'landscape') {
176                    $paperSize .= '-L';
177                }
178                $css .= "sheet-size: $paperSize";
179            } else {
180                $css .= "size: $paperSize $orientation";
181            }
182
183            $css .= 'margin-right: ' . (string) ($section->getStyle()->getMarginRight() / Converter::INCH_TO_TWIP) . 'in; ';
184            $css .= 'margin-left: ' . (string) ($section->getStyle()->getMarginLeft() / Converter::INCH_TO_TWIP) . 'in; ';
185            $css .= 'margin-top: ' . (string) ($section->getStyle()->getMarginTop() / Converter::INCH_TO_TWIP) . 'in; ';
186            $css .= 'margin-bottom: ' . (string) ($section->getStyle()->getMarginBottom() / Converter::INCH_TO_TWIP) . 'in; ';
187            $css .= '}' . PHP_EOL;
188        }
189
190        $css .= '</style>' . PHP_EOL;
191
192        return $css;
193    }
194
195    /**
196     * Set font and alternates for css font-family.
197     */
198    private function getFontFamily(string $font, string $genericFont): string
199    {
200        if (empty($font)) {
201            return '';
202        }
203        $fontfamily = "'" . htmlspecialchars($font, ENT_QUOTES, 'UTF-8') . "'";
204        if (!empty($genericFont)) {
205            $fontfamily .= "$genericFont";
206        }
207
208        return $fontfamily;
209    }
210}