Allow null values in XWPFParagraph.get/setAlignment(). (#829)

* Allow null values in XWPFParagraph.get/setAlignment().

A null value in this field would indicate that the paragraph should
follow the alignment provided by the style hierarchy.

Fixes: https://bz.apache.org/bugzilla/show_bug.cgi?id=69720

* Revert getAlignment() and implement isAlignmentSet() instead.

* Replace test case file.

* Implement XWPFParagraph.getTCPPr(create).

It lets the caller choose if a new PPr should be created in case it
doesn't exist.

* use getCTPPr(boolean)

* compile issue

---------

Co-authored-by: PJ Fanning <pjfanning@users.noreply.github.com>
This commit is contained in:
Jacobo Aragunde Pérez 2025-07-09 21:00:14 +02:00 committed by GitHub
parent 550be7c201
commit 6d8f1901dc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 83 additions and 24 deletions

View File

@ -482,16 +482,19 @@ public class XWPFParagraph implements IBodyElement, IRunBody, ISDTContents, Para
* Returns the paragraph alignment which shall be applied to text in this
* paragraph.
* <p>
* If this element is not set on a given paragraph, its value is determined
* If this element is not set on a given paragraph, this function returns
* ParagraphAlignment.LEFT as a placeholder value, and isAlignmentSet()
* returns false. In such case, the alignment value must be determined
* by the setting previously set at any level of the style hierarchy (i.e.
* that previous setting remains unchanged). If this setting is never
* specified in the style hierarchy, then no alignment is applied to the
* paragraph.
*
* @see #isAlignmentSet()
* @return the paragraph alignment of this paragraph.
*/
public ParagraphAlignment getAlignment() {
CTPPr pr = getCTPPr();
CTPPr pr = getCTPPr(false);
return pr == null || !pr.isSetJc() ? ParagraphAlignment.LEFT
: ParagraphAlignment.valueOf(pr.getJc().getVal().intValue());
}
@ -506,13 +509,32 @@ public class XWPFParagraph implements IBodyElement, IRunBody, ISDTContents, Para
* specified in the style hierarchy, then no alignment is applied to the
* paragraph.
*
* @param align the paragraph alignment to apply to this paragraph.
* @param align the paragraph alignment to apply to this paragraph. It can
* be null to unset it and fall back to the style hierarchy.
*/
public void setAlignment(ParagraphAlignment align) {
CTPPr pr = getCTPPr();
CTJc jc = pr.isSetJc() ? pr.getJc() : pr.addNewJc();
STJc.Enum en = STJc.Enum.forInt(align.getValue());
jc.setVal(en);
if (align == null) {
CTPPr pr = getCTPPr(false);
if (pr != null)
pr.unsetJc();
} else {
CTPPr pr = getCTPPr(true);
CTJc jc = pr.isSetJc() ? pr.getJc() : pr.addNewJc();
STJc.Enum en = STJc.Enum.forInt(align.getValue());
jc.setVal(en);
}
}
/**
* Returns true if the paragraph has a paragraph alignment value of its own
* or false in case it should fall back to the alignment value set by the
* paragraph style.
*
* @return boolean
*/
public boolean isAlignmentSet() {
CTPPr pr = getCTPPr(false);
return pr != null && pr.isSetJc();
}
/**
@ -548,7 +570,7 @@ public class XWPFParagraph implements IBodyElement, IRunBody, ISDTContents, Para
* @return the vertical alignment of this paragraph.
*/
public TextAlignment getVerticalAlignment() {
CTPPr pr = getCTPPr();
CTPPr pr = getCTPPr(false);
return (pr == null || !pr.isSetTextAlignment()) ? TextAlignment.AUTO
: TextAlignment.valueOf(pr.getTextAlignment().getVal()
.intValue());
@ -864,7 +886,10 @@ public class XWPFParagraph implements IBodyElement, IRunBody, ISDTContents, Para
* @return boolean - if page break is set
*/
public boolean isPageBreak() {
final CTPPr ppr = getCTPPr();
final CTPPr ppr = getCTPPr(false);
if (ppr == null) {
return false;
}
final CTOnOff ctPageBreak = ppr.isSetPageBreakBefore() ? ppr.getPageBreakBefore() : null;
if (ctPageBreak == null) {
return false;
@ -1353,7 +1378,8 @@ public class XWPFParagraph implements IBodyElement, IRunBody, ISDTContents, Para
*/
@Override
public boolean isWordWrapped() {
return getCTPPr().isSetWordWrap() && POIXMLUnits.parseOnOff(getCTPPr().getWordWrap());
CTPPr ppr = getCTPPr(false);
return ppr != null && ppr.isSetWordWrap() && POIXMLUnits.parseOnOff(ppr.getWordWrap());
}
/**
@ -1390,7 +1416,10 @@ public class XWPFParagraph implements IBodyElement, IRunBody, ISDTContents, Para
* @return the style of the paragraph
*/
public String getStyle() {
CTPPr pr = getCTPPr();
CTPPr pr = getCTPPr(false);
if (pr == null) {
return null;
}
CTString style = pr.isSetPStyle() ? pr.getPStyle() : null;
return style != null ? style.getVal() : null;
}
@ -1411,7 +1440,10 @@ public class XWPFParagraph implements IBodyElement, IRunBody, ISDTContents, Para
* a new instance.
*/
private CTPBdr getCTPBrd(boolean create) {
CTPPr pr = getCTPPr();
CTPPr pr = getCTPPr(create);
if (pr == null) {
return null;
}
CTPBdr ct = pr.isSetPBdr() ? pr.getPBdr() : null;
if (create && ct == null) {
ct = pr.addNewPBdr();
@ -1424,7 +1456,7 @@ public class XWPFParagraph implements IBodyElement, IRunBody, ISDTContents, Para
* return a new instance.
*/
private CTSpacing getCTSpacing(boolean create) {
CTPPr pr = getCTPPr();
CTPPr pr = getCTPPr(create);
CTSpacing ct = pr.getSpacing();
if (create && ct == null) {
ct = pr.addNewSpacing();
@ -1437,7 +1469,10 @@ public class XWPFParagraph implements IBodyElement, IRunBody, ISDTContents, Para
* a new instance.
*/
private CTInd getCTInd(boolean create) {
CTPPr pr = getCTPPr();
CTPPr pr = getCTPPr(create);
if (pr == null) {
return null;
}
CTInd ct = pr.getInd();
if (create && ct == null) {
ct = pr.addNewInd();
@ -1451,8 +1486,18 @@ public class XWPFParagraph implements IBodyElement, IRunBody, ISDTContents, Para
*/
@Internal
public CTPPr getCTPPr() {
return paragraph.getPPr() == null ? paragraph.addNewPPr()
: paragraph.getPPr();
return getCTPPr(true);
}
/**
* Get a <b>copy</b> of the currently used CTPPr. If none is used, return
* a new instance when create is true, or null when create is false.
*
* @param create create a new instance if none exists.
*/
private CTPPr getCTPPr(final boolean create) {
return (paragraph.isSetPPr() || !create) ? paragraph.getPPr()
: paragraph.addNewPPr();
}

View File

@ -16,10 +16,7 @@
==================================================================== */
package org.apache.poi.xwpf;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import java.io.File;
@ -42,10 +39,7 @@ import org.apache.poi.poifs.crypt.HashAlgorithm;
import org.apache.poi.poifs.filesystem.Ole10Native;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.junit.jupiter.api.Test;
@ -241,4 +235,17 @@ class TestXWPFBugs {
XWPFTable xwpfTable = document.insertNewTbl(xmlCursor);
xwpfTable.getRow(0).getCell(0).setText("Hello");
}
@Test
void correctParagraphAlignment() throws IOException {
try (XWPFDocument document = new XWPFDocument(samples.openResourceAsStream("bug-paragraph-alignment.docx"))) {
XWPFParagraph centeredParagraph = document.getParagraphArray(0);
assertFalse(centeredParagraph.isAlignmentSet());
assertEquals(ParagraphAlignment.LEFT, centeredParagraph.getAlignment()); // LEFT is a fallback value here.
XWPFParagraph leftParagraph = document.getParagraphArray(1);
assertTrue(leftParagraph.isAlignmentSet());
assertEquals(ParagraphAlignment.LEFT, leftParagraph.getAlignment()); // LEFT is the real alignment value.
}
}
}

View File

@ -132,6 +132,7 @@ public final class TestXWPFParagraph {
XWPFParagraph p = doc.createParagraph();
assertEquals(STJc.LEFT.intValue(), p.getAlignment().getValue());
assertFalse(p.isAlignmentSet());
CTP ctp = p.getCTP();
CTPPr ppr = ctp.getPPr() == null ? ctp.addNewPPr() : ctp.getPPr();
@ -139,9 +140,15 @@ public final class TestXWPFParagraph {
CTJc align = ppr.addNewJc();
align.setVal(STJc.CENTER);
assertEquals(ParagraphAlignment.CENTER, p.getAlignment());
assertTrue(p.isAlignmentSet());
p.setAlignment(ParagraphAlignment.BOTH);
assertEquals(STJc.BOTH, ppr.getJc().getVal());
assertTrue(p.isAlignmentSet());
p.setAlignment(null);
assertEquals(STJc.LEFT.intValue(), p.getAlignment().getValue());
assertFalse(p.isAlignmentSet());
}
}

Binary file not shown.