Skip to content

Commit

Permalink
[bug-67735] Add Complex scripts support in XWPFRun. Thanks to Mohamme…
Browse files Browse the repository at this point in the history
…d Alhaddar. This closes #536

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1912963 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
pjfanning committed Oct 14, 2023
1 parent 407bb37 commit bc870ab
Show file tree
Hide file tree
Showing 2 changed files with 206 additions and 18 deletions.
173 changes: 159 additions & 14 deletions poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFRun.java
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ public void setLang(String lang) {
* Whether the bold property shall be applied to all non-complex script
* characters in the contents of this run when displayed in a document
*
* @return {@code true} if the bold property is applied
* @return {@code true} if the bold property for non-complex scripts is applied
*/
@Override
public boolean isBold() {
Expand All @@ -261,7 +261,19 @@ public boolean isBold() {
}

/**
* Whether the bold property shall be applied to all non-complex script
* Whether the bold property shall be applied to the complex
* characters in the contents of this run when displayed in a document.
*
* @return {@code true} if the bold property for complex scripts is applied
* @since POI 5.2.5
*/
public boolean isComplexScriptBold() {
CTRPr pr = getRunProperties(false);
return pr != null && pr.sizeOfBCsArray() > 0 && isCTOnOff(pr.getBCsArray(0));
}

/**
* Whether the bold property shall be applied to the non-complex
* characters in the contents of this run when displayed in a document.
* <p>
* This formatting property is a toggle property, which specifies that its
Expand All @@ -281,8 +293,7 @@ public boolean isBold() {
* applied to non-complex script characters.
* </p>
*
* @param value {@code true} if the bold property is applied to
* this run
* @param value {@code true} if the bold property is applied for non-complex characters.
*/
@Override
public void setBold(boolean value) {
Expand All @@ -291,6 +302,36 @@ public void setBold(boolean value) {
bold.setVal(value ? STOnOff1.ON : STOnOff1.OFF);
}

/**
* Whether the bold property shall be applied to the complex
* characters in the contents of this run when displayed in a document.
* <p>
* This formatting property is a toggle property, which specifies that its
* behavior differs between its use within a style definition and its use as
* direct formatting. When used as part of a style definition, setting this
* property shall toggle the current state of that property as specified up
* to this point in the hierarchy (i.e. applied to not applied, and vice
* versa). Setting it to {@code false} (or an equivalent) shall
* result in the current setting remaining unchanged. However, when used as
* direct formatting, setting this property to true or false shall set the
* absolute state of the resulting property.
* </p>
* <p>
* If this element is not present, the default value is to leave the
* formatting applied at previous level in the style hierarchy. If this
* element is never applied in the style hierarchy, then bold shall not be
* applied to the complex characters.
* </p>
*
* @param value {@code true} if the bold property is applied for complex characters
* @since POI 5.2.5
*/
public void setComplexScriptBold(boolean value) {
CTRPr pr = getRunProperties(true);
CTOnOff bold = pr.sizeOfBCsArray() > 0 ? pr.getBCsArray(0) : pr.addNewBCs();
bold.setVal(value ? STOnOff1.ON : STOnOff1.OFF);
}

/**
* Get text color. The returned value is a string in the hex form "RRGGBB". This can be <code>null</code>.
*/
Expand Down Expand Up @@ -362,7 +403,7 @@ public void setText(String value, int pos) {
* Whether the italic property should be applied to all non-complex script
* characters in the contents of this run when displayed in a document.
*
* @return {@code true} if the italic property is applied
* @return {@code true} if the italic property is applied for non-complex characters.
*/
@Override
public boolean isItalic() {
Expand All @@ -371,8 +412,20 @@ public boolean isItalic() {
}

/**
* Whether the bold property shall be applied to all non-complex script
* characters in the contents of this run when displayed in a document
* Whether the italic property should be applied to the complex
* characters in the contents of this run when displayed in a document.
*
* @return {@code true} if the italic property is applied for complex characters.
* @since POI 5.2.5
*/
public boolean isComplexScriptItalic() {
CTRPr pr = getRunProperties(false);
return pr != null && pr.sizeOfICsArray() > 0 && isCTOnOff(pr.getICsArray(0));
}

/**
* Whether the italic property shall be applied to the non-complex
* characters in the contents of this run when displayed in a document.
* <p>
* This formatting property is a toggle property, which specifies that its
* behavior differs between its use within a style definition and its use as
Expand All @@ -389,8 +442,7 @@ public boolean isItalic() {
* element is never applied in the style hierarchy, then bold shall not be
* applied to non-complex script characters.
*
* @param value {@code true} if the italic property is applied to
* this run
* @param value {@code true} if the italic property is applied for non-complex characters.
*/
@Override
public void setItalic(boolean value) {
Expand All @@ -399,6 +451,34 @@ public void setItalic(boolean value) {
italic.setVal(value ? STOnOff1.ON : STOnOff1.OFF);
}

/**
* Whether the italic property shall be applied to the complex
* characters in the contents of this run when displayed in a document.
* <p>
* This formatting property is a toggle property, which specifies that its
* behavior differs between its use within a style definition and its use as
* direct formatting. When used as part of a style definition, setting this
* property shall toggle the current state of that property as specified up
* to this point in the hierarchy (i.e. applied to not applied, and vice
* versa). Setting it to {@code false} (or an equivalent) shall
* result in the current setting remaining unchanged. However, when used as
* direct formatting, setting this property to true or false shall set the
* absolute state of the resulting property.
* <p>
* If this element is not present, the default value is to leave the
* formatting applied at previous level in the style hierarchy. If this
* element is never applied in the style hierarchy, then italic shall not be
* applied to the complex characters.
*
* @param value {@code true} if the italic property is applied for complex characters.
* @since POI 5.2.5
*/
public void setComplexScriptItalic(boolean value) {
CTRPr pr = getRunProperties(true);
CTOnOff italic = pr.sizeOfICsArray() > 0 ? pr.getICsArray(0) : pr.addNewICs();
italic.setVal(value ? STOnOff1.ON : STOnOff1.OFF);
}

/**
* Get the underline setting for the run.
*
Expand Down Expand Up @@ -849,7 +929,7 @@ public int getFontSize() {
}

/**
* Specifies the font size which shall be applied to all non complex script
* Specifies the font size which shall be applied to the non-complex
* characters in the contents of this run when displayed.
*
* @return value representing the font size (can be null if size not set)
Expand All @@ -861,11 +941,31 @@ public Double getFontSizeAsDouble() {
return bd == null ? null : bd.doubleValue();
}

/**
* Specifies the font size which shall be applied to the complex script
* characters in the contents of this run when displayed.
*
* @return value representing the font size for the complex scripts (can be null if size not set)
* @since POI 5.2.5
*/
public Double getComplexScriptFontSizeAsDouble() {
BigDecimal bd = getComplexScriptFontSizeAsBigDecimal(1);
return bd == null ? null : bd.doubleValue();
}


private BigDecimal getFontSizeAsBigDecimal(int scale) {
CTRPr pr = getRunProperties(false);
return (pr != null && pr.sizeOfSzArray() > 0)
? BigDecimal.valueOf(Units.toPoints(POIXMLUnits.parseLength(pr.getSzArray(0).xgetVal()))).divide(BigDecimal.valueOf(4), scale, RoundingMode.HALF_UP)
: null;
? BigDecimal.valueOf(Units.toPoints(POIXMLUnits.parseLength(pr.getSzArray(0).xgetVal()))).divide(BigDecimal.valueOf(4), scale, RoundingMode.HALF_UP)
: null;
}

private BigDecimal getComplexScriptFontSizeAsBigDecimal(int scale) {
CTRPr pr = getRunProperties(false);
return (pr != null && pr.sizeOfSzCsArray() > 0)
? BigDecimal.valueOf(Units.toPoints(POIXMLUnits.parseLength(pr.getSzCsArray(0).xgetVal()))).divide(BigDecimal.valueOf(4), scale, RoundingMode.HALF_UP)
: null;
}

/**
Expand All @@ -890,8 +990,30 @@ public void setFontSize(int size) {
}

/**
* Specifies the font size which shall be applied to all non complex script
* Specifies the font size which shall be applied to the currently specified complex
* script characters in the contents of this run when displayed.
* <p>
* If this element is not present, the default value is to leave the value
* applied at previous level in the style hierarchy. If this element is
* never applied in the style hierarchy, then any appropriate font size may
* be used for the non-complex characters.
* </p>
*
* @param size The font size as number of point measurements.
* @see #setComplexScriptFontSize(double)
* @since POI 5.2.5
*/
public void setComplexScriptFontSize(int size) {
CTRPr pr = getRunProperties(true);
BigInteger bint = BigInteger.valueOf(size);
CTHpsMeasure ctCsSize = pr.sizeOfSzCsArray() > 0 ? pr.getSzCsArray(0) : pr.addNewSzCs();
ctCsSize.setVal(bint.multiply(BigInteger.valueOf(2)));
}

/**
* Specifies the font size which shall be applied to the currently specified non-complex
* characters in the contents of this run when displayed.
*
* <p>
* If this element is not present, the default value is to leave the value
* applied at previous level in the style hierarchy. If this element is
Expand All @@ -911,6 +1033,29 @@ public void setFontSize(double size) {
ctSize.setVal(bd.multiply(BigDecimal.valueOf(2)).setScale(0, RoundingMode.HALF_UP).toBigInteger());
}


/**
* Specifies the font size which shall be applied to the currently specified complex
* characters in the contents of this run when displayed.
*
* <p>
* If this element is not present, the default value is to leave the value
* applied at previous level in the style hierarchy. If this element is
* never applied in the style hierarchy, then any appropriate font size may
* be used for the non-complex characters.
* </p>
*
* @param size The font size as number of point measurements.
* @see #setFontSize(int)
* @since POI 5.2.5
*/
public void setComplexScriptFontSize(double size) {
CTRPr pr = getRunProperties(true);
BigDecimal bd = BigDecimal.valueOf(size);
CTHpsMeasure ctCsSize = pr.sizeOfSzCsArray() > 0 ? pr.getSzCsArray(0) : pr.addNewSzCs();
ctCsSize.setVal(bd.multiply(BigDecimal.valueOf(2)).setScale(0, RoundingMode.HALF_UP).toBigInteger());
}

/**
* This element specifies the amount by which text shall be raised or
* lowered for this run in relation to the default baseline of the
Expand Down Expand Up @@ -1250,7 +1395,7 @@ public List<XWPFPicture> getEmbeddedPictures() {
public void setStyle(String styleId) {
CTRPr pr = getCTR().getRPr();
if (null == pr) {
pr = getCTR().addNewRPr();
pr = getCTR().addNewRPr();
}
CTString style = pr.sizeOfRStyleArray() > 0 ? pr.getRStyleArray(0) : pr.addNewRStyle();
style.setVal(styleId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,20 @@ void testSetGetBold() {
assertEquals("off", rpr.getBArray(0).getVal());
}

@Test
void testSetGetComplexBold() {
CTRPr rpr = ctRun.addNewRPr();
rpr.addNewBCs().setVal(STOnOff1.ON);

XWPFRun run = new XWPFRun(ctRun, irb);
assertTrue(run.isComplexScriptBold());

run.setComplexScriptBold(false);
// Implementation detail: POI natively prefers <w:b w:val="false"/>,
// but should correctly read val="0" and val="off"
assertEquals("off", rpr.getBCsArray(0).getVal());
}

@Test
void testSetGetItalic() {
CTRPr rpr = ctRun.addNewRPr();
Expand All @@ -157,6 +171,18 @@ void testSetGetItalic() {
assertEquals("off", rpr.getIArray(0).getVal());
}

@Test
void testSetGetItalicComplex() {
CTRPr rpr = ctRun.addNewRPr();
rpr.addNewICs().setVal(STOnOff1.ON);

XWPFRun run = new XWPFRun(ctRun, irb);
assertTrue(run.isComplexScriptItalic());

run.setComplexScriptItalic(false);
assertEquals("off", rpr.getICsArray(0).getVal());
}

@Test
void testSetGetStrike() {
CTRPr rpr = ctRun.addNewRPr();
Expand Down Expand Up @@ -213,6 +239,23 @@ void testSetGetFontSize() {
assertEquals(24.5, run.getFontSizeAsDouble(), 0.01);
}

@Test
void testSetGetFontSizeComplex() {
CTRPr rpr = ctRun.addNewRPr();
rpr.addNewSzCs().setVal(BigInteger.valueOf(14));

XWPFRun run = new XWPFRun(ctRun, irb);

assertEquals(7.0, run.getComplexScriptFontSizeAsDouble(), 0.01);

run.setComplexScriptFontSize(24);
assertEquals("48", rpr.getSzCsArray(0).getVal().toString());

run.setComplexScriptFontSize(24.5f);
assertEquals("49", rpr.getSzCsArray(0).getVal().toString());
assertEquals(24.5, run.getComplexScriptFontSizeAsDouble(), 0.01);
}

@Test
void testSetGetTextForegroundBackground() {
CTRPr rpr = ctRun.addNewRPr();
Expand Down Expand Up @@ -561,9 +604,9 @@ void testAddPictureInHeader() throws IOException, InvalidFormatException {
void testSetFontFamily_52288() throws IOException {
try (XWPFDocument doc = openSampleDocument("52288.docx")) {
doc.getParagraphs().stream()
.flatMap(p -> p.getRuns().stream())
.filter(p -> p != null && p.getText(0) != null)
.forEach(r -> assertDoesNotThrow(() -> r.setFontFamily("Times New Roman")));
.flatMap(p -> p.getRuns().stream())
.filter(p -> p != null && p.getText(0) != null)
.forEach(r -> assertDoesNotThrow(() -> r.setFontFamily("Times New Roman")));
}
}

Expand All @@ -573,7 +616,7 @@ void testBug55476() throws IOException, InvalidFormatException {
try (XWPFDocument document = new XWPFDocument()) {

document.createParagraph().createRun().addPicture(
new ByteArrayInputStream(image), Document.PICTURE_TYPE_JPEG, "test.jpg", Units.toEMU(300), Units.toEMU(100));
new ByteArrayInputStream(image), Document.PICTURE_TYPE_JPEG, "test.jpg", Units.toEMU(300), Units.toEMU(100));

try (XWPFDocument docBack = writeOutAndReadBack(document)) {
List<XWPFPicture> pictures = docBack.getParagraphArray(0).getRuns().get(0).getEmbeddedPictures();
Expand Down

0 comments on commit bc870ab

Please sign in to comment.