From aead782eb2980ae26d1c015b19cd0372cf7f822e Mon Sep 17 00:00:00 2001 From: Etienne Gautier Date: Fri, 9 Jan 2026 23:09:12 +1100 Subject: [PATCH] XWPF Themes: allow public access of theme and add helpers for theme fonts (#986) * allow public access of theme and add helpers for theme fonts * nit:naming * add javadoc and tests * Fix formatting of getMajorFontForScript method * Refactor font retrieval methods for null checks * Update XWPFTheme.java --------- Co-authored-by: PJ Fanning --- .../apache/poi/xwpf/usermodel/XWPFTheme.java | 74 ++++++++++++++++++- .../poi/xwpf/usermodel/TestXWPFTheme.java | 7 ++ 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFTheme.java b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFTheme.java index 735b930c88..b73311ef3f 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFTheme.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFTheme.java @@ -27,7 +27,9 @@ import org.apache.xmlbeans.XmlOptions; import org.openxmlformats.schemas.drawingml.x2006.main.CTBaseStyles; import org.openxmlformats.schemas.drawingml.x2006.main.CTColor; import org.openxmlformats.schemas.drawingml.x2006.main.CTColorScheme; +import org.openxmlformats.schemas.drawingml.x2006.main.CTFontCollection; import org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeStyleSheet; +import org.openxmlformats.schemas.drawingml.x2006.main.CTSupplementalFont; import org.openxmlformats.schemas.drawingml.x2006.main.ThemeDocument; import org.openxmlformats.schemas.wordprocessingml.x2006.main.*; @@ -67,6 +69,14 @@ public class XWPFTheme extends POIXMLDocumentPart { _theme = theme.getXmlObject(); } + /** + * @return the underlying CTOfficeStyleSheet instance. + * @since 6.0.0 + */ + public CTOfficeStyleSheet getCTOfficeStyleSheet() { + return _theme; + } + /** * * @return name of this theme, e.g. "Office Theme" @@ -137,8 +147,10 @@ public class XWPFTheme extends POIXMLDocumentPart { * */ @SuppressWarnings("WeakerAccess") - public String getMajorFont(){ - return _theme.getThemeElements().getFontScheme().getMajorFont().getLatin().getTypeface(); + public String getMajorFont() { + CTFontCollection majorFonts = getMajorFonts(); + return majorFonts == null || majorFonts.getLatin() == null ? + null : majorFonts.getLatin().getTypeface(); } /** @@ -147,8 +159,62 @@ public class XWPFTheme extends POIXMLDocumentPart { * */ @SuppressWarnings("WeakerAccess") - public String getMinorFont(){ - return _theme.getThemeElements().getFontScheme().getMinorFont().getLatin().getTypeface(); + public String getMinorFont() { + CTFontCollection minorFonts = getMinorFonts(); + return minorFonts == null || minorFonts.getLatin() == null ? + null : minorFonts.getLatin().getTypeface(); + } + + /** + * @param script a 4-letter script code, e.g. "Latn", "Jpan" + * @return typeface of the major font for the given script + * @since 6.0.0 + */ + public String getMajorFontForScript(String script) { + CTFontCollection majorFonts = getMajorFonts(); + return majorFonts == null ? null : getFontTypeface(majorFonts, script); + } + + /** + * @param script a 4-letter script code, e.g. "Latn", "Jpan" + * @return typeface of the minor font for the given script + * @since 6.0.0 + */ + public String getMinorFontForScript(String script) { + CTFontCollection minorFonts = getMinorFonts(); + return minorFonts == null ? null : getFontTypeface(minorFonts, script); + } + + private CTFontCollection getMajorFonts() { + if (_theme == null + || _theme.getThemeElements() == null + || _theme.getThemeElements().getFontScheme() == null + || _theme.getThemeElements().getFontScheme().getMajorFont() == null) { + return null; + } + return _theme.getThemeElements().getFontScheme().getMajorFont(); + } + + private CTFontCollection getMinorFonts() { + if (_theme == null + || _theme.getThemeElements() == null + || _theme.getThemeElements().getFontScheme() == null + || _theme.getThemeElements().getFontScheme().getMinorFont() == null) { + return null; + } + return _theme.getThemeElements().getFontScheme().getMinorFont(); + } + + private static String getFontTypeface(CTFontCollection fontCollection, String script) { + CTSupplementalFont[] fonts = fontCollection.getFontArray(); + if (fonts != null) { + for (CTSupplementalFont font : fonts) { + if (font.getScript() != null && font.getScript().equals(script)) { + return font.getTypeface(); + } + } + } + return null; } /** diff --git a/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFTheme.java b/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFTheme.java index 0ae73cfd23..38c9fb949e 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFTheme.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFTheme.java @@ -21,11 +21,13 @@ import org.apache.poi.xslf.usermodel.XSLFColor; import org.apache.poi.xwpf.XWPFTestDataSamples; import org.junit.jupiter.api.Test; import org.openxmlformats.schemas.drawingml.x2006.main.CTColor; +import org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeStyleSheet; import java.awt.*; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; public final class TestXWPFTheme { @@ -36,6 +38,11 @@ public final class TestXWPFTheme { assertEquals("Office Theme", theme.getName()); assertEquals("Cambria", theme.getMajorFont()); assertEquals("Calibri", theme.getMinorFont()); + assertEquals("Angsana New", theme.getMajorFontForScript("Thai")); + assertEquals("Cordia New", theme.getMinorFontForScript("Thai")); + CTOfficeStyleSheet styleSheet = theme.getCTOfficeStyleSheet(); + assertNotNull(styleSheet); + assertEquals("Office", styleSheet.getThemeElements().getFontScheme().getName()); CTColor accent1 = theme.getCTColor("accent1"); XSLFColor color = new XSLFColor(accent1, null, null, null); assertEquals(new Color(79, 129, 189), color.getColor());