Pengantar Informal untuk DOCX
Diterbitkan: 2022-03-11Dengan sekitar satu miliar orang menggunakan Microsoft Office, format DOCX adalah standar de facto paling populer untuk bertukar file dokumen antar kantor. Pesaing terdekatnya - format ODT - hanya didukung oleh Open/LibreOffice dan beberapa produk open source, sehingga jauh dari standar. Format PDF bukanlah pesaing karena PDF tidak dapat diedit dan tidak berisi struktur dokumen lengkap, sehingga hanya dapat mengambil perubahan lokal terbatas seperti tanda air, tanda tangan, dan sejenisnya. Inilah sebabnya mengapa sebagian besar dokumen bisnis dibuat dalam format DOCX; tidak ada alternatif yang baik untuk menggantikannya.
Meskipun DOCX adalah format yang kompleks, Anda mungkin ingin menguraikannya secara manual untuk tugas-tugas yang lebih sederhana seperti pengindeksan, mengonversi ke TXT, dan membuat modifikasi kecil lainnya. Saya ingin memberi Anda informasi yang cukup tentang internal DOCX sehingga Anda tidak perlu merujuk spesifikasi ECMA, manual 5.000 halaman yang sangat besar.
Cara terbaik untuk memahami formatnya adalah dengan membuat dokumen satu kata sederhana dengan MSWord dan mengamati bagaimana mengedit dokumen mengubah XML yang mendasarinya. Anda akan menghadapi beberapa kasus di mana DOCX tidak memformat dengan benar di MS Word dan Anda tidak tahu mengapa, atau menemukan contoh ketika tidak jelas cara menghasilkan pemformatan yang diinginkan. Melihat dan memahami dengan tepat apa yang terjadi di XML akan membantu itu.
Saya bekerja selama sekitar satu tahun di editor DOCX kolaboratif, CollabOffice, dan saya ingin berbagi sebagian dari pengetahuan itu dengan komunitas pengembang. Pada artikel ini saya akan menjelaskan struktur file DOCX, merangkum informasi yang tersebar di internet. Artikel ini adalah perantara antara spesifikasi ECMA yang besar dan kompleks dan tutorial internet sederhana yang tersedia saat ini. Anda dapat menemukan file yang menyertai artikel ini di proyek toptal-docx
di akun github saya.
File DOCX Sederhana
File DOCX adalah arsip ZIP dari file XML. Jika Anda membuat dokumen Microsoft Word baru yang kosong, tulis satu kata 'Test' di dalamnya dan unzip isinya, Anda akan melihat struktur file berikut:
Meskipun kami telah membuat dokumen sederhana, proses penyimpanan di Microsoft Word telah menghasilkan tema default, properti dokumen, tabel font, dan sebagainya, dalam format XML.
Untuk memulai, mari kita hapus hal-hal yang tidak digunakan dan fokus pada document.xml
, yang berisi elemen teks utama. Saat Anda menghapus file, pastikan Anda telah menghapus semua referensi hubungan ke file tersebut dari file xml lainnya. Berikut adalah contoh perbedaan kode tentang cara saya menghapus dependensi ke app.xml dan core.xml. Jika Anda memiliki referensi yang belum terselesaikan/hilang, MSWord akan menganggap file tersebut rusak.
Inilah struktur dokumen DOCX minimal kami yang disederhanakan (dan inilah proyeknya di github):
Mari kita uraikan berdasarkan file dari sini, dari atas:
_rels/.rels
Ini mendefinisikan referensi yang memberi tahu MS Word di mana mencari konten dokumen. Dalam hal ini, referensi 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
File ini mendefinisikan referensi ke sumber daya, seperti gambar, yang disematkan dalam konten dokumen. Dokumen sederhana kami tidak memiliki sumber daya yang disematkan, jadi tag hubungan kosong:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"> </Relationships>
[Content_Types].xml
[Content_Types].xml
berisi informasi tentang jenis media di dalam dokumen. Karena kami hanya memiliki konten teks, ini cukup sederhana:
<?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>
dokumen.xml
Akhirnya, inilah XML utama dengan konten teks dokumen. Saya telah menghapus beberapa deklarasi namespace untuk kejelasan, tetapi Anda dapat menemukan versi lengkap file tersebut di proyek github. Dalam file itu Anda akan menemukan bahwa beberapa referensi namespace dalam dokumen tidak digunakan, tetapi Anda tidak boleh menghapusnya karena MS Word membutuhkannya.
Berikut contoh sederhana kami:
<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>
Node utama <w:document>
mewakili dokumen itu sendiri, <w:body>
berisi paragraf, dan bersarang di dalam <w:body>
adalah dimensi halaman yang ditentukan oleh <w:sectPr>
.
<w:rsidR>
adalah atribut yang dapat Anda abaikan; itu digunakan oleh internal MS Word.
Mari kita lihat dokumen yang lebih kompleks dengan tiga paragraf. Saya telah menyoroti XML dengan warna yang sama pada tangkapan layar dari Microsoft Word, sehingga Anda dapat melihat korelasinya:
<w:pw:rsidR="0081206C" w:rsidRDefault="00E10CAE"> <w:r> <w:t xml:space="preserve">Ini adalah contoh paragraf pertama kami. Standarnya adalah rata kiri, dan sekarang saya ingin memperkenalkan</w:t> </w:r> <w:r> <w:rPr> <w:rFonts w:ascii="Arial" w:hAnsi="Arial" w:cs="Arial"/> <w:warna w:val="000000"/> </w:rPr> <w:t>sedikit tebal</w:t> </w:r> <w:r> <w:rPr> <w:rFonts w:ascii="Arial" w:hAnsi="Arial" w:cs="Arial"/> <w:b/> <w:warna w:val="000000"/> </w:rPr> <w:t xml:space="preserve"> teks</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="melestarikan">, </w:t> </w:r> <w:proofErr w:type="gramStart"/> <w:r> <w:t xml:space="preserve">dan juga ubah</w:t> </w:r> <w:rw:rsidRPr="00E10CAE"> <w:rPr><w:rFonts w:ascii="Impact" w:hAnsi="Impact"/> </w:rPr> <w:t>gaya font</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>menjadi 'Dampak'.</w:t></w:r> </w:p> <w:pw:rsidR="00E10CAE" w:rsidRDefault="00E10CAE"> <w:r> <w:t>Ini adalah paragraf baru.</w:t> </w:r></w:p > <w:pw:rsidR="00E10CAE" w:rsidRPr="00E10CAE" w:rsidRDefault="00E10CAE"> <w:r> <w:t>Ini satu paragraf lagi, sedikit lebih panjang.</w:t> </w:r> </w:p>
Struktur Paragraf
Dokumen sederhana terdiri dari paragraf, paragraf terdiri dari run (serangkaian teks dengan font, warna, dll) yang sama, dan run terdiri dari karakter (seperti <w:t>
). Tag <w:t>
mungkin memiliki beberapa karakter di dalamnya, dan mungkin ada beberapa karakter dalam proses yang sama.
Sekali lagi, kita dapat mengabaikan <w:rsidR>
.
Properti teks
Properti teks dasar adalah font, ukuran, warna, gaya, dan sebagainya. Ada sekitar 40 tag yang menentukan tampilan teks. Seperti yang dapat Anda lihat dalam contoh tiga paragraf kami, setiap proses memiliki propertinya sendiri di dalam <w:rPr>
, menetapkan <w:color>
, <w:rFonts>
dan boldness <w:b>
.
Hal penting yang perlu diperhatikan adalah bahwa properti membuat perbedaan antara dua kelompok karakter, skrip normal dan kompleks (Arab, misalnya), dan properti memiliki tag yang berbeda tergantung pada jenis karakter yang terpengaruh.
Sebagian besar tag properti skrip normal memiliki tag skrip kompleks yang cocok dengan "C" tambahan yang menentukan properti untuk skrip kompleks. Misalnya: <w:i>
(miring) menjadi <w:iCs>
, dan tag tebal untuk skrip normal, <w:b>
, menjadi <w:bCs>
untuk skrip kompleks.
Gaya
Ada seluruh toolbar di Microsoft Word yang didedikasikan untuk gaya: normal, tanpa spasi, heading 1, heading 2, title, dan seterusnya. Gaya ini disimpan di /word/styles.xml
(catatan: pada langkah pertama dalam contoh sederhana kami, kami menghapus XML ini dari DOCX. Buat DOCX baru untuk melihatnya).
Setelah teks didefinisikan sebagai gaya, Anda akan menemukan referensi ke gaya ini di dalam tag properti paragraf, <w:pPr>
. Berikut adalah contoh di mana saya telah mendefinisikan teks saya dengan gaya Judul 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>
dan inilah gaya itu sendiri dari 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>
xpath menentukan bahwa font dicetak tebal, dan <w:style/w:rPr/w:color>
menunjukkan warna font. <w:basedOn>
menginstruksikan MSWord untuk menggunakan gaya "Normal" untuk properti yang hilang.
Warisan Properti
Properti teks diwariskan. Run memiliki propertinya sendiri ( w:p/w:r/w:rPr/*
), tetapi juga mewarisi properti dari paragraf ( w:r/w:pPr/*
), dan keduanya dapat mereferensikan properti gaya dari /word/styles.xml
.

<w:r> <w:rPr> <w:rStyle w:val="DefaultParagraphFont"/> <w:sz w:val="16"/> </w:rPr> <w:tab/> </w:r>
Paragraf dan run dimulai dengan properti default: w:styles/w:docDefaults/w:rPrDefault/*
dan w:styles/w:docDefaults/w:pPrDefault/*
. Untuk mendapatkan hasil akhir dari properti karakter, Anda harus:
- Gunakan properti run/paragraph default
- Tambahkan properti gaya run/paragraph
- Tambahkan properti run/paragraf lokal
- Tambahkan properti hasil run di atas properti paragraf
Ketika saya mengatakan "tambahkan" B ke A, maksud saya untuk mengulangi semua properti B dan menimpa semua properti A, meninggalkan semua properti yang tidak berpotongan apa adanya.
Satu lagi tempat di mana properti default dapat ditemukan adalah di <w:style>
dengan w:type="paragraph"
dan w:default="1"
. Perhatikan, bahwa karakter itu sendiri di dalam proses tidak pernah memiliki gaya default, jadi <w:style w:type="character" w:default="1">
sebenarnya tidak memengaruhi teks apa pun.
1554402290400-dbb29eef3ba6035df7ad726dfc99b2af.png)
Beralih properti
Beberapa properti adalah properti “toggle”, seperti <w:b>
(bold) atau <w:i>
(italic); atribut ini berperilaku seperti operator XOR.
Ini berarti jika gaya induk dicetak tebal dan turunan dicetak tebal, hasilnya akan berupa teks biasa dan tidak dicetak tebal.
Anda harus melakukan banyak pengujian dan rekayasa balik untuk menangani atribut sakelar dengan benar. Lihatlah paragraf 17.7.3 dari spesifikasi Open XML ECMA-376 untuk mendapatkan aturan formal dan mendetail untuk beralih properti/
font
Font mengikuti aturan umum yang sama seperti atribut teks lainnya, tetapi nilai default properti font ditentukan dalam file tema terpisah, direferensikan di bawah word/_rels/document.xml.rels
seperti ini:
<Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml"/>
Berdasarkan referensi di atas, nama font default akan ditemukan di word/theme/themes1.xml
, di dalam <a:theme>
, a:themeElements/a:fontScheme/a:majorFont
atau a:minorFont
tag.
Ukuran font default adalah 10 kecuali tag w:docDefaults/w:rPrDefault
tidak ada, maka ukurannya 11.
Perataan teks
Perataan teks ditentukan oleh <w:jc>
dengan empat mode w:val
yang tersedia: "left"
, "center"
, "right"
dan "both"
.
"left"
adalah mode default; teks dimulai di sebelah kiri persegi panjang paragraf (biasanya lebar halaman). (Paragraf ini disejajarkan ke kiri, yang merupakan standar.)
mode "center"
, dapat diduga, memusatkan semua karakter di dalam lebar halaman. (Sekali lagi, paragraf ini mencontohkan perataan tengah.)
Dalam mode "right"
, teks paragraf disejajarkan dengan margin kanan. (Perhatikan bagaimana teks ini disejajarkan dengan sisi kanan.)
Mode "both"
menempatkan spasi ekstra di antara kata-kata sehingga garis menjadi lebih lebar dan menempati lebar paragraf penuh, dengan pengecualian baris terakhir yang dibiarkan rata. (Paragraf ini adalah demonstrasi dari itu.)
Gambar-gambar
DOCX mendukung dua jenis gambar: inline dan floating.
Gambar sebaris muncul di dalam paragraf bersama dengan karakter lainnya, <w:drawing>
digunakan daripada menggunakan <w:t>
(teks). Anda dapat menemukan ID gambar dengan sintaks xpath berikut:
w:drawing/wp:inline/a:graphic/a:graphicData/pic:pic/pic:blipFill/a:blip/@r:embed
ID gambar digunakan untuk mencari nama file di file word/_rels/document.xml.rels
, dan harus mengarah ke file gif/jpeg di dalam subfolder word/media. (Lihat file word/_rels/document.xml.rels
proyek github, tempat Anda dapat melihat ID gambar.)
Gambar mengambang ditempatkan relatif terhadap paragraf dengan teks mengalir di sekitarnya. (Inilah contoh dokumen proyek github dengan gambar mengambang.)
Gambar mengambang menggunakan <wp:anchor>
alih-alih <w:drawing>
, jadi jika Anda menghapus teks apa pun di dalam <w:p>
, berhati-hatilah dengan jangkar jika Anda tidak ingin gambarnya dihapus.
Tabel
Tag XML untuk tabel mirip dengan markup tabel HTML– cocok dengan <tr>, dll.
<w:tbl>
, tabel itu sendiri, memiliki properti tabel <w:tblPr>
, dan setiap properti kolom disajikan oleh <w:gridCol>
di dalam <w:tblGrid>
. Baris mengikuti satu per satu sebagai <w:tr>
dan setiap baris harus memiliki jumlah kolom yang sama seperti yang ditentukan dalam <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>
Lebar untuk kolom tabel dapat ditentukan dalam <w:tblW>
, tetapi jika Anda tidak mendefinisikannya, MS Word akan menggunakan algoritme internalnya untuk menemukan lebar kolom yang optimal untuk ukuran tabel efektif terkecil.
Satuan
Banyak atribut XML di dalam DOCX menentukan ukuran atau jarak. Meskipun mereka bilangan bulat di dalam XML, mereka semua memiliki unit yang berbeda sehingga beberapa konversi diperlukan. Topiknya rumit, jadi saya akan merekomendasikan artikel ini oleh Lars Corneliussen tentang unit dalam file DOCX. Tabel yang disajikannya berguna, meskipun dengan kesalahan cetak kecil: inci harus pt/72, bukan pt*72.
Berikut adalah lembar contekan:
KONVERSI UNIT XML DOCX UMUM | ||||||
20 poin | Poin dxa/20 | Inci poin/72 | Sentimeter di *2,54 | Font setengah ukuran pt/144 | EMU di *914400 | |
Contoh | 11906 | 595.3 | 8,27… | 21.00086… | 4.135 | 7562088 |
Tag menggunakan ini | pgSz/pgMar/w:spasi | w:sz | wp:ekstensi, a:ekst |
Tip untuk Menerapkan Layouter
Jika Anda ingin mengonversi file DOCX (ke PDF, misalnya), menggambarnya di kanvas, atau menghitung jumlah halaman, Anda harus menerapkan layouter. Layouter adalah algoritma untuk menghitung posisi karakter dari file DOCX.
Ini adalah tugas yang rumit jika Anda membutuhkan rendering kesetiaan 100 persen. Jumlah waktu yang dibutuhkan untuk menerapkan layouter yang baik diukur dalam man-years, tetapi jika Anda hanya membutuhkan yang sederhana dan terbatas, itu dapat dilakukan dengan relatif cepat.
Seorang layouter mengisi persegi panjang induk, yang biasanya persegi panjang halaman. Itu menambahkan kata-kata dari lari satu per satu. Ketika baris saat ini meluap, itu memulai yang baru. Jika paragraf terlalu tinggi untuk persegi panjang induk, paragraf tersebut akan dibungkus ke halaman berikutnya.
Berikut adalah beberapa hal penting yang perlu diingat jika Anda memutuskan untuk menerapkan layouter:
- Penata letak harus berhati-hati tentang perataan teks dan teks yang mengambang di atas gambar
- Itu harus mampu menangani objek bersarang, seperti tabel bersarang
- Jika Anda ingin memberikan dukungan penuh untuk gambar seperti itu, Anda harus menerapkan layouter dengan setidaknya dua lintasan, langkah pertama mengumpulkan posisi gambar mengambang dan yang kedua mengisi ruang kosong dengan karakter teks.
- Waspadai lekukan dan spasi. Setiap paragraf memiliki spasi sebelum dan sesudah, dan angka-angka ini ditentukan oleh tag
w:spacing
. Spasi vertikal ditentukan oleh tagw:after
danw:before
. Perhatikan bahwa spasi baris ditentukan olehw:line
, tetapi ini bukan ukuran garis seperti yang diharapkan. Untuk mendapatkan ukuran garis, ambil tinggi font saat ini, kalikan denganw:line
dan bagi dengan 12. - File DOCX tidak berisi informasi tentang pagination. Anda tidak akan menemukan jumlah halaman dalam dokumen kecuali Anda menghitung berapa banyak ruang yang Anda butuhkan untuk setiap baris untuk memastikan jumlah halaman. Jika Anda perlu menemukan koordinat yang tepat dari setiap karakter pada halaman, pastikan untuk memperhitungkan semua jarak, lekukan, dan ukuran.
- Jika Anda menerapkan layouter DOCX berfitur lengkap yang menangani tabel, perhatikan kasus khusus saat tabel menjangkau beberapa halaman. Sel yang menyebabkan halaman meluap juga memengaruhi sel lain.
- Membuat algoritme optimal untuk menghitung lebar kolom tabel adalah masalah matematika yang menantang dan pengolah kata dan layouter biasanya menggunakan beberapa implementasi suboptimal. Saya mengusulkan menggunakan algoritma dari dokumentasi tabel HTML W3C sebagai pendekatan pertama. Saya belum menemukan deskripsi algoritme yang digunakan oleh MS Word, dan Microsoft telah menyempurnakan algoritme tersebut dari waktu ke waktu sehingga versi Word yang berbeda dapat membuat tabel sedikit berbeda.
Jika ada yang tidak jelas: rekayasa balik XML!
Ketika tidak jelas bagaimana tag XML ini atau itu bekerja di dalam MS Word, ada dua pendekatan utama untuk mengetahuinya:
Buat konten yang diinginkan selangkah demi selangkah. Mulailah dengan file docx sederhana. Simpan setiap langkah ke filenya sendiri, seperti pada
1.docx
,2.docx
, misalnya. Buka zip masing-masing dan gunakan alat diff visual untuk perbandingan folder untuk melihat tag mana yang muncul setelah perubahan Anda. (Untuk opsi komersial, coba Araxis Merge, atau untuk opsi gratis, WinMerge.)Jika Anda membuat file DOCX yang tidak disukai MS Word, kerjakan mundur. Sederhanakan XML Anda selangkah demi selangkah. Pada titik tertentu Anda akan mempelajari perubahan mana yang ditemukan salah di MS Word.
DOCX cukup kompleks, bukan?
Ini rumit, dan lisensi Microsoft melarang penggunaan MS Word di sisi server untuk memproses DOCX– ini cukup standar untuk produk komersial. Microsoft telah, bagaimanapun, menyediakan file XSLT untuk menangani sebagian besar tag DOCX, tetapi itu tidak akan memberi Anda 100 persen atau bahkan 99 persen kesetiaan. Proses seperti pembungkusan teks di atas gambar tidak didukung, tetapi Anda akan dapat mendukung sebagian besar dokumen. (Jika Anda tidak membutuhkan kerumitan, pertimbangkan untuk menggunakan Penurunan Harga sebagai alternatif.)
Jika Anda memiliki anggaran yang cukup (tidak ada mesin rendering DOCX gratis), Anda mungkin ingin menggunakan produk komersial seperti Aspose atau docx4j. Solusi gratis paling populer adalah LibreOffice untuk mengonversi antara DOCX dan format lain, termasuk PDF. Sayangnya, LibreOffice mengandung banyak bug kecil selama konversi, dan karena ini adalah produk C++ open-source yang canggih, ini lambat dan sulit untuk memperbaiki masalah fidelitas.
Atau, jika Anda menemukan tata letak DOCX terlalu rumit untuk diterapkan sendiri, Anda juga dapat mengonversinya menjadi HTML dan menggunakan browser untuk merendernya. Anda juga dapat mempertimbangkan salah satu pengembang XML lepas Toptal.
Sumber DOCX untuk bacaan lebih lanjut
- Spesifikasi ECMA DOCX
- Pustaka OpenXML untuk manipulasi DOCX dari C#. Itu tidak berisi informasi tentang tata letak atau kode rendering, tetapi menawarkan hierarki kelas yang cocok dengan setiap kemungkinan simpul XML di DOCX.
- Anda selalu dapat mencari atau bertanya di stackoverflow dengan kata kunci seperti docx4j, OpenXML dan docx; ada orang-orang di masyarakat yang berpengetahuan.