Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Writer/Reader Word2007: Added support for the firstLineChars for Indentation #2753

Merged
merged 5 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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