Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
050d393
Update AbstractElement.php - fgColor and bgColor
rasamassen Sep 1, 2025
5f97167
Update Font.php - add many missing features
rasamassen Sep 1, 2025
c12ed05
Update Font.php - Fix Extra Space
rasamassen Sep 2, 2025
9c1618f
Update Font.php - Fix formatting
rasamassen Sep 2, 2025
e8a1101
Update 1.5.0.md - Changelog for Pull 2819
rasamassen Sep 2, 2025
86a990c
Update Font.php - Fix formatting
rasamassen Sep 2, 2025
fb17d41
Update Font.php - Revert previous commit
rasamassen Sep 2, 2025
710bdfb
Update AbstractElement.php - Fix binary operation error
rasamassen Sep 3, 2025
d9d2176
Update AbstractElement.php - Address CS Fixer issues
rasamassen Sep 5, 2025
2ddc151
Update StyleTest.php - Remove \\cf0
rasamassen Sep 6, 2025
77d48ce
Update Element2Test.php - Remove \\cf0\\f0
rasamassen Sep 6, 2025
f77213e
Update TableTest.php - Remove \\cf0\\f0
rasamassen Sep 6, 2025
f089e4d
Create FontTest.php - Create FontTest
rasamassen Sep 6, 2025
d7fd891
Update StyleTest.php - Fix test
rasamassen Sep 6, 2025
bc3d139
Update ElementTest.php - Fix tests
rasamassen Sep 6, 2025
8495bfa
Update RtfEscaper3Test.php - Fix Test
rasamassen Sep 6, 2025
fe0300b
Update RtfEscaper2Test.php - Fix Test
rasamassen Sep 6, 2025
d39f499
Update FontTest.php - First Basic Test
rasamassen Sep 6, 2025
098d617
Update FontTest.php - minor fix
rasamassen Sep 6, 2025
5de30d1
Update RtfEscaper3Test.php - Minor Fix
rasamassen Sep 6, 2025
17e9c28
Update ElementTest.php - Remove \\cf0\\f0
rasamassen Sep 6, 2025
8e91bbe
Update FontTest.php - Fix
rasamassen Sep 6, 2025
dda1cf1
Update FontTest.php - Add full suite of tests
rasamassen Sep 6, 2025
9258770
Update FontTest.php - Missed some cancelations
rasamassen Sep 6, 2025
b4914f2
Update Font.php - CS Fix
rasamassen Sep 6, 2025
b633680
Update Language.php - Makes langId work for RTF
rasamassen Sep 12, 2025
01f0fc5
Update Font.php - support for setting language
rasamassen Sep 12, 2025
13fedee
Update 1.5.0.md - Update changes
rasamassen Sep 12, 2025
7e64464
Update Language.php - Forgot to remove variable
rasamassen Sep 12, 2025
6949ee0
Update Language.php - Make it so tests don't break.
rasamassen Sep 12, 2025
7110bc2
Update Language.php - 1024 for none/undefined
rasamassen Sep 12, 2025
f2ae7d8
Update Language.php - Testing cause of issue.
rasamassen Sep 12, 2025
eb7cec1
Update Language.php - Hoping this will fix issues.
rasamassen Sep 12, 2025
c26353a
Update Language.php
rasamassen Sep 12, 2025
7810169
Update Language.php - Not sure cause of errors; testing
rasamassen Sep 12, 2025
0df857d
Update Language.php - Testing cause of issue.
rasamassen Sep 12, 2025
4c23ee6
Update Language.php - Testing cause of issue.
rasamassen Sep 12, 2025
6e45af7
Update Language.php - Complete revert to see where problem is
rasamassen Sep 12, 2025
ec20460
Update Language.php - Finalize
rasamassen Sep 12, 2025
65fc5c7
Update Language.php - Commit for future language additions
rasamassen Sep 12, 2025
aec9df8
Update Font.php - Fixing errors
rasamassen Sep 12, 2025
f9a8d6e
Update Font.php - Revert
rasamassen Sep 12, 2025
533ef57
Update Language.php - Format Fixes
rasamassen Sep 12, 2025
c12e599
Update Font.php - Revert
rasamassen Sep 12, 2025
4f0b52d
Update Language.php - Could be string or int
rasamassen Sep 12, 2025
17ac2cd
Update Language.php - Keep it simpler
rasamassen Sep 12, 2025
e66881a
Update Language.php - See if fixes...
rasamassen Sep 12, 2025
30b819b
Update Language.php - Testing
rasamassen Sep 12, 2025
20bec15
Update Font.php - Reverting
rasamassen Sep 12, 2025
693fe07
Update Language.php - Complete Revert
rasamassen Sep 12, 2025
6025217
Update Language.php - Revert the Revert
rasamassen Sep 12, 2025
37ffeb6
Update Font.php - Style fix
rasamassen Sep 12, 2025
e17a293
Update FontTest.php - Greatly expand tests
rasamassen Oct 4, 2025
daec456
Update FontTest.php
rasamassen Oct 4, 2025
048e62d
Update Font.php
rasamassen Oct 4, 2025
509f672
Update Font.php
rasamassen Oct 4, 2025
964300d
Update FontTest.php
rasamassen Oct 4, 2025
78632f1
Update StyleTest.php - remove unnecessary setDefaultRtl(false)
rasamassen Oct 4, 2025
7bc914a
Update StyleTest.php
rasamassen Oct 4, 2025
e296f52
Update Element2Test.php - remove unnecessary setDefaultRtl(false)
rasamassen Oct 4, 2025
f3c3e94
Update TableTest.php - remove unnecessary setDefaultRtl(false)
rasamassen Oct 4, 2025
0503884
Update RtfEscaper3Test.php
rasamassen Oct 4, 2025
ffb151d
Update FontTest.php - create test with header table set
rasamassen Oct 4, 2025
d407afd
Update FontTest.php
rasamassen Oct 4, 2025
3c34abf
Update FontTest.php
rasamassen Oct 4, 2025
76ab10b
Update FontTest.php
rasamassen Oct 4, 2025
ebe5a58
Update FontTest.php
rasamassen Oct 4, 2025
25b85c3
Update FontTest.php
rasamassen Oct 4, 2025
3a59a68
Update Header.php - register font background colors
rasamassen Oct 4, 2025
79c89c4
Update FontTest.php
rasamassen Oct 4, 2025
ffa633a
Update FontTest.php
rasamassen Oct 4, 2025
a13bc1e
Update FontTest.php - add comments on non-working features in tests
rasamassen Oct 4, 2025
36e31e7
Update FontTest.php
rasamassen Oct 8, 2025
20c6bdf
Update Font.php - Fix Background Color, see page 23 of RTF specification
rasamassen Oct 10, 2025
64397b2
Update Font.php - Fixes spacing issue, reorganizes to match Word2007W…
rasamassen Oct 17, 2025
d04293e
Update Font.php
rasamassen Oct 17, 2025
7774093
Update Font.php
rasamassen Oct 17, 2025
8f8be80
Update ElementTest.php
rasamassen Oct 17, 2025
107fc28
Update FontTest.php
rasamassen Oct 17, 2025
c4fbd60
Update FontTest.php
rasamassen Oct 17, 2025
4c1c127
Update Header.php - Register Default Color
rasamassen Oct 25, 2025
1655031
Update FontTest.php - Registering default font color shifted color ta…
rasamassen Oct 25, 2025
7c6de8a
Update FontTest.php
rasamassen Oct 25, 2025
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.5.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
### Bug fixes

