r2soft / docx-toolkit
Manipulação de DOCX por tags e exportação para DOCX/PDF (PHP puro).
Requires
- php: >=8.0
- ext-dom: *
- ext-zip: *
- phpoffice/phpword: ^1.4
Requires (Dev)
- dompdf/dompdf: ^3.1
- phpunit/phpunit: ^11.5
- r2soft/utils: ^1.0
Suggests
- dompdf/dompdf: Necessário para conversão PDF via DomPDF
- mpdf/mpdf: Alternativa de conversão PDF
- tecnickcom/tcpdf: Alternativa de conversão PDF
- dev-develop
- v1.000.033
- v1.000.032
- v1.000.031
- v1.000.030
- v1.000.029
- v1.000.028
- v1.000.027
- v1.000.026
- v1.000.025
- v1.000.024
- v1.000.023
- v1.000.022
- v1.000.021
- v1.000.020
- v1.000.019
- v1.000.018
- v1.000.017
- v1.000.016
- v1.000.015
- v1.0.014
- v1.0.13
- v1.0.12
- v1.0.11
- v1.0.10
- v1.0.9
- v1.0.8
- v1.0.7
- v1.0.6
- v1.0.5
- v1.0.4
- v1.0.3
- v1.0.2
- v1.0.1
- v1.0.0
- v0.2.0
- v0.1.7
- v0.1.0
- dev-bugfix/fix-breakline-for-onlyoffice
- dev-master
- dev-dev-ajustes-tags
This package is auto-updated.
Last update: 2025-08-29 21:25:31 UTC
README
Biblioteca PHP para preencher templates DOCX (variáveis, tabelas e blocos) e converter DOCX em PDF, usando PhpOffice/PhpWord por baixo dos panos. Foco em simplicidade, zero dependências de framework e compatível com Docker.
- Preenchimento de variáveis simples (setValue)
- Preenchimento de tabelas (cloneRowAndSetValues)
- Preenchimento de blocos (cloneBlock com índices #1, #2, ...)
- Suporte a textos ricos básicos via setComplexValue a partir de tags HTML: , , , ,
- Conversão de DOCX para PDF com DomPDF, mPDF, TCPDF ou LibreOffice (alta fidelidade)
- Aceita template como binário ou Base64
Requisitos
- PHP >= 8.0
- Extensão ext-zip habilitada
- PhpOffice/phpword ^1.4
- Para PDF via renderizadores PHP (opcional): dompdf/dompdf, mpdf/mpdf ou tecnickcom/tcpdf
- Para PDF via LibreOffice (opcional): é necessário ter o LibreOffice instalado na máquina (binário
soffice
acessível no PATH ou informe o caminho completo)
Consulte composer.json
para detalhes de dependências e sugestões.
Instalação
Via Composer (dentro do seu projeto):
composer require r2soft/docx-toolkit
# Para PDF (opcional escolha um):
composer require dompdf/dompdf
# ou
composer require mpdf/mpdf
# ou
composer require tecnickcom/tcpdf
Uso rápido
Exemplo mínimo (baseado em test.php):
<?php
require __DIR__ . '/vendor/autoload.php';
use R2Soft\Docx\DocxEngine;
$engine = new DocxEngine(pdfRenderer: 'DomPDF'); // ou 'mPDF', 'TCPDF' ou null
$templateBinary = file_get_contents('exemplo.docx');
$docx = $engine->renderDocx($templateBinary, [
'nome' => 'Fulano',
]);
file_put_contents('saida.docx', $docx);
Converter para PDF (renderizadores PHP: DomPDF/mPDF/TCPDF)
Por padrão, toPdf()
retorna um Data URL Base64 (data:application/pdf;base64,...
). Para obter o binário diretamente, passe false
no segundo parâmetro.
// Retorno como Data URL Base64 (padrão)
$pdfDataUrl = $engine->toPdf($docx); // $docx pode ser binário ou base64
// Se quiser gravar o PDF a partir do Data URL:
[$meta, $b64] = explode(',', $pdfDataUrl, 2);
file_put_contents('saida.pdf', base64_decode($b64));
// OU, mais simples: peça binário diretamente
$pdfBinary = $engine->toPdf($docx, false);
file_put_contents('saida.pdf', $pdfBinary);
Observações importantes:
- Se você passar Base64 no
renderDocx()
outoPdf()
, a biblioteca detecta e converte automaticamente. - Para
toPdf()
, é obrigatório informarpdfRenderer
no construtor e ter a dependência do motor instalada.
Converter para PDF com LibreOffice (alta fidelidade)
Em muitos casos, a melhor fidelidade de conversão de DOCX para PDF é obtida via LibreOffice. Este projeto expõe o método toPdfLibreOffice()
na DocxEngine
, que usa internamente R2Soft\Docx\Support\LibreOfficePdfConverter
.
Requisitos obrigatórios:
- Ter o LibreOffice instalado na máquina/contêiner.
- O binário
soffice
deve estar acessível no PATH do sistema. Caso contrário, informe o caminho completo criando o conversor manualmente.
Uso básico:
use R2Soft\Docx\DocxEngine;
$engine = new DocxEngine(); // não precisa configurar pdfRenderer
$pdfBin = $engine->toPdfLibreOffice($docx, false); // $docx binário ou base64
file_put_contents('saida_libreoffice.pdf', $pdfBin);
Definindo um caminho customizado do binário (avançado):
use R2Soft\Docx\Support\LibreOfficePdfConverter;
$converter = new LibreOfficePdfConverter('/usr/bin/soffice');
$pdfBin = $converter->convert($docxBin);
Atualizações recentes do conversor (internos importantes):
- Diretório temporário otimizado: quando disponível, o conversor utiliza
/dev/shm
(tmpfs) para reduzir I/O e acelerar a conversão. Caso contrário, usasys_get_temp_dir()
. - Perfil dedicado do LibreOffice: cada execução usa um perfil limpo. Opcionalmente, você pode habilitar um perfil persistente para melhorar performance e estabilidade entre execuções (ver
LO_PROFILE_DIR
abaixo). - Execução headless e estável: usamos
--headless --invisible --nologo --nodefault --nolockcheck --norestore --nofirststartwizard
, desativamos Java e OpenCL e fixamos variáveis de ambiente para evitar problemas de UI/dconf. - Limpeza segura: somente os diretórios temporários criados com prefixo controlado são removidos ao final da execução.
Variável de ambiente LO_PROFILE_DIR (recomendado em produção):
- O que é: caminho de um diretório no qual o LibreOffice manterá seu "perfil de usuário" (cache/config) de forma persistente.
- Para que serve: evita o custo de primeira execução do LibreOffice a cada conversão, reaproveita cache de fontes e configurações e tende a reduzir falhas intermitentes em ambientes headless.
- Requisitos: o caminho precisa existir (ou ser criável) e ser gravável pelo processo PHP/contêiner. Pode ser montado como volume em Docker.
- Exemplo de valor:
LO_PROFILE_DIR=/var/lib/lo-profile
. - Como funciona aqui: se
LO_PROFILE_DIR
estiver definido, o conversor usará esse diretório; caso contrário, criará um perfil temporário isolado por execução.
Exemplos de uso:
- Linux/macOS (shell):
export LO_PROFILE_DIR=/var/lib/lo-profile php seu_script.php
- Docker Compose (persistindo o perfil):
environment: LO_PROFILE_DIR: /var/lib/lo-profile volumes: - lo-profile:/var/lib/lo-profile
- Dockerfile desta imagem: define
LO_PROFILE_DIR=/var/lib/lo-profile
e realiza um "warm-up" chamando osoffice
uma vez para preparar cache de fontes.
Notas técnicas (variáveis de ambiente usadas internamente durante a conversão):
HOME
,XDG_CACHE_HOME
,XDG_CONFIG_HOME
são apontados para o perfil (persistente ou temporário).SAL_USE_VCLPLUGIN=headless
,NO_AT_BRIDGE=1
,SAL_DISABLE_OPENCL=1
,OMP_THREAD_LIMIT=2
para estabilidade/performance em headless.LANG
herda do ambiente ou usapt_BR.UTF-8
por padrão.
Dicas:
- Em Docker (imagem deste repo), o LibreOffice já é instalado no Dockerfile.
- Em Ubuntu/Debian, instale com:
sudo apt-get update && sudo apt-get install -y libreoffice
. - Em Alpine, prefira imagens base Debian/Ubuntu para melhor compatibilidade; em Alpine puro, use
apk add libreoffice
(pode resultar em uma instalação pesada). - Em Windows, instale o LibreOffice e garanta que
soffice.exe
esteja no PATH (ou forneça o caminho, ex.:C:\\Program Files\\LibreOffice\\program\\soffice.exe
). - Em macOS, via Homebrew:
brew install --cask libreoffice
. O binário geralmente fica em/Applications/LibreOffice.app/Contents/MacOS/soffice
.
Como preparar o template DOCX
Você pode criar seu DOCX no Word/LibreOffice e inserir marcadores entre chaves. Exemplos:
- Variáveis simples: ${nome}
- Tabela (cloneRowAndSetValues): defina uma linha com marcadores como ${item_nome}, ${item_preco} e repita a linha. Ao chamar
preencherTabela('item', $linhas)
a engine multiplicará as linhas. - Blocos (cloneBlock): crie um bloco nomeado, por exemplo,
tabela_lotes
no conteúdo e use marcadores com sufixos#1
,#2
, ... ex.: ${campo#1}, ${campo#2}.
A engine já contempla alguns nomes especiais de chave para facilitar:
tabela_lotes
(array): usa cloneBlock com índices (#1, #2, ...)tabela_atrasadas
(array): aplica formatação de dinheiro e datas automaticamente e clona uma tabela com referêncianumero
tabela_lotes_lista
(array): se a variávelquadra_q
existir no template, clona a tabela com essa referênciadados_tabela_simples
(array): clona tabela com referênciaitem
Se você não usar esses nomes especiais, ainda pode preencher variáveis simples diretamente com o mesmo nome das chaves no array $termos
.
Texto rico básico
Quando o valor tiver HTML simples com <b>
, <strong>
, <i>
, <u>
ou <br>
, a engine converte para um TextRun
e usa setComplexValue()
, preservando estilo e quebras de linha. Caso contrário, usa setValue()
com limpeza de tags.
Há um tratamento especial para chaves contendo "assinatura": nelas o HTML não é removido na limpeza de variáveis simples.
API principal
R2Soft\Docx\DocxEngine
Construtor:
new DocxEngine(
?string $pdfRenderer = null, // 'DomPDF' | 'mPDF' | 'TCPDF' | null
?string $pdfRendererPath = null, // opcional; normalmente não é necessário
$moneyFormatter = null // callable|null para formatar dinheiro
)
$pdfRenderer
: define o motor para gerar PDF. Obrigatório para usartoPdf()
.$pdfRendererPath
: caminho do renderer (casos avançados). Normalmente omita.$moneyFormatter
: callback para formatar valores monetários. Se não informado, a engine utilizaFormat::money()
.
Métodos:
renderDocx(string $templateBase64OrBinary, array $termos): string
- Recebe o template como binário ou Base64 e um array de termos.
- Entrega o DOCX gerado (binário).
toPdf(string $docxBinaryOrBase64, bool $base64 = true): string
- Converte um DOCX (binário/Base64) para PDF usando o renderer configurado (DomPDF/mPDF/TCPDF). Requer
$pdfRenderer
.
- Converte um DOCX (binário/Base64) para PDF usando o renderer configurado (DomPDF/mPDF/TCPDF). Requer
toPdfLibreOffice(string $docxBinaryOrBase64, bool $base64 = true): string
- Converte um DOCX (binário/Base64) para PDF usando o LibreOffice (requer LibreOffice instalado e
soffice
acessível).
- Converte um DOCX (binário/Base64) para PDF usando o LibreOffice (requer LibreOffice instalado e
DocxEngine::toBase64(string $binary): string
eDocxEngine::fromBase64(string $b64): string
R2Soft\Docx\Support\LibreOfficePdfConverter
__construct(string $binary = 'soffice')
- Permite definir o caminho do binário do LibreOffice.
convert(string $docxBinary): string
- Recebe o DOCX binário e retorna o PDF binário.
R2Soft\Docx\Support\Templating
toTextRun(string $html): TextRun
— converte HTML básico para TextRun.preencherTabela(TemplateProcessor $template, string $tagReferencia, array $dados): void
— clona linhas de tabela baseadas na tag de referência (ex.:item
). O array$dados
deve ser uma lista de arrays associativos.preencherBloco(TemplateProcessor $template, string $nomeBloco, array $dados): void
— clona blocos usando sufixos#1
,#2
, etc.
R2Soft\Docx\Support\Format
toDate(mixed $v): ?DateTime
— tenta converter para data.money(mixed $v, $moneyFormatter = null): string
— formata dinheiro usando callback customizado (se houver) ours_dinheiro()
.formatTabelaAtrasadas(array $linhas, $moneyFormatter = null): array
— formata números (dinheiro) e datas (dd/mm/YYYY) para a tabela especialtabela_atrasadas
.rs_dinheiro($value, int $decimal=2, string $dec=",", string $mil="."): string
— formatação brasileira:R$ 1.234,56
.
Estrutura esperada dos dados
Exemplos práticos:
$termos = [
'nome' => 'Fulano',
// Tabela simples com referência 'item' no template:
'dados_tabela_simples' => [
['item' => 'Produto A', 'preco' => 10.5],
['item' => 'Produto B', 'preco' => 22],
],
// Tabela atrasadas (formatação automática)
'tabela_atrasadas' => [
['numero' => 1, 'vencimento' => '2025-08-10', 'valor' => 140.9],
['numero' => 2, 'vencimento' => '2025-09-10', 'valor' => 200],
],
// Bloco 'tabela_lotes' com índices #1, #2 no template
'tabela_lotes' => [
['quadra' => 'Q1', 'lote' => 'L10'],
['quadra' => 'Q2', 'lote' => 'L20'],
],
];
No template, você usaria por exemplo:
${item}
e${preco}
na linha da tabela de referênciaitem
.- Para blocos,
${quadra#1}
,${lote#1}
,${quadra#2}
,${lote#2}
, etc.
Fluxo de Teste
Este projeto inclui um fluxo de teste simples dentro da pasta tests/
para validar o preenchimento de um template DOCX e a geração do arquivo de saída.
Arquivos relevantes:
tests/test.php
: script que orquestra o teste.tests/tags.php
: retorna um array associativo com as chaves e valores que serão injetados no template.tests/exemplo.docx
: template de exemplo contendo as variáveis e estruturas de tabela/bloco.tests/saida.docx
: arquivo de saída gerado pelo teste (será sobrescrito a cada execução).
Como o teste funciona:
- O script
tests/test.php
carrega o autoloader do Composer:vendor/autoload.php
. - Instancia
R2Soft\Docx\DocxEngine
compdfRenderer: 'DomPDF'
(opcional, apenas necessário se for converter para PDF em seguida). - Lê o template binário de
tests/exemplo.docx
viafile_get_contents
. - Carrega os termos (tags) a partir de
tests/tags.php
(o arquivo retorna um array com chaves que precisam existir no template). - Chama
$engine->renderDocx($template, $tags)
, que:- Substitui variáveis simples (ex.:
${nome}
), - Preenche tabelas e blocos quando os nomes convencionados são encontrados (ex.:
dados_tabela_simples
,tabela_atrasadas
,tabela_lotes
), - Converte HTML básico em TextRun quando apropriado (para
setComplexValue
).
- Substitui variáveis simples (ex.:
- Escreve o resultado em
tests/saida.docx
e exibe uma mensagem[SUCESSO]
com o caminho de saída.
Executando localmente:
- Pré-requisitos: PHP 8.0+, ext-zip, Composer, e (opcional) um renderer de PDF se for testar PDF.
Passos:
composer install
php tests/test.php
Saída esperada no terminal:
[SUCESSO] Arquivo gerado em: /caminho/para/o/projeto/tests/saida.docx
Em caso de erro, será exibido:
[ERRO] <mensagem>
Executando em Docker/Docker Compose:
- Suba o ambiente:
docker compose up --build -d
- Entre no contêiner, instale dependências e execute:
docker compose exec app bash composer install php tests/test.php
Gerando PDF (opcional):
- O
test.php
de exemplo demonstra geração de PDF com dois caminhos: 1) via renderizador PHP (toPdf()
), 2) via LibreOffice (toPdfLibreOffice()
).
Exemplo (trecho do teste):
$pdf = $engine->toPdf($docx, false);
$pdfLibreOffice = $engine->toPdfLibreOffice($docx, false);
file_put_contents(__DIR__ . '/results/saida.pdf', $pdf);
file_put_contents(__DIR__ . '/results/saida_libreoffice.pdf', $pdfLibreOffice);
Para o caminho 1, instale um renderer, por exemplo:
composer require dompdf/dompdf
Para o caminho 2 (LibreOffice), é obrigatório ter o LibreOffice instalado e soffice
acessível.
Personalizando o teste:
- Edite
tests/tags.php
para alterar valores e estruturas. As chaves devem corresponder às variáveis do template DOCX. - Substitua
tests/exemplo.docx
pelo seu template contendo os marcadores${chave}
esperados.
Limpeza:
- Remova os arquivos gerados:
rm -f tests/saida.docx tests/saida.pdf
Solução de problemas específicos do teste:
- "Renderer de PDF não configurado": só necessário se você tentar converter para PDF. Configure
pdfRenderer
e instale o pacote do renderer. - "Variável não substituída": verifique se o nome da chave em
tags.php
coincide exatamente com a variável no template (${chave}
). - Problemas de formatação HTML: limite-se a
<b>
,<strong>
,<i>
,<u>
,<br>
.
Docker e Docker Compose
O projeto inclui Dockerfile
e docker-compose.yaml
para desenvolvimento.
Subindo o contêiner de desenvolvimento:
docker compose up --build -d
Entrar no contêiner e instalar dependências:
docker compose exec app bash
composer install
Rodar o exemplo tests/test.php
dentro do contêiner:
php tests/test.php
Arquivos são montados via volume em /app
. Ajuste PHP_MEMORY_LIMIT
em docker-compose.yaml
se necessário.
Boas práticas e dicas
- Use apenas HTML simples suportado para
setComplexValue
(, , , ,
). - Garanta que os nomes das chaves no array
$termos
existam como variáveis no DOCX. - Para tabelas com
cloneRowAndSetValues
, defina uma única linha modelo com os marcadores e deixe a engine multiplicar. - Valores não escalares em variáveis simples são convertidos para texto padrão "NÃO DEFINIDO".
- Campos contendo "assinatura" não têm o HTML removido automaticamente.
Solução de problemas
- Erro "Renderer de PDF não configurado": forneça
pdfRenderer
no construtor e instale o pacote do renderer (DomPDF/mPDF/TCPDF). - Problemas com fontes no PDF: pode ser necessário instalar fontes adicionais no contêiner/sistema ou configurar o renderer.
- Quebras de linha: use
<br>
ou\r\n
no valor para quebrar linhas adequadamente.
Solução de problemas (LibreOffice)
- "Falha ao iniciar processo do LibreOffice" ou "LibreOffice falhou":
- Verifique se o binário
soffice
está instalado e acessível (which soffice
). - Tente informar o caminho completo via
new LibreOfficePdfConverter('/caminho/para/soffice')
e usar o conversor diretamente. - Em ambientes headless (servidores), o conversor já usa
--headless
e um perfil temporário. Ainda assim, garanta permissões de escrita em/tmp
.
- Verifique se o binário
- "Command not found" (soffice): instale o LibreOffice conforme sua plataforma (veja dicas na seção de LibreOffice acima).
- Problemas de acentuação/locale: garanta um locale UTF-8 (ex.:
pt_BR.UTF-8
).
Licença
MIT.
Autores
- Ricardo de Paula Coelho — ricardo@r2soft.com.br
- R2 Soft - Informática e Softwares Ltda-ME — r2soft@r2soft.com.br