Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
86 / 86
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
Numbering
100.00% covered (success)
100.00%
86 / 86
100.00% covered (success)
100.00%
5 / 5
14
100.00% covered (success)
100.00%
1 / 1
 write
100.00% covered (success)
100.00%
39 / 39
100.00% covered (success)
100.00%
1 / 1
7
 writeLevel
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
4
 writeParagraph
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
1
 writeFont
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 getRandomHexNumber
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
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 *
15 * @license     http://www.gnu.org/licenses/lgpl.txt LGPL version 3
16 */
17
18namespace PhpOffice\PhpWord\Writer\Word2007\Part;
19
20use PhpOffice\PhpWord\Shared\XMLWriter;
21use PhpOffice\PhpWord\Style;
22use PhpOffice\PhpWord\Style\Numbering as NumberingStyle;
23use PhpOffice\PhpWord\Style\NumberingLevel;
24
25/**
26 * Word2007 numbering part writer: word/numbering.xml.
27 *
28 * @since 0.10.0
29 */
30class Numbering extends AbstractPart
31{
32    /**
33     * Write part.
34     *
35     * @return string
36     */
37    public function write()
38    {
39        $xmlWriter = $this->getXmlWriter();
40        $styles = Style::getStyles();
41        $drawingSchema = 'http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing';
42
43        $xmlWriter->startDocument('1.0', 'UTF-8', 'yes');
44        $xmlWriter->startElement('w:numbering');
45        $xmlWriter->writeAttribute('xmlns:ve', 'http://schemas.openxmlformats.org/markup-compatibility/2006');
46        $xmlWriter->writeAttribute('xmlns:o', 'urn:schemas-microsoft-com:office:office');
47        $xmlWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
48        $xmlWriter->writeAttribute('xmlns:m', 'http://schemas.openxmlformats.org/officeDocument/2006/math');
49        $xmlWriter->writeAttribute('xmlns:v', 'urn:schemas-microsoft-com:vml');
50        $xmlWriter->writeAttribute('xmlns:wp', $drawingSchema);
51        $xmlWriter->writeAttribute('xmlns:w10', 'urn:schemas-microsoft-com:office:word');
52        $xmlWriter->writeAttribute('xmlns:w', 'http://schemas.openxmlformats.org/wordprocessingml/2006/main');
53        $xmlWriter->writeAttribute('xmlns:wne', 'http://schemas.microsoft.com/office/word/2006/wordml');
54
55        // Abstract numbering definitions
56        foreach ($styles as $style) {
57            if ($style instanceof NumberingStyle) {
58                $levels = $style->getLevels();
59
60                $xmlWriter->startElement('w:abstractNum');
61                $xmlWriter->writeAttribute('w:abstractNumId', $style->getIndex());
62
63                $xmlWriter->startElement('w:nsid');
64                $xmlWriter->writeAttribute('w:val', $this->getRandomHexNumber());
65                $xmlWriter->endElement(); // w:nsid
66
67                $xmlWriter->startElement('w:multiLevelType');
68                $xmlWriter->writeAttribute('w:val', $style->getType());
69                $xmlWriter->endElement(); // w:multiLevelType
70
71                if (is_array($levels)) {
72                    foreach ($levels as $level) {
73                        $this->writeLevel($xmlWriter, $level);
74                    }
75                }
76                $xmlWriter->endElement(); // w:abstractNum
77            }
78        }
79
80        // Numbering definition instances
81        foreach ($styles as $style) {
82            if ($style instanceof NumberingStyle) {
83                $xmlWriter->startElement('w:num');
84                $xmlWriter->writeAttribute('w:numId', $style->getIndex());
85                $xmlWriter->startElement('w:abstractNumId');
86                $xmlWriter->writeAttribute('w:val', $style->getIndex());
87                $xmlWriter->endElement(); // w:abstractNumId
88                $xmlWriter->endElement(); // w:num
89            }
90        }
91
92        $xmlWriter->endElement(); // w:numbering
93
94        return $xmlWriter->getData();
95    }
96
97    /**
98     * Write level.
99     */
100    private function writeLevel(XMLWriter $xmlWriter, NumberingLevel $level): void
101    {
102        $xmlWriter->startElement('w:lvl');
103        $xmlWriter->writeAttribute('w:ilvl', $level->getLevel());
104
105        // Numbering level properties
106        $properties = [
107            'start' => 'start',
108            'format' => 'numFmt',
109            'restart' => 'lvlRestart',
110            'pStyle' => 'pStyle',
111            'suffix' => 'suff',
112            'text' => 'lvlText',
113            'alignment' => 'lvlJc',
114        ];
115        foreach ($properties as $property => $nodeName) {
116            $getMethod = "get{$property}";
117            if ('' !== $level->$getMethod()         // this condition is now supported by `alignment` only
118                && null !== $level->$getMethod()) {
119                $xmlWriter->startElement("w:{$nodeName}");
120                $xmlWriter->writeAttribute('w:val', $level->$getMethod());
121                $xmlWriter->endElement(); // w:start
122            }
123        }
124
125        // Paragraph & font styles
126        $this->writeParagraph($xmlWriter, $level);
127        $this->writeFont($xmlWriter, $level);
128
129        $xmlWriter->endElement(); // w:lvl
130    }
131
132    /**
133     * Write level paragraph.
134     *
135     * @since 0.11.0
136     *
137     * @todo Use paragraph style writer
138     */
139    private function writeParagraph(XMLWriter $xmlWriter, NumberingLevel $level): void
140    {
141        $tabPos = $level->getTabPos();
142        $left = $level->getLeft();
143        $hanging = $level->getHanging();
144
145        $xmlWriter->startElement('w:pPr');
146
147        $xmlWriter->startElement('w:tabs');
148        $xmlWriter->startElement('w:tab');
149        $xmlWriter->writeAttribute('w:val', 'num');
150        $xmlWriter->writeAttributeIf($tabPos !== null, 'w:pos', $tabPos);
151        $xmlWriter->endElement(); // w:tab
152        $xmlWriter->endElement(); // w:tabs
153
154        $xmlWriter->startElement('w:ind');
155        $xmlWriter->writeAttributeIf($left !== null, 'w:left', $left);
156        $xmlWriter->writeAttributeIf($hanging !== null, 'w:hanging', $hanging);
157        $xmlWriter->endElement(); // w:ind
158
159        $xmlWriter->endElement(); // w:pPr
160    }
161
162    /**
163     * Write level font.
164     *
165     * @since 0.11.0
166     *
167     * @todo Use font style writer
168     */
169    private function writeFont(XMLWriter $xmlWriter, NumberingLevel $level): void
170    {
171        $font = $level->getFont();
172        $hint = $level->getHint();
173
174        $xmlWriter->startElement('w:rPr');
175        $xmlWriter->startElement('w:rFonts');
176        $xmlWriter->writeAttributeIf($font !== null, 'w:ascii', $font);
177        $xmlWriter->writeAttributeIf($font !== null, 'w:hAnsi', $font);
178        $xmlWriter->writeAttributeIf($font !== null, 'w:cs', $font);
179        $xmlWriter->writeAttributeIf($hint !== null, 'w:hint', $hint);
180        $xmlWriter->endElement(); // w:rFonts
181        $xmlWriter->endElement(); // w:rPr
182    }
183
184    /**
185     * Get random hexadecimal number value.
186     *
187     * @param int $length
188     *
189     * @return string
190     */
191    private function getRandomHexNumber($length = 8)
192    {
193        return strtoupper(substr(md5(mt_rand()), 0, $length));
194    }
195}