Convertendo HTML para PDF usando o iText
Estou postando esta pergunta porque muitos desenvolvedores fazem mais ou menos a mesma pergunta de formas diferentes. Eu mesmo responderei a essa pergunta (sou o Fundador / CTO do iText Group), para que possa ser uma "resposta da Wiki". Se o recurso "documentação" do estouro de pilha ainda existisse, esse seria um bom candidato para um tópico de documentação.
O arquivo de origem:Estou tentando converter o seguinte arquivo HTML em PDF:
<html>
<head>
<title>Colossal (movie)</title>
<style>
.poster { width: 120px;float: right; }
.director { font-style: italic; }
.description { font-family: serif; }
.imdb { font-size: 0.8em; }
a { color: red; }
</style>
</head>
<body>
<img src="img/colossal.jpg" class="poster" />
<h1>Colossal (2016)</h1>
<div class="director">Directed by Nacho Vigalondo</div>
<div class="description">Gloria is an out-of-work party girl
forced to leave her life in New York City, and move back home.
When reports surface that a giant creature is destroying Seoul,
she gradually comes to the realization that she is somehow connected
to this phenomenon.
</div>
<div class="imdb">Read more about this movie on
<a href="www.imdb.com/title/tt4680182">IMDB</a>
</div>
</body>
</html>
Em um navegador, esse HTML se parece com o seguinte:
Os problemas que encontrei:O HTMLWorker não leva em consideração o CSS
Quando eu useiHTMLWorker
, Preciso criar umImageProvider
para evitar um erro que me informa que a imagem não pode ser encontrada. Eu também preciso criar umStyleSheet
instância para alterar alguns dos estilos:
public static class MyImageFactory implements ImageProvider {
public Image getImage(String src, Map<String, String> h,
ChainedProperties cprops, DocListener doc) {
try {
return Image.getInstance(
String.format("resources/html/img/%s",
src.substring(src.lastIndexOf("/") + 1)));
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
public static void main(String[] args) throws IOException, DocumentException {
Document document = new Document();
PdfWriter.getInstance(document, new FileOutputStream("results/htmlworker.pdf"));
document.open();
StyleSheet styles = new StyleSheet();
styles.loadStyle("imdb", "size", "-3");
HTMLWorker htmlWorker = new HTMLWorker(document, null, styles);
HashMap<String,Object> providers = new HashMap<String, Object>();
providers.put(HTMLWorker.IMG_PROVIDER, new MyImageFactory());
htmlWorker.setProviders(providers);
htmlWorker.parse(new FileReader("resources/html/sample.html"));
document.close();
}
O resultado fica assim:
Por algum motivo,HTMLWorker
também mostra o conteúdo do<title>
etiqueta, rótulo, palavra-chave. Não sei como evitar isso. O CSS no cabeçalho não é analisado, tenho que definir todos os estilos no meu código, usando oStyleSheet
objeto.
Quando olho para o meu código, vejo que muitos objetos e métodos que estou usando estão obsoletos:
Então, decidi atualizar para o XML Worker.
Imagens não são encontradas ao usar o XML Worker
Eu tentei o seguinte código:
public static final String DEST = "results/xmlworker1.pdf";
public static final String HTML = "resources/html/sample.html";
public void createPdf(String file) throws IOException, DocumentException {
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(file));
document.open();
XMLWorkerHelper.getInstance().parseXHtml(writer, document,
new FileInputStream(HTML));
document.close();
}
Isso resultou no seguinte PDF:
Em vez de Times-Roman, a fonte padrão Helvetica é usada; isso é típico para o iText (eu deveria ter definido uma fonte explicitamente no meu HTML). Caso contrário, o CSS parece ser respeitado, mas a imagem está faltando e eu não recebi uma mensagem de erro.
ComHTMLWorker
, uma exceção foi lançada e eu pude corrigir o problema introduzindo umImageProvider
. Vamos ver se isso funciona para o XML Worker.
Nem todos os estilos CSS são suportados no XML Worker
Eu adaptei meu código assim:
public static final String DEST = "results/xmlworker2.pdf";
public static final String HTML = "resources/html/sample.html";
public static final String IMG_PATH = "resources/html/";
public void createPdf(String file) throws IOException, DocumentException {
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(file));
document.open();
CSSResolver cssResolver =
XMLWorkerHelper.getInstance().getDefaultCssResolver(true);
HtmlPipelineContext htmlContext = new HtmlPipelineContext(null);
htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
htmlContext.setImageProvider(new AbstractImageProvider() {
public String getImageRootPath() {
return IMG_PATH;
}
});
PdfWriterPipeline pdf = new PdfWriterPipeline(document, writer);
HtmlPipeline html = new HtmlPipeline(htmlContext, pdf);
CssResolverPipeline css = new CssResolverPipeline(cssResolver, html);
XMLWorker worker = new XMLWorker(css, true);
XMLParser p = new XMLParser(worker);
p.parse(new FileInputStream(HTML));
document.close();
}
Meu código é muito mais longo, mas agora a imagem é renderizada:
A imagem é maior do que quando a renderizei usandoHTMLWorker
o que me diz que o atributo CSSwidth
para oposter
classe é levada em consideração, mas ofloat
O atributo é ignorado. Como faço para corrigir isso?
Então a questão se resume a isso: eu tenho umespecífico Arquivo HTML que tento converter em PDF. Passei por muito trabalho, corrigindo um problema após o outro, mas há umespecífico problema que não consigo resolver: como faço para o iText respeitar o CSS que define a posição de um elemento, comofloat: right
?
Quando meu HTML contém elementos de formulário (como<input>
), esses elementos do formulário são ignorados.