- Set writeAttribute return type by [@radarhere](https://github.com/radarhere) fixing [#2204](https://github.com/PHPOffice/PHPWord/issues/2204) in [#2776](https://github.com/PHPOffice/PHPWord/pull/2776)
- Writer RTF: Support for various underline styles, doublestrikethrough, smallcaps, allcaps, fgcolor, hidden, scale, spacing, kerning, position, lang, noproof, and bgcolor in Font by [@rasamassen](https://github.com/rasamassen) in [#2819](https://github.com/PHPOffice/PHPWord/pull/2819), also fixing [#321](https://github.com/PHPOffice/PHPWord/issues/321)

### Miscellaneous

Expand All @@ -16,4 +17,4 @@

### BC Breaks

### Notes
### Notes
27 changes: 27 additions & 0 deletions src/PhpWord/Style/Language.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
* Language
* A couple of predefined values are defined here, see the websites below for more values.
*
* @see https://learn.microsoft.com/en-us/openspecs/office_standards/ms-oe376/6c085406-a698-4e12-9d4d-c3b0ee3dbc4a
* @see http://www.datypic.com/sc/ooxml/t-w_CT_Language.html
* @see https://technet.microsoft.com/en-us/library/cc287874(v=office.12).aspx
*/
Expand Down Expand Up @@ -126,6 +127,7 @@ public function __construct(?string $latin = null, ?string $eastAsia = null, ?st
{
if (!empty($latin)) {
$this->setLatin($latin);
$this->setLangId($this->convertLocale($this->validateLocale($latin)));
}
if (!empty($eastAsia)) {
$this->setEastAsia($eastAsia);
Expand Down Expand Up @@ -258,4 +260,29 @@ private function validateLocale($locale)

return $locale;
}

/**
* Converts a language from the format xx-xx to decimal.
*
* @param string $latin
* The value for the latin language
*
* @return int
*/
private function convertLocale($latin)
{
if (empty($latin)) {
return 1024;
}

$locale = strtoupper(str_replace('-', '_', $latin)) . '_ID';

if (defined($locale)) {
return constant($locale);
} elseif (defined("self::$locale")) {
return constant("self::$locale");
}

return 1024;
}
}
16 changes: 14 additions & 2 deletions src/PhpWord/Writer/RTF/Element/AbstractElement.php
Original file line number Diff line number Diff line change
Expand Up @@ -197,13 +197,25 @@ protected function writeFontStyle()
if ($this->fontStyle->getColor() != null) {
$colorIndex = array_search($this->fontStyle->getColor(), $parentWriter->getColorTable());
if ($colorIndex !== false) {
$styleWriter->setColorIndex($colorIndex + 1);
$styleWriter->setColorIndex((int) $colorIndex + 1);
}
}
if ($this->fontStyle->getFgColor() != null) {
$colorIndex = array_search($this->fontStyle->getFgColor(), $parentWriter->getColorTable());
if ($colorIndex !== false) {
$styleWriter->setFgColorIndex((int) $colorIndex + 1);
}
}
if ($this->fontStyle->getBgColor() != null) {
$colorIndex = array_search($this->fontStyle->getBgColor(), $parentWriter->getColorTable());
if ($colorIndex !== false) {
$styleWriter->setBgColorIndex((int) $colorIndex + 1);
}
}
if ($this->fontStyle->getName() != null) {
$fontIndex = array_search($this->fontStyle->getName(), $parentWriter->getFontTable());
if ($fontIndex !== false) {
$styleWriter->setNameIndex($fontIndex);
$styleWriter->setNameIndex((int) $fontIndex);
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/PhpWord/Writer/RTF/Part/Header.php
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ private function registerFont(): void
{
$phpWord = $this->getParentWriter()->getPhpWord();
$this->fontTable[] = Settings::getDefaultFontName();
$this->colorTable[] = Settings::getDefaultFontColor();

// Search named styles
$styles = Style::getStyles();
Expand Down Expand Up @@ -238,6 +239,7 @@ private function registerFontItems($style): void
$this->registerTableItem($this->fontTable, $style->getName(), $defaultFont);
$this->registerTableItem($this->colorTable, $style->getColor(), $defaultColor);
$this->registerTableItem($this->colorTable, $style->getFgColor(), $defaultColor);
$this->registerTableItem($this->colorTable, $style->getBgColor(), $defaultColor);

return;
}
Expand Down
124 changes: 118 additions & 6 deletions src/PhpWord/Writer/RTF/Style/Font.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ class Font extends AbstractStyle
*/
private $colorIndex = 0;

/**
* @var int Font foreground color index
*/
private $fgColorIndex = 0;

/**
* @var int Font background color index
*/
private $bgColorIndex = 0;

/**
* Write style.
*
Expand All @@ -50,20 +60,102 @@ public function write()
}

$content = '';
$content .= $this->getValueIf($style->isRTL(), '\rtlch');
$content .= '\cf' . $this->colorIndex;
$content .= '\f' . $this->nameIndex;

$size = $style->getSize();
$content .= $this->getValueIf(is_numeric($size), '\fs' . round($size * 2));
// Underline Keywords
$underlines = [
FontStyle::UNDERLINE_DASH => '\uldash',
FontStyle::UNDERLINE_DASHHEAVY => '\ulthdash',
FontStyle::UNDERLINE_DASHLONG => '\ulldash',
FontStyle::UNDERLINE_DASHLONGHEAVY => '\ulthldash',
FontStyle::UNDERLINE_DOUBLE => '\uldb',
FontStyle::UNDERLINE_DOTDASH => '\uldashd',
FontStyle::UNDERLINE_DOTDASHHEAVY => '\ulthdashd',
FontStyle::UNDERLINE_DOTDOTDASH => '\uldashdd',
FontStyle::UNDERLINE_DOTDOTDASHHEAVY => '\ulthdashdd',
FontStyle::UNDERLINE_DOTTED => '\uld',
FontStyle::UNDERLINE_DOTTEDHEAVY => '\ulthd',
FontStyle::UNDERLINE_HEAVY => '\ulth',
FontStyle::UNDERLINE_SINGLE => '\ul',
FontStyle::UNDERLINE_WAVY => '\ulwave',
FontStyle::UNDERLINE_WAVYDOUBLE => '\ululdbwave',
FontStyle::UNDERLINE_WAVYHEAVY => '\ulhwave',
FontStyle::UNDERLINE_WORDS => '\ulw',
];

// Style Name

// Font name/family
$content .= $this->getValueIf($style->getName() !== null, '\f' . $this->nameIndex);

// Language
if ($style->getLang() !== null) {
if ($style->isNoProof()) {
$content .= $this->getValueIf($style->getLang()->getLangId() !== null, '\langnp' . $style->getLang()->getLangId());
} else {
$content .= $this->getValueIf($style->getLang()->getLangId() !== null, '\lang' . $style->getLang()->getLangId());
}
}

// Color
$content .= $this->getValueIf($style->getColor() !== null, '\cf' . $this->colorIndex);

// Size
$content .= $this->getValueIf($style->getSize() !== null, '\fs' . round($style->getSize() * 2));

// Bold, italic
$content .= $this->getValueIf($style->isBold(), '\b');
$content .= $this->getValueIf($style->isBold() === false, '\b0');
$content .= $this->getValueIf($style->isItalic(), '\i');
$content .= $this->getValueIf($style->getUnderline() != FontStyle::UNDERLINE_NONE, '\ul');
$content .= $this->getValueIf($style->isItalic() === false, '\i0');

// Strikethrough, double strikethrough
$content .= $this->getValueIf($style->isStrikethrough(), '\strike');
$content .= $this->getValueIf($style->isDoubleStrikethrough(), '\striked1');
$content .= $this->getValueIf($style->isStrikethrough() === false && $style->isDoubleStrikethrough() === false, '\strike0');

// Small caps, all caps
$content .= $this->getValueIf($style->isSmallCaps(), '\scaps');
$content .= $this->getValueIf($style->isAllCaps(), '\caps');

// Hidden text
$content .= $this->getValueIf($style->isHidden(), '\v');
$content .= $this->getValueIf($style->isHidden() === false, '\v0');

// Underline
if (isset($underlines[$style->getUnderline()])) {
$content .= $underlines[$style->getUnderline()];
}

// Foreground color
$content .= $this->getValueIf($style->getFgColor() !== null, '\highlight' . $this->fgColorIndex);

// Superscript/subscript
$content .= $this->getValueIf($style->isSuperScript(), '\super');
$content .= $this->getValueIf($style->isSubScript(), '\sub');

// Spacing
$content .= $this->getValueIf($style->getScale() !== null, '\charscalex' . round($style->getScale() ?? 0));
$content .= $this->getValueIf($style->getSpacing() !== null, '\expnd' . round($style->getSpacing() * 0.2));
$content .= $this->getValueIf($style->getSpacing() !== null, '\expndtw' . round($style->getSpacing() ?? 0));
$content .= $this->getValueIf($style->getKerning() !== null, '\kerning' . round($style->getKerning() * 2));

// noProof
$content .= $this->getValueIf($style->isNoProof(), '\noproof\lang1024');

// Bakground-Color
$content .= $this->getValueIf($style->getBgColor() !== null, '\chshdng0\chcbpat' . $this->bgColorIndex . '\cb' . $this->bgColorIndex);

// RTL
$content .= $this->getValueIf($style->isRTL(), '\rtlch');
$content .= $this->getValueIf($style->isRTL() === false, '\ltrch');

// Position
$content .= $this->getValueIf($style->getPosition() !== null, '\up' . $style->getPosition());

if (empty($content)) {
return $content;
}

return $content . ' ';
}

Expand All @@ -86,4 +178,24 @@ public function setColorIndex($value = 0): void
{
$this->colorIndex = $value;
}

/**
* Set font foreground color index.
*
* @param int $value
*/
public function setFgColorIndex($value = 0): void
{
$this->fgColorIndex = $value;
}

/**
* Set font background color index.
*
* @param int $value
*/
public function setBgColorIndex($value = 0): void
{
$this->bgColorIndex = $value;
}
}
2 changes: 1 addition & 1 deletion tests/PhpWordTests/Escaper/RtfEscaper2Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
*/
class RtfEscaper2Test extends \PHPUnit\Framework\TestCase
{
const HEADER = '\\pard\\nowidctlpar {\\cf0\\f0 ';
const HEADER = '\\pard\\nowidctlpar {';
const TRAILER = '}\\par';

public function escapestring($str)
Expand Down
4 changes: 2 additions & 2 deletions tests/PhpWordTests/Escaper/RtfEscaper3Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
*/
class RtfEscaper3Test extends \PHPUnit\Framework\TestCase
{
const HEADER = '\\pard\\nowidctlpar \ql{\\cf0\\f0 ';
const HEADER_RTL = '\\pard\\nowidctlpar \qr{\\rtlch\\cf0\\f0 ';
const HEADER = '\\pard\\nowidctlpar \ql{\ltrch ';
const HEADER_RTL = '\\pard\\nowidctlpar \qr{\\rtlch ';
const TRAILER = '}\\par';

protected function tearDown(): void
Expand Down
18 changes: 7 additions & 11 deletions tests/PhpWordTests/Writer/RTF/Element/TableTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ public function removeCr(WriterTable $field): string

public function testTable(): void
{
Settings::setDefaultRtl(false);
$parentWriter = new RTF();
$element = new Table();
$width = 100;
Expand All @@ -59,18 +58,18 @@ public function testTable(): void
'\\pard',
"\\trowd \\cellx$width \\cellx$width2 ",
'\\intbl',
'\\ql{\\cf0\\f0 1}\\par',
'{1}\\par',
'\\cell',
'\\intbl',
'{\\cf0\\f0 2}\\par',
'{2}\\par',
'\\cell',
'\\row',
"\\trowd \\cellx$width \\cellx$width2 ",
'\\intbl',
'\\ql{\\cf0\\f0 3}\\par',
'{3}\\par',
'\\cell',
'\\intbl',
'{\\cf0\\f0 4}\par',
'{4}\par',
'\\cell',
'\\row',
'\\pard',
Expand All @@ -84,7 +83,6 @@ public function testTableStyle(): void
{
$width = 100;

Settings::setDefaultRtl(false);
$parentWriter = new RTF();

Style::addTableStyle('TableStyle', ['borderSize' => 6, 'borderColor' => '006699']);
Expand All @@ -102,7 +100,7 @@ public function testTableStyle(): void
'\\clbrdrr\\brdrs\\brdrw2\\brdrcf0',
"\\cellx$width ",
'\\intbl',
'\\ql{\\cf0\\f0 1}\\par',
'{1}\\par',
'\\cell',
'\\row',
'\\pard',
Expand All @@ -116,7 +114,6 @@ public function testTableStyleNotExisting(): void
{
$width = 100;

Settings::setDefaultRtl(false);
$parentWriter = new RTF();

$element = new Table('TableStyleNotExisting');
Expand All @@ -128,7 +125,7 @@ public function testTableStyleNotExisting(): void
'\\pard',
"\\trowd \\cellx$width ",
'\\intbl',
'\\ql{\\cf0\\f0 1}\\par',
'{1}\\par',
'\\cell',
'\\row',
'\\pard',
Expand All @@ -142,7 +139,6 @@ public function testTableCellStyle(): void
{
$width = 100;

Settings::setDefaultRtl(false);
$parentWriter = new RTF();

$element = new Table();
Expand All @@ -158,7 +154,7 @@ public function testTableCellStyle(): void
'\\clbrdrr\\brdrdot\\brdrw2\\brdrcf0',
"\\cellx$width ",
'\\intbl',
'\\ql{\\cf0\\f0 1}\\par',
'{1}\\par',
'\\cell',
'\\row',
'\\pard',
Expand Down
9 changes: 3 additions & 6 deletions tests/PhpWordTests/Writer/RTF/Element2Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,38 +41,35 @@ public function removeCr($field): string

public function testTextRun(): void
{
Settings::setDefaultRtl(false);
$parentWriter = new RTF();
$element = new \PhpOffice\PhpWord\Element\TextRun();
$element->addText('Hello ');
$element->addText('there.');
$textrun = new WriterTextRun($parentWriter, $element);
$expect = "\\pard\\nowidctlpar \\ql{{\\cf0\\f0 Hello }{\\cf0\\f0 there.}}\\par\n";
$expect = "\\pard\\nowidctlpar {{Hello }{there.}}\\par\n";
self::assertEquals($expect, $this->removeCr($textrun));
}

public function testTextRunParagraphStyle(): void
{
Settings::setDefaultRtl(false);
$parentWriter = new RTF();
$element = new \PhpOffice\PhpWord\Element\TextRun(['spaceBefore' => 0, 'spaceAfter' => 0]);
$element->addText('Hello ');
$element->addText('there.');
$textrun = new WriterTextRun($parentWriter, $element);
$expect = "\\pard\\nowidctlpar \\ql\\sb0\\sa0{{\\cf0\\f0 Hello }{\\cf0\\f0 there.}}\\par\n";
$expect = "\\pard\\nowidctlpar \\sb0\\sa0{{Hello }{there.}}\\par\n";
self::assertEquals($expect, $this->removeCr($textrun));
}

public function testTitle(): void
{
$parentWriter = new RTF();
$phpWord = new \PhpOffice\PhpWord\PhpWord();
Settings::setDefaultRtl(false);
$phpWord->addTitleStyle(1, [], ['spaceBefore' => 0, 'spaceAfter' => 0]);
$section = $phpWord->addSection();
$element = $section->addTitle('First Heading', 1);
$elwrite = new WriterTitle($parentWriter, $element);
$expect = "\\pard\\nowidctlpar \\ql\\sb0\\sa0{\\outlinelevel0{\\cf0\\f0 First Heading}\\par\n}";
$expect = "\\pard\\nowidctlpar \\sb0\\sa0{\\outlinelevel0{First Heading}\\par\n}";
self::assertEquals($expect, $this->removeCr($elwrite));
Settings::setDefaultRtl(null);
}
Expand Down
Loading
Loading