From bc870ab76497d3f968a32282104fb170bea7a77f Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 14 Oct 2023 14:31:52 +0000 Subject: [PATCH] [bug-67735] Add Complex scripts support in XWPFRun. Thanks to Mohammed Alhaddar. This closes #536 git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1912963 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/xwpf/usermodel/XWPFRun.java | 173 ++++++++++++++++-- .../poi/xwpf/usermodel/TestXWPFRun.java | 51 +++++- 2 files changed, 206 insertions(+), 18 deletions(-) diff --git a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFRun.java b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFRun.java index a8a1af4ca9..e86ca85720 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFRun.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFRun.java @@ -252,7 +252,7 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { * 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() { @@ -261,7 +261,19 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { } /** - * 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. *

* This formatting property is a toggle property, which specifies that its @@ -281,8 +293,7 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { * applied to non-complex script characters. *

* - * @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) { @@ -291,6 +302,36 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { 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. + *

+ * 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. + *

+ *

+ * 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. + *

+ * + * @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 null. */ @@ -362,7 +403,7 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { * 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() { @@ -371,8 +412,20 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { } /** - * 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. *

* This formatting property is a toggle property, which specifies that its * behavior differs between its use within a style definition and its use as @@ -389,8 +442,7 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { * 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) { @@ -399,6 +451,34 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { 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. + *

+ * 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. + *

+ * 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. * @@ -849,7 +929,7 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { } /** - * 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) @@ -861,11 +941,31 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { 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; } /** @@ -890,8 +990,30 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { } /** - * 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. + *

+ * 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. + *

+ * + * @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. + * *

* 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 @@ -911,6 +1033,29 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { 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. + * + *

+ * 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. + *

+ * + * @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 @@ -1250,7 +1395,7 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun { 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); diff --git a/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFRun.java b/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFRun.java index dcdfa4c3b9..eee37446e1 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFRun.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFRun.java @@ -145,6 +145,20 @@ class TestXWPFRun { 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 , + // but should correctly read val="0" and val="off" + assertEquals("off", rpr.getBCsArray(0).getVal()); + } + @Test void testSetGetItalic() { CTRPr rpr = ctRun.addNewRPr(); @@ -157,6 +171,18 @@ class TestXWPFRun { 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(); @@ -213,6 +239,23 @@ class TestXWPFRun { 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(); @@ -561,9 +604,9 @@ class TestXWPFRun { 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"))); } } @@ -573,7 +616,7 @@ class TestXWPFRun { 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 pictures = docBack.getParagraphArray(0).getRuns().get(0).getEmbeddedPictures(); -- 2.39.5