Skip to content

Commit a0c665c

Browse files
authored
Merge pull request #663 from Progi1984/develop
Added column spacing in RichText & line spacing mode & spacing before/after for Paragraph
2 parents 7932eb1 + b9e1934 commit a0c665c

File tree

20 files changed

+729
-221
lines changed

20 files changed

+729
-221
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"php": "^7.1|^8.0",
2323
"ext-xml": "*",
2424
"ext-zip": "*",
25-
"phpoffice/common": "0.2.*",
25+
"phpoffice/common": "dev-develop",
2626
"phpoffice/phpspreadsheet": "^1.9.0"
2727
},
2828
"require-dev": {

docs/changes/1.0.0.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@
5252
- Support for line smooth for line and scatter chart - @ksmeeks0001 GH-626 & @Progi1984 GH-662
5353
- ODPresentation Writer
5454
- PowerPoint2007 Writer
55+
- Support for column spacing for RichText - @zoff83 GH-617 & @Progi1984 GH-663
56+
- PowerPoint2007 Reader
57+
- PowerPoint2007 Writer
58+
- Support for line spacing mode & spacing before/after for Paragraph - @zoff83 GH-617 & @Progi1984 GH-663
59+
- ODPresentation Reader
60+
- ODPresentation Writer
61+
- PowerPoint2007 Reader
62+
- PowerPoint2007 Writer
5563

5664
## Project Management
5765
- Migrated from Travis CI to Github Actions - @Progi1984 GH-635

docs/usage/shapes/richtext.md

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
Rich text shapes contain paragraphs of texts. To create a rich text shape, use `createRichTextShape` method of slide.
44

5+
Each rich text can contain multiples paragraphs.
6+
Each paragraph can contain:
7+
- a `TextElement`
8+
- a `BreakElement`
9+
- a `Run`
10+
511
Below are the properties that you can set for a rich text shape.
612

713
- `wrap`
@@ -19,15 +25,33 @@ Below are the properties that you can set for a rich text shape.
1925
- `topInset` in pixels
2026
- `autoShrinkHorizontal` (boolean)
2127
- `autoShrinkVertical` (boolean)
28+
- `columnSpacing` see *Column Spacing*
2229

2330
Properties that can be set for each paragraphs are as follow.
2431

2532
- `alignment` <!-- see *[Alignment](#alignment)*-->
2633
- `bulletStyle` see *[Bullet](#bullet)*
27-
- `lineSpacing` see *[LineSpacing](#linespacing)*
34+
- `lineSpacing` see *Line Spacing*
2835
- `font` <!-- see *[Font](#font)*-->
2936

30-
## Bullet
37+
## Column Spacing
38+
39+
For a paragraph, you can define the column spacing.
40+
41+
Example:
42+
43+
``` php
44+
<?php
45+
46+
use PhpOffice\PhpPresentation\Shape\RichText;
47+
48+
$richText = new RichText();
49+
$richText->setColumnSpacing(200);
50+
$columnSpacing = $richText->getColumnSpacing();
51+
```
52+
53+
## Paragraph
54+
### Bullet
3155

3256
For a paragraph, you can define the bullet style.
3357

@@ -58,9 +82,10 @@ $paragraph->getBulletStyle()->setBulletType(Bullet::TYPE_BULLET);
5882
$paragraph->getBulletStyle()->setBulletColor(new Color(Color::COLOR_RED));
5983
```
6084

61-
## LineSpacing
85+
### Line Spacing
6286

6387
For a paragraph, you can define the line spacing.
88+
By default, mode is in percent (`Paragraph::LINE_SPACING_MODE_PERCENT`), but you can use the point mode (`Paragraph::LINE_SPACING_MODE_POINT`).
6489

6590
Example:
6691

@@ -72,6 +97,27 @@ use PhpOffice\PhpPresentation\Shape\RichText\Paragraph;
7297
$paragraph = new Paragraph();
7398
$paragraph->setLineSpacing(200);
7499
$lineSpacing = $paragraph->getLineSpacing();
100+
101+
$paragraph->setLineSpacingMode(Paragraph::LINE_SPACING_MODE_POINT);
102+
$lineSpacingMode = $paragraph->getLineSpacingMode();
103+
```
104+
105+
### Spacing
106+
107+
For a paragraph, you can define the spacing before and after the paragraph in point
108+
Example:
109+
110+
``` php
111+
<?php
112+
113+
use PhpOffice\PhpPresentation\Shape\RichText\Paragraph;
114+
115+
$paragraph = new Paragraph();
116+
$paragraph->setSpacingAfter(12);
117+
$spacingAfter = $paragraph->getSpacingAfter();
118+
119+
$paragraph->setSpacingBefore(34);
120+
$spacingBefore = $paragraph->getSpacingBefore();
75121
```
76122

77123
## Run

samples/Sample_01_Simple.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
$shape = $currentSlide->createDrawingShape();
3131
$shape->setName('PHPPresentation logo')
3232
->setDescription('PHPPresentation logo')
33-
->setPath('./resources/phppowerpoint_logo.gif')
33+
->setPath(__DIR__ . '/resources/phppowerpoint_logo.gif')
3434
->setHeight(36)
3535
->setOffsetX(10)
3636
->setOffsetY(10);

src/PhpPresentation/Reader/ODPresentation.php

Lines changed: 79 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ protected function loadStyle(DOMElement $nodeStyle)
269269
$distance = ($offsetY < 0 ? $offsetY * -1 : $offsetY);
270270
}
271271
$oShadow->setDirection((int) rad2deg(atan2($offsetY, $offsetX)));
272-
$oShadow->setDistance((int) round(CommonDrawing::centimetersToPixels($distance)));
272+
$oShadow->setDistance(CommonDrawing::centimetersToPixels($distance));
273273
}
274274
}
275275
// Read Fill
@@ -365,6 +365,19 @@ protected function loadStyle(DOMElement $nodeStyle)
365365

366366
$nodeParagraphProps = $this->oXMLReader->getElement('style:paragraph-properties', $nodeStyle);
367367
if ($nodeParagraphProps instanceof DOMElement) {
368+
if ($nodeParagraphProps->hasAttribute('fo:line-height')) {
369+
$lineHeightUnit = $this->getExpressionUnit($nodeParagraphProps->getAttribute('fo:margin-bottom'));
370+
$lineSpacingMode = $lineHeightUnit == '%' ? Paragraph::LINE_SPACING_MODE_PERCENT : Paragraph::LINE_SPACING_MODE_POINT;
371+
$lineSpacing = $this->getExpressionValue($nodeParagraphProps->getAttribute('fo:margin-bottom'));
372+
}
373+
if ($nodeParagraphProps->hasAttribute('fo:margin-bottom')) {
374+
$spacingAfter = (float) substr($nodeParagraphProps->getAttribute('fo:margin-bottom'), 0, -2);
375+
$spacingAfter = CommonDrawing::centimetersToPoints($spacingAfter);
376+
}
377+
if ($nodeParagraphProps->hasAttribute('fo:margin-top')) {
378+
$spacingBefore = (float) substr($nodeParagraphProps->getAttribute('fo:margin-top'), 0, -2);
379+
$spacingBefore = CommonDrawing::centimetersToPoints($spacingBefore);
380+
}
368381
$oAlignment = new Alignment();
369382
if ($nodeParagraphProps->hasAttribute('fo:text-align')) {
370383
$oAlignment->setHorizontal($nodeParagraphProps->getAttribute('fo:text-align'));
@@ -407,10 +420,10 @@ protected function loadStyle(DOMElement $nodeStyle)
407420
$oNodeListProperties = $this->oXMLReader->getElement('style:list-level-properties', $oNodeListLevel);
408421
if ($oNodeListProperties instanceof DOMElement) {
409422
if ($oNodeListProperties->hasAttribute('text:min-label-width')) {
410-
$oAlignment->setIndent((int) round(CommonDrawing::centimetersToPixels(substr($oNodeListProperties->getAttribute('text:min-label-width'), 0, -2))));
423+
$oAlignment->setIndent(CommonDrawing::centimetersToPixels((float) substr($oNodeListProperties->getAttribute('text:min-label-width'), 0, -2)));
411424
}
412425
if ($oNodeListProperties->hasAttribute('text:space-before')) {
413-
$iSpaceBefore = (int) CommonDrawing::centimetersToPixels(substr($oNodeListProperties->getAttribute('text:space-before'), 0, -2));
426+
$iSpaceBefore = CommonDrawing::centimetersToPixels((float) substr($oNodeListProperties->getAttribute('text:space-before'), 0, -2));
414427
$iMarginLeft = $iSpaceBefore + $oAlignment->getIndent();
415428
$oAlignment->setMarginLeft($iMarginLeft);
416429
}
@@ -432,12 +445,16 @@ protected function loadStyle(DOMElement $nodeStyle)
432445
}
433446

434447
$this->arrayStyles[$keyStyle] = [
435-
'alignment' => isset($oAlignment) ? $oAlignment : null,
436-
'background' => isset($oBackground) ? $oBackground : null,
437-
'fill' => isset($oFill) ? $oFill : null,
438-
'font' => isset($oFont) ? $oFont : null,
439-
'shadow' => isset($oShadow) ? $oShadow : null,
440-
'listStyle' => isset($arrayListStyle) ? $arrayListStyle : null,
448+
'alignment' => $oAlignment ?? null,
449+
'background' => $oBackground ?? null,
450+
'fill' => $oFill ?? null,
451+
'font' => $oFont ?? null,
452+
'shadow' => $oShadow ?? null,
453+
'listStyle' => $arrayListStyle ?? null,
454+
'spacingAfter' => $spacingAfter ?? null,
455+
'spacingBefore' => $spacingBefore ?? null,
456+
'lineSpacingMode' => $lineSpacingMode ?? null,
457+
'lineSpacing' => $lineSpacing ?? null,
441458
];
442459

443460
return true;
@@ -507,11 +524,11 @@ protected function loadShapeDrawing(DOMElement $oNodeFrame): void
507524
$oShape->setName($oNodeFrame->hasAttribute('draw:name') ? $oNodeFrame->getAttribute('draw:name') : '');
508525
$oShape->setDescription($oNodeFrame->hasAttribute('draw:name') ? $oNodeFrame->getAttribute('draw:name') : '');
509526
$oShape->setResizeProportional(false);
510-
$oShape->setWidth($oNodeFrame->hasAttribute('svg:width') ? (int) round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:width'), 0, -2))) : '');
511-
$oShape->setHeight($oNodeFrame->hasAttribute('svg:height') ? (int) round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:height'), 0, -2))) : '');
527+
$oShape->setWidth($oNodeFrame->hasAttribute('svg:width') ? CommonDrawing::centimetersToPixels((float) substr($oNodeFrame->getAttribute('svg:width'), 0, -2)) : 0);
528+
$oShape->setHeight($oNodeFrame->hasAttribute('svg:height') ? CommonDrawing::centimetersToPixels((float) substr($oNodeFrame->getAttribute('svg:height'), 0, -2)) : 0);
512529
$oShape->setResizeProportional(true);
513-
$oShape->setOffsetX($oNodeFrame->hasAttribute('svg:x') ? (int) round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:x'), 0, -2))) : '');
514-
$oShape->setOffsetY($oNodeFrame->hasAttribute('svg:y') ? (int) round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:y'), 0, -2))) : '');
530+
$oShape->setOffsetX($oNodeFrame->hasAttribute('svg:x') ? CommonDrawing::centimetersToPixels((float) substr($oNodeFrame->getAttribute('svg:x'), 0, -2)) : 0);
531+
$oShape->setOffsetY($oNodeFrame->hasAttribute('svg:y') ? CommonDrawing::centimetersToPixels((float) substr($oNodeFrame->getAttribute('svg:y'), 0, -2)) : 0);
515532

516533
if ($oNodeFrame->hasAttribute('draw:style-name')) {
517534
$keyStyle = $oNodeFrame->getAttribute('draw:style-name');
@@ -535,10 +552,10 @@ protected function loadShapeRichText(DOMElement $oNodeFrame): void
535552
$oShape = $this->oPhpPresentation->getActiveSlide()->createRichTextShape();
536553
$oShape->setParagraphs([]);
537554

538-
$oShape->setWidth($oNodeFrame->hasAttribute('svg:width') ? (int) round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:width'), 0, -2))) : '');
539-
$oShape->setHeight($oNodeFrame->hasAttribute('svg:height') ? (int) round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:height'), 0, -2))) : '');
540-
$oShape->setOffsetX($oNodeFrame->hasAttribute('svg:x') ? (int) round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:x'), 0, -2))) : '');
541-
$oShape->setOffsetY($oNodeFrame->hasAttribute('svg:y') ? (int) round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:y'), 0, -2))) : '');
555+
$oShape->setWidth($oNodeFrame->hasAttribute('svg:width') ? CommonDrawing::centimetersToPixels((float) substr($oNodeFrame->getAttribute('svg:width'), 0, -2)) : 0);
556+
$oShape->setHeight($oNodeFrame->hasAttribute('svg:height') ? CommonDrawing::centimetersToPixels((float) substr($oNodeFrame->getAttribute('svg:height'), 0, -2)) : 0);
557+
$oShape->setOffsetX($oNodeFrame->hasAttribute('svg:x') ? CommonDrawing::centimetersToPixels((float) substr($oNodeFrame->getAttribute('svg:x'), 0, -2)) : 0);
558+
$oShape->setOffsetY($oNodeFrame->hasAttribute('svg:y') ? CommonDrawing::centimetersToPixels((float) substr($oNodeFrame->getAttribute('svg:y'), 0, -2)) : 0);
542559

543560
foreach ($this->oXMLReader->getElements('draw:text-box/*', $oNodeFrame) as $oNodeParagraph) {
544561
$this->levelParagraph = 0;
@@ -565,6 +582,23 @@ protected function loadShapeRichText(DOMElement $oNodeFrame): void
565582
protected function readParagraph(RichText $oShape, DOMElement $oNodeParent): void
566583
{
567584
$oParagraph = $oShape->createParagraph();
585+
if ($oNodeParent->hasAttribute('text:style-name')) {
586+
$keyStyle = $oNodeParent->getAttribute('text:style-name');
587+
if (isset($this->arrayStyles[$keyStyle])) {
588+
if (!empty($this->arrayStyles[$keyStyle]['spacingAfter'])) {
589+
$oParagraph->setSpacingAfter($this->arrayStyles[$keyStyle]['spacingAfter']);
590+
}
591+
if (!empty($this->arrayStyles[$keyStyle]['spacingBefore'])) {
592+
$oParagraph->setSpacingBefore($this->arrayStyles[$keyStyle]['spacingBefore']);
593+
}
594+
if (!empty($this->arrayStyles[$keyStyle]['lineSpacingMode'])) {
595+
$oParagraph->setLineSpacingMode($this->arrayStyles[$keyStyle]['lineSpacingMode']);
596+
}
597+
if (!empty($this->arrayStyles[$keyStyle]['lineSpacing'])) {
598+
$oParagraph->setLineSpacing($this->arrayStyles[$keyStyle]['lineSpacing']);
599+
}
600+
}
601+
}
568602
$oDomList = $this->oXMLReader->getElements('text:span', $oNodeParent);
569603
$oDomTextNodes = $this->oXMLReader->getElements('text()', $oNodeParent);
570604
foreach ($oDomTextNodes as $oDomTextNode) {
@@ -666,4 +700,32 @@ protected function loadStylesFile(): void
666700
}
667701
}
668702
}
703+
704+
/**
705+
* @param string $expr
706+
*
707+
* @return string
708+
*/
709+
private function getExpressionUnit(string $expr): string
710+
{
711+
if (substr($expr, -1) == '%') {
712+
return '%';
713+
}
714+
715+
return substr($expr, -2);
716+
}
717+
718+
/**
719+
* @param string $expr
720+
*
721+
* @return string
722+
*/
723+
private function getExpressionValue(string $expr): string
724+
{
725+
if (substr($expr, -1) == '%') {
726+
return substr($expr, 0, -1);
727+
}
728+
729+
return substr($expr, 0, -2);
730+
}
669731
}

src/PhpPresentation/Reader/PowerPoint2007.php

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -798,7 +798,7 @@ protected function loadShapeDrawing(XMLReader $document, DOMElement $node, Abstr
798798
$oElement = $document->getElement('p:spPr/a:xfrm', $node);
799799
if ($oElement instanceof DOMElement) {
800800
if ($oElement->hasAttribute('rot')) {
801-
$oShape->setRotation(CommonDrawing::angleToDegrees($oElement->getAttribute('rot')));
801+
$oShape->setRotation((int) CommonDrawing::angleToDegrees($oElement->getAttribute('rot')));
802802
}
803803
}
804804

@@ -835,7 +835,7 @@ protected function loadShapeDrawing(XMLReader $document, DOMElement $node, Abstr
835835
$oShape->getShadow()->setDistance(CommonDrawing::emuToPixels($oSubElement->getAttribute('dist')));
836836
}
837837
if ($oSubElement->hasAttribute('dir')) {
838-
$oShape->getShadow()->setDirection(CommonDrawing::angleToDegrees($oSubElement->getAttribute('dir')));
838+
$oShape->getShadow()->setDirection((int) CommonDrawing::angleToDegrees($oSubElement->getAttribute('dir')));
839839
}
840840
if ($oSubElement->hasAttribute('algn')) {
841841
$oShape->getShadow()->setAlignment($oSubElement->getAttribute('algn'));
@@ -880,7 +880,7 @@ protected function loadShapeRichText(XMLReader $document, DOMElement $node, Abst
880880

881881
$oElement = $document->getElement('p:spPr/a:xfrm', $node);
882882
if ($oElement instanceof DOMElement && $oElement->hasAttribute('rot')) {
883-
$oShape->setRotation(CommonDrawing::angleToDegrees($oElement->getAttribute('rot')));
883+
$oShape->setRotation((int) CommonDrawing::angleToDegrees($oElement->getAttribute('rot')));
884884
}
885885

886886
$oElement = $document->getElement('p:spPr/a:xfrm/a:off', $node);
@@ -1098,6 +1098,25 @@ protected function loadParagraph(XMLReader $document, DOMElement $oElement, $oSh
10981098
$oParagraph->getAlignment()->setIsRTL((bool) $oSubElement->getAttribute('rtl'));
10991099
}
11001100

1101+
$oElementLineSpacingPoints = $document->getElement('a:lnSpc/a:spcPts', $oSubElement);
1102+
if ($oElementLineSpacingPoints instanceof DOMElement) {
1103+
$oParagraph->setLineSpacingMode(Paragraph::LINE_SPACING_MODE_POINT);
1104+
$oParagraph->setLineSpacing($oElementLineSpacingPoints->getAttribute('val') / 100);
1105+
}
1106+
$oElementLineSpacingPercent = $document->getElement('a:lnSpc/a:spcPct', $oSubElement);
1107+
if ($oElementLineSpacingPercent instanceof DOMElement) {
1108+
$oParagraph->setLineSpacingMode(Paragraph::LINE_SPACING_MODE_PERCENT);
1109+
$oParagraph->setLineSpacing($oElementLineSpacingPercent->getAttribute('val') / 1000);
1110+
}
1111+
$oElementSpacingBefore = $document->getElement('a:spcBef/a:spcPts', $oSubElement);
1112+
if ($oElementSpacingBefore instanceof DOMElement) {
1113+
$oParagraph->setSpacingBefore($oElementSpacingBefore->getAttribute('val') / 100);
1114+
}
1115+
$oElementSpacingAfter = $document->getElement('a:spcAft/a:spcPts', $oSubElement);
1116+
if ($oElementSpacingAfter instanceof DOMElement) {
1117+
$oParagraph->setSpacingAfter($oElementSpacingAfter->getAttribute('val') / 100);
1118+
}
1119+
11011120
$oParagraph->getBulletStyle()->setBulletType(Bullet::TYPE_NONE);
11021121

11031122
$oElementBuFont = $document->getElement('a:buFont', $oSubElement);

0 commit comments

Comments
 (0)