Come aggiustare l’altezza della pagina all’altezza del contenuto?

Sto usando iTextPDF + FreeMarker per il mio progetto. Fondamentalmente carico e riempio un template HTML con FreeMarker e poi lo renderizzo in pdf con XMLWorker di XMLWorker .

Il modello è:

   
${timestampLabel}  ${timestampValue}
${errorIdLabel}  ${errorIdValue}
${systemIdLabel}  ${systemIdValue}
${descriptionLabel}  ${descriptionValue}

E questo è il mio codice:

 SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); String errorId = "ERROR-01"; String systemId = "SYSTEM-01"; String description = "A SHORT DESCRIPTION OF THE ISSUE"; Map parametersMap = new HashMap(); parametersMap.put("fontName", fontName); //valid font name parametersMap.put("timestampLabel", " TIMESTAMP:"); parametersMap.put("errorIdLabel", " ERROR ID:"); parametersMap.put("systemIdLabel", " SYSTEM ID:"); parametersMap.put("descriptionLabel", " DESCRIPTION:"); parametersMap.put("timestampValue", DATE_FORMAT.format(new Date())); parametersMap.put("errorIdValue", errorId); parametersMap.put("systemIdValue", systemId); parametersMap.put("descriptionValue", description); FreeMarkerRenderer renderer = new FreeMarkerRenderer(); //A utility class renderer.loadTemplate(errorTemplateFile); //the file exists String rendered = renderer.render(parametersMap); File temp = File.createTempFile("document", ".pdf"); file = new FileOutputStream(temp); Document document = new Document(); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ document.setPageSize(new Rectangle(290f, 150f)); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ document.setMargins(10, 10, 10, 10); PdfWriter writer = PdfWriter.getInstance(document, file); document.open(); InputStream is = new ByteArrayInputStream(rendered.getBytes()); XMLWorkerFontProvider provider = new XMLWorkerFontProvider(); provider.register(fontFile); //the file exists FontFactory.setFontImp(provider); byte[] errorStyle = getErrorStyleByteArray(); //returns a byte array from a css file (works) XMLWorkerHelper helper = XMLWorkerHelper.getInstance(); helper.parseXHtml(writer, document, is, new ByteArrayInputStream(errorStyle), provider); document.close(); file.close(); 

Questo codice funziona bene, ma con l’altezza fissa è un problema.

IE Diciamo che:

 errorId = "ERROR-01" systemId = "SYSTEM-01" description = "A SHORT DESCRIPTION OF THE ISSUE" 

Il documento prodotto è:

inserisci la descrizione dell'immagine qui

Se invece io uso

 errorId = "ERROR-01" systemId = "SYSTEM-01" description = "A SHORT DESCRIPTION OF THE ISSUE. THIS IS MULTILINE AND IT SHOULD STAY ALL IN THE SAME PDF PAGE." 

Il documento prodotto è:

inserisci la descrizione dell'immagine qui

Come puoi vedere, nell’ultimo documento ho due pagine. Mi piacerebbe avere solo una pagina che cambia la sua altezza in base all’altezza del contenuto.

È qualcosa di simile a questo ansible con iText?

Non è ansible modificare le dimensioni della pagina dopo aver aggiunto il contenuto a quella pagina. Un modo per aggirare questo problema, sarebbe quello di creare il documento in due passaggi: prima creare un documento per aggiungere il contenuto, quindi manipolare il documento per modificare le dimensioni della pagina. Questa sarebbe stata la mia prima risposta se avessi avuto il tempo di rispondere immediatamente.

Ora che ho avuto più tempo per pensarci, ho trovato una soluzione migliore che non richiede due passaggi. Dai un’occhiata a HtmlAdjustPageSize

In questo esempio, prima analizzo il contenuto in una lista di oggetti Element usando questo metodo:

 public ElementList parseHtml(String html, String css) throws IOException { // CSS CSSResolver cssResolver = new StyleAttrCSSResolver(); CssFile cssFile = XMLWorkerHelper.getCSS(new ByteArrayInputStream(css.getBytes())); cssResolver.addCss(cssFile); // HTML CssAppliers cssAppliers = new CssAppliersImpl(FontFactory.getFontImp()); HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers); htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory()); htmlContext.autoBookmark(false); // Pipelines ElementList elements = new ElementList(); ElementHandlerPipeline end = new ElementHandlerPipeline(elements, null); HtmlPipeline htmlPipeline = new HtmlPipeline(htmlContext, end); CssResolverPipeline cssPipeline = new CssResolverPipeline(cssResolver, htmlPipeline); // XML Worker XMLWorker worker = new XMLWorker(cssPipeline, true); XMLParser p = new XMLParser(worker); p.parse(new ByteArrayInputStream(html.getBytes())); return elements; } 

Nota: ho copiato / incollato questo metodo così tante volte che ho deciso di renderlo un metodo statico nella class XMLWorkerHelper . Sarà disponibile nella prossima versione di iText.

Importante: ho fatto ciò che ho promesso, questo metodo è ora disponibile nella versione di XML Worker.

A scopo di test, ho usato valori di String statici per HTML e CSS:

 public static final String HTML = "" + "" + "" + "" + "" + "
TIMESTAMP2014-11-28 11:06:09
ERROR IDERROR-01
SYSTEM IDSYSTEM-01
DESCRIPTIONTEST WITH A VERY, VERY LONG DESCRIPTION LINE THAT NEEDS MULTIPLE LINES
"; public static final String CSS = "table {width: 200pt; } .ra { text-align: right; }"; public static final String DEST = "results/xmlworker/html_page_size.pdf";

Puoi vedere che ho preso l’HTML che assomiglia più o meno all’HTML con cui hai a che fare.

Analizzo questo HTML e CSS in una ElementList :

 ElementList el = parseHtml(HTML, CSS); 

Oppure, a partire da XML Worker 5.5.4:

 ElementList el = XMLWorkerHelper.parseToElementList(HTML, CSS); 

Fin qui tutto bene. Non ti ho detto nulla che non sapessi già, tranne questo: ora userò questo el due volte:

  1. Aggiungerò la lista a ColumnText in modalità simulazione. Questo ColumnText non è ancora ColumnText a nessun documento o scrittore. L’unico scopo è quello di sapere quanto spazio ho bisogno verticalmente.
  2. Aggiungerò la lista a un ColumnText per davvero. Questo ColumnText si adatterà esattamente su una pagina di una dimensione che definisco utilizzando i risultati ottenuti in modalità simulazione.

Qualche codice chiarirà cosa intendo:

 // I define a width of 200pt float width = 200; // I define the height as 10000pt (which is much more than I'll ever need) float max = 10000; // I create a column without a `writer` (strange, but it works) ColumnText ct = new ColumnText(null); ct.setSimpleColumn(new Rectangle(width, max)); for (Element e : el) { ct.addElement(e); } // I add content in simulation mode ct.go(true); // Now I ask the column for its Y position float y = ct.getYLine(); 

Il codice sopra riportato è utile solo per una cosa: ottenere il valore y che verrà utilizzato per definire la dimensione della pagina del Document e la dimensione della colonna di ColumnText che verrà aggiunta per davvero:

 Rectangle pagesize = new Rectangle(width, max - y); // step 1 Document document = new Document(pagesize, 0, 0, 0, 0); // step 2 PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(file)); // step 3 document.open(); // step 4 ct = new ColumnText(writer.getDirectContent()); ct.setSimpleColumn(pagesize); for (Element e : el) { ct.addElement(e); } ct.go(); // step 5 document.close(); 

Scarica il codice HtmlAdjustPageSize.java completo e modifica il valore di HTML . Vedrai che questo porta a diverse dimensioni della pagina.