Skip to content

Commit

Permalink
Writer/Reader Word2007: Added support for the firstLineChars for Inde…
Browse files Browse the repository at this point in the history
…ntation (#2753)

* Add support for the firstLineChars style in Word2007.

* Writer Word2007: Added support for the firstLineChars for Indentation

* Fixed Changelog

* Fixed PHPStan & PHPCSFixer

* Fixed PHPStan

---------

Co-authored-by: ling <ling@yundian>
  • Loading branch information
Progi1984 and ling authored Feb 20, 2025
1 parent cdf3d31 commit c92ee4d
Show file tree
Hide file tree
Showing 15 changed files with 273 additions and 24 deletions.
3 changes: 2 additions & 1 deletion docs/changes/1.x/1.4.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
- Add basic ruby text (phonetic guide) support for Word2007 and HTML Reader/Writer, RTF Writer, basic support for ODT writing by [@Deadpikle](https://github.com/Deadpikle) in [#2727](https://github.com/PHPOffice/PHPWord/pull/2727)
- Reader HTML: Support font styles for h1/h6 by [@Progi1984](https://github.com/Progi1984) fixing [#2619](https://github.com/PHPOffice/PHPWord/issues/2619) in [#2737](https://github.com/PHPOffice/PHPWord/pull/2737)
- Writer EPub3: Basic support by [@Sambit003](https://github.com/Sambit003) fixing [#55](https://github.com/PHPOffice/PHPWord/issues/55) in [#2724](https://github.com/PHPOffice/PHPWord/pull/2724)
- Writer2007: Added support for background and border color transparency in Text Box element [@chudy20007](https://github.com/Chudy20007) in [#2555](https://github.com/PHPOffice/PHPWord/pull/2555)
- Writer Word2007: Added support for background and border color transparency in Text Box element by [@chudy20007](https://github.com/Chudy20007) in [#2555](https://github.com/PHPOffice/PHPWord/pull/2555)
- Writer/Reader Word2007: Added support for the firstLineChars for Indentation by [@liuzhilinux](https://github.com/liuzhilinux) & [@Progi1984](https://github.com/Progi1984) in [#2753](https://github.com/PHPOffice/PHPWord/pull/2753)

### Bug fixes

Expand Down
2 changes: 1 addition & 1 deletion docs/usage/styles/paragraph.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Available Paragraph style options:
- ``basedOn``. Parent style.
- ``hanging``. Hanging indentation in *half inches*.
- ``indent``. Indent (left indentation) in *half inches*.
- ``indentation``. An array of indentation key => value pairs in *twip*. Supports *left*, *right*, *firstLine* and *hanging* indentation.
- ``indentation``. An array of indentation key => value pairs in *twip*. Supports *left*, *right*, *firstLine*, *firstLineChars* and *hanging* indentation.
See ``\PhpOffice\PhpWord\Style\Indentation`` for possible identation types.
- ``keepLines``. Keep all lines on one page, *true* or *false*.
- ``keepNext``. Keep paragraph with next paragraph, *true* or *false*.
Expand Down
1 change: 1 addition & 0 deletions src/PhpWord/Reader/Word2007/AbstractPart.php
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,7 @@ protected function readParagraphStyle(XMLReader $xmlReader, DOMElement $domNode)
'indentRight' => [self::READ_VALUE, 'w:ind', 'w:right'],
'indentHanging' => [self::READ_VALUE, 'w:ind', 'w:hanging'],
'indentFirstLine' => [self::READ_VALUE, 'w:ind', 'w:firstLine'],
'indentFirstLineChars' => [self::READ_VALUE, 'w:ind', 'w:firstLineChars'],
'spaceAfter' => [self::READ_VALUE, 'w:spacing', 'w:after'],
'spaceBefore' => [self::READ_VALUE, 'w:spacing', 'w:before'],
'widowControl' => [self::READ_FALSE, 'w:widowControl'],
Expand Down
25 changes: 25 additions & 0 deletions src/PhpWord/Style/Indentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ class Indentation extends AbstractStyle
*/
private $firstLine = 0;

/**
* Additional first line chars indentation (twip).
*
* @var int
*/
private $firstLineChars = 0;

/**
* Indentation removed from first line (twip).
*
Expand Down Expand Up @@ -118,6 +125,24 @@ public function setFirstLine(?float $value): self
return $this;
}

/**
* Get first line chars.
*/
public function getFirstLineChars(): int
{
return $this->firstLineChars;
}

/**
* Set first line chars.
*/
public function setFirstLineChars(int $value): self
{
$this->firstLineChars = $this->setIntVal($value, $this->firstLineChars);

return $this;
}

/**
* Get hanging.
*/
Expand Down
8 changes: 8 additions & 0 deletions src/PhpWord/Style/Paragraph.php
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,14 @@ public function setIndentFirstLine(?float $value = null): self
return $this->setIndentation(['firstLine' => $value]);
}

/**
* Set firstlineChars indentation.
*/
public function setIndentFirstLineChars(int $value = 0): self
{
return $this->setIndentation(['firstLineChars' => $value]);
}

/**
* Set left indentation.
*/
Expand Down
3 changes: 3 additions & 0 deletions src/PhpWord/Writer/Word2007/Style/Indentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ public function write(): void
$firstLine = $style->getFirstLine();
$xmlWriter->writeAttributeIf(null !== $firstLine, 'w:firstLine', $this->convertTwip($firstLine));

$firstLineChars = $style->getFirstLineChars();
$xmlWriter->writeAttributeIf(0 !== $firstLineChars, 'w:firstLineChars', $this->convertTwip($firstLineChars));

$hanging = $style->getHanging();
$xmlWriter->writeAttributeIf(null !== $hanging, 'w:hanging', $this->convertTwip($hanging));

Expand Down
16 changes: 14 additions & 2 deletions tests/PhpWordTests/Element/AbstractElementTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ class AbstractElementTest extends \PHPUnit\Framework\TestCase
*/
public function testElementIndex(): void
{
$stub = $this->getMockForAbstractClass(AbstractElement::class);
if (method_exists($this, 'getMockForAbstractClass')) {
$stub = $this->getMockForAbstractClass(AbstractElement::class);
} else {
/** @var AbstractElement $stub */
$stub = new class() extends AbstractElement {
};
}
$ival = mt_rand(0, 100);
$stub->setElementIndex($ival);
self::assertEquals($ival, $stub->getElementIndex());
Expand All @@ -41,7 +47,13 @@ public function testElementIndex(): void
*/
public function testElementId(): void
{
$stub = $this->getMockForAbstractClass(AbstractElement::class);
if (method_exists($this, 'getMockForAbstractClass')) {
$stub = $this->getMockForAbstractClass(AbstractElement::class);
} else {
/** @var AbstractElement $stub */
$stub = new class() extends AbstractElement {
};
}
$stub->setElementId();
self::assertEquals(6, strlen($stub->getElementId()));
}
Expand Down
57 changes: 50 additions & 7 deletions tests/PhpWordTests/Reader/Word2007/StyleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -334,8 +334,14 @@ public function testPageVerticalAlign(): void
/**
* @dataProvider providerIndentation
*/
public function testIndentation(string $indent, float $left, float $right, ?float $hanging, float $firstLine): void
{
public function testIndentation(
string $indent,
float $left,
float $right,
?float $hanging,
float $firstLine,
int $firstLineChars
): void {
$documentXml = "<w:p>
<w:pPr>
$indent
Expand All @@ -359,16 +365,53 @@ public function testIndentation(string $indent, float $left, float $right, ?floa
self::assertSame($right, $indentation->getRight());
self::assertSame($hanging, $indentation->getHanging());
self::assertSame($firstLine, $indentation->getFirstLine());
self::assertSame($firstLineChars, $indentation->getFirstLineChars());
}

/**
* @return Generator<array{0:string, 1:float, 2:float, 3:null|float, 4: float}>
* @return Generator<array{0:string, 1:float, 2:float, 3:null|float, 4: float, 5: int}>
*/
public static function providerIndentation()
{
yield ['<w:ind w:left="709" w:right="488" w:hanging="10" w:firstLine="490"/>', 709.00, 488.00, 10.0, 490.00];
yield ['<w:ind w:hanging="10" w:firstLine="490"/>', 0, 0, 10.0, 490.00];
yield ['<w:ind w:left="709"/>', 709.00, 0, 0, 0];
yield ['<w:ind w:right="488"/>', 0, 488.00, 0, 0];
yield [
'<w:ind w:left="709" w:right="488" w:hanging="10" w:firstLine="490" w:firstLineChars="140"/>',
709.00,
488.00,
10.0,
490.00,
140,
];
yield [
'<w:ind w:left="709" w:right="488" w:hanging="10" w:firstLine="490"/>',
709.00,
488.00,
10.0,
490.00,
0,
];
yield [
'<w:ind w:hanging="10" w:firstLine="490"/>',
0,
0,
10.0,
490.00,
0,
];
yield [
'<w:ind w:left="709"/>',
709.00,
0,
0,
0,
0,
];
yield [
'<w:ind w:right="488"/>',
0,
488.00,
0,
0,
0,
];
}
}
33 changes: 29 additions & 4 deletions tests/PhpWordTests/Style/AbstractStyleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

use InvalidArgumentException;
use PhpOffice\PhpWord\SimpleType\Jc;
use PhpOffice\PhpWord\Style\AbstractStyle;
use PhpOffice\PhpWord\Style\Paragraph;
use ReflectionClass;

Expand All @@ -35,7 +36,13 @@ class AbstractStyleTest extends \PHPUnit\Framework\TestCase
*/
public function testSetStyleByArray(): void
{
$stub = $this->getMockForAbstractClass('\PhpOffice\PhpWord\Style\AbstractStyle');
if (method_exists($this, 'getMockForAbstractClass')) {
$stub = $this->getMockForAbstractClass(AbstractStyle::class);
} else {
/** @var AbstractStyle $stub */
$stub = new class() extends AbstractStyle {
};
}
$stub->setStyleByArray(['index' => 1]);

self::assertEquals(1, $stub->getIndex());
Expand All @@ -62,7 +69,13 @@ public function testSetStyleByArrayWithAlignment(): void
*/
public function testSetValNormal(): void
{
$stub = $this->getMockForAbstractClass('\PhpOffice\PhpWord\Style\AbstractStyle');
if (method_exists($this, 'getMockForAbstractClass')) {
$stub = $this->getMockForAbstractClass(AbstractStyle::class);
} else {
/** @var AbstractStyle $stub */
$stub = new class() extends AbstractStyle {
};
}

self::assertTrue(self::callProtectedMethod($stub, 'setBoolVal', [true, false]));
self::assertEquals(12, self::callProtectedMethod($stub, 'setIntVal', [12, 200]));
Expand All @@ -76,7 +89,13 @@ public function testSetValNormal(): void
*/
public function testSetValDefault(): void
{
$stub = $this->getMockForAbstractClass('\PhpOffice\PhpWord\Style\AbstractStyle');
if (method_exists($this, 'getMockForAbstractClass')) {
$stub = $this->getMockForAbstractClass(AbstractStyle::class);
} else {
/** @var AbstractStyle $stub */
$stub = new class() extends AbstractStyle {
};
}

self::assertNotTrue(self::callProtectedMethod($stub, 'setBoolVal', ['a', false]));
self::assertEquals(200, self::callProtectedMethod($stub, 'setIntVal', ['foo', 200]));
Expand All @@ -90,7 +109,13 @@ public function testSetValDefault(): void
public function testSetValEnumException(): void
{
$this->expectException(InvalidArgumentException::class);
$stub = $this->getMockForAbstractClass('\PhpOffice\PhpWord\Style\AbstractStyle');
if (method_exists($this, 'getMockForAbstractClass')) {
$stub = $this->getMockForAbstractClass(AbstractStyle::class);
} else {
/** @var AbstractStyle $stub */
$stub = new class() extends AbstractStyle {
};
}

self::assertEquals('b', self::callProtectedMethod($stub, 'setEnumVal', ['z', ['a', 'b'], 'b']));
}
Expand Down
1 change: 1 addition & 0 deletions tests/PhpWordTests/Style/IndentationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public function testGetSetProperties(): void
'left' => [0, 10],
'right' => [0, 10],
'firstLine' => [null, 20],
'firstLineChars' => [0, 20],
'hanging' => [null, 20],
];
foreach ($properties as $property => $value) {
Expand Down
11 changes: 10 additions & 1 deletion tests/PhpWordTests/Writer/EPub3/Part/AbstractPartTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,16 @@ class AbstractPartTest extends TestCase

protected function setUp(): void
{
$this->part = $this->getMockForAbstractClass(AbstractPart::class);
if (method_exists($this, 'getMockForAbstractClass')) {
$this->part = $this->getMockForAbstractClass(AbstractPart::class);
} else {
$this->part = new class() extends AbstractPart {
public function write(): string
{
return '';
}
};
}
}

public function testParentWriter(): void
Expand Down
12 changes: 11 additions & 1 deletion tests/PhpWordTests/Writer/EPub3/Style/AbstractStyleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,17 @@ class AbstractStyleTest extends TestCase
public function testParentWriter(): void
{
$parentWriter = new EPub3();
$style = $this->getMockForAbstractClass(AbstractStyle::class);
if (method_exists($this, 'getMockForAbstractClass')) {
$style = $this->getMockForAbstractClass(AbstractStyle::class);
} else {
/** @var AbstractStyle $style */
$style = new class() extends AbstractStyle {
public function write(): string
{
return '';
}
};
}

$result = $style->setParentWriter($parentWriter);

Expand Down
24 changes: 22 additions & 2 deletions tests/PhpWordTests/Writer/ODText/Part/AbstractPartTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,17 @@ class AbstractPartTest extends \PHPUnit\Framework\TestCase
*/
public function testSetGetParentWriter(): void
{
$object = $this->getMockForAbstractClass(ODText\Part\AbstractPart::class);
if (method_exists($this, 'getMockForAbstractClass')) {
$object = $this->getMockForAbstractClass(ODText\Part\AbstractPart::class);
} else {
/** @var ODText\Part\AbstractPart $object */
$object = new class() extends ODText\Part\AbstractPart {
public function write(): string
{
return '';
}
};
}
$object->setParentWriter(new ODText());
self::assertEquals(new ODText(), $object->getParentWriter());
}
Expand All @@ -46,7 +56,17 @@ public function testSetGetParentWriterNull(): void
{
$this->expectException(Exception::class);
$this->expectExceptionMessage('No parent WriterInterface assigned.');
$object = $this->getMockForAbstractClass(ODText\Part\AbstractPart::class);
if (method_exists($this, 'getMockForAbstractClass')) {
$object = $this->getMockForAbstractClass(ODText\Part\AbstractPart::class);
} else {
/** @var ODText\Part\AbstractPart $object */
$object = new class() extends ODText\Part\AbstractPart {
public function write(): string
{
return '';
}
};
}
$object->getParentWriter();
}
}
30 changes: 25 additions & 5 deletions tests/PhpWordTests/Writer/Word2007/Part/AbstractPartTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,19 @@ class AbstractPartTest extends \PHPUnit\Framework\TestCase
*/
public function testSetGetParentWriter(): void
{
$object = $this->getMockForAbstractClass(Word2007\Part\AbstractPart::class);
$object->setParentWriter(new Word2007());
self::assertEquals(new Word2007(), $object->getParentWriter());
if (method_exists($this, 'getMockForAbstractClass')) {
$stub = $this->getMockForAbstractClass(Word2007\Part\AbstractPart::class);
} else {
/** @var Word2007\Part\AbstractPart $stub */
$stub = new class() extends Word2007\Part\AbstractPart {
public function write(): string
{
return '';
}
};
}
$stub->setParentWriter(new Word2007());
self::assertEquals(new Word2007(), $stub->getParentWriter());
}

/**
Expand All @@ -44,7 +54,17 @@ public function testSetGetParentWriterNull(): void
{
$this->expectException(Exception::class);
$this->expectExceptionMessage('No parent WriterInterface assigned.');
$object = $this->getMockForAbstractClass(Word2007\Part\AbstractPart::class);
$object->getParentWriter();
if (method_exists($this, 'getMockForAbstractClass')) {
$stub = $this->getMockForAbstractClass(Word2007\Part\AbstractPart::class);
} else {
/** @var Word2007\Part\AbstractPart $stub */
$stub = new class() extends Word2007\Part\AbstractPart {
public function write(): string
{
return '';
}
};
}
$stub->getParentWriter();
}
}
Loading

0 comments on commit c92ee4d

Please # to comment.