Я тестировал несколько вещей с iText7, и у меня есть сценарий, в котором мне нужно иметь параграф DocumentRenderer вверху, а затем запустить ColumnDocumentRender с двумя столбцами прямо под ним на той же странице. Проблема, с которой я сталкиваюсь, заключается в том, что когда я меняю содержимое на той же странице, оно перекрывает содержимое из DocumentRenderer с содержимым из ColumnDocumentRenderer. Я считаю, что это потому, что один рендер не знает о другом рендере и содержимое начинается с верхней части страницы. Я следил за этот учебник, но он показывает только, как добавить контент на следующую страницу. Это говорит
we'll have to instruct iText not to flush the content to the OutputStream
Но может ли кто-нибудь показать мне, как именно этого добиться?
public void createPdf(String dest) throws IOException {
PdfDocument pdf = new PdfDocument(new PdfWriter(dest));
Document document = new Document(pdf);
Paragraph p = new Paragraph()
.add("Be prepared to read a story about a London lawyer "
+ "named Gabriel John Utterson who investigates strange "
+ "occurrences between his old friend, Dr. Henry Jekyll, "
+ "and the evil Edward Hyde.");
document.add(p);
document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
... // Define column areas
document.setRenderer(new ColumnDocumentRenderer(document, columns));
document.add(new AreaBreak(AreaBreakType.LAST_PAGE));
... // Add novel in two columns
document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
document.setRenderer(new DocumentRenderer(document));
document.add(new AreaBreak(AreaBreakType.LAST_PAGE));
p = new Paragraph()
.add("This was the story about the London lawyer "
+ "named Gabriel John Utterson who investigates strange "
+ "occurrences between his old friend, Dr. Henry Jekyll, "
+ "and the evil Edward Hyde. THE END!");
document.add(p);
document.close();
}
Мне нужно что-то вроде этого:
Whenever you create a new DocumentRenderer, iText starts returns to the top of the document –that is: from the first page. This allows you to use different renderers on the same document next to each other on the same page. If that is needed, we'll have to instruct iText not to flush the content to the OutputStream; otherwise we won't have access to previous pages. In this case, we don't need to change anything on previous pages. We just want to switch to another renderer on the next page. Introducing a page break that goes to the last page will avoid that new content overwrites old content.
Я попытался воспроизвести вашу проблему, но либо я неправильно понял вопрос, либо не могу воспроизвести проблему. Прокомментируйте, пожалуйста, мой ответ.
@BrunoLowagie какие-нибудь решения, которые я могу изучить? MultiColumnText помогал в старых версиях iText. i.stack.imgur.com/s53A0.png




