Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
85 / 85
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
Numbering
100.00% covered (success)
100.00%
85 / 85
100.00% covered (success)
100.00%
5 / 5
13
100.00% covered (success)
100.00%
1 / 1
 write
100.00% covered (success)
100.00%
38 / 38
100.00% covered (success)
100.00%
1 / 1
6
 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                foreach ($levels as $level) {
73                    $this->writeLevel($xmlWriter, $level);
74                }
75                $xmlWriter->endElement(); // w:abstractNum
76            }
77        }
78
79        // Numbering definition instances
80        foreach ($styles as $style) {
81            if ($style instanceof NumberingStyle) {
82                $xmlWriter->startElement('w:num');
83                $xmlWriter->writeAttribute('w:numId', $style->getIndex());
84                $xmlWriter->startElement('w:abstractNumId');
85                $xmlWriter->writeAttribute('w:val', $style->getIndex());
86                $xmlWriter->endElement(); // w:abstractNumId
87                $xmlWriter->endElement(); // w:num
88            }
89        }
90
91        $xmlWriter->endElement(); // w:numbering
92
93        return $xmlWriter->getData();
94    }
95
96    /**
97     * Write level.
98     */
99    private function writeLevel(XMLWriter $xmlWriter, NumberingLevel $level): void
100    {
101        $xmlWriter->startElement('w:lvl');
102        $xmlWriter->writeAttribute('w:ilvl', $level->getLevel());
103
104        // Numbering level properties
105        $properties = [
106            'start' => 'start',
107            'format' => 'numFmt',
108            'restart' => 'lvlRestart',
109            'pStyle' => 'pStyle',
110            'suffix' => 'suff',
111            'text' => 'lvlText',
112            'alignment' => 'lvlJc',
113        ];
114        foreach ($properties as $property => $nodeName) {
115            $getMethod = "get{$property}";
116            if ('' !== $level->$getMethod()         // this condition is now supported by `alignment` only
117                && null !== $level->$getMethod()) {
118                $xmlWriter->startElement("w:{$nodeName}");
119                $xmlWriter->writeAttribute('w:val', $level->$getMethod());
120                $xmlWriter->endElement(); // w:start
121            }
122        }
123
124        // Paragraph & font styles
125        $this->writeParagraph($xmlWriter, $level);
126        $this->writeFont($xmlWriter, $level);
127
128        $xmlWriter->endElement(); // w:lvl
129    }
130
131    /**
132     * Write level paragraph.
133     *
134     * @since 0.11.0
135     *
136     * @todo Use paragraph style writer
137     */
138    private function writeParagraph(XMLWriter $xmlWriter, NumberingLevel $level): void
139    {
140        $tabPos = $level->getTabPos();
141        $left = $level->getLeft();
142        $hanging = $level->getHanging();
143
144        $xmlWriter->startElement('w:pPr');
145
146        $xmlWriter->startElement('w:tabs');
147        $xmlWriter->startElement('w:tab');
148        $xmlWriter->writeAttribute('w:val', 'num');
149        $xmlWriter->writeAttributeIf($tabPos !== null, 'w:pos', $tabPos);
150        $xmlWriter->endElement(); // w:tab
151        $xmlWriter->endElement(); // w:tabs
152
153        $xmlWriter->startElement('w:ind');
154        $xmlWriter->writeAttributeIf($left !== null, 'w:left', $left);
155        $xmlWriter->writeAttributeIf($hanging !== null, 'w:hanging', $hanging);
156        $xmlWriter->endElement(); // w:ind
157
158        $xmlWriter->endElement(); // w:pPr
159    }
160
161    /**
162     * Write level font.
163     *
164     * @since 0.11.0
165     *
166     * @todo Use font style writer
167     */
168    private function writeFont(XMLWriter $xmlWriter, NumberingLevel $level): void
169    {
170        $font = $level->getFont();
171        $hint = $level->getHint();
172
173        $xmlWriter->startElement('w:rPr');
174        $xmlWriter->startElement('w:rFonts');
175        $xmlWriter->writeAttributeIf($font !== null, 'w:ascii', $font);
176        $xmlWriter->writeAttributeIf($font !== null, 'w:hAnsi', $font);
177        $xmlWriter->writeAttributeIf($font !== null, 'w:cs', $font);
178        $xmlWriter->writeAttributeIf($hint !== null, 'w:hint', $hint);
179        $xmlWriter->endElement(); // w:rFonts
180        $xmlWriter->endElement(); // w:rPr
181    }
182
183    /**
184     * Get random hexadecimal number value.
185     *
186     * @param int $length
187     *
188     * @return string
189     */
190    private function getRandomHexNumber($length = 8)
191    {
192        return strtoupper((string) substr(md5((string) mt_rand()), 0, $length));
193    }
194}