Nieformalne wprowadzenie do DOCX
Opublikowany: 2022-03-11Z około miliardem ludzi korzystających z Microsoft Office, format DOCX jest de facto najpopularniejszym standardem wymiany plików dokumentów między biurami. Jego najbliższy konkurent - format ODT - jest obsługiwany tylko przez Open/LibreOffice i niektóre produkty typu open source, co czyni go dalekim od standardu. Format PDF nie jest konkurentem, ponieważ pliki PDF nie mogą być edytowane i nie zawierają pełnej struktury dokumentu, więc mogą wprowadzać tylko ograniczone lokalne zmiany, takie jak znaki wodne, podpisy i tym podobne. Dlatego większość dokumentów biznesowych jest tworzonych w formacie DOCX; nie ma dobrej alternatywy, aby go zastąpić.
Chociaż DOCX jest złożonym formatem, możesz chcieć przeanalizować go ręcznie w celu prostszych zadań, takich jak indeksowanie, konwersja do formatu TXT i wprowadzanie innych drobnych modyfikacji. Chciałbym udzielić wystarczającej ilości informacji na temat wewnętrznych elementów DOCX, abyś nie musiał odwoływać się do specyfikacji ECMA, ogromnej 5000-stronicowej instrukcji.
Najlepszym sposobem zrozumienia formatu jest utworzenie prostego dokumentu składającego się z jednego słowa za pomocą MSWord i obserwowanie, jak edytowanie dokumentu zmienia bazowy kod XML. Będziesz mieć do czynienia z kilkoma przypadkami, w których DOCX nie jest poprawnie sformatowany w MS Word i nie wiesz dlaczego, lub natkniesz się na przypadki, w których nie jest jasne, jak wygenerować żądane formatowanie. Pomoże w tym zobaczenie i zrozumienie dokładnie tego, co dzieje się w XML.
Pracowałem przez około rok nad współpracującym edytorem DOCX, CollabOffice, i chcę podzielić się częścią tej wiedzy ze społecznością programistów. W tym artykule wyjaśnię strukturę plików DOCX, podsumowując informacje, które są rozproszone w Internecie. Ten artykuł jest pośrednikiem między ogromną, złożoną specyfikacją ECMA a dostępnymi obecnie prostymi samouczkami internetowymi. Pliki towarzyszące temu artykułowi można znaleźć w projekcie toptal-docx
na moim koncie github.
Prosty plik DOCX
Plik DOCX to archiwum ZIP zawierające pliki XML. Jeśli utworzysz nowy, pusty dokument Microsoft Word, wpiszesz w nim jedno słowo „Test” i rozpakujesz jego zawartość, zobaczysz następującą strukturę plików:
Mimo że stworzyliśmy prosty dokument, proces zapisywania w programie Microsoft Word wygenerował domyślne motywy, właściwości dokumentu, tabele czcionek itd. w formacie XML.
Na początek usuńmy nieużywane rzeczy i skupmy się na document.xml
, który zawiera główne elementy tekstowe. Kiedy usuwasz plik, upewnij się, że usunąłeś wszystkie odniesienia do niego z innych plików xml. Oto przykład różnicy kodu, w jaki sposób wyczyściłem zależności do app.xml i core.xml. Jeśli masz jakieś nierozwiązane/brakujące odniesienia, MSWord uzna plik za uszkodzony.
Oto struktura naszego uproszczonego, minimalnego dokumentu DOCX (a oto projekt na githubie):
Rozbijmy to według plików stąd, od góry:
_rels/.rels
Definiuje odniesienie, które mówi MS Word, gdzie szukać zawartości dokumentu. W tym przypadku odwołuje się do word/document.xml
:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"> <Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/> </Relationships>
_rels/document.xml.rels
Ten plik definiuje odniesienia do zasobów, takich jak obrazy, osadzone w treści dokumentu. Nasz prosty dokument nie ma osadzonych zasobów, więc znacznik relacji jest pusty:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"> </Relationships>
[Typy_treści].xml
[Content_Types].xml
zawiera informacje o typach multimediów w dokumencie. Ponieważ mamy tylko treść tekstową, jest to całkiem proste:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"> <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/> <Default Extension="xml" ContentType="application/xml"/> <Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/> </Types>
dokument.xml
Wreszcie, oto główny kod XML z treścią tekstową dokumentu. Dla jasności usunąłem niektóre deklaracje przestrzeni nazw, ale pełną wersję pliku można znaleźć w projekcie github. W tym pliku zauważysz, że niektóre odniesienia do przestrzeni nazw w dokumencie są nieużywane, ale nie powinieneś ich usuwać, ponieważ MS Word ich potrzebuje.
Oto nasz uproszczony przykład:
<w:document> <w:body> <w:pw:rsidR="005F670F" w:rsidRDefault="005F79F5"> <w:r><w:t>Test</w:t></w:r> </w:p> <w:sectPr w:rsidR="005F670F"> <w:pgSz w:w="12240" w:h="15840"/> <w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="720" w:footer="720" w:gutter="0"/> <w:cols w:space="720"/> <w:docGrid w:linePitch="360"/> </w:sectPr> </w:body> </w:document>
Główny węzeł <w:document>
reprezentuje sam dokument, <w:body>
zawiera akapity, a zagnieżdżone w <w:body>
są wymiary strony zdefiniowane przez <w:sectPr>
.
<w:rsidR>
to atrybut, który możesz zignorować; jest używany przez wewnętrzne elementy MS Word.
Przyjrzyjmy się bardziej złożonemu dokumentowi z trzema akapitami. Zaznaczyłem XML tymi samymi kolorami na zrzucie ekranu z Microsoft Word, więc możesz zobaczyć korelację:
<w:pw:rsidR="0081206C" w:rsidRDefault="00E10CAE"> <w:r> <w:t xml:space="preserve">To jest nasz przykładowy pierwszy akapit. Domyślnie jest wyrównany do lewej, a teraz chciałbym przedstawić</w:t> </w:r> <w:r> <w:rPr> <w:rFonts w:ascii="Arial" w:hAnsi="Arial" w:cs="Arial"/> <w:kolor w:val="000000"/> </w:rPr> <w:t>niektóre pogrubienie</w:t> </w:r> <w:r> <w:rPr> <w:rFonts w:ascii="Arial" w:hAnsi="Arial" w:cs="Arial"/> <w:b/> <w:kolor w:val="000000"/> </w:rPr> <w:t xml:space="preserve"> tekst</w:t> </w:r> <w:r> <w:rPr> <w:rFonts w:ascii="Arial" w:hAnsi="Arial" w:cs="Arial"/> <w:color w:val="000000"/> </w:rPr> <w:t xml:space="preserve">, </w:t> </w:r> <w:proofErr w:type="gramStart"/> <w:r> <w:t xml:space="preserve">a także zmień</w:t> </w:r> <w:rw:rsidRPr="00E10CAE"> <w:rPr><w:rFonts w:ascii="Impact" w:hAnsi="Impact"/> </w:rPr> <w:t>styl czcionki</w:t> </w:r> <w:r> <w:rPr> <w:rFonts w:ascii="Impact" w:hAnsi="Impact"/> </w:rPr> <w:t xml:space="preserve"> </w:t> </w:r> <w:r> <w:t>na „Wpływ”.</w:t></w:r> </w:p> <w:pw:rsidR="00E10CAE" w:rsidRDefault="00E10CAE"> <w:r> <w:t>To jest nowy akapit.</w:t> </w:r></w:p > <w:pw:rsidR="00E10CAE" w:rsidRPr="00E10CAE" w:rsidRDefault="00E10CAE"> <w:r> <w:t>To jeszcze jeden akapit, trochę dłuższy.</w:t> </w:r> </w:p>
Struktura akapitu
Prosty dokument składa się z akapitów, akapit składa się z ciągów (seria tekstu z tą samą czcionką, kolorem itp.), a ciągi składają się ze znaków (takich jak <w:t>
). Tagi <w:t>
mogą mieć kilka znaków w środku i może być ich kilka w tym samym przebiegu.
Ponownie możemy zignorować <w:rsidR>
.
Właściwości tekstu
Podstawowe właściwości tekstu to czcionka, rozmiar, kolor, styl i tak dalej. Istnieje około 40 tagów, które określają wygląd tekstu. Jak widać w naszym przykładzie z trzema akapitami, każdy przebieg ma swoje własne właściwości wewnątrz <w:rPr>
, określając <w:color>
, <w:rFonts>
i pogrubienie <w:b>
.
Ważną rzeczą do zapamiętania jest to, że właściwości rozróżniają dwie grupy znaków, zwykły i złożony skrypt (na przykład arabski) oraz że właściwości mają różne znaczniki w zależności od typu znaku, na który wpływają.
Większość zwykłych tagów właściwości skryptu ma pasujący złożony tag skryptu z dodatkiem „C”, który określa, że właściwość jest przeznaczona dla złożonych skryptów. Na przykład: <w:i>
(kursywa) staje się <w:iCs>
, a pogrubiony znacznik dla zwykłego skryptu, <w:b>
, zmienia się <w:bCs>
dla złożonego skryptu.
Style
W programie Microsoft Word znajduje się cały pasek narzędzi poświęcony stylom: normalny, bez odstępów, nagłówek 1, nagłówek 2, tytuł i tak dalej. Te style są przechowywane w /word/styles.xml
(uwaga: w pierwszym kroku naszego prostego przykładu usunęliśmy ten kod XML z DOCX. Aby to zobaczyć, utwórz nowy DOCX).
Po zdefiniowaniu tekstu jako stylu, odniesienie do tego stylu znajdziesz wewnątrz znacznika właściwości akapitu, <w:pPr>
. Oto przykład, w którym zdefiniowałem tekst za pomocą stylu Heading 1:
<w:p> <w:pPr> <w:pStyle w:val="Heading1"/> </w:pPr> <w:r> <w:t>My heading 1</w:t> </w:r> </w:p>
a oto sam styl z styles.xml
:
<w:style w:type="paragraph" w:style> <w:name w:val="heading 1"/> <w:basedOn w:val="Normal"/> <w:next w:val="Normal"/> <w:link w:val="Heading1Char"/> <w:uiPriority w:val="9"/> <w:qFormat/> <w:rsid w:val="002F7F18"/> <w:pPr> <w:keepNext/> <w:keepLines/> <w:spacing w:before="480" w:after="0"/> <w:outlineLvl w:val="0"/> </w:pPr> <w:rPr> <w:rFonts w:asciiTheme="majorHAnsi" w:eastAsiaTheme="majorEastAsia" w:hAnsiTheme="majorHAnsi" w:cstheme="majorBidi"/> <w:b/> <w:bCs/> <w:color w:val="365F91" w:themeColor="accent1" w:themeShade="BF"/> <w:sz w:val="28"/> <w:szCs w:val="28"/> </w:rPr> </w:style>
<w:style/w:rPr/w:b>
określa, że czcionka jest pogrubiona, a <w:style/w:rPr/w:color>
wskazuje kolor czcionki. <w:basedOn>
instruuje program MSWord, aby używał stylu „Normalny” dla wszelkich brakujących właściwości.
Dziedziczenie własności
Właściwości tekstu są dziedziczone. Przebieg ma swoje własne właściwości ( w:p/w:r/w:rPr/*
), ale dziedziczy również właściwości z akapitu ( w:r/w:pPr/*
) i oba mogą odwoływać się do właściwości stylu z /word/styles.xml
.

<w:r> <w:rPr> <w:rStyle w:val="DefaultParagraphFont"/> <w:sz w:val="16"/> </w:rPr> <w:tab/> </w:r>
Akapity i przebiegi zaczynają się od domyślnych właściwości: w:styles/w:docDefaults/w:rPrDefault/*
i w:styles/w:docDefaults/w:pPrDefault/*
. Aby uzyskać końcowy wynik właściwości postaci, należy:
- Użyj domyślnych właściwości run/paragraph
- Dołącz właściwości stylu przebiegu/akapitu
- Dołącz lokalne właściwości przebiegu/paragrafu
- Dołącz właściwości przebiegu wyników do właściwości akapitu
Kiedy mówię „dołącz” B do A, mam na myśli iterację wszystkich właściwości B i zastąpienie wszystkich właściwości A, pozostawiając wszystkie nieprzecinające się właściwości bez zmian.
Jeszcze jedno miejsce, w którym mogą znajdować się właściwości domyślne, to tag <w:style>
z w:type="paragraph"
i w:default="1"
. Zwróć uwagę, że same znaki wewnątrz przebiegu nigdy nie mają stylu domyślnego, więc <w:style w:type="character" w:default="1">
tak naprawdę nie wpływa na żaden tekst.
1554402290400-dbb29eef3ba6035df7ad726dfc99b2af.png)
Przełącz właściwości
Niektóre właściwości to właściwości „przełączane”, takie jak <w:b>
(pogrubienie) lub <w:i>
(kursywa); te atrybuty zachowują się jak operator XOR.
Oznacza to, że jeśli styl nadrzędny jest pogrubiony, a przebieg potomny jest pogrubiony, wynikiem będzie zwykły, nie pogrubiony tekst.
Musisz wykonać wiele testów i inżynierii wstecznej, aby poprawnie obsługiwać atrybuty przełączania. Spójrz na paragraf 17.7.3 specyfikacji ECMA-376 Open XML, aby uzyskać formalne, szczegółowe zasady dotyczące właściwości przełączania/
Czcionki
Czcionki podlegają tym samym zasadom, co inne atrybuty tekstu, ale wartości domyślne właściwości czcionki są określone w osobnym pliku motywu, do którego odwołuje się word/_rels/document.xml.rels
w następujący sposób:
<Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml"/>
Na podstawie powyższego odniesienia domyślna nazwa czcionki zostanie znaleziona w word/theme/themes1.xml
, wewnątrz <a:theme>
, a:themeElements/a:fontScheme/a:majorFont
lub a:minorFont
.
Domyślny rozmiar czcionki to 10, chyba że brakuje tagu w:docDefaults/w:rPrDefault
, wtedy jest to rozmiar 11.
Wyrównanie tekstu
Wyrównanie tekstu jest określone przez tag <w:jc>
z dostępnymi czterema trybami w:val
: "left"
, "center"
, "right"
i "both"
.
"left"
to tryb domyślny; tekst zaczyna się po lewej stronie prostokąta akapitu (zwykle szerokość strony). (Ten akapit jest wyrównany do lewej, co jest standardem).
Tryb "center"
, przewidywalnie, wyśrodkowuje wszystkie znaki na szerokości strony. (Ponownie, ten paragraf ilustruje wyrównanie do środka.)
W trybie "right"
tekst akapitu jest wyrównany do prawego marginesu. (Zwróć uwagę, jak ten tekst jest wyrównany do prawej strony.)
Tryb "both"
dodaje dodatkowe odstępy między słowami, dzięki czemu wiersze stają się szersze i zajmują całą szerokość akapitu, z wyjątkiem ostatniej linii, która jest wyrównana do lewej. (Ten akapit jest tego dowodem.)
Zdjęcia
DOCX obsługuje dwa rodzaje obrazów: wbudowane i pływające.
Obrazy w tekście pojawiają się w akapicie wraz z innymi znakami, używany jest <w:drawing>
zamiast <w:t>
(tekst). Możesz znaleźć identyfikator obrazu z następującą składnią xpath:
w:drawing/wp:inline/a:graphic/a:graphicData/pic:pic/pic:blipFill/a:blip/@r:embed
Identyfikator obrazu służy do wyszukiwania nazwy pliku w pliku word/_rels/document.xml.rels
i powinien wskazywać na plik gif/jpeg w podfolderze word/media. (Zobacz plik word/_rels/document.xml.rels
projektu github, gdzie możesz zobaczyć identyfikator obrazu.)
Pływające obrazy są umieszczane względem akapitów z tekstem opływającym je. (Oto przykładowy dokument projektu github z pływającym obrazem.)
Pływające obrazy używają <wp:anchor>
zamiast <w:drawing>
, więc jeśli usuniesz jakikolwiek tekst wewnątrz <w:p>
, zachowaj ostrożność z kotwicami, jeśli nie chcesz, aby obrazy zostały usunięte.
Stoły
Tagi XML dla tabel są podobne do znaczników tabeli HTML – pasuje do <tr> itp.
<w:tbl>
, sama tabela, ma właściwości tabeli <w:tblPr>
, a każda właściwość kolumny jest reprezentowana przez <w:gridCol>
wewnątrz <w:tblGrid>
. Wiersze następują jeden po drugim jako <w:tr>
, a każdy wiersz powinien mieć taką samą liczbę kolumn, jak określono w <w:tblGrid>
:
<w:tbl> <w:tblPr> <w:tblW w:w="5000" w:type="pct" /> </w:tblPr> <w:tblGrid><w:gridCol/><w:gridCol/></w:tblGrid> <w:tr> <w:tc><w:p><w:r><w:t>left</w:t></w:r></w:p></w:tc> <w:tc><w:p><w:r><w:t>right</w:t></w:r></w:p></w:tc> </w:tr> </w:tbl>
Szerokość kolumn tabeli można określić w <w:tblW>
, ale jeśli go nie zdefiniujesz, MS Word użyje swoich wewnętrznych algorytmów, aby znaleźć optymalną szerokość kolumn dla najmniejszego efektywnego rozmiaru tabeli.
Jednostki
Wiele atrybutów XML w DOCX określa rozmiary lub odległości. Chociaż są to liczby całkowite w XML, wszystkie mają różne jednostki, więc konieczna jest pewna konwersja. Temat jest skomplikowany, więc polecam ten artykuł Larsa Corneliussena o jednostkach w plikach DOCX. Tabela, którą przedstawia, jest przydatna, choć z małym błędem drukarskim: cale powinny być pt/72, a nie pt*72.
Oto ściągawka:
WSPÓLNE KONWERSJE JEDNOSTEK DOCX XML | ||||||
20 punkt | Zwrotnica dxa/20 | Cale pkt/72 | Cm w*2,54 | Połowa rozmiaru czcionki pkt/144 | EMU w*914400 | |
Przykład | 11906 | 595,3 | 8,27… | 21.00086… | 4135 | 7562088 |
Tagi używające tego | pgSz/pgMar/w:odstępy | w:sz | wp:zakres, a:wewnętrzny |
Wskazówki dotyczące implementacji Layouter
Jeśli chcesz przekonwertować plik DOCX (na przykład do PDF), narysować go na płótnie lub policzyć liczbę stron, będziesz musiał zaimplementować layouter. Layouter to algorytm obliczania pozycji znaków z pliku DOCX.
Jest to złożone zadanie, jeśli potrzebujesz renderowania w 100 procentach wierności. Czas potrzebny na wdrożenie dobrego layoutera mierzy się osobolatami, ale jeśli potrzebujesz tylko prostego, ograniczonego, można to zrobić stosunkowo szybko.
Layout wypełnia prostokąt nadrzędny, który zwykle jest prostokątem strony. Dodaje słowa z biegu jeden po drugim. Kiedy bieżąca linia się przepełni, rozpoczyna nową. Jeśli akapit jest zbyt wysoki dla prostokąta nadrzędnego, jest zawijany do następnej strony.
Oto kilka ważnych rzeczy, o których należy pamiętać, jeśli zdecydujesz się zaimplementować layouter:
- Layouter powinien zadbać o wyrównanie tekstu i tekst unoszący się nad obrazami
- Powinien być w stanie obsługiwać zagnieżdżone obiekty, takie jak zagnieżdżone tabele
- Jeśli chcesz zapewnić pełną obsługę takich obrazów, będziesz musiał zaimplementować layouter z co najmniej dwoma przebiegami, pierwszy krok zbiera pozycje pływających obrazów, a drugi wypełnia pustą przestrzeń znakami tekstowymi.
- Uważaj na wcięcia i odstępy. Każdy akapit ma odstępy przed i po, a te liczby są określone przez znacznik
w:spacing
. Odstępy w pionie są określane przez znacznikiw:after
iw:before
. Zauważ, że odstępy między wierszami są określone przezw:line
, ale nie jest to rozmiar linii, jak można by się spodziewać. Aby uzyskać rozmiar linii, weź aktualną wysokość czcionki, pomnóż przezw:line
i podziel przez 12. - Pliki DOCX nie zawierają informacji o paginacji. Nie znajdziesz liczby stron w dokumencie, chyba że obliczysz, ile miejsca potrzebujesz na każdy wiersz, aby ustalić liczbę stron. Jeśli chcesz znaleźć dokładne współrzędne każdego znaku na stronie, pamiętaj o uwzględnieniu wszystkich odstępów, wcięć i rozmiarów.
- Jeśli zaimplementujesz w pełni funkcjonalny układ DOCX, który obsługuje tabele, zwróć uwagę na szczególne przypadki, gdy tabele obejmują wiele stron. Komórka, która powoduje przepełnienie strony, wpływa również na inne komórki.
- Stworzenie optymalnego algorytmu obliczania szerokości kolumn tabeli jest trudnym zadaniem matematycznym, a edytory tekstu i układy zazwyczaj używają niektórych nieoptymalnych implementacji. Jako pierwsze przybliżenie proponuję wykorzystać algorytm z dokumentacji tabeli HTML W3C. Nie znalazłem opisu algorytmu używanego przez MS Word, a Microsoft z czasem dopracował algorytm, więc różne wersje Worda mogą nieco inaczej układać tabele.
Jeśli coś jest niejasne: wykonaj inżynierię wsteczną XML!
Jeśli nie jest oczywiste, jak ten lub inny tag XML działa w MS Word, istnieją dwa główne podejścia do jego rozwiązania:
Stwórz żądaną treść krok po kroku. Zacznij od prostego pliku docx. Zapisz każdy krok w osobnym pliku, na przykład w
1.docx
,2.docx
. Rozpakuj każdy z nich i użyj narzędzia wizualnego porównywania do porównania folderów, aby zobaczyć, które tagi pojawią się po Twoich zmianach. (W przypadku opcji komercyjnej wypróbuj Araxis Merge lub bezpłatną opcję WinMerge.)Jeśli wygenerujesz plik DOCX, którego MS Word nie lubi, pracuj wstecz. Uprość swój XML krok po kroku. W pewnym momencie dowiesz się, którą zmianę MS Word uznał za niepoprawną.
DOCX jest dość złożony, prawda?
Jest to skomplikowane, a licencja Microsoftu zabrania używania MS Word po stronie serwera do przetwarzania DOCX – jest to dość standardowe dla produktów komercyjnych. Microsoft dostarczył jednak plik XSLT do obsługi większości tagów DOCX, ale nie zapewni on 100-procentowej, a nawet 99-procentowej wierności. Procesy takie jak zawijanie tekstu na obrazach nie są obsługiwane, ale będziesz w stanie obsługiwać większość dokumentów. (Jeśli nie potrzebujesz złożoności, rozważ użycie Markdown jako alternatywy).
Jeśli masz wystarczający budżet (nie ma darmowego silnika renderującego DOCX), możesz użyć produktów komercyjnych, takich jak Aspose lub docx4j. Najpopularniejszym darmowym rozwiązaniem jest LibreOffice do konwersji między DOCX a innymi formatami, w tym PDF. Niestety LibreOffice zawiera wiele drobnych błędów podczas konwersji, a ponieważ jest to wyrafinowany produkt C++ o otwartym kodzie źródłowym, naprawianie problemów z wiernością jest powolne i trudne.
Alternatywnie, jeśli uważasz, że układ DOCX jest zbyt skomplikowany do samodzielnego wdrożenia, możesz również przekonwertować go na HTML i użyć przeglądarki do renderowania. Możesz również wziąć pod uwagę jednego z niezależnych programistów XML firmy Toptal.
Zasoby DOCX do dalszego czytania
- Specyfikacja ECMA DOCX
- Biblioteka OpenXML do manipulacji DOCX z C#. Nie zawiera informacji o układzie lub kodzie renderującym, ale oferuje hierarchię klas pasującą do każdego możliwego węzła XML w DOCX.
- Zawsze możesz wyszukiwać lub pytać o przepełnienie stosu za pomocą słów kluczowych, takich jak docx4j, OpenXML i docx; w społeczności są ludzie, którzy mają wiedzę.