Line 0
Link Here
|
|
|
1 |
/* ==================================================================== |
2 |
Licensed to the Apache Software Foundation (ASF) under one or more |
3 |
contributor license agreements. See the NOTICE file distributed with |
4 |
this work for additional information regarding copyright ownership. |
5 |
The ASF licenses this file to You under the Apache License, Version 2.0 |
6 |
(the "License"); you may not use this file except in compliance with |
7 |
the License. You may obtain a copy of the License at |
8 |
|
9 |
http://www.apache.org/licenses/LICENSE-2.0 |
10 |
|
11 |
Unless required by applicable law or agreed to in writing, software |
12 |
distributed under the License is distributed on an "AS IS" BASIS, |
13 |
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 |
See the License for the specific language governing permissions and |
15 |
limitations under the License. |
16 |
==================================================================== */ |
17 |
package org.apache.poi.xssf.usermodel; |
18 |
|
19 |
import org.apache.poi.util.Beta; |
20 |
import org.apache.poi.util.Internal; |
21 |
import org.apache.poi.util.Units; |
22 |
import org.apache.xmlbeans.XmlObject; |
23 |
import org.openxmlformats.schemas.drawingml.x2006.main.*; |
24 |
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTShape; |
25 |
|
26 |
import java.awt.Color; |
27 |
import java.awt.Graphics2D; |
28 |
import java.awt.font.LineBreakMeasurer; |
29 |
import java.awt.font.TextAttribute; |
30 |
import java.awt.font.TextLayout; |
31 |
import java.awt.geom.Rectangle2D; |
32 |
import java.text.AttributedCharacterIterator; |
33 |
import java.text.AttributedString; |
34 |
import java.util.ArrayList; |
35 |
import java.util.Iterator; |
36 |
import java.util.List; |
37 |
|
38 |
/** |
39 |
* Represents a paragraph of text within the containing text body. |
40 |
* The paragraph is the highest level text separation mechanism. |
41 |
*/ |
42 |
public class XSSFTextParagraph implements Iterable<XSSFTextRun>{ |
43 |
private final CTTextParagraph _p; |
44 |
private final CTShape _shape; |
45 |
private final List<XSSFTextRun> _runs; |
46 |
|
47 |
XSSFTextParagraph(CTTextParagraph p, CTShape ctShape){ |
48 |
_p = p; |
49 |
_shape = ctShape; |
50 |
_runs = new ArrayList<XSSFTextRun>(); |
51 |
|
52 |
for(XmlObject ch : _p.selectPath("*")){ |
53 |
if(ch instanceof CTRegularTextRun){ |
54 |
CTRegularTextRun r = (CTRegularTextRun)ch; |
55 |
_runs.add(new XSSFTextRun(r, this)); |
56 |
} else if (ch instanceof CTTextLineBreak){ |
57 |
CTTextLineBreak br = (CTTextLineBreak)ch; |
58 |
CTRegularTextRun r = CTRegularTextRun.Factory.newInstance(); |
59 |
r.setRPr(br.getRPr()); |
60 |
r.setT("\n"); |
61 |
_runs.add(new XSSFTextRun(r, this)); |
62 |
} else if (ch instanceof CTTextField){ |
63 |
CTTextField f = (CTTextField)ch; |
64 |
CTRegularTextRun r = CTRegularTextRun.Factory.newInstance(); |
65 |
r.setRPr(f.getRPr()); |
66 |
r.setT(f.getT()); |
67 |
_runs.add(new XSSFTextRun(r, this)); |
68 |
} |
69 |
} |
70 |
} |
71 |
|
72 |
public String getText(){ |
73 |
StringBuilder out = new StringBuilder(); |
74 |
for (XSSFTextRun r : _runs) { |
75 |
out.append(r.getText()); |
76 |
} |
77 |
return out.toString(); |
78 |
} |
79 |
|
80 |
@Internal |
81 |
public CTTextParagraph getXmlObject(){ |
82 |
return _p; |
83 |
} |
84 |
|
85 |
@Internal |
86 |
public CTShape getParentShape(){ |
87 |
return _shape; |
88 |
} |
89 |
|
90 |
public List<XSSFTextRun> getTextRuns(){ |
91 |
return _runs; |
92 |
} |
93 |
|
94 |
public Iterator<XSSFTextRun> iterator(){ |
95 |
return _runs.iterator(); |
96 |
} |
97 |
|
98 |
/** |
99 |
* Add a new run of text |
100 |
* |
101 |
* @return a new run of text |
102 |
*/ |
103 |
public XSSFTextRun addNewTextRun(){ |
104 |
CTRegularTextRun r = _p.addNewR(); |
105 |
CTTextCharacterProperties rPr = r.addNewRPr(); |
106 |
rPr.setLang("en-US"); |
107 |
XSSFTextRun run = new XSSFTextRun(r, this); |
108 |
_runs.add(run); |
109 |
return run; |
110 |
} |
111 |
|
112 |
/** |
113 |
* Insert a line break |
114 |
* |
115 |
* @return text run representing this line break ('\n') |
116 |
*/ |
117 |
public XSSFTextRun addLineBreak(){ |
118 |
CTTextLineBreak br = _p.addNewBr(); |
119 |
CTTextCharacterProperties brProps = br.addNewRPr(); |
120 |
if(_runs.size() > 0){ |
121 |
// by default line break has the font size of the last text run |
122 |
CTTextCharacterProperties prevRun = _runs.get(_runs.size() - 1).getRPr(); |
123 |
brProps.set(prevRun); |
124 |
} |
125 |
CTRegularTextRun r = CTRegularTextRun.Factory.newInstance(); |
126 |
r.setRPr(brProps); |
127 |
r.setT("\n"); |
128 |
XSSFTextRun run = new XSSFLineBreak(r, this, brProps); |
129 |
_runs.add(run); |
130 |
return run; |
131 |
} |
132 |
|
133 |
/** |
134 |
* Returns the alignment that is applied to the paragraph. |
135 |
* |
136 |
* If this attribute is omitted, then a value of left is implied. |
137 |
* @return ??? alignment that is applied to the paragraph |
138 |
*/ |
139 |
public TextAlign getTextAlign(){ |
140 |
CTTextParagraphProperties pr = _p.getPPr(); |
141 |
if(pr != null) { |
142 |
return pr.isSetAlgn() ? TextAlign.values()[pr.getAlgn().intValue() - 1] : TextAlign.LEFT; |
143 |
} |
144 |
return TextAlign.LEFT; |
145 |
} |
146 |
|
147 |
/** |
148 |
* Specifies the alignment that is to be applied to the paragraph. |
149 |
* Possible values for this include left, right, centered, justified and distributed, |
150 |
* see {@link org.apache.poi.xssf.usermodel.TextAlign}. |
151 |
* |
152 |
* @param align text align |
153 |
*/ |
154 |
public void setTextAlign(TextAlign align){ |
155 |
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); |
156 |
if(align == null) { |
157 |
if(pr.isSetAlgn()) pr.unsetAlgn(); |
158 |
} else { |
159 |
pr.setAlgn(STTextAlignType.Enum.forInt(align.ordinal() + 1)); |
160 |
} |
161 |
} |
162 |
|
163 |
/** |
164 |
* Returns the font alignment that is applied to the paragraph. |
165 |
* |
166 |
* If this attribute is omitted, then a value of baseline is implied. |
167 |
* @return ??? alignment that is applied to the paragraph |
168 |
*/ |
169 |
public TextFontAlign getTextFontAlign(){ |
170 |
CTTextParagraphProperties pr = _p.getPPr(); |
171 |
if(pr != null) { |
172 |
return pr.isSetFontAlgn() ? TextFontAlign.values()[pr.getFontAlgn().intValue() - 1] : TextFontAlign.BASELINE; |
173 |
} |
174 |
return TextFontAlign.BASELINE; |
175 |
} |
176 |
|
177 |
/** |
178 |
* Determines where vertically on a line of text the actual words are positioned. This deals |
179 |
* with vertical placement of the characters with respect to the baselines. For instance |
180 |
* having text anchored to the top baseline, anchored to the bottom baseline, centered in |
181 |
* between, etc. |
182 |
* |
183 |
* @param align text font align |
184 |
*/ |
185 |
public void setTextFontAlign(TextFontAlign align){ |
186 |
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); |
187 |
if(align == null) { |
188 |
if(pr.isSetFontAlgn()) pr.unsetFontAlgn(); |
189 |
} else { |
190 |
pr.setFontAlgn(STTextFontAlignType.Enum.forInt(align.ordinal() + 1)); |
191 |
} |
192 |
} |
193 |
|
194 |
|
195 |
|
196 |
/** |
197 |
* Specifies the indent size that will be applied to the first line of text in the paragraph. |
198 |
* |
199 |
* @param value the indent in points. |
200 |
*/ |
201 |
public void setIndent(double value){ |
202 |
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); |
203 |
if(value == -1) { |
204 |
if(pr.isSetIndent()) pr.unsetIndent(); |
205 |
} else { |
206 |
pr.setIndent(Units.toEMU(value)); |
207 |
} |
208 |
} |
209 |
|
210 |
/** |
211 |
* |
212 |
* @return the indent applied to the first line of text in the paragraph. |
213 |
*/ |
214 |
public double getIndent(){ |
215 |
CTTextParagraphProperties pr = _p.getPPr(); |
216 |
if(pr == null) return 0; |
217 |
|
218 |
return Units.toPoints(pr.getIndent()); |
219 |
} |
220 |
|
221 |
|
222 |
|
223 |
/** |
224 |
* Specifies the left margin of the paragraph. This is specified in addition to the text body |
225 |
* inset and applies only to this text paragraph. That is the text body Inset and the LeftMargin |
226 |
* attributes are additive with respect to the text position. |
227 |
* |
228 |
* @param value the left margin of the paragraph |
229 |
*/ |
230 |
public void setLeftMargin(double value){ |
231 |
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); |
232 |
if(value == -1) { |
233 |
if(pr.isSetMarL()) pr.unsetMarL(); |
234 |
} else { |
235 |
pr.setMarL(Units.toEMU(value)); |
236 |
} |
237 |
|
238 |
} |
239 |
|
240 |
/** |
241 |
* |
242 |
* @return the left margin of the paragraph |
243 |
*/ |
244 |
public double getLeftMargin(){ |
245 |
CTTextParagraphProperties pr = _p.getPPr(); |
246 |
if(pr != null && pr.isSetMarL()) { |
247 |
return Units.toPoints(pr.getMarL()); |
248 |
} |
249 |
return 347663; // return the default which is 347663 |
250 |
} |
251 |
|
252 |
/** |
253 |
* Specifies the right margin of the paragraph. This is specified in addition to the text body |
254 |
* inset and applies only to this text paragraph. That is the text body inset and the marR |
255 |
* attributes are additive with respect to the text position. |
256 |
* |
257 |
* @param value the right margin of the paragraph |
258 |
*/ |
259 |
public void setRightMargin(double value){ |
260 |
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); |
261 |
if(value == -1) { |
262 |
if(pr.isSetMarR()) pr.unsetMarR(); |
263 |
} else { |
264 |
pr.setMarR(Units.toEMU(value)); |
265 |
} |
266 |
|
267 |
} |
268 |
|
269 |
/** |
270 |
* |
271 |
* @return the right margin of the paragraph |
272 |
*/ |
273 |
public double getRightMargin(){ |
274 |
CTTextParagraphProperties pr = _p.getPPr(); |
275 |
if(pr != null && pr.isSetMarR()) { |
276 |
return Units.toPoints(pr.getMarR()); |
277 |
} |
278 |
return 0; // return the default which is 0 |
279 |
} |
280 |
|
281 |
/** |
282 |
* Add a single tab stop to be used on a line of text when there are one or more tab characters |
283 |
* present within the text. |
284 |
* |
285 |
* @param value the position of the tab stop relative to the left margin |
286 |
*/ |
287 |
public void addTabStop(double value){ |
288 |
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); |
289 |
CTTextTabStopList tabStops = pr.isSetTabLst() ? pr.getTabLst() : pr.addNewTabLst(); |
290 |
tabStops.addNewTab().setPos(Units.toEMU(value)); |
291 |
} |
292 |
|
293 |
/** |
294 |
* This element specifies the vertical line spacing that is to be used within a paragraph. |
295 |
* This may be specified in two different ways, percentage spacing and font point spacing: |
296 |
* <p> |
297 |
* If linespacing >= 0, then linespacing is a percentage of normal line height |
298 |
* If linespacing < 0, the absolute value of linespacing is the spacing in points |
299 |
* </p> |
300 |
* Examples: |
301 |
* <pre><code> |
302 |
* // spacing will be 120% of the size of the largest text on each line |
303 |
* paragraph.setLineSpacing(120); |
304 |
* |
305 |
* // spacing will be 200% of the size of the largest text on each line |
306 |
* paragraph.setLineSpacing(200); |
307 |
* |
308 |
* // spacing will be 48 points |
309 |
* paragraph.setLineSpacing(-48.0); |
310 |
* </code></pre> |
311 |
* |
312 |
* @param linespacing the vertical line spacing |
313 |
*/ |
314 |
public void setLineSpacing(double linespacing){ |
315 |
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); |
316 |
CTTextSpacing spc = CTTextSpacing.Factory.newInstance(); |
317 |
if(linespacing >= 0) spc.addNewSpcPct().setVal((int)(linespacing*1000)); |
318 |
else spc.addNewSpcPts().setVal((int)(-linespacing*100)); |
319 |
pr.setLnSpc(spc); |
320 |
} |
321 |
|
322 |
/** |
323 |
* Returns the vertical line spacing that is to be used within a paragraph. |
324 |
* This may be specified in two different ways, percentage spacing and font point spacing: |
325 |
* <p> |
326 |
* If linespacing >= 0, then linespacing is a percentage of normal line height. |
327 |
* If linespacing < 0, the absolute value of linespacing is the spacing in points |
328 |
* </p> |
329 |
* |
330 |
* @return the vertical line spacing. |
331 |
*/ |
332 |
public double getLineSpacing(){ |
333 |
CTTextParagraphProperties pr = _p.getPPr(); |
334 |
double lnSpc = 100; // assume 100% default |
335 |
if(pr != null) { |
336 |
if(pr.isSetLnSpc()){ |
337 |
CTTextSpacing spc = pr.getLnSpc(); |
338 |
|
339 |
if(spc.isSetSpcPct()) |
340 |
lnSpc = spc.getSpcPct().getVal()*0.001; |
341 |
else if (spc.isSetSpcPts()) |
342 |
lnSpc = -spc.getSpcPts().getVal()*0.01; |
343 |
} |
344 |
} |
345 |
|
346 |
if(lnSpc > 0) { |
347 |
// check if the percentage value is scaled |
348 |
CTTextNormalAutofit normAutofit = getParentShape().getTxBody().getBodyPr().getNormAutofit(); |
349 |
if(normAutofit != null) { |
350 |
double scale = 1 - (double)normAutofit.getLnSpcReduction() / 100000; |
351 |
lnSpc *= scale; |
352 |
} |
353 |
} |
354 |
return lnSpc; |
355 |
} |
356 |
|
357 |
/** |
358 |
* Set the amount of vertical white space that will be present before the paragraph. |
359 |
* This space is specified in either percentage or points: |
360 |
* <p> |
361 |
* If spaceBefore >= 0, then space is a percentage of normal line height. |
362 |
* If spaceBefore < 0, the absolute value of linespacing is the spacing in points |
363 |
* </p> |
364 |
* Examples: |
365 |
* <pre><code> |
366 |
* // The paragraph will be formatted to have a spacing before the paragraph text. |
367 |
* // The spacing will be 200% of the size of the largest text on each line |
368 |
* paragraph.setSpaceBefore(200); |
369 |
* |
370 |
* // The spacing will be a size of 48 points |
371 |
* paragraph.setSpaceBefore(-48.0); |
372 |
* </code></pre> |
373 |
* |
374 |
* @param spaceBefore the vertical white space before the paragraph. |
375 |
*/ |
376 |
public void setSpaceBefore(double spaceBefore){ |
377 |
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); |
378 |
CTTextSpacing spc = CTTextSpacing.Factory.newInstance(); |
379 |
if(spaceBefore >= 0) spc.addNewSpcPct().setVal((int)(spaceBefore*1000)); |
380 |
else spc.addNewSpcPts().setVal((int)(-spaceBefore*100)); |
381 |
pr.setSpcBef(spc); |
382 |
} |
383 |
|
384 |
/** |
385 |
* The amount of vertical white space before the paragraph |
386 |
* This may be specified in two different ways, percentage spacing and font point spacing: |
387 |
* <p> |
388 |
* If spaceBefore >= 0, then space is a percentage of normal line height. |
389 |
* If spaceBefore < 0, the absolute value of linespacing is the spacing in points |
390 |
* </p> |
391 |
* |
392 |
* @return the vertical white space before the paragraph |
393 |
*/ |
394 |
public double getSpaceBefore(){ |
395 |
CTTextParagraphProperties pr = _p.getPPr(); |
396 |
if(pr != null) { |
397 |
if(pr.isSetSpcBef()) { |
398 |
CTTextSpacing spc = pr.getSpcBef(); |
399 |
|
400 |
if(spc.isSetSpcPct()) |
401 |
return spc.getSpcPct().getVal()*0.001; |
402 |
else if (spc.isSetSpcPts()) |
403 |
return -spc.getSpcPts().getVal()*0.01; |
404 |
} |
405 |
} |
406 |
return 0; |
407 |
} |
408 |
|
409 |
/** |
410 |
* Set the amount of vertical white space that will be present after the paragraph. |
411 |
* This space is specified in either percentage or points: |
412 |
* <p> |
413 |
* If spaceAfter >= 0, then space is a percentage of normal line height. |
414 |
* If spaceAfter < 0, the absolute value of linespacing is the spacing in points |
415 |
* </p> |
416 |
* Examples: |
417 |
* <pre><code> |
418 |
* // The paragraph will be formatted to have a spacing after the paragraph text. |
419 |
* // The spacing will be 200% of the size of the largest text on each line |
420 |
* paragraph.setSpaceAfter(200); |
421 |
* |
422 |
* // The spacing will be a size of 48 points |
423 |
* paragraph.setSpaceAfter(-48.0); |
424 |
* </code></pre> |
425 |
* |
426 |
* @param spaceAfter the vertical white space after the paragraph. |
427 |
*/ |
428 |
public void setSpaceAfter(double spaceAfter){ |
429 |
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); |
430 |
CTTextSpacing spc = CTTextSpacing.Factory.newInstance(); |
431 |
if(spaceAfter >= 0) spc.addNewSpcPct().setVal((int)(spaceAfter*1000)); |
432 |
else spc.addNewSpcPts().setVal((int)(-spaceAfter*100)); |
433 |
pr.setSpcAft(spc); |
434 |
} |
435 |
|
436 |
/** |
437 |
* The amount of vertical white space after the paragraph |
438 |
* This may be specified in two different ways, percentage spacing and font point spacing: |
439 |
* <p> |
440 |
* If spaceBefore >= 0, then space is a percentage of normal line height. |
441 |
* If spaceBefore < 0, the absolute value of linespacing is the spacing in points |
442 |
* </p> |
443 |
* |
444 |
* @return the vertical white space after the paragraph |
445 |
*/ |
446 |
public double getSpaceAfter(){ |
447 |
CTTextParagraphProperties pr = _p.getPPr(); |
448 |
if(pr != null) { |
449 |
if(pr.isSetSpcAft()) { |
450 |
CTTextSpacing spc = pr.getSpcAft(); |
451 |
|
452 |
if(spc.isSetSpcPct()) |
453 |
return spc.getSpcPct().getVal()*0.001; |
454 |
else if (spc.isSetSpcPts()) |
455 |
return -spc.getSpcPts().getVal()*0.01; |
456 |
} |
457 |
} |
458 |
return 0; |
459 |
} |
460 |
|
461 |
/** |
462 |
* Specifies the particular level text properties that this paragraph will follow. |
463 |
* The value for this attribute formats the text according to the corresponding level |
464 |
* paragraph properties defined in the SlideMaster. |
465 |
* |
466 |
* @param level the level (0 ... 4) |
467 |
*/ |
468 |
public void setLevel(int level){ |
469 |
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); |
470 |
|
471 |
pr.setLvl(level); |
472 |
} |
473 |
|
474 |
/** |
475 |
* |
476 |
* @return the text level of this paragraph (0-based). Default is 0. |
477 |
*/ |
478 |
public int getLevel(){ |
479 |
CTTextParagraphProperties pr = _p.getPPr(); |
480 |
if(pr == null) return 0; |
481 |
|
482 |
return pr.getLvl(); |
483 |
|
484 |
} |
485 |
|
486 |
@Override |
487 |
public String toString(){ |
488 |
return "[" + getClass() + "]" + getText(); |
489 |
} |
490 |
|
491 |
/* |
492 |
void copy(XSLFTextParagraph p){ |
493 |
TextAlign srcAlign = p.getTextAlign(); |
494 |
if(srcAlign != getTextAlign()){ |
495 |
setTextAlign(srcAlign); |
496 |
} |
497 |
|
498 |
boolean isBullet = p.isBullet(); |
499 |
if(isBullet != isBullet()){ |
500 |
setBullet(isBullet); |
501 |
if(isBullet) { |
502 |
String buFont = p.getBulletFont(); |
503 |
if(buFont != null && !buFont.equals(getBulletFont())){ |
504 |
setBulletFont(buFont); |
505 |
} |
506 |
String buChar = p.getBulletCharacter(); |
507 |
if(buChar != null && !buChar.equals(getBulletCharacter())){ |
508 |
setBulletCharacter(buChar); |
509 |
} |
510 |
Color buColor = p.getBulletFontColor(); |
511 |
if(buColor != null && !buColor.equals(getBulletFontColor())){ |
512 |
setBulletFontColor(buColor); |
513 |
} |
514 |
double buSize = p.getBulletFontSize(); |
515 |
if(buSize != getBulletFontSize()){ |
516 |
setBulletFontSize(buSize); |
517 |
} |
518 |
} |
519 |
} |
520 |
|
521 |
double leftMargin = p.getLeftMargin(); |
522 |
if(leftMargin != getLeftMargin()){ |
523 |
setLeftMargin(leftMargin); |
524 |
} |
525 |
|
526 |
double indent = p.getIndent(); |
527 |
if(indent != getIndent()){ |
528 |
setIndent(indent); |
529 |
} |
530 |
|
531 |
double spaceAfter = p.getSpaceAfter(); |
532 |
if(spaceAfter != getSpaceAfter()){ |
533 |
setSpaceAfter(spaceAfter); |
534 |
} |
535 |
double spaceBefore = p.getSpaceBefore(); |
536 |
if(spaceBefore != getSpaceBefore()){ |
537 |
setSpaceBefore(spaceBefore); |
538 |
} |
539 |
double lineSpacing = p.getLineSpacing(); |
540 |
if(lineSpacing != getLineSpacing()){ |
541 |
setLineSpacing(lineSpacing); |
542 |
} |
543 |
|
544 |
List<XSSFTextRun> srcR = p.getTextRuns(); |
545 |
List<XSSFTextRun> tgtR = getTextRuns(); |
546 |
for(int i = 0; i < srcR.size(); i++){ |
547 |
XSSFTextRun r1 = srcR.get(i); |
548 |
XSSFTextRun r2 = tgtR.get(i); |
549 |
r2.copy(r1); |
550 |
} |
551 |
}*/ |
552 |
|
553 |
} |