Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
103 / 103
100.00% covered (success)
100.00%
50 / 50
CRAP
100.00% covered (success)
100.00%
1 / 1
Paragraph
100.00% covered (success)
100.00%
103 / 103
100.00% covered (success)
100.00%
50 / 50
58
100.00% covered (success)
100.00%
1 / 1
 setStyleValue
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 getStyleValues
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
1 / 1
1
 getAlignment
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setAlignment
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getBasedOn
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setBasedOn
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getNext
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setNext
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getIndentation
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setIndentation
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getIndent
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setIndent
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getHanging
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setHanging
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSpace
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setSpace
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getSpaceBefore
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setSpaceBefore
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSpaceAfter
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setSpaceAfter
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSpacing
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setSpacing
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSpacingLineRule
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setSpacingLineRule
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getLineHeight
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setLineHeight
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
5
 hasWidowControl
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setWidowControl
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 isKeepNext
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setKeepNext
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 isKeepLines
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setKeepLines
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 hasPageBreakBefore
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setPageBreakBefore
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getNumStyle
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setNumStyle
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getNumLevel
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setNumLevel
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getTabs
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setTabs
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getShading
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setShading
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 hasContextualSpacing
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setContextualSpacing
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 isBidi
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setBidi
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getTextAlignment
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setTextAlignment
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 hasSuppressAutoHyphens
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setSuppressAutoHyphens
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\Style;
19
20use PhpOffice\PhpWord\Exception\InvalidStyleException;
21use PhpOffice\PhpWord\Settings;
22use PhpOffice\PhpWord\Shared\Text;
23use PhpOffice\PhpWord\SimpleType\Jc;
24use PhpOffice\PhpWord\SimpleType\TextAlignment;
25
26/**
27 * Paragraph style.
28 *
29 * OOXML:
30 * - General: alignment, outline level
31 * - Indentation: left, right, firstline, hanging
32 * - Spacing: before, after, line spacing
33 * - Pagination: widow control, keep next, keep line, page break before
34 * - Formatting exception: suppress line numbers, don't hyphenate
35 * - Textbox options
36 * - Tabs
37 * - Shading
38 * - Borders
39 *
40 * OpenOffice:
41 * - Indents & spacing
42 * - Alignment
43 * - Text flow
44 * - Outline & numbering
45 * - Tabs
46 * - Dropcaps
47 * - Tabs
48 * - Borders
49 * - Background
50 *
51 * @see  http://www.schemacentral.com/sc/ooxml/t-w_CT_PPr.html
52 */
53class Paragraph extends Border
54{
55    /**
56     * @const int One line height equals 240 twip
57     */
58    const LINE_HEIGHT = 240;
59
60    /**
61     * Aliases.
62     *
63     * @var array
64     */
65    protected $aliases = ['line-height' => 'lineHeight', 'line-spacing' => 'spacing'];
66
67    /**
68     * Parent style.
69     *
70     * @var string
71     */
72    private $basedOn = 'Normal';
73
74    /**
75     * Style for next paragraph.
76     *
77     * @var string
78     */
79    private $next;
80
81    /**
82     * @var string
83     */
84    private $alignment = '';
85
86    /**
87     * Indentation.
88     *
89     * @var null|\PhpOffice\PhpWord\Style\Indentation
90     */
91    private $indentation;
92
93    /**
94     * Spacing.
95     *
96     * @var \PhpOffice\PhpWord\Style\Spacing
97     */
98    private $spacing;
99
100    /**
101     * Text line height.
102     *
103     * @var null|float|int
104     */
105    private $lineHeight;
106
107    /**
108     * Allow first/last line to display on a separate page.
109     *
110     * @var bool
111     */
112    private $widowControl = true;
113
114    /**
115     * Keep paragraph with next paragraph.
116     *
117     * @var bool
118     */
119    private $keepNext = false;
120
121    /**
122     * Keep all lines on one page.
123     *
124     * @var bool
125     */
126    private $keepLines = false;
127
128    /**
129     * Start paragraph on next page.
130     *
131     * @var bool
132     */
133    private $pageBreakBefore = false;
134
135    /**
136     * Numbering style name.
137     *
138     * @var string
139     */
140    private $numStyle;
141
142    /**
143     * Numbering level.
144     *
145     * @var int
146     */
147    private $numLevel = 0;
148
149    /**
150     * Set of Custom Tab Stops.
151     *
152     * @var \PhpOffice\PhpWord\Style\Tab[]
153     */
154    private $tabs = [];
155
156    /**
157     * Shading.
158     *
159     * @var \PhpOffice\PhpWord\Style\Shading
160     */
161    private $shading;
162
163    /**
164     * Ignore Spacing Above and Below When Using Identical Styles.
165     *
166     * @var bool
167     */
168    private $contextualSpacing = false;
169
170    /**
171     * Right to Left Paragraph Layout.
172     *
173     * @var ?bool
174     */
175    private $bidi;
176
177    /**
178     * Vertical Character Alignment on Line.
179     *
180     * @var string
181     */
182    private $textAlignment;
183
184    /**
185     * Suppress hyphenation for paragraph.
186     *
187     * @var bool
188     */
189    private $suppressAutoHyphens = false;
190
191    /**
192     * Set Style value.
193     *
194     * @param string $key
195     * @param mixed $value
196     *
197     * @return self
198     */
199    public function setStyleValue($key, $value)
200    {
201        $key = Text::removeUnderscorePrefix($key);
202        if ('indent' == $key || 'hanging' == $key) {
203            $value = $value * 720;  // 720 twips is 0.5 inch
204        }
205
206        return parent::setStyleValue($key, $value);
207    }
208
209    /**
210     * Get style values.
211     *
212     * An experiment to retrieve all style values in one function. This will
213     * reduce function call and increase cohesion between functions. Should be
214     * implemented in all styles.
215     *
216     * @ignoreScrutinizerPatch
217     *
218     * @return array
219     */
220    public function getStyleValues()
221    {
222        $styles = [
223            'name' => $this->getStyleName(),
224            'basedOn' => $this->getBasedOn(),
225            'next' => $this->getNext(),
226            'alignment' => $this->getAlignment(),
227            'indentation' => $this->getIndentation(),
228            'spacing' => $this->getSpace(),
229            'pagination' => [
230                'widowControl' => $this->hasWidowControl(),
231                'keepNext' => $this->isKeepNext(),
232                'keepLines' => $this->isKeepLines(),
233                'pageBreak' => $this->hasPageBreakBefore(),
234            ],
235            'numbering' => [
236                'style' => $this->getNumStyle(),
237                'level' => $this->getNumLevel(),
238            ],
239            'tabs' => $this->getTabs(),
240            'shading' => $this->getShading(),
241            'contextualSpacing' => $this->hasContextualSpacing(),
242            'bidi' => $this->isBidi(),
243            'textAlignment' => $this->getTextAlignment(),
244            'suppressAutoHyphens' => $this->hasSuppressAutoHyphens(),
245        ];
246
247        return $styles;
248    }
249
250    /**
251     * @since 0.13.0
252     *
253     * @return string
254     */
255    public function getAlignment()
256    {
257        return $this->alignment;
258    }
259
260    /**
261     * @since 0.13.0
262     *
263     * @param string $value
264     *
265     * @return self
266     */
267    public function setAlignment($value)
268    {
269        if (Jc::isValid($value)) {
270            $this->alignment = $value;
271        }
272
273        return $this;
274    }
275
276    /**
277     * Get parent style ID.
278     *
279     * @return string
280     */
281    public function getBasedOn()
282    {
283        return $this->basedOn;
284    }
285
286    /**
287     * Set parent style ID.
288     *
289     * @param string $value
290     *
291     * @return self
292     */
293    public function setBasedOn($value = 'Normal')
294    {
295        $this->basedOn = $value;
296
297        return $this;
298    }
299
300    /**
301     * Get style for next paragraph.
302     *
303     * @return string
304     */
305    public function getNext()
306    {
307        return $this->next;
308    }
309
310    /**
311     * Set style for next paragraph.
312     *
313     * @param string $value
314     *
315     * @return self
316     */
317    public function setNext($value = null)
318    {
319        $this->next = $value;
320
321        return $this;
322    }
323
324    /**
325     * Get indentation.
326     *
327     * @return null|\PhpOffice\PhpWord\Style\Indentation
328     */
329    public function getIndentation()
330    {
331        return $this->indentation;
332    }
333
334    /**
335     * Set shading.
336     *
337     * @param mixed $value
338     *
339     * @return self
340     */
341    public function setIndentation($value = null)
342    {
343        $this->setObjectVal($value, 'Indentation', $this->indentation);
344
345        return $this;
346    }
347
348    /**
349     * Get indentation.
350     *
351     * @return int
352     */
353    public function getIndent()
354    {
355        return $this->getChildStyleValue($this->indentation, 'left');
356    }
357
358    /**
359     * Set indentation.
360     *
361     * @param int $value
362     *
363     * @return self
364     */
365    public function setIndent($value = null)
366    {
367        return $this->setIndentation(['left' => $value]);
368    }
369
370    /**
371     * Get hanging.
372     *
373     * @return int
374     */
375    public function getHanging()
376    {
377        return $this->getChildStyleValue($this->indentation, 'hanging');
378    }
379
380    /**
381     * Set hanging.
382     *
383     * @param int $value
384     *
385     * @return self
386     */
387    public function setHanging($value = null)
388    {
389        return $this->setIndentation(['hanging' => $value]);
390    }
391
392    /**
393     * Get spacing.
394     *
395     * @return \PhpOffice\PhpWord\Style\Spacing
396     *
397     * @todo Rename to getSpacing in 1.0
398     */
399    public function getSpace()
400    {
401        return $this->spacing;
402    }
403
404    /**
405     * Set spacing.
406     *
407     * @param mixed $value
408     *
409     * @return self
410     *
411     * @todo Rename to setSpacing in 1.0
412     */
413    public function setSpace($value = null)
414    {
415        $this->setObjectVal($value, 'Spacing', $this->spacing);
416
417        return $this;
418    }
419
420    /**
421     * Get space before paragraph.
422     *
423     * @return null|float|int
424     */
425    public function getSpaceBefore()
426    {
427        return $this->getChildStyleValue($this->spacing, 'before');
428    }
429
430    /**
431     * Set space before paragraph.
432     *
433     * @param null|float|int $value
434     *
435     * @return self
436     */
437    public function setSpaceBefore($value = null)
438    {
439        return $this->setSpace(['before' => $value]);
440    }
441
442    /**
443     * Get space after paragraph.
444     *
445     * @return null|float|int
446     */
447    public function getSpaceAfter()
448    {
449        return $this->getChildStyleValue($this->spacing, 'after');
450    }
451
452    /**
453     * Set space after paragraph.
454     *
455     * @param null|float|int $value
456     *
457     * @return self
458     */
459    public function setSpaceAfter($value = null)
460    {
461        return $this->setSpace(['after' => $value]);
462    }
463
464    /**
465     * Get spacing between lines.
466     *
467     * @return null|float|int
468     */
469    public function getSpacing()
470    {
471        return $this->getChildStyleValue($this->spacing, 'line');
472    }
473
474    /**
475     * Set spacing between lines.
476     *
477     * @param null|float|int $value
478     *
479     * @return self
480     */
481    public function setSpacing($value = null)
482    {
483        return $this->setSpace(['line' => $value]);
484    }
485
486    /**
487     * Get spacing line rule.
488     *
489     * @return string
490     */
491    public function getSpacingLineRule()
492    {
493        return $this->getChildStyleValue($this->spacing, 'lineRule');
494    }
495
496    /**
497     * Set the spacing line rule.
498     *
499     * @param string $value Possible values are defined in LineSpacingRule
500     *
501     * @return \PhpOffice\PhpWord\Style\Paragraph
502     */
503    public function setSpacingLineRule($value)
504    {
505        return $this->setSpace(['lineRule' => $value]);
506    }
507
508    /**
509     * Get line height.
510     *
511     * @return null|float|int
512     */
513    public function getLineHeight()
514    {
515        return $this->lineHeight;
516    }
517
518    /**
519     * Set the line height.
520     *
521     * @param float|int|string $lineHeight
522     *
523     * @return self
524     */
525    public function setLineHeight($lineHeight)
526    {
527        if (is_string($lineHeight)) {
528            $lineHeight = (float) (preg_replace('/[^0-9\.\,]/', '', $lineHeight));
529        }
530
531        if ((!is_int($lineHeight) && !is_float($lineHeight)) || !$lineHeight) {
532            throw new InvalidStyleException('Line height must be a valid number');
533        }
534
535        $this->lineHeight = $lineHeight;
536        $this->setSpacing(($lineHeight - 1) * self::LINE_HEIGHT);
537        $this->setSpacingLineRule(\PhpOffice\PhpWord\SimpleType\LineSpacingRule::AUTO);
538
539        return $this;
540    }
541
542    /**
543     * Get allow first/last line to display on a separate page setting.
544     *
545     * @return bool
546     */
547    public function hasWidowControl()
548    {
549        return $this->widowControl;
550    }
551
552    /**
553     * Set keep paragraph with next paragraph setting.
554     *
555     * @param bool $value
556     *
557     * @return self
558     */
559    public function setWidowControl($value = true)
560    {
561        $this->widowControl = $this->setBoolVal($value, $this->widowControl);
562
563        return $this;
564    }
565
566    /**
567     * Get keep paragraph with next paragraph setting.
568     *
569     * @return bool
570     */
571    public function isKeepNext()
572    {
573        return $this->keepNext;
574    }
575
576    /**
577     * Set keep paragraph with next paragraph setting.
578     *
579     * @param bool $value
580     *
581     * @return self
582     */
583    public function setKeepNext($value = true)
584    {
585        $this->keepNext = $this->setBoolVal($value, $this->keepNext);
586
587        return $this;
588    }
589
590    /**
591     * Get keep all lines on one page setting.
592     *
593     * @return bool
594     */
595    public function isKeepLines()
596    {
597        return $this->keepLines;
598    }
599
600    /**
601     * Set keep all lines on one page setting.
602     *
603     * @param bool $value
604     *
605     * @return self
606     */
607    public function setKeepLines($value = true)
608    {
609        $this->keepLines = $this->setBoolVal($value, $this->keepLines);
610
611        return $this;
612    }
613
614    /**
615     * Get start paragraph on next page setting.
616     *
617     * @return bool
618     */
619    public function hasPageBreakBefore()
620    {
621        return $this->pageBreakBefore;
622    }
623
624    /**
625     * Set start paragraph on next page setting.
626     *
627     * @param bool $value
628     *
629     * @return self
630     */
631    public function setPageBreakBefore($value = true)
632    {
633        $this->pageBreakBefore = $this->setBoolVal($value, $this->pageBreakBefore);
634
635        return $this;
636    }
637
638    /**
639     * Get numbering style name.
640     *
641     * @return string
642     */
643    public function getNumStyle()
644    {
645        return $this->numStyle;
646    }
647
648    /**
649     * Set numbering style name.
650     *
651     * @param string $value
652     *
653     * @return self
654     */
655    public function setNumStyle($value)
656    {
657        $this->numStyle = $value;
658
659        return $this;
660    }
661
662    /**
663     * Get numbering level.
664     *
665     * @return int
666     */
667    public function getNumLevel()
668    {
669        return $this->numLevel;
670    }
671
672    /**
673     * Set numbering level.
674     *
675     * @param int $value
676     *
677     * @return self
678     */
679    public function setNumLevel($value = 0)
680    {
681        $this->numLevel = $this->setIntVal($value, $this->numLevel);
682
683        return $this;
684    }
685
686    /**
687     * Get tabs.
688     *
689     * @return \PhpOffice\PhpWord\Style\Tab[]
690     */
691    public function getTabs()
692    {
693        return $this->tabs;
694    }
695
696    /**
697     * Set tabs.
698     *
699     * @param array $value
700     *
701     * @return self
702     */
703    public function setTabs($value = null)
704    {
705        if (is_array($value)) {
706            $this->tabs = $value;
707        }
708
709        return $this;
710    }
711
712    /**
713     * Get shading.
714     *
715     * @return \PhpOffice\PhpWord\Style\Shading
716     */
717    public function getShading()
718    {
719        return $this->shading;
720    }
721
722    /**
723     * Set shading.
724     *
725     * @param mixed $value
726     *
727     * @return self
728     */
729    public function setShading($value = null)
730    {
731        $this->setObjectVal($value, 'Shading', $this->shading);
732
733        return $this;
734    }
735
736    /**
737     * Get contextualSpacing.
738     *
739     * @return bool
740     */
741    public function hasContextualSpacing()
742    {
743        return $this->contextualSpacing;
744    }
745
746    /**
747     * Set contextualSpacing.
748     *
749     * @param bool $contextualSpacing
750     *
751     * @return self
752     */
753    public function setContextualSpacing($contextualSpacing)
754    {
755        $this->contextualSpacing = $contextualSpacing;
756
757        return $this;
758    }
759
760    /**
761     * Get bidirectional.
762     *
763     * @return ?bool
764     */
765    public function isBidi()
766    {
767        return $this->bidi ?? Settings::isDefaultRtl();
768    }
769
770    /**
771     * Set bidi.
772     *
773     * @param ?bool $bidi
774     *            Set to true to write from right to left
775     *
776     * @return self
777     */
778    public function setBidi($bidi)
779    {
780        $this->bidi = $bidi;
781
782        return $this;
783    }
784
785    /**
786     * Get textAlignment.
787     *
788     * @return string
789     */
790    public function getTextAlignment()
791    {
792        return $this->textAlignment;
793    }
794
795    /**
796     * Set textAlignment.
797     *
798     * @param string $textAlignment
799     *
800     * @return self
801     */
802    public function setTextAlignment($textAlignment)
803    {
804        TextAlignment::validate($textAlignment);
805        $this->textAlignment = $textAlignment;
806
807        return $this;
808    }
809
810    /**
811     * @return bool
812     */
813    public function hasSuppressAutoHyphens()
814    {
815        return $this->suppressAutoHyphens;
816    }
817
818    /**
819     * @param bool $suppressAutoHyphens
820     */
821    public function setSuppressAutoHyphens($suppressAutoHyphens): void
822    {
823        $this->suppressAutoHyphens = (bool) $suppressAutoHyphens;
824    }
825}