Skip to content

Commit 2d27595

Browse files
authored
Feature: Ruby (phonetic guide) text - Word2007 Read/Write, HTML Read/Write, RTF write, ODT basic write (PHPOffice#2727)
* Allow reading ruby text in from Word2007 docs * Rename ruby language to language Id * Add basic ruby Word2007 writer * Update changelog * Add ruby element docs * Run PHP CS Fixer * Fix PHPStan errors * Run PHP CS Fixer again * Fix ruby in titles when reading * Add/tweak tests for reading/writing Title with ruby * Use elementExists to try and fix PHP7.1 * Review: add missing type hints; refine types * Add setters to Ruby class * Add basic sample for ruby text * Add base constructor to RubyProperties * Add basic ruby tests * Run PHP CS Fixer * Update sample to handle properties better * Add Ruby HTML output including tests * Update changelog * Update PhpStand baseline * Forgot to run php-cs-fixer again * Update 1.4.0.md * Fix TextRun::getText not working with Ruby element * Add test for TextRun ruby element * Add RTF ruby output * Add ruby HTML reading * Tweak param calls in testRubyWriting * Update changelog * Update sample 46 to be less confusing with ruby output * Move ODText/Element/Text::replaceTabs method Moved to parent AbstractElement so other classes could make use of it * Add very basic ODT Ruby output * Run PHP CS Fixer * Update 1.4.0.md * Try to fix unexpected EOF in HtmlTest * Add missing tests for RubyProperties
1 parent a4468f2 commit 2d27595

File tree

28 files changed

+1725
-43
lines changed

28 files changed

+1725
-43
lines changed

docs/changes/1.x/1.4.0.md

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
- Autoload : Allow to use PHPWord without Composer fixing [#2543](https://github.com/PHPOffice/PHPWord/issues/2543), [#2552](https://github.com/PHPOffice/PHPWord/issues/2552), [#2716](https://github.com/PHPOffice/PHPWord/issues/2716), [#2717](https://github.com/PHPOffice/PHPWord/issues/2717) in [#2722](https://github.com/PHPOffice/PHPWord/pull/2722)
1414
- Add Default font color for Word by [@Collie-IT](https://github.com/Collie-IT) in [#2700](https://github.com/PHPOffice/PHPWord/pull/2700)
1515
- Writer HTML: Support Default font color by [@MichaelPFrey](https://github.com/MichaelPFrey)
16+
- 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)
1617

1718
### Bug fixes
1819

docs/usage/elements/ruby.md

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Ruby
2+
3+
Ruby (phonetic guide) text can be added by using the ``addRuby`` method. Ruby elements require a ``RubyProperties`` object, a ``TextRun`` for the base text, and a ``TextRun`` for the actual ruby (phonetic guide) text.
4+
5+
Here is one example for a complete ruby element setup:
6+
7+
``` php
8+
<?php
9+
$phpWord = new PhpWord();
10+
11+
$section = $phpWord->addSection();
12+
$properties = new RubyProperties();
13+
$properties->setAlignment(RubyProperties::ALIGNMENT_RIGHT_VERTICAL);
14+
$properties->setFontFaceSize(10);
15+
$properties->setFontPointsAboveBaseText(4);
16+
$properties->setFontSizeForBaseText(18);
17+
$properties->setLanguageId('ja-JP');
18+
19+
$baseTextRun = new TextRun(null);
20+
$baseTextRun->addText('私');
21+
$rubyTextRun = new TextRun(null);
22+
$rubyTextRun->addText('わたし');
23+
24+
$section->addRuby($baseTextRun, $rubyTextRun, $properties);
25+
```
26+
27+
- ``$baseTextRun``. ``TextRun`` to be used for the base text.
28+
- ``$rubyTextRun``. ``TextRun`` to be used for the ruby text.
29+
- ``$properties``. ``RubyProperties`` properties object for the ruby text.
30+
31+
A title with a phonetic guide is a little more complex, but still possible. Make sure you add the appropraite title style to your document.
32+
33+
```php
34+
$phpWord = new PhpWord();
35+
$fontStyle = new Font();
36+
$fontStyle->setAllCaps(true);
37+
$fontStyle->setBold(true);
38+
$fontStyle->setSize(24);
39+
$phpWord->addTitleStyle(1, ['name' => 'Arial', 'size' => 24, 'bold' => true, 'color' => '990000']);
40+
41+
$section = $phpWord->addSection();
42+
$properties = new RubyProperties();
43+
$properties->setAlignment(RubyProperties::ALIGNMENT_RIGHT_VERTICAL);
44+
$properties->setFontFaceSize(10);
45+
$properties->setFontPointsAboveBaseText(4);
46+
$properties->setFontSizeForBaseText(18);
47+
$properties->setLanguageId('ja-JP');
48+
49+
$baseTextRun = new TextRun(null);
50+
$baseTextRun->addText('私');
51+
$rubyTextRun = new TextRun(null);
52+
$rubyTextRun->addText('わたし');
53+
54+
$textRun = new TextRun();
55+
$textRun->addRuby($baseTextRun, $rubyTextRun, $properties);
56+
$section->addTitle($textRun, 1);
57+
```

mkdocs.yml

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ nav:
6363
- OLE Object: 'usage/elements/oleobject.md'
6464
- Page Break: 'usage/elements/pagebreak.md'
6565
- Preserve Text: 'usage/elements/preservetext.md'
66+
- Ruby: 'usage/elements/ruby.md'
6667
- Text: 'usage/elements/text.md'
6768
- TextBox: 'usage/elements/textbox.md'
6869
- Text Break: 'usage/elements/textbreak.md'

phpstan-baseline.neon

+15-5
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,11 @@ parameters:
430430
count: 1
431431
path: src/PhpWord/Shared/Html.php
432432

433+
-
434+
message: "#^Method PhpOffice\\\\PhpWord\\\\Shared\\\\Html\\:\\:parseRuby\\(\\) has no return type specified\\.$#"
435+
count: 1
436+
path: src/PhpWord/Shared/Html.php
437+
433438
-
434439
message: "#^Method PhpOffice\\\\PhpWord\\\\Shared\\\\Html\\:\\:parseStyleDeclarations\\(\\) has no return type specified\\.$#"
435440
count: 1
@@ -442,7 +447,7 @@ parameters:
442447

443448
-
444449
message: "#^Parameter \\#1 \\$attribute of static method PhpOffice\\\\PhpWord\\\\Shared\\\\Html\\:\\:parseStyle\\(\\) expects DOMAttr, DOMNode given\\.$#"
445-
count: 1
450+
count: 3
446451
path: src/PhpWord/Shared/Html.php
447452

448453
-
@@ -1051,14 +1056,14 @@ parameters:
10511056
path: src/PhpWord/Writer/ODText/Element/Table.php
10521057

10531058
-
1054-
message: "#^Method PhpOffice\\\\PhpWord\\\\Writer\\\\ODText\\\\Element\\\\Text\\:\\:replacetabs\\(\\) has parameter \\$text with no type specified\\.$#"
1059+
message: "#^Method PhpOffice\\\\PhpWord\\\\Writer\\\\ODText\\\\Element\\\\AbstractElement\\:\\:replaceTabs\\(\\) has parameter \\$text with no type specified\\.$#"
10551060
count: 1
1056-
path: src/PhpWord/Writer/ODText/Element/Text.php
1061+
path: src/PhpWord/Writer/ODText/Element/AbstractElement.php
10571062

10581063
-
1059-
message: "#^Method PhpOffice\\\\PhpWord\\\\Writer\\\\ODText\\\\Element\\\\Text\\:\\:replacetabs\\(\\) has parameter \\$xmlWriter with no type specified\\.$#"
1064+
message: "#^Method PhpOffice\\\\PhpWord\\\\Writer\\\\ODText\\\\Element\\\\AbstractElement\\:\\:replaceTabs\\(\\) has parameter \\$xmlWriter with no type specified\\.$#"
10601065
count: 1
1061-
path: src/PhpWord/Writer/ODText/Element/Text.php
1066+
path: src/PhpWord/Writer/ODText/Element/AbstractElement.php
10621067

10631068
-
10641069
message: "#^Method PhpOffice\\\\PhpWord\\\\Writer\\\\ODText\\\\Element\\\\Text\\:\\:writeChangeInsertion\\(\\) has parameter \\$start with no type specified\\.$#"
@@ -1689,6 +1694,11 @@ parameters:
16891694
message: "#^Cannot access property \\$length on DOMNodeList\\<DOMNode\\>\\|false\\.$#"
16901695
count: 9
16911696
path: tests/PhpWordTests/Writer/HTML/ElementTest.php
1697+
1698+
-
1699+
message: "#^Cannot access property \\$length on DOMNodeList\\<DOMNode\\>\\|false\\.$#"
1700+
count: 2
1701+
path: tests/PhpWordTests/Writer/HTML/Element/RubyTest.php
16921702

16931703
-
16941704
message: "#^Cannot call method item\\(\\) on DOMNodeList\\<DOMNode\\>\\|false\\.$#"
+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
use PhpOffice\PhpWord\ComplexType\RubyProperties;
4+
use PhpOffice\PhpWord\Element\TextRun;
5+
6+
include_once 'Sample_Header.php';
7+
8+
// New Word Document
9+
echo date('H:i:s'), ' Create sample for Ruby (Phonetic Guide) use', EOL;
10+
$phpWord = new PhpOffice\PhpWord\PhpWord();
11+
12+
// Section for demonstrating ruby (phonetic guide) features
13+
$section = $phpWord->addSection();
14+
15+
$section->addText('Here is some normal text with no ruby, also known as "phonetic guide", text.');
16+
17+
$properties = new RubyProperties();
18+
$properties->setAlignment(RubyProperties::ALIGNMENT_CENTER);
19+
$properties->setFontFaceSize(10);
20+
$properties->setFontPointsAboveBaseText(20);
21+
$properties->setFontSizeForBaseText(18);
22+
$properties->setLanguageId('en-US');
23+
24+
$textRun = $section->addTextRun();
25+
$textRun->addText('Here is a demonstration of ruby text for ');
26+
$baseTextRun = new TextRun(null);
27+
$baseTextRun->addText('this');
28+
$rubyTextRun = new TextRun(null);
29+
$rubyTextRun->addText('ruby-text');
30+
$textRun->addRuby($baseTextRun, $rubyTextRun, $properties);
31+
$textRun->addText(' word.');
32+
33+
$textRun = $section->addTextRun();
34+
$properties = new RubyProperties();
35+
$properties->setAlignment(RubyProperties::ALIGNMENT_CENTER);
36+
$properties->setFontFaceSize(10);
37+
$properties->setFontPointsAboveBaseText(20);
38+
$properties->setFontSizeForBaseText(18);
39+
$properties->setLanguageId('ja-JP');
40+
$textRun->addText('Here is a demonstration of ruby text for Japanese text: ');
41+
$baseTextRun = new TextRun(null);
42+
$baseTextRun->addText('');
43+
$rubyTextRun = new TextRun(null);
44+
$rubyTextRun->addText('わたし');
45+
$textRun->addRuby($baseTextRun, $rubyTextRun, $properties);
46+
47+
$section->addText('You can also have ruby text for titles:');
48+
49+
$phpWord->addTitleStyle(1, ['name' => 'Arial', 'size' => 24, 'bold' => true, 'color' => '000099']);
50+
51+
$properties = new RubyProperties();
52+
$properties->setAlignment(RubyProperties::ALIGNMENT_CENTER);
53+
$properties->setFontFaceSize(10);
54+
$properties->setFontPointsAboveBaseText(50);
55+
$properties->setFontSizeForBaseText(18);
56+
$properties->setLanguageId('ja-JP');
57+
58+
$baseTextRun = new TextRun(null);
59+
$baseTextRun->addText('');
60+
$rubyTextRun = new TextRun(null);
61+
$rubyTextRun->addText('わたし');
62+
$textRun = new TextRun();
63+
$textRun->addRuby($baseTextRun, $rubyTextRun, $properties);
64+
$section->addTitle($textRun, 1);
65+
66+
// Save file
67+
echo write($phpWord, basename(__FILE__, '.php'), $writers);
68+
if (!CLI) {
69+
include_once 'Sample_Footer.php';
70+
}
+188
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
<?php
2+
3+
/**
4+
* This file is part of PHPWord - A pure PHP library for reading and writing
5+
* word processing documents.
6+
*
7+
* PHPWord is free software distributed under the terms of the GNU Lesser
8+
* General Public License version 3 as published by the Free Software Foundation.
9+
*
10+
* For the full copyright and license information, please read the LICENSE
11+
* file that was distributed with this source code. For the full list of
12+
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
13+
*
14+
* @see https://github.com/PHPOffice/PHPWord
15+
*
16+
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
17+
*/
18+
19+
namespace PhpOffice\PhpWord\ComplexType;
20+
21+
use InvalidArgumentException;
22+
23+
/**
24+
* Ruby properties.
25+
*
26+
* @see https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.wordprocessing.rubyproperties?view=openxml-3.0.1
27+
*/
28+
class RubyProperties
29+
{
30+
const ALIGNMENT_CENTER = 'center';
31+
const ALIGNMENT_DISTRIBUTE_LETTER = 'distributeLetter';
32+
const ALIGNMENT_DISTRIBUTE_SPACE = 'distributeSpace';
33+
const ALIGNMENT_LEFT = 'left';
34+
const ALIGNMENT_RIGHT = 'right';
35+
const ALIGNMENT_RIGHT_VERTICAL = 'rightVertical';
36+
37+
/**
38+
* Ruby alignment (w:rubyAlign).
39+
*
40+
* @var string
41+
*/
42+
private $alignment;
43+
44+
/**
45+
* Ruby font face size (w:hps).
46+
*
47+
* @var float
48+
*/
49+
private $fontFaceSize;
50+
51+
/**
52+
* Ruby font points above base text (w:hpsRaise).
53+
*
54+
* @var float
55+
*/
56+
private $fontPointsAboveText;
57+
58+
/**
59+
* Ruby font size for base text (w:hpsBaseText).
60+
*
61+
* @var float
62+
*/
63+
private $baseTextFontSize;
64+
65+
/**
66+
* Ruby type/language id (w:lid).
67+
*
68+
* @var string
69+
*/
70+
private $languageId;
71+
72+
/**
73+
* Create a new RubyProperties object.
74+
*/
75+
public function __construct()
76+
{
77+
// these defaults came from opening a new Word doc, adding some ruby text to some
78+
// Japanese text, and copying out the defaults.
79+
$this->alignment = self::ALIGNMENT_DISTRIBUTE_SPACE;
80+
$this->fontFaceSize = 12;
81+
$this->fontPointsAboveText = 22;
82+
$this->languageId = 'ja-JP';
83+
$this->baseTextFontSize = 24;
84+
}
85+
86+
/**
87+
* Get the ruby alignment.
88+
*/
89+
public function getAlignment(): string
90+
{
91+
return $this->alignment;
92+
}
93+
94+
/**
95+
* Set the Ruby Alignment (center, distributeLetter, distributeSpace, left, right, rightVertical).
96+
*/
97+
public function setAlignment(string $alignment): self
98+
{
99+
$alignmentTypes = [
100+
self::ALIGNMENT_CENTER,
101+
self::ALIGNMENT_DISTRIBUTE_LETTER,
102+
self::ALIGNMENT_DISTRIBUTE_SPACE,
103+
self::ALIGNMENT_LEFT,
104+
self::ALIGNMENT_RIGHT,
105+
self::ALIGNMENT_RIGHT_VERTICAL,
106+
];
107+
108+
if (in_array($alignment, $alignmentTypes)) {
109+
$this->alignment = $alignment;
110+
} else {
111+
throw new InvalidArgumentException('Invalid value, alignments of ' . implode(', ', $alignmentTypes) . ' possible');
112+
}
113+
114+
return $this;
115+
}
116+
117+
/**
118+
* Get the ruby font face size.
119+
*/
120+
public function getFontFaceSize(): float
121+
{
122+
return $this->fontFaceSize;
123+
}
124+
125+
/**
126+
* Set the ruby font face size.
127+
*/
128+
public function setFontFaceSize(float $size): self
129+
{
130+
$this->fontFaceSize = $size;
131+
132+
return $this;
133+
}
134+
135+
/**
136+
* Get the ruby font points above base text.
137+
*/
138+
public function getFontPointsAboveBaseText(): float
139+
{
140+
return $this->fontPointsAboveText;
141+
}
142+
143+
/**
144+
* Set the ruby font points above base text.
145+
*/
146+
public function setFontPointsAboveBaseText(float $size): self
147+
{
148+
$this->fontPointsAboveText = $size;
149+
150+
return $this;
151+
}
152+
153+
/**
154+
* Get the ruby font size for base text.
155+
*/
156+
public function getFontSizeForBaseText(): float
157+
{
158+
return $this->baseTextFontSize;
159+
}
160+
161+
/**
162+
* Set the ruby font size for base text.
163+
*/
164+
public function setFontSizeForBaseText(float $size): self
165+
{
166+
$this->baseTextFontSize = $size;
167+
168+
return $this;
169+
}
170+
171+
/**
172+
* Get the ruby language id.
173+
*/
174+
public function getLanguageId(): string
175+
{
176+
return $this->languageId;
177+
}
178+
179+
/**
180+
* Set the ruby language id.
181+
*/
182+
public function setLanguageId(string $langId): self
183+
{
184+
$this->languageId = $langId;
185+
186+
return $this;
187+
}
188+
}

0 commit comments

Comments
 (0)