Таблицы в PDF с горизонтальными разрывами страниц
Кто-нибудь знает (предпочтительно с открытым исходным кодом) механизм верстки PDF для Java, способный отображать таблицы с горизонтальными разрывами страниц? "Горизонтальное разбиение страницы" - это, по крайней мере, название объекта в BIRT, но для пояснения: если в таблице слишком много столбцов, чтобы поместиться по доступной ширине страницы, я хочу, чтобы таблица была разделена горизонтально на несколько страниц, например, для Таблица с 10 столбцами, столбцы 1-4, которые должны быть выведены на первой странице, и столбцы 5-10 на второй странице. Это, конечно, также следует повторить на следующих страницах, если в таблице слишком много строк, чтобы поместиться по вертикали на одной странице.
Пока что было довольно сложно искать товары. Я считаю, что такая функция может называться по-другому в других продуктах, что затрудняет использование тетей Google для поиска подходящего решения.
Пока что я пробовал:
BIRT утверждает, что поддерживает это, но фактическая реализация настолько ошибочна, что не может быть использована. Я считаю, что для такой функциональности очевидно, что высота строк поддерживается одинаковой на всех страницах, что позволяет выравнивать строки при размещении страниц рядом друг с другом. BIRT, однако, рассчитывает необходимую высоту строки отдельно для каждой страницы.
Джаспер не имеет поддержки.
Я также рассмотрел Apache FOP, но не нашел подходящего синтаксиса для этого в спецификации XSL-FO.
iText, как правило, немного слишком "низкоуровневый" для этой задачи в любом случае (что затрудняет компоновку других частей предполагаемых документов PDF), но, похоже, не обеспечивает поддержки.
Так как, кажется, есть несколько десятков других механизмов отчетности или компоновки, которые могут или не могут подходить, и мне немного трудно угадать, что именно искать, я надеялся, что кто-то, возможно, уже имел аналогичные требования и может предоставить по крайней мере предложение в правильном направлении. Относительно важно, чтобы продукт можно было легко интегрировать в серверное Java-приложение, а нативная библиотека Java была бы идеальной.
Теперь, чтобы строки были выровнены по всем страницам, высота строк должна быть рассчитана следующим образом:
Row1.height = max(A1.height, B1.height, C1.height, D1.height)
Row2.height = max(A2.height, B2.height, C2.height, D2.height)
Хотя BIRT в настоящее время, кажется, делает что-то вроде:
Page1.Row1.height = max(A1.height, B1.height)
Page2.Row1.height = max(C1.height, D1.height)
Page1.Row2.height = max(A2.height, B2.height)
Page2.Row2.height = max(C2.height, D2.height)
5 ответов
Можно отобразить таблицу так, как вы хотите с iText
, Вам нужно использовать настраиваемое позиционирование таблицы и настраиваемую запись строк и столбцов.
Мне удалось адаптировать этот пример iText для записи на нескольких страницах по горизонтали и вертикали. Идея состоит в том, чтобы запомнить начальную и конечную строки, которые попадают на страницу вертикально. Я поместил весь код, чтобы вы могли легко запустить его.
public class Main {
public static final String RESULT = "results/part1/chapter04/zhang.pdf";
public static final float PAGE_HEIGHT = PageSize.A4.getHeight() - 100f;
public void createPdf(String filename)
throws IOException, DocumentException {
// step 1
Document document = new Document();
// step 2
PdfWriter writer
= PdfWriter.getInstance(document, new FileOutputStream(filename));
// step 3
document.open();
//setup of the table: first row is a really tall one
PdfPTable table = new PdfPTable(new float[] {1, 5, 5, 1});
StringBuilder sb = new StringBuilder();
for(int i = 0; i < 50; i++) {
sb.append("tall text").append(i + 1).append("\n");
}
for(int i = 0; i < 4; i++) {
table.addCell(sb.toString());
}
for (int i = 0; i < 50; i++) {
sb = new StringBuilder("some text");
table.addCell(sb.append(i + 1).append(" col1").toString());
sb = new StringBuilder("some text");
table.addCell(sb.append(i + 1).append(" col2").toString());
sb = new StringBuilder("some text");
table.addCell(sb.append(i + 1).append(" col3").toString());
sb = new StringBuilder("some text");
table.addCell(sb.append(i + 1).append(" col4").toString());
}
// set the total width of the table
table.setTotalWidth(600);
PdfContentByte canvas = writer.getDirectContent();
ArrayList<PdfPRow> rows = table.getRows();
//check every row height and split it if is taller than the page height
//can be enhanced to split if the row is 2,3, ... n times higher than the page
for (int i = 0; i < rows.size(); i++) {
PdfPRow currentRow = rows.get(i);
float rowHeight = currentRow.getMaxHeights();
if(rowHeight > PAGE_HEIGHT) {
PdfPRow newRow = currentRow.splitRow(table,i, PAGE_HEIGHT);
if(newRow != null) {
rows.add(++i, newRow);
}
}
}
List<Integer[]> chunks = new ArrayList<Integer[]>();
int startRow = 0;
int endRow = 0;
float chunkHeight = 0;
//determine how many rows gets in one page vertically
//and remember the first and last row that gets in one page
for (int i = 0; i < rows.size(); i++) {
PdfPRow currentRow = rows.get(i);
chunkHeight += currentRow.getMaxHeights();
endRow = i;
//verify against some desired height
if (chunkHeight > PAGE_HEIGHT) {
//remember start and end row
chunks.add(new Integer[]{startRow, endRow});
startRow = endRow;
chunkHeight = 0;
i--;
}
}
//last pair
chunks.add(new Integer[]{startRow, endRow + 1});
//render each pair of startRow - endRow on 2 pages horizontally, get to the next page for the next pair
for(Integer[] chunk : chunks) {
table.writeSelectedRows(0, 2, chunk[0], chunk[1], 236, 806, canvas);
document.newPage();
table.writeSelectedRows(2, -1, chunk[0], chunk[1], 36, 806, canvas);
document.newPage();
}
document.close();
}
public static void main(String[] args) throws IOException, DocumentException {
new Main().createPdf(RESULT);
}
}
Я понимаю что возможно iText
слишком низкий уровень только для отчетов, но его можно использовать рядом со стандартными инструментами отчетности для особых нужд, подобных этой.
Обновление: теперь строки выше высоты страницы сначала разделяются. Код не выполняет разбиение, если строка в 2, 3,..., n раз выше, но также может быть адаптирована для этого.
Та же идея, что и у Dev Blanked, но с помощью wkhtmltopdf ( https://code.google.com/p/wkhtmltopdf/) и некоторого javascript вы можете достичь того, что вам нужно. При запуске wkhtmltopdf для этой скрипки вы получите результат, показанный ниже (скриншот pdf-страниц). Вы можете разместить класс break-after в любом месте строки заголовка. Мы используем wkhtmltopdf на стороне сервера в веб-приложении Java EE для создания динамических отчетов, и производительность на самом деле очень хорошая.
HTML
<body>
<table id="table">
<thead>
<tr><th >Header 1</th><th class="break-after">Header 2</th><th>Header 3</th><th>Header 4</th></tr>
</thead>
<tbody>
<tr valign="top">
<td>A1<br/>text<br/>text</td>
<td>B1<br/>text</td>
<td>C1</td>
<td>D1</td>
</tr>
<tr valign="top">
<td>A2</td>
<td>B2<br/>text<br/>text<br/>text</td>
<td>C2</td>
<td>D2<br/>text</td>
</tr>
</tbody>
</table>
</body>
скрипт
$(document).ready(function() {
var thisTable = $('#table'),
otherTable= thisTable.clone(false, true),
breakAfterIndex = $('tr th', thisTable).index($('tr th.break-after', thisTable)),
wrapper = $('<div/>');
wrapper.css({'page-break-before': 'always'});
wrapper.append(otherTable);
thisTable.after(wrapper);
$('tr', thisTable).find('th:gt(' + breakAfterIndex + ')').remove();
$('tr', thisTable).find('td:gt(' + breakAfterIndex + ')').remove();
$('tr', otherTable).find('th:lt(' + (breakAfterIndex + 1) + ')').remove();
$('tr', otherTable).find('td:lt(' + (breakAfterIndex + 1) + ')').remove();
$('tr', table).each(function(index) {
var $this =$(this),
$otherTr = $($('tr', otherTable).get(index)),
maxHeight = Math.max($this.height(), $otherTr.height());
$this.height(maxHeight);
$otherTr.height(maxHeight);
});
});
Вы пробовали http://code.google.com/p/flying-saucer/. Предполагается конвертировать HTML в PDF.
Джаспер не имеет поддержки.
Согласно документации Jasper, она имеет поддержку через:
- элемент разрыва столбца (т. е. элемент разрыва с атрибутом type = column). Это может быть размещено в любом месте отчета.
- Атрибут isStartNewColumn для групп / заголовков
Если вы действительно застряли, в качестве последнего средства вы можете использовать Excel / OpenOffice Calc: вручную скопировать данные в ячейки, вручную отформатировать их по своему желанию, сохранить в формате xls. Затем используйте Apache POI из Java для динамического заполнения / замены нужных данных и печати в файл /PDF. По крайней мере, это дает очень детальный контроль над форматированием столбцов и строк / разрывов / полей и т. Д.