Skip to content

Commit 34a1be0

Browse files
authored
Add support for XE and INDEX fields (#922)
1 parent 8ce1a19 commit 34a1be0

File tree

7 files changed

+295
-32
lines changed

7 files changed

+295
-32
lines changed

docs/elements.rst

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,35 @@ To be completed
377377
Fields
378378
------
379379

380-
To be completed
380+
Currently the following fields are supported:
381+
382+
- PAGE
383+
- NUMPAGES
384+
- DATE
385+
- XE
386+
- INDEX
387+
388+
.. code-block:: php
389+
390+
$section->addField($fieldType, [$properties], [$options], [$fieldText])
391+
392+
See ``\PhpOffice\PhpWord\Element\Field`` for list of properties and options available for each field type.
393+
Options which are not specifically defined can be added. Those must start with a ``\``.
394+
395+
For instance for the INDEX field, you can do the following (See `Index Field for list of available options <https://support.office.com/en-us/article/Field-codes-Index-field-adafcf4a-cb30-43f6-85c7-743da1635d9e?ui=en-US&rs=en-US&ad=US>`_ ):
396+
397+
.. code-block:: php
398+
399+
//the $fieldText can be either a simple string
400+
$fieldText = 'The index value';
401+
402+
//or a 'TextRun', to be able to format the text you want in the index
403+
$fieldText = new TextRun();
404+
$fieldText->addText('My ');
405+
$fieldText->addText('bold index', ['bold' => true]);
406+
$fieldText->addText(' entry');
407+
408+
$section->addField('INDEX', array(), array('\\e " " \\h "A" \\c "3"'), $fieldText);
381409
382410
Line
383411
------

samples/Sample_27_Field.php

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<?php
2+
use PhpOffice\PhpWord\Element\TextRun;
3+
24
include_once 'Sample_Header.php';
35

46
// New Word document
@@ -14,10 +16,28 @@
1416
$section->addField('DATE', array('dateformat' => 'dddd d MMMM yyyy H:mm:ss'), array('PreserveFormat'));
1517

1618
$section->addText('Page field:');
17-
$section->addField('PAGE', array('format' => 'ArabicDash'));
19+
$section->addField('PAGE', array('format' => 'Arabic'));
1820

1921
$section->addText('Number of pages field:');
20-
$section->addField('NUMPAGES', array('format' => 'Arabic', 'numformat' => '0,00'), array('PreserveFormat'));
22+
$section->addField('NUMPAGES', array('numformat' => '0,00', 'format' => 'Arabic'), array('PreserveFormat'));
23+
24+
$textrun = $section->addTextRun();
25+
$textrun->addText('An index field is ');
26+
$textrun->addField('XE', array(), array('Italic'), 'My first index');
27+
$textrun->addText('here:');
28+
29+
$indexEntryText = new TextRun();
30+
$indexEntryText->addText('My ');
31+
$indexEntryText->addText('bold index', ['bold' => true]);
32+
$indexEntryText->addText(' entry');
33+
34+
$textrun = $section->addTextRun();
35+
$textrun->addText('A complex index field is ');
36+
$textrun->addField('XE', array(), array('Bold'), $indexEntryText);
37+
$textrun->addText('here:');
38+
39+
$section->addText('The actual index:');
40+
$section->addField('INDEX', array(), array('\\e " "'), 'right click to update the index');
2141

2242
$textrun = $section->addTextRun(array('alignment' => \PhpOffice\PhpWord\SimpleType\Jc::CENTER));
2343
$textrun->addText('This is the date of lunar calendar ');

src/PhpWord/Element/AbstractContainer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
* @method Image addImage(string $source, mixed $style = null, bool $isWatermark = false)
4040
* @method Object addObject(string $source, mixed $style = null)
4141
* @method TextBox addTextBox(mixed $style = null)
42-
* @method Field addField(string $type = null, array $properties = array(), array $options = array())
42+
* @method Field addField(string $type = null, array $properties = array(), array $options = array(), mixed $text = null)
4343
* @method Line addLine(mixed $lineStyle = null)
4444
* @method Shape addShape(string $type, mixed $style = null)
4545
* @method Chart addChart(string $type, array $categories, array $values, array $style = null)

src/PhpWord/Element/Field.php

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ class Field extends AbstractElement
4040
),
4141
'NUMPAGES'=>array(
4242
'properties'=>array(
43-
'format' => array('Arabic', 'ArabicDash', 'alphabetic', 'ALPHABETIC', 'roman', 'ROMAN'),
43+
'format' => array('Arabic', 'ArabicDash', 'CardText', 'DollarText', 'Ordinal', 'OrdText',
44+
'alphabetic', 'ALPHABETIC', 'roman', 'ROMAN', 'Caps', 'FirstCap', 'Lower', 'Upper'),
4445
'numformat' => array('0', '0,00', '#.##0', '#.##0,00', '€ #.##0,00(€ #.##0,00)', '0%', '0,00%')
4546
),
4647
'options'=>array('PreserveFormat')
@@ -52,6 +53,14 @@ class Field extends AbstractElement
5253
'h:mm am/pm', 'h:mm:ss am/pm', 'HH:mm', 'HH:mm:ss')
5354
),
5455
'options'=>array('PreserveFormat', 'LunarCalendar', 'SakaEraCalendar', 'LastUsedFormat')
56+
),
57+
'XE'=>array(
58+
'properties' => array(),
59+
'options' => array('Bold', 'Italic')
60+
),
61+
'INDEX'=>array(
62+
'properties' => array(),
63+
'options' => array('PreserveFormat')
5564
)
5665
);
5766

@@ -62,6 +71,13 @@ class Field extends AbstractElement
6271
*/
6372
protected $type;
6473

74+
/**
75+
* Field text
76+
*
77+
* @var TextRun | string
78+
*/
79+
protected $text;
80+
6581
/**
6682
* Field properties
6783
*
@@ -82,12 +98,14 @@ class Field extends AbstractElement
8298
* @param string $type
8399
* @param array $properties
84100
* @param array $options
101+
* @param TextRun | string $text
85102
*/
86-
public function __construct($type = null, $properties = array(), $options = array())
103+
public function __construct($type = null, $properties = array(), $options = array(), $text = null)
87104
{
88105
$this->setType($type);
89106
$this->setProperties($properties);
90107
$this->setOptions($options);
108+
$this->setText($text);
91109
}
92110

93111
/**
@@ -105,7 +123,7 @@ public function setType($type = null)
105123
if (isset($this->fieldsArray[$type])) {
106124
$this->type = $type;
107125
} else {
108-
throw new \InvalidArgumentException("Invalid type");
126+
throw new \InvalidArgumentException("Invalid type '$type'");
109127
}
110128
}
111129
return $this->type;
@@ -135,7 +153,7 @@ public function setProperties($properties = array())
135153
if (is_array($properties)) {
136154
foreach (array_keys($properties) as $propkey) {
137155
if (!(isset($this->fieldsArray[$this->type]['properties'][$propkey]))) {
138-
throw new \InvalidArgumentException("Invalid property");
156+
throw new \InvalidArgumentException("Invalid property '$propkey'");
139157
}
140158
}
141159
$this->properties = array_merge($this->properties, $properties);
@@ -166,8 +184,8 @@ public function setOptions($options = array())
166184
{
167185
if (is_array($options)) {
168186
foreach (array_keys($options) as $optionkey) {
169-
if (!(isset($this->fieldsArray[$this->type]['options'][$optionkey]))) {
170-
throw new \InvalidArgumentException("Invalid option");
187+
if (!(isset($this->fieldsArray[$this->type]['options'][$optionkey])) && substr($optionkey, 0, 1) !== '\\') {
188+
throw new \InvalidArgumentException("Invalid option '$optionkey', possible values are " . implode(', ', $this->fieldsArray[$this->type]['options']));
171189
}
172190
}
173191
$this->options = array_merge($this->options, $options);
@@ -184,4 +202,35 @@ public function getOptions()
184202
{
185203
return $this->options;
186204
}
205+
206+
/**
207+
* Set Field text
208+
*
209+
* @param string | TextRun $text
210+
*
211+
* @return string | TextRun
212+
*
213+
* @throws \InvalidArgumentException
214+
*/
215+
public function setText($text)
216+
{
217+
if (isset($text)) {
218+
if (is_string($text) || $text instanceof TextRun) {
219+
$this->text = $text;
220+
} else {
221+
throw new \InvalidArgumentException("Invalid text");
222+
}
223+
}
224+
return $this->text;
225+
}
226+
227+
/**
228+
* Get Field text
229+
*
230+
* @return string | TextRun
231+
*/
232+
public function getText()
233+
{
234+
return $this->text;
235+
}
187236
}

src/PhpWord/Writer/Word2007/Element/Field.php

Lines changed: 89 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,90 @@ public function write()
3737
return;
3838
}
3939

40+
$this->startElementP();
41+
42+
$xmlWriter->startElement('w:r');
43+
$xmlWriter->startElement('w:fldChar');
44+
$xmlWriter->writeAttribute('w:fldCharType', 'begin');
45+
$xmlWriter->endElement(); // w:fldChar
46+
$xmlWriter->endElement(); // w:r
47+
4048
$instruction = ' ' . $element->getType() . ' ';
49+
if ($element->getText() != null) {
50+
if (is_string($element->getText())) {
51+
$instruction .= '"' . $element->getText() . '" ';
52+
$instruction .= $this->buildPropertiesAndOptions($element);
53+
} else {
54+
$instruction .= '"';
55+
}
56+
} else {
57+
$instruction .= $this->buildPropertiesAndOptions($element);
58+
}
59+
$xmlWriter->startElement('w:r');
60+
$xmlWriter->startElement('w:instrText');
61+
$xmlWriter->writeAttribute('xml:space', 'preserve');
62+
$xmlWriter->text($instruction);
63+
$xmlWriter->endElement(); // w:instrText
64+
$xmlWriter->endElement(); // w:r
65+
66+
if ($element->getText() != null) {
67+
if ($element->getText() instanceof \PhpOffice\PhpWord\Element\TextRun) {
68+
69+
$containerWriter = new Container($xmlWriter, $element->getText(), true);
70+
$containerWriter->write();
71+
72+
$xmlWriter->startElement('w:r');
73+
$xmlWriter->startElement('w:instrText');
74+
$xmlWriter->text('"' . $this->buildPropertiesAndOptions($element));
75+
$xmlWriter->endElement(); // w:instrText
76+
$xmlWriter->endElement(); // w:r
77+
78+
$xmlWriter->startElement('w:r');
79+
$xmlWriter->startElement('w:instrText');
80+
$xmlWriter->writeAttribute('xml:space', 'preserve');
81+
$xmlWriter->text(' ');
82+
$xmlWriter->endElement(); // w:instrText
83+
$xmlWriter->endElement(); // w:r
84+
}
85+
}
86+
87+
$xmlWriter->startElement('w:r');
88+
$xmlWriter->startElement('w:fldChar');
89+
$xmlWriter->writeAttribute('w:fldCharType', 'separate');
90+
$xmlWriter->endElement(); // w:fldChar
91+
$xmlWriter->endElement(); // w:r
92+
93+
$xmlWriter->startElement('w:r');
94+
$xmlWriter->startElement('w:rPr');
95+
$xmlWriter->startElement('w:noProof');
96+
$xmlWriter->endElement(); // w:noProof
97+
$xmlWriter->endElement(); // w:rPr
98+
$xmlWriter->writeElement('w:t', $element->getText() != null && is_string($element->getText()) ? $element->getText() : '1');
99+
$xmlWriter->endElement(); // w:r
100+
101+
$xmlWriter->startElement('w:r');
102+
$xmlWriter->startElement('w:fldChar');
103+
$xmlWriter->writeAttribute('w:fldCharType', 'end');
104+
$xmlWriter->endElement(); // w:fldChar
105+
$xmlWriter->endElement(); // w:r
106+
107+
$this->endElementP(); // w:p
108+
}
109+
110+
private function buildPropertiesAndOptions(\PhpOffice\PhpWord\Element\Field $element)
111+
{
112+
$propertiesAndOptions = '';
41113
$properties = $element->getProperties();
42114
foreach ($properties as $propkey => $propval) {
43115
switch ($propkey) {
44116
case 'format':
117+
$propertiesAndOptions.= '\* ' . $propval . ' ';
118+
break;
45119
case 'numformat':
46-
$instruction .= '\* ' . $propval . ' ';
120+
$propertiesAndOptions.= '\# ' . $propval . ' ';
47121
break;
48122
case 'dateformat':
49-
$instruction .= '\@ "' . $propval . '" ';
123+
$propertiesAndOptions.= '\@ "' . $propval . '" ';
50124
break;
51125
}
52126
}
@@ -55,34 +129,27 @@ public function write()
55129
foreach ($options as $option) {
56130
switch ($option) {
57131
case 'PreserveFormat':
58-
$instruction .= '\* MERGEFORMAT ';
132+
$propertiesAndOptions.= '\* MERGEFORMAT ';
59133
break;
60134
case 'LunarCalendar':
61-
$instruction .= '\h ';
135+
$propertiesAndOptions.= '\h ';
62136
break;
63137
case 'SakaEraCalendar':
64-
$instruction .= '\s ';
138+
$propertiesAndOptions.= '\s ';
65139
break;
66140
case 'LastUsedFormat':
67-
$instruction .= '\l ';
141+
$propertiesAndOptions.= '\l ';
142+
break;
143+
case 'Bold':
144+
$propertiesAndOptions.= '\b ';
145+
break;
146+
case 'Italic':
147+
$propertiesAndOptions.= '\i ';
68148
break;
149+
default:
150+
$propertiesAndOptions.= $option .' ';
69151
}
70152
}
71-
72-
$this->startElementP();
73-
74-
$xmlWriter->startElement('w:fldSimple');
75-
$xmlWriter->writeAttribute('w:instr', $instruction);
76-
$xmlWriter->startElement('w:r');
77-
$xmlWriter->startElement('w:rPr');
78-
$xmlWriter->startElement('w:noProof');
79-
$xmlWriter->endElement(); // w:noProof
80-
$xmlWriter->endElement(); // w:rPr
81-
82-
$xmlWriter->writeElement('w:t', '1');
83-
$xmlWriter->endElement(); // w:r
84-
$xmlWriter->endElement(); // w:fldSimple
85-
86-
$this->endElementP(); // w:p
153+
return $propertiesAndOptions;
87154
}
88155
}

0 commit comments

Comments
 (0)