Uma introdução informal ao DOCX
Publicados: 2022-03-11Com aproximadamente um bilhão de pessoas usando o Microsoft Office, o formato DOCX é o padrão de fato mais popular para a troca de arquivos de documentos entre escritórios. Seu concorrente mais próximo - o formato ODT - é suportado apenas pelo Open/LibreOffice e alguns produtos de código aberto, tornando-o longe do padrão. O formato PDF não é um concorrente porque os PDFs não podem ser editados e não contêm uma estrutura de documento completa, portanto, eles só podem fazer alterações locais limitadas, como marcas d'água, assinaturas e similares. É por isso que a maioria dos documentos comerciais são criados no formato DOCX; não há nenhuma boa alternativa para substituí-lo.
Embora o DOCX seja um formato complexo, convém analisá-lo manualmente para tarefas mais simples, como indexação, conversão para TXT e outras pequenas modificações. Gostaria de fornecer informações suficientes sobre os componentes internos do DOCX para que você não precise consultar as especificações do ECMA, um enorme manual de 5.000 páginas.
A melhor maneira de entender o formato é criar um documento simples de uma palavra com o MSWord e observar como a edição do documento altera o XML subjacente. Você enfrentará alguns casos em que o DOCX não formata corretamente no MS Word e você não sabe o porquê, ou se deparará com casos em que não é evidente como gerar a formatação desejada. Ver e entender exatamente o que está acontecendo no XML ajudará nisso.
Trabalhei por cerca de um ano em um editor DOCX colaborativo, CollabOffice, e quero compartilhar um pouco desse conhecimento com a comunidade de desenvolvedores. Neste artigo vou explicar a estrutura do arquivo DOCX, resumindo as informações que estão espalhadas pela internet. Este artigo é um intermediário entre a enorme e complexa especificação ECMA e os simples tutoriais da Internet atualmente disponíveis. Você pode encontrar os arquivos que acompanham este artigo no projeto toptal-docx
na minha conta do github.
Um arquivo DOCX simples
Um arquivo DOCX é um arquivo ZIP de arquivos XML. Se você criar um novo documento vazio do Microsoft Word, escreva uma única palavra 'Test' dentro e descompacte o conteúdo, você verá a seguinte estrutura de arquivo:
Embora tenhamos criado um documento simples, o processo de salvamento no Microsoft Word gerou temas padrão, propriedades do documento, tabelas de fontes e assim por diante, no formato XML.
Para começar, vamos remover o material não utilizado e focar document.xml
, que contém os principais elementos de texto. Ao excluir um arquivo, certifique-se de ter excluído todas as referências de relacionamento a ele de outros arquivos xml. Aqui está um exemplo de diferença de código sobre como limpei as dependências para app.xml e core.xml. Se você tiver referências não resolvidas/ausentes, o MSWord considerará o arquivo quebrado.
Aqui está a estrutura do nosso documento DOCX simplificado e mínimo (e aqui está o projeto no github):
Vamos dividi-lo por arquivo a partir daqui, do topo:
_rels/.rels
Isso define a referência que informa ao MS Word onde procurar o conteúdo do documento. Nesse caso, ele faz referência a 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
Este arquivo define referências a recursos, como imagens, incorporadas ao conteúdo do documento. Nosso documento simples não possui recursos incorporados, portanto, a tag de relacionamento está vazia:
<?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
contém informações sobre os tipos de mídia dentro do documento. Como temos apenas conteúdo de texto, é bem simples:
<?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>
document.xml
Por fim, aqui está o XML principal com o conteúdo de texto do documento. Eu removi algumas declarações de namespace para maior clareza, mas você pode encontrar a versão completa do arquivo no projeto github. Nesse arquivo, você verá que algumas das referências de namespace no documento não são usadas, mas você não deve excluí-las porque o MS Word precisa delas.
Aqui está nosso exemplo simplificado:
<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>
O nó principal <w:document>
representa o próprio documento, <w:body>
contém parágrafos e aninhados em <w:body>
estão as dimensões da página definidas por <w:sectPr>
.
<w:rsidR>
é um atributo que você pode ignorar; é usado pelos internos do MS Word.
Vamos dar uma olhada em um documento mais complexo com três parágrafos. Eu destaquei o XML com as mesmas cores na captura de tela do Microsoft Word, para que você possa ver a correlação:
<w:pw:rsidR="0081206C" w:rsidRDefault="00E10CAE"> <w:r> <w:t xml:space="preserve">Este é nosso primeiro parágrafo de exemplo. O padrão é alinhado à esquerda, e agora eu gostaria de apresentar</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>algum negrito</w:t> </w:r> <w:r> <w:rPr> <w:rFonts w:ascii="Arial" w:hAnsi="Arial" w:cs="Arial"/> <w:b/> <w:color w:val="000000"/> </w:rPr> <w:t xml:space="preserve"> texto</w:t> </w:r> <w:r> <w:rPr> <w:rFontes 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">e também altere o</w:t> </w:r> <w:rw:rsidRPr="00E10CAE"> <w:rPr><w:rFonts w:ascii="Impact" w:hAnsi="Impact"/> </w:rPr> <w:t>estilo de fonte</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>para 'Impacto'.</w:t></w:r> </w:p> <w:pw:rsidR="00E10CAE" w:rsidRDefault="00E10CAE"> <w:r> <w:t>Este é um novo parágrafo.</w:t> </w:r></w:p > <w:pw:rsidR="00E10CAE" w:rsidRPr="00E10CAE" w:rsidRDefault="00E10CAE"> <w:r> <w:t>Este é mais um parágrafo, um pouco mais longo.</w:t> </w:r> </w:p>
Estrutura do parágrafo
Um documento simples consiste em parágrafos, um parágrafo consiste em execuções (uma série de texto com a mesma fonte, cor etc.) e execuções consistem em caracteres (como <w:t>
). As tags <w:t>
podem ter vários caracteres dentro, e pode haver alguns na mesma execução.
Novamente, podemos ignorar <w:rsidR>
.
Propriedades do texto
As propriedades básicas do texto são fonte, tamanho, cor, estilo e assim por diante. Existem cerca de 40 tags que especificam a aparência do texto. Como você pode ver em nosso exemplo de três parágrafos, cada execução tem suas próprias propriedades dentro de <w:rPr>
, especificando <w:color>
, <w:rFonts>
e boldness <w:b>
.
Uma coisa importante a notar é que as propriedades fazem uma distinção entre os dois grupos de caracteres, script normal e complexo (árabe, por exemplo), e que as propriedades têm uma tag diferente dependendo do tipo de caractere que está afetando.
A maioria das tags de propriedade de script normal tem uma tag de script complexa correspondente com um “C” adicionado especificando que a propriedade é para scripts complexos. Por exemplo: <w:i>
(itálico) torna-se <w:iCs>
e a tag em negrito para script normal, <w:b>
, torna-se <w:bCs>
para script complexo.
Estilos
Há uma barra de ferramentas inteira no Microsoft Word dedicada a estilos: normal, sem espaçamento, título 1, título 2, título e assim por diante. Esses estilos são armazenados em /word/styles.xml
(observação: na primeira etapa do nosso exemplo simples, removemos esse XML do DOCX. Faça um novo DOCX para ver isso).
Depois de definir o texto como um estilo, você encontrará referência a esse estilo dentro da tag de propriedades do parágrafo, <w:pPr>
. Aqui está um exemplo em que defini meu texto com o estilo Título 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>
e aqui está o próprio estilo de 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>
O xpath <w:style/w:rPr/w:b>
especifica que a fonte está em negrito e <w:style/w:rPr/w:color>
indica a cor da fonte. <w:basedOn>
instrui o MSWord a usar o estilo “Normal” para quaisquer propriedades ausentes.
Herança de propriedade
As propriedades de texto são herdadas. Uma execução tem suas próprias propriedades ( w:p/w:r/w:rPr/*
), mas também herda propriedades do parágrafo ( w:r/w:pPr/*
), e ambas podem referenciar propriedades de estilo do /word/styles.xml
.
<w:r> <w:rPr> <w:rStyle w:val="DefaultParagraphFont"/> <w:sz w:val="16"/> </w:rPr> <w:tab/> </w:r>
Parágrafos e execuções começam com propriedades padrão: w:styles/w:docDefaults/w:rPrDefault/*
e w:styles/w:docDefaults/w:pPrDefault/*
. Para obter o resultado final das propriedades de um personagem, você deve:

- Usar propriedades de execução/parágrafo padrão
- Anexar propriedades de estilo de execução/parágrafo
- Anexar propriedades de execução/parágrafo locais
- Anexar propriedades de execução de resultado sobre propriedades de parágrafo
Quando digo “anexar” B a A, quero dizer iterar por todas as propriedades de B e substituir todas as propriedades de A, deixando todas as propriedades sem interseção como estão.
Mais um lugar onde as propriedades padrão podem estar localizadas na tag <w:style>
com w:type="paragraph"
e w:default="1"
. Observe que os próprios caracteres dentro de uma execução nunca têm um estilo padrão, portanto, <w:style w:type="character" w:default="1">
na verdade não afeta nenhum texto.
1554402290400-dbb29eef3ba6035df7ad726dfc99b2af.png)
Alternar propriedades
Algumas das propriedades são propriedades “toggle”, como <w:b>
(negrito) ou <w:i>
(itálico); esses atributos se comportam como um operador XOR.
Isso significa que se o estilo pai estiver em negrito e uma execução filho estiver em negrito, o resultado será um texto normal, sem negrito.
Você precisa fazer muitos testes e engenharia reversa para lidar com os atributos de alternância corretamente. Dê uma olhada no parágrafo 17.7.3 da especificação ECMA-376 Open XML para obter as regras formais e detalhadas para alternância de propriedades/
Fontes
As fontes seguem as mesmas regras comuns que outros atributos de texto, mas os valores padrão da propriedade da fonte são especificados em um arquivo de tema separado, referenciado em word/_rels/document.xml.rels
assim:
<Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml"/>
Com base na referência acima, o nome da fonte padrão será encontrado em word/theme/themes1.xml
, dentro de uma tag <a:theme>
, a:themeElements/a:fontScheme/a:majorFont
ou a:minorFont
tag.
O tamanho da fonte padrão é 10, a menos que a tag w:docDefaults/w:rPrDefault
esteja ausente, então é tamanho 11.
Alinhamento de texto
O alinhamento do texto é especificado por uma tag <w:jc>
com quatro modos w:val
disponíveis: "left"
, "center"
, "right"
e "both"
.
"left"
é o modo padrão; o texto é iniciado à esquerda do retângulo do parágrafo (geralmente a largura da página). (Este parágrafo está alinhado à esquerda, o que é padrão.)
o modo "center"
, previsivelmente, centraliza todos os caracteres dentro da largura da página. (Novamente, este parágrafo exemplifica o alinhamento centralizado.)
No modo "right"
, o texto do parágrafo é alinhado à margem direita. (Observe como este texto está alinhado ao lado direito.)
O modo "both"
coloca um espaçamento extra entre as palavras para que as linhas fiquem mais largas e ocupem toda a largura do parágrafo, com exceção da última linha alinhada à esquerda. (Este parágrafo é uma demonstração disso.)
Imagens
O DOCX suporta dois tipos de imagens: inline e flutuante.
As imagens inline aparecem dentro de um parágrafo junto com os outros caracteres, <w:drawing>
é usado em vez de usar <w:t>
(texto). Você pode encontrar o ID da imagem com a seguinte sintaxe xpath:
w:drawing/wp:inline/a:graphic/a:graphicData/pic:pic/pic:blipFill/a:blip/@r:embed
O ID da imagem é usado para procurar o nome do arquivo no arquivo word/_rels/document.xml.rels
e deve apontar para o arquivo gif/jpeg dentro da subpasta word/media. (Veja o arquivo word/_rels/document.xml.rels
do projeto github, onde você pode ver o ID da imagem.)
As imagens flutuantes são colocadas em relação aos parágrafos com texto fluindo ao redor deles. (Aqui está o documento de amostra do projeto github com uma imagem flutuante.)
As imagens flutuantes usam <wp:anchor>
em vez de <w:drawing>
, portanto, se você excluir qualquer texto dentro de <w:p>
, tenha cuidado com as âncoras se não quiser que as imagens sejam removidas.
Tabelas
As tags XML para tabelas são semelhantes à marcação de tabela HTML– combina com <tr>, etc.
<w:tbl>
, a própria tabela, tem propriedades de tabela <w:tblPr>
, e cada propriedade de coluna é apresentada por <w:gridCol>
dentro de <w:tblGrid>
. As linhas seguem uma a uma como tags <w:tr>
e cada linha deve ter o mesmo número de colunas especificado em <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>
A largura das colunas da tabela pode ser especificada na tag <w:tblW>
, mas se você não a definir, o MS Word usará seus algoritmos internos para encontrar a largura ideal das colunas para o menor tamanho efetivo da tabela.
Unidades
Muitos atributos XML dentro do DOCX especificam tamanhos ou distâncias. Enquanto eles são inteiros dentro do XML, todos eles têm unidades diferentes, então alguma conversão é necessária. O tópico é complicado, então eu recomendo este artigo de Lars Corneliussen sobre unidades em arquivos DOCX. A tabela que ele apresenta é útil, embora com um pequeno erro de impressão: polegadas devem ser pt/72, não pt*72.
Aqui está uma folha de dicas:
CONVERSÕES COMUNS DE UNIDADES XML DOCX | ||||||
20 de um ponto | Pontos dxa/20 | Polegadas pt/72 | Centímetros em*2,54 | Fonte metade do tamanho pt/144 | UEM em*914400 | |
Exemplo | 11906 | 595,3 | 8,27… | 21.00086… | 4.135 | 7562088 |
Tags usando isso | pgSz/pgMar/w:espaçamento | w:sz | wp:extensão, a:extensão |
Dicas para implementar um Layouter
Se você deseja converter um arquivo DOCX (para PDF, por exemplo), desenhá-lo na tela ou contar o número de páginas, você terá que implementar um layouter. Um layouter é um algoritmo para calcular posições de caracteres de um arquivo DOCX.
Esta é uma tarefa complexa se você precisar de 100% de renderização de fidelidade. A quantidade de tempo necessária para implementar um bom layout é medida em homens-anos, mas se você precisar apenas de um simples e limitado, isso pode ser feito de forma relativamente rápida.
Um layouter preenche um retângulo pai, que geralmente é um retângulo da página. Ele adiciona palavras de uma corrida, uma por uma. Quando a linha atual transborda, ela inicia uma nova. Se o parágrafo for muito alto para o retângulo pai, ele será quebrado na próxima página.
Aqui estão algumas coisas importantes a serem lembradas se você decidir implementar um layouter:
- O layouter deve cuidar do alinhamento do texto e do texto flutuando sobre as imagens
- Deve ser capaz de lidar com objetos aninhados, como tabelas aninhadas
- Se você quiser fornecer suporte completo para essas imagens, terá que implementar um layouter com pelo menos duas passagens, a primeira etapa coleta as posições das imagens flutuantes e a segunda preenche o espaço vazio com caracteres de texto.
- Esteja ciente dos recuos e espaçamentos. Cada parágrafo tem espaçamento antes e depois, e esses números são especificados pela tag
w:spacing
. O espaçamento vertical é especificado pelas tagsw:after
ew:before
. Observe que o espaçamento entre linhas é especificado porw:line
, mas esse não é o tamanho da linha como se poderia esperar. Para obter o tamanho da linha, pegue a altura da fonte atual, multiplique porw:line
e divida por 12. - Os arquivos DOCX não contêm informações sobre paginação. Você não encontrará o número de páginas no documento a menos que calcule quanto espaço você precisa para cada linha para determinar o número de páginas. Se você precisar encontrar as coordenadas exatas de cada caractere na página, certifique-se de levar em consideração todos os espaçamentos, recuos e tamanhos.
- Se você implementar um layout DOCX com recursos completos que manipula tabelas, observe os casos especiais em que as tabelas abrangem várias páginas. Uma célula que causa um estouro de página também afeta outras células.
- Criar um algoritmo ideal para calcular a largura das colunas de uma tabela é um problema matemático desafiador e processadores de texto e layouters geralmente usam algumas implementações abaixo do ideal. Proponho usar o algoritmo da documentação da tabela HTML do W3C como uma primeira aproximação. Não encontrei uma descrição do algoritmo usado pelo MS Word, e a Microsoft ajustou o algoritmo ao longo do tempo, de modo que versões diferentes do Word podem apresentar tabelas de forma ligeiramente diferente.
Se algo não estiver claro: faça a engenharia reversa do XML!
Quando não é óbvio como esta ou aquela tag XML funciona dentro do MS Word, existem duas abordagens principais para descobrir isso:
Crie o conteúdo desejado passo a passo. Comece com um arquivo docx simples. Salve cada etapa em seu próprio arquivo, como em
1.docx
,2.docx
, por exemplo. Descompacte cada um deles e use uma ferramenta de comparação visual para comparação de pastas para ver quais tags aparecem após suas alterações. (Para uma opção comercial, experimente o Araxis Merge ou, para uma opção gratuita, o WinMerge.)Se você gerar um arquivo DOCX que o MS Word não gosta, trabalhe de trás para frente. Simplifique seu XML passo a passo. Em algum momento, você aprenderá qual alteração o MS Word encontrou incorreta.
DOCX é bastante complexo, não é?
É complexo, e a licença da Microsoft proíbe o uso do MS Word no lado do servidor para processar DOCX – isso é bastante padrão para produtos comerciais. A Microsoft, no entanto, forneceu o arquivo XSLT para lidar com a maioria das tags DOCX, mas não fornecerá 100% ou mesmo 99% de fidelidade. Processos como quebra de texto sobre imagens não são suportados, mas você poderá suportar a maioria dos documentos. (Se você não precisar de complexidade, considere usar o Markdown como alternativa.)
Se você tiver um orçamento suficiente (não há mecanismo de renderização DOCX gratuito), talvez queira usar produtos comerciais como Aspose ou docx4j. A solução gratuita mais popular é o LibreOffice para converter entre DOCX e outros formatos, incluindo PDF. Infelizmente, o LibreOffice contém muitos pequenos bugs durante a conversão e, como é um produto C++ sofisticado e de código aberto, é lento e difícil corrigir problemas de fidelidade.
Alternativamente, se você achar o layout DOCX muito complicado de implementar, você também pode convertê-lo para HTML e usar um navegador para renderizá-lo. Você também pode considerar um dos desenvolvedores XML freelance da Toptal.
Recursos DOCX para leitura adicional
- Especificação ECMA DOCX
- Biblioteca OpenXML para manipulação de DOCX a partir de C#. Ele não contém informações sobre layout ou código de renderização, mas oferece uma hierarquia de classes que corresponde a cada nó XML possível no DOCX.
- Você sempre pode pesquisar ou perguntar no stackoverflow com palavras-chave como docx4j, OpenXML e docx; há pessoas na comunidade que têm conhecimento.