Я взял этот код: C02E08_JekyllHydeV4
И я обновил его в соответствии с тем, что вы указали в своем вопросе:
//Initialize PDF document
PdfDocument pdf = new PdfDocument(new PdfWriter(dest));
// Initialize document
Document document = new Document(pdf);
Paragraph p = new Paragraph()
.add("Be prepared to read a story about a London lawyer "
+ "named Gabriel John Utterson who investigates strange "
+ "occurrences between his old friend, Dr. Henry Jekyll, "
+ "and the evil Edward Hyde.");
document.add(p);
document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
//Set column parameters
...
//Define column areas
...
document.setRenderer(new ColumnDocumentRenderer(document, columns));
document.add(new AreaBreak(AreaBreakType.LAST_PAGE));
// Add the full Jekyl and Hyde text
document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
document.setRenderer(new DocumentRenderer(document));
document.add(new AreaBreak(AreaBreakType.LAST_PAGE));
p = new Paragraph()
.add("This was the story about the London lawyer "
+ "named Gabriel John Utterson who investigates strange "
+ "occurrences between his old friend, Dr. Henry Jekyll, "
+ "and the evil Edward Hyde. THE END!");
document.add(p);
//Close document
document.close();
Результат выглядит так:
Я думаю, это то поведение, которое вы ищете. Если нет, объясните, что пошло не так.
Обновлено:
После уточнения вопроса становится ясно, что приведенный выше ответ не решает проблему. Это решение актуальной проблемы:
Нам нужно создать собственный ParagraphRenderer для определения Y-позиции:
class MyParagraphRenderer расширяет ParagraphRenderer {
float y;
public MyParagraphRenderer(Paragraph modelElement) {
super(modelElement);
}
@Override
public void drawBorder(DrawContext drawContext) {
super.drawBorder(drawContext);
y = getOccupiedAreaBBox().getBottom();
}
public float getY() {
return y;
}
}
Когда мы добавляем первый абзац, нам нужно использовать этот пользовательский ParagraphRenderer:
Paragraph p = new Paragraph()
.add("Be prepared to read a story about a London lawyer "
+ "named Gabriel John Utterson who investigates strange "
+ "occurrences between his old friend, Dr. Henry Jekyll, "
+ "and the evil Edward Hyde.");
MyParagraphRenderer renderer = new MyParagraphRenderer(p);
p.setNextRenderer(renderer);
document.add(p);
Теперь мы можем получить нужную позицию Y следующим образом: renderer.getY(); мы используем эту позицию Y для определения первого набора столбцов:
float offSet = 36;
float gutter = 23;
float columnWidth = (PageSize.A4.getWidth() - offSet * 2) / 2 - gutter;
float columnHeight1 = renderer.getY() - offSet * 2;
Rectangle[] columns1 = {
new Rectangle(offSet, offSet, columnWidth, columnHeight1),
new Rectangle(offSet + columnWidth + gutter, offSet, columnWidth, columnHeight1)};
Мы могли бы использовать этот набор столбцов для создания ColumnDocumentRenderer, но если для рендеринга всего содержимого требуется более одной страницы, то смещение столбцов на второй странице будет неправильным, поэтому мы также создаем собственный ColumnDocumentRenderer:
class MyColumnDocumentRenderer extends ColumnDocumentRenderer {
Rectangle[] columns2;
public MyColumnDocumentRenderer(Document document, Rectangle[] columns1, Rectangle[] columns2) {
super(document, columns1);
this.columns2 = columns2;
}
@Override
protected PageSize addNewPage(PageSize customPageSize) {
PageSize size = super.addNewPage(customPageSize);
columns = columns2;
return size;
}
}
Этот ColumnDocumentRenderer принимает два набора столбцов, один набор будет использоваться на первой странице, второй набор будет использоваться на всех последующих страницах. Вот как мы определяем и применяем собственный ColumnDocumentRenderer:
float columnHeight2 = PageSize.A4.getHeight() - offSet * 2;
Rectangle[] columns2 = {
new Rectangle(offSet, offSet, columnWidth, columnHeight2),
new Rectangle(offSet + columnWidth + gutter, offSet, columnWidth, columnHeight2)};
document.setRenderer(new MyColumnDocumentRenderer(document, columns1, columns2));
Теперь результат выглядит так:
В зависимости от необходимого расстояния между первым абзацем на всей странице и последующим содержимым в столбцах вы можете настроить значение renderer.getY() - offSet * 2.
Спасибо, Бруно, за вашу помощь! :)
Просто интересно, является ли сейчас ограничение самой библиотеки на достижение это? Или это выполнимо?
Это выполнимо, но сейчас у меня нет времени отвечать. Часть решения можно найти здесь: developers.itextpdf.com/content/…
хм, так что вы говорите, как только я добавляю абзац, получаю координаты x, y указателя курсора, а затем добавляю столбец, начинающийся с этой координаты?
@ comwiz756 Я обновил свой ответ. Самый простой способ добиться этого - создать собственные средства визуализации. Если у вас есть только одна страница содержимого, вам может не понадобиться настраиваемое средство визуализации столбцов, но я все равно сделал его для полноты картины.
это очень полезно. Спасибо. Думаю, было бы неплохо добавить это решение и в developers.itextpdf.com/content/itext-7-building-blocks/…. Очень помогло бы таким людям, как я. Еще раз спасибо!
Одно из решений, о котором я думаю сейчас, - это использовать ColumnDocumentRenderer повсюду и установить номер столбца как 1 вверху, а затем использовать номер столбца как 2 внизу. Я не знаю, поможет ли это, но просто думаю, возможно ли это.