java - ¿Cómo ajustar la altura de la página a la altura del contenido?
height itext (1)
No puede cambiar el tamaño de la página después de haber agregado contenido a esa página. Una forma de evitar esto sería crear el documento en dos pasadas: primero cree un documento para agregar el contenido, luego manipule el documento para cambiar el tamaño de la página. Esa habría sido mi primera respuesta si hubiera tenido tiempo de responder de inmediato.
Ahora que me he tomado más tiempo para pensarlo, he encontrado una mejor solución que no requiere dos pases. Echa un vistazo a HtmlAdjustPageSize
En este ejemplo, primero analizo el contenido en una lista de objetos
Element
usando este método:
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:
He copiado / pegado este método tantas veces que decidí convertirlo en un método estático en la clase
XMLWorkerHelper
.
Estará disponible en la próxima versión de iText.
Importante: he hecho lo que prometí, este método ahora está disponible en la versión XML Worker.
Para fines de prueba, utilicé valores de
String
estáticos para HTML y CSS:
public static final String HTML = "<table>" +
"<tr><td class=/"ra/">TIMESTAMP</td><td><b>2014-11-28 11:06:09</b></td></tr>" +
"<tr><td class=/"ra/">ERROR ID</td><td><b>ERROR-01</b></td></tr>" +
"<tr><td class=/"ra/">SYSTEM ID</td><td><b>SYSTEM-01</b></td></tr>" +
"<tr><td class=/"ra/">DESCRIPTION</td><td><b>TEST WITH A VERY, VERY LONG DESCRIPTION LINE THAT NEEDS MULTIPLE LINES</b></td></tr>" +
"</table>";
public static final String CSS = "table {width: 200pt; } .ra { text-align: right; }";
public static final String DEST = "results/xmlworker/html_page_size.pdf";
Puedes ver que tomé HTML que se parece más o menos al HTML con el que estás tratando.
Analizo este HTML y CSS en una
ElementList
:
ElementList el = parseHtml(HTML, CSS);
O, comenzando con XML Worker 5.5.4:
ElementList el = XMLWorkerHelper.parseToElementList(HTML, CSS);
Hasta aquí todo bien.
No te he dicho nada que no supieras, excepto esto: ahora voy a usar este
el
dos veces:
-
Agregaré la lista a un
ColumnText
en modo de simulación. EsteColumnText
no está vinculado a ningún documento o escritor. El único propósito de hacer esto es saber cuánto espacio necesito verticalmente. -
Agregaré la lista a un
ColumnText
de verdad. EsteColumnText
se ajustará exactamente en una página de un tamaño que defino utilizando los resultados obtenidos en modo de simulación.
Algún código aclarará lo que quiero decir:
// 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();
El código anterior es útil solo para una cosa: obtener el valor
y
que se utilizará para definir el tamaño de página del
Document
y la dimensión de la columna de
ColumnText
que se agregará de forma real:
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();
Descargue el código completo
HtmlAdjustPageSize.java
y cambie el valor de
HTML
.
Verá que esto conduce a diferentes tamaños de página.
Estoy usando iTextPDF + FreeMarker para mi proyecto.
Básicamente, cargo y lleno una plantilla HTML con FreeMarker y luego la renderizo a PDF con XMLWorker de
XMLWorker
.
La plantilla es:
<html>
<body style="font-family; ${fontName}">
<table>
<tr>
<td style="text-align: right">${timestampLabel} </td>
<td><b>${timestampValue}</b></td>
</tr>
<tr>
<td style="text-align: right">${errorIdLabel} </td>
<td><b>${errorIdValue}</b></td>
</tr>
<tr>
<td style="text-align: right">${systemIdLabel} </td>
<td><b>${systemIdValue}</b></td>
</tr>
<tr>
<td style="text-align: right">${descriptionLabel} </td>
<td><b>${descriptionValue}</b></td>
</tr>
</table>
</body>
</html>
Y este es mi código:
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<String, String> parametersMap = new HashMap<String, String>();
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();
Este código funciona bien, pero con la altura fija es un problema.
IE Digamos que:
errorId = "ERROR-01"
systemId = "SYSTEM-01"
description = "A SHORT DESCRIPTION OF THE ISSUE"
El documento producido es:
Si en cambio 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."
El documento producido es:
Como puede ver, en el último documento tengo dos páginas. Me gustaría tener solo una página que cambie su altura de acuerdo con la altura del contenido.
¿Es posible algo así con iText?