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