1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
|
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hslf.usermodel;
import java.awt.Color;
import java.util.List;
import java.util.stream.Stream;
import org.apache.logging.log4j.Logger;
import org.apache.poi.logging.PoiLogManager;
import org.apache.poi.common.usermodel.fonts.FontGroup;
import org.apache.poi.common.usermodel.fonts.FontInfo;
import org.apache.poi.hslf.exceptions.HSLFException;
import org.apache.poi.hslf.model.textproperties.BitMaskTextProp;
import org.apache.poi.hslf.model.textproperties.CharFlagsTextProp;
import org.apache.poi.hslf.model.textproperties.TextProp;
import org.apache.poi.hslf.model.textproperties.TextPropCollection;
import org.apache.poi.hslf.model.textproperties.TextPropCollection.TextPropType;
import org.apache.poi.sl.draw.DrawPaint;
import org.apache.poi.sl.usermodel.MasterSheet;
import org.apache.poi.sl.usermodel.PaintStyle;
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
import org.apache.poi.sl.usermodel.Placeholder;
import org.apache.poi.sl.usermodel.Shape;
import org.apache.poi.sl.usermodel.TextParagraph;
import org.apache.poi.sl.usermodel.TextRun;
import org.apache.poi.sl.usermodel.TextShape;
/**
* Represents a run of text, all with the same style
*
*/
@SuppressWarnings({"WeakerAccess", "Duplicates", "unused"})
public final class HSLFTextRun implements TextRun {
private static final Logger LOG = PoiLogManager.getLogger(HSLFTextRun.class);
/** The TextRun we belong to */
private HSLFTextParagraph parentParagraph;
private String _runText = "";
/** Caches the font info objects until the text runs are attached to the container */
private HSLFFontInfo[] cachedFontInfo;
private HSLFHyperlink link;
/**
* Our paragraph and character style.
* Note - we may share these styles with other RichTextRuns
*/
private TextPropCollection characterStyle = new TextPropCollection(1, TextPropType.character);
/**
* Create a new wrapper around a rich text string
* @param parentParagraph the parent paragraph
*/
public HSLFTextRun(HSLFTextParagraph parentParagraph) {
this.parentParagraph = parentParagraph;
}
public TextPropCollection getCharacterStyle() {
return characterStyle;
}
public void setCharacterStyle(TextPropCollection characterStyle) {
this.characterStyle = characterStyle.copy();
this.characterStyle.updateTextSize(_runText.length());
}
/**
* Supply the SlideShow we belong to
*/
public void updateSheet() {
if (cachedFontInfo != null) {
for (FontGroup tt : FontGroup.values()) {
setFontInfo(cachedFontInfo[tt.ordinal()], tt);
}
cachedFontInfo = null;
}
}
/**
* Get the length of the text
*/
public int getLength() {
return _runText.length();
}
/**
* Fetch the text, in raw storage form
*/
@Override
public String getRawText() {
return _runText;
}
/**
* Change the text
*/
@Override
public void setText(String text) {
if (text == null) {
throw new HSLFException("text must not be null");
}
String newText = HSLFTextParagraph.toInternalString(text);
if (!newText.equals(_runText)) {
_runText = newText;
if (HSLFSlideShow.getLoadSavePhase() == HSLFSlideShow.LoadSavePhase.LOADED) {
parentParagraph.setDirty();
}
}
}
// --------------- Internal helpers on rich text properties -------
/**
* Fetch the value of the given flag in the CharFlagsTextProp.
* Returns false if the CharFlagsTextProp isn't present, since the
* text property won't be set if there's no CharFlagsTextProp.
*/
private boolean isCharFlagsTextPropVal(int index) {
return getFlag(index);
}
boolean getFlag(int index) {
BitMaskTextProp prop = (characterStyle == null) ? null : characterStyle.findByName(CharFlagsTextProp.NAME);
if (prop == null || !prop.getSubPropMatches()[index]) {
prop = getMasterProp();
}
return prop != null && prop.getSubValue(index);
}
private <T extends TextProp> T getMasterProp() {
final int txtype = parentParagraph.getRunType();
final HSLFSheet sheet = parentParagraph.getSheet();
if (sheet == null) {
LOG.atError().log("Sheet is not available");
return null;
}
final HSLFMasterSheet master = sheet.getMasterSheet();
if (master == null) {
LOG.atWarn().log("MasterSheet is not available");
return null;
}
String name = CharFlagsTextProp.NAME;
final TextPropCollection col = master.getPropCollection(txtype, parentParagraph.getIndentLevel(), name, true);
return (col == null) ? null : col.findByName(name);
}
/**
* Set the value of the given flag in the CharFlagsTextProp, adding
* it if required.
*/
private void setCharFlagsTextPropVal(int index, boolean value) {
// TODO: check if paragraph/chars can be handled the same ...
if (getFlag(index) != value) {
setFlag(index, value);
parentParagraph.setDirty();
}
}
/**
* Sets the value of the given Paragraph TextProp, add if required
* @param propName The name of the Paragraph TextProp
* @param val The value to set for the TextProp
*/
public void setCharTextPropVal(String propName, Integer val) {
getTextParagraph().setPropVal(characterStyle, propName, val);
}
// --------------- Friendly getters / setters on rich text properties -------
@Override
public boolean isBold() {
return isCharFlagsTextPropVal(CharFlagsTextProp.BOLD_IDX);
}
@Override
public void setBold(boolean bold) {
setCharFlagsTextPropVal(CharFlagsTextProp.BOLD_IDX, bold);
}
@Override
public boolean isItalic() {
return isCharFlagsTextPropVal(CharFlagsTextProp.ITALIC_IDX);
}
@Override
public void setItalic(boolean italic) {
setCharFlagsTextPropVal(CharFlagsTextProp.ITALIC_IDX, italic);
}
@Override
public boolean isUnderlined() {
return isCharFlagsTextPropVal(CharFlagsTextProp.UNDERLINE_IDX);
}
@Override
public void setUnderlined(boolean underlined) {
setCharFlagsTextPropVal(CharFlagsTextProp.UNDERLINE_IDX, underlined);
}
/**
* Does the text have a shadow?
*/
public boolean isShadowed() {
return isCharFlagsTextPropVal(CharFlagsTextProp.SHADOW_IDX);
}
/**
* Does the text have a shadow?
*/
public void setShadowed(boolean flag) {
setCharFlagsTextPropVal(CharFlagsTextProp.SHADOW_IDX, flag);
}
/**
* Is this text embossed?
*/
public boolean isEmbossed() {
return isCharFlagsTextPropVal(CharFlagsTextProp.RELIEF_IDX);
}
/**
* Is this text embossed?
*/
public void setEmbossed(boolean flag) {
setCharFlagsTextPropVal(CharFlagsTextProp.RELIEF_IDX, flag);
}
@Override
public boolean isStrikethrough() {
return isCharFlagsTextPropVal(CharFlagsTextProp.STRIKETHROUGH_IDX);
}
@Override
public void setStrikethrough(boolean flag) {
setCharFlagsTextPropVal(CharFlagsTextProp.STRIKETHROUGH_IDX, flag);
}
/**
* Gets the subscript/superscript option
*
* @return the percentage of the font size. If the value is positive, it is superscript, otherwise it is subscript
*/
public int getSuperscript() {
TextProp tp = getTextParagraph().getPropVal(characterStyle, "superscript");
return tp == null ? 0 : tp.getValue();
}
/**
* Sets the subscript/superscript option
*
* @param val the percentage of the font size. If the value is positive, it is superscript, otherwise it is subscript
*/
public void setSuperscript(int val) {
setCharTextPropVal("superscript", val);
}
@Override
public Double getFontSize() {
TextProp tp = getTextParagraph().getPropVal(characterStyle, "font.size");
return tp == null ? null : (double)tp.getValue();
}
@Override
public void setFontSize(Double fontSize) {
Integer iFontSize = (fontSize == null) ? null : fontSize.intValue();
setCharTextPropVal("font.size", iFontSize);
}
/**
* Gets the font index
*/
public int getFontIndex() {
TextProp tp = getTextParagraph().getPropVal(characterStyle, "font.index");
return tp == null ? -1 : tp.getValue();
}
/**
* Sets the font index
*/
public void setFontIndex(int idx) {
setCharTextPropVal("font.index", idx);
}
@Override
public void setFontFamily(String typeface) {
setFontFamily(typeface, FontGroup.LATIN);
}
@Override
public void setFontFamily(String typeface, FontGroup fontGroup) {
setFontInfo(new HSLFFontInfo(typeface), fontGroup);
}
@Override
public void setFontInfo(FontInfo fontInfo, FontGroup fontGroup) {
FontGroup fg = safeFontGroup(fontGroup);
HSLFSheet sheet = parentParagraph.getSheet();
@SuppressWarnings("resource")
HSLFSlideShow slideShow = (sheet == null) ? null : sheet.getSlideShow();
if (sheet == null || slideShow == null) {
// we can't set font since slideshow is not assigned yet
if (cachedFontInfo == null) {
cachedFontInfo = new HSLFFontInfo[FontGroup.values().length];
}
cachedFontInfo[fg.ordinal()] = (fontInfo != null) ? new HSLFFontInfo(fontInfo) : null;
return;
}
String propName;
switch (fg) {
default:
case LATIN:
propName = "ansi.font.index";
break;
case COMPLEX_SCRIPT:
// TODO: implement TextCFException10 structure
case EAST_ASIAN:
propName = "asian.font.index";
break;
case SYMBOL:
propName = "symbol.font.index";
break;
}
// Get the index for this font, if it is not to be removed (typeface == null)
Integer fontIdx = null;
if (fontInfo != null) {
fontIdx = slideShow.addFont(fontInfo).getIndex();
}
setCharTextPropVal("font.index", fontIdx);
setCharTextPropVal(propName, fontIdx);
}
@Override
public String getFontFamily() {
return getFontFamily(null);
}
@Override
public String getFontFamily(FontGroup fontGroup) {
HSLFFontInfo fi = getFontInfo(fontGroup);
return (fi != null) ? fi.getTypeface() : null;
}
@Override
public HSLFFontInfo getFontInfo(final FontGroup fontGroup) {
FontGroup fg = safeFontGroup(fontGroup);
HSLFSheet sheet = parentParagraph.getSheet();
@SuppressWarnings("resource")
HSLFSlideShow slideShow = (sheet == null) ? null : sheet.getSlideShow();
if (sheet == null || slideShow == null) {
return (cachedFontInfo != null) ? cachedFontInfo[fg.ordinal()] : null;
}
String propName;
switch (fg) {
default:
case LATIN:
propName = "font.index,ansi.font.index";
break;
case COMPLEX_SCRIPT:
case EAST_ASIAN:
propName = "asian.font.index";
break;
case SYMBOL:
propName = "symbol.font.index";
break;
}
TextProp tp = getTextParagraph().getPropVal(characterStyle, propName);
return (tp != null) ? slideShow.getFont(tp.getValue()) : null;
}
/**
* @return font color as PaintStyle
*/
@Override
public SolidPaint getFontColor() {
TextProp tp = getTextParagraph().getPropVal(characterStyle, "font.color");
if (tp == null) {
return null;
}
Color color = HSLFTextParagraph.getColorFromColorIndexStruct(tp.getValue(), parentParagraph.getSheet());
return DrawPaint.createSolidPaint(color);
}
/**
* Sets color of the text, as an int bgr.
* (PowerPoint stores as BlueGreenRed, not the more
* usual RedGreenBlue)
* @see Color
*/
public void setFontColor(int bgr) {
setCharTextPropVal("font.color", bgr);
}
@Override
public void setFontColor(Color color) {
setFontColor(DrawPaint.createSolidPaint(color));
}
@Override
public void setFontColor(PaintStyle color) {
if (!(color instanceof SolidPaint)) {
throw new IllegalArgumentException("HSLF only supports solid paint");
}
// In PowerPont RGB bytes are swapped, as BGR
SolidPaint sp = (SolidPaint)color;
Color c = DrawPaint.applyColorTransform(sp.getSolidColor());
int rgb = new Color(c.getBlue(), c.getGreen(), c.getRed(), 254).getRGB();
setFontColor(rgb);
}
private void setFlag(int index, boolean value) {
BitMaskTextProp prop = characterStyle.addWithName(CharFlagsTextProp.NAME);
prop.setSubValue(value, index);
}
public HSLFTextParagraph getTextParagraph() {
return parentParagraph;
}
@Override
public TextCap getTextCap() {
return TextCap.NONE;
}
@Override
public boolean isSubscript() {
return getSuperscript() < 0;
}
@Override
public boolean isSuperscript() {
return getSuperscript() > 0;
}
@Override
public byte getPitchAndFamily() {
return 0;
}
/**
* Sets the hyperlink - used when parsing the document
*
* @param link the hyperlink
*/
/* package */ void setHyperlink(HSLFHyperlink link) {
this.link = link;
}
@Override
public HSLFHyperlink getHyperlink() {
return link;
}
@Override
public HSLFHyperlink createHyperlink() {
if (link == null) {
link = HSLFHyperlink.createHyperlink(this);
parentParagraph.setDirty();
}
return link;
}
@Override
public FieldType getFieldType() {
HSLFTextShape ts = getTextParagraph().getParentShape();
Placeholder ph = ts.getPlaceholder();
if (ph != null) {
switch (ph) {
case SLIDE_NUMBER:
return FieldType.SLIDE_NUMBER;
case DATETIME:
return FieldType.DATE_TIME;
default:
break;
}
}
Shape<?,?> ms = (ts.getSheet() instanceof MasterSheet) ? ts.getMetroShape() : null;
if (ms instanceof TextShape) {
return Stream.of((TextShape<?,?>)ms).
flatMap(tsh -> ((List<? extends TextParagraph<?,?,? extends TextRun>>)tsh.getTextParagraphs()).stream()).
flatMap(tph -> tph.getTextRuns().stream()).
findFirst().
map(TextRun::getFieldType).
orElse(null);
}
return null;
}
private FontGroup safeFontGroup(FontGroup fontGroup) {
return (fontGroup != null) ? fontGroup : FontGroup.getFontGroupFirst(getRawText());
}
@Override
public HSLFTextParagraph getParagraph() {
return parentParagraph;
}
}
|