После того как я проверил ваш файл test.docx
, я могу сказать вам следующее:
Текст "Üldosa" и "Mõisted" не выделен жирным шрифтом, потому что они были отформатированы жирным шрифтом, а потому, что весь абзац выполнен в стиле "Заголовок 2" .И текст «Pooled» также не отформатирован жирным шрифтом, но применяется специальный стиль символов «Paks».Итак, кто-то широко использовал Word Styles .Совсем неплохо.Так же, как HTML лучше форматировать с использованием таблиц стилей CSS, а не напрямую, в Word также предпочтительнее использовать стиль.Но, конечно же, проблемы при разборе те же.Без дополнительного разбора таблиц стилей невозможно определить, каким образом будет представлен текст.К сожалению, apache poi
до сих пор мало заботится о стилях.
Как можно прийти к такому пониманию?Файл *.docx
- это просто архив ZIP
.Таким образом, мы можем разархивировать его и найдем:
/word/document.xml
:
<w:r ...>
<w:rPr>
...
<w:b/>
...
</w:rPr>
<w:t>HANKELEPINGU ÜLDTINGIMUSED</w:t>
</w:r>
Это текстовый прогон, отформатированный жирным шрифтом напрямую.
Но
<w:p ...>
<w:pPr>
<w:pStyle w:val="Heading2"/>
<w:numPr><w:ilvl w:val="0"/><w:numId w:val="2"/></w:numPr>
...
</w:pPr>
<w:r ...>
<w:t>Üldosa</w:t>
</w:r>
</w:p>
это абзац со стилем "Заголовок2", который автоматически нумеруется.
Так почему же этот текст выделен жирным шрифтом?В /word/styles.xml
мы находим:
<w:style w:type="paragraph" w:styleId="Heading2">
<w:name w:val="heading 2"/>
<w:basedOn w:val="Normal"/>
...
<w:link w:val="Heading2Char"/>
...
</w:style>
Это стиль абзаца "Heading2", который ссылается на стиль символа "Heading2Char".
<w:style w:type="character" w:customStyle="1" w:styleId="Heading2Char">
<w:name w:val="Heading 2 Char"/>
...
<w:link w:val="Heading2"/>
...
<w:rPr>
...
<w:b/>
...
</w:rPr>
</w:style>
Это стиль символа "Heading2Char", который выделен жирным шрифтом.
Чтобы ответить на вопрос, как выполнить это, используя apache poi
, необходимо знать, что apache poi
XWPF
основывается на org.openxmlformats.schemas.wordprocessingml.x2006.main.*
классах, которые происходят от ooxml-schemas.*.jar
.Поэтому нам нужна информация об этом.К сожалению, не существует общедоступной документации API
.Поэтому нам нужно скачать исходники и сделать javadoc
себя.
Так что же делать дальше?Перебираем абзацы и выполняем, как вы уже сделали.Но дополнительно для каждого абзаца попробуйте получить стиль для этого абзаца.Если он есть, найдите его и стиль его персонажа и проверьте, какие настройки он предоставляет.Также для каждого запуска попробуйте получить стиль персонажа для этого запуска.Если он есть, найдите его и проверьте, какие настройки он предоставляет.
Следующий код делает это, но только для того, чтобы проверить, обеспечивает ли стиль полужирную настройку.Так что это на самом деле не завершено, и сделать его полным будет действительно очень дорого.
import java.io.FileInputStream;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
public class ReadWordHavingStyles {
public static void main(String[] args) throws Exception {
XWPFDocument document = new XWPFDocument(new FileInputStream("test.docx"));
XWPFStyles styles = document.getStyles();
XWPFStyle style = null;
boolean isPBold = false;
boolean isRBold = false;
String boldReasonP = "";
String boldReasonR = "";
CTRPr cTRPr = null;
for(XWPFParagraph paragraph : document.getParagraphs()) {
isPBold = false;
boldReasonP = "";
String pStyleId = paragraph.getStyleID();
if (pStyleId != null) {
style = styles.getStyle(pStyleId);
if (style != null) {
String linkStyleId = style.getLinkStyleID();
style = styles.getStyle(linkStyleId);
if (style != null) {
cTRPr = style.getCTStyle().getRPr();
if (cTRPr != null) {
if (!cTRPr.isSetB()) {
isPBold = false;
} else {
STOnOff.Enum val = cTRPr.getB().getVal();
isPBold = !((STOnOff.FALSE == val) || (STOnOff.X_0 == val) || (STOnOff.OFF == val));
}
}
boldReasonP = " whole P is " + ((isPBold)?"":"not ") + "bold because of style " + linkStyleId;
}
}
}
if (!isPBold) boldReasonP = " P is not bold";
for(XWPFRun run : paragraph.getRuns()){
isRBold = isPBold;
boldReasonR = "";
cTRPr = run.getCTR().getRPr();
if (cTRPr != null) {
CTString rStyle = cTRPr.getRStyle();
if (rStyle != null) {
String rStyleId = rStyle.getVal();
style = styles.getStyle(rStyleId);
if (style != null) {
cTRPr = style.getCTStyle().getRPr();
if (cTRPr != null) {
if (!cTRPr.isSetB()) {
isRBold = false;
} else {
STOnOff.Enum val = cTRPr.getB().getVal();
isRBold = !((STOnOff.FALSE == val) || (STOnOff.X_0 == val) || (STOnOff.OFF == val));
}
}
boldReasonR = " run is " + ((isRBold)?"":"not ") + "bold because of style " + rStyleId;
}
}
}
if (!isRBold) boldReasonR = " run is not bold";
cTRPr = run.getCTR().getRPr();
if (cTRPr != null) {
if (cTRPr.isSetB()) {
STOnOff.Enum val = cTRPr.getB().getVal();
isRBold = !((STOnOff.FALSE == val) || (STOnOff.X_0 == val) || (STOnOff.OFF == val));
boldReasonR = " run is " + ((isRBold)?"":"not ") + "bold because of direct formatting";
}
}
System.out.println(run.text() + " isBold:" + isRBold + ":" + boldReasonP + boldReasonR);
}
}
document.close();
}
}