easytable не поддерживает повторяющиеся верхние и нижние колонтитулы таблицы. Еще нет Я должен сказать, потому что эта функция на самом деле проста в реализации.
Тем не менее, трудно реализовать поверх easytable, потому что эта библиотека (как и многие другие) страдает от чрезмерного сокрытия данных: многие интересные переменные-члены и методы private
, поэтому расширение классов не является жизнеспособным вариантом.
Но вы можете обрабатывать строки заголовков как отдельную таблицу, которую вы рисуете снова и снова! Недостатком является двойственность настроек.
В случае тестового кода TwoPagesTableTest
, на который вы ссылались, его можно изменить следующим образом:
final Table.TableBuilder tableHeaderBuilder = Table.builder()
.addColumnOfWidth(200)
.addColumnOfWidth(200);
CellText dummyHeaderCell = CellText.builder()
.text("Header dummy")
.backgroundColor(Color.BLUE)
.textColor(Color.WHITE)
.borderWidth(1F)
.build();
tableHeaderBuilder.addRow(
Row.builder()
.add(dummyHeaderCell)
.add(dummyHeaderCell)
.build());
Table tableHeader = tableHeaderBuilder.build();
final Table.TableBuilder tableBuilder = Table.builder()
.addColumnOfWidth(200)
.addColumnOfWidth(200);
CellText dummyCell = CellText.builder()
.text("dummy")
.borderWidth(1F)
.build();
for (int i = 0; i < 50; i++) {
tableBuilder.addRow(
Row.builder()
.add(dummyCell)
.add(dummyCell)
.build());
}
TableDrawer drawer = TableDrawer.builder()
.table(tableBuilder.build())
.startX(50)
.endY(50F) // note: if not set, table is drawn over the end of the page
.build();
final PDDocument document = new PDDocument();
float startY = 100F;
do {
TableDrawer headerDrawer = TableDrawer.builder()
.table(tableHeader)
.startX(50)
.build();
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);
try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
headerDrawer.startY(startY);
headerDrawer.contentStream(contentStream).draw();
drawer.startY(startY - tableHeader.getHeight());
drawer.contentStream(contentStream).draw();
}
startY = page.getMediaBox().getHeight() - 50;
} while (!drawer.isFinished());
document.save("twoPageTable-repeatingHeader.pdf");
document.close();
( RepeatingTableHeaders test createTwoPageTableRepeatingHeader
) [1110 ]
Как видите, код сначала создает отдельный Table tableHeader
, содержащий только строку заголовка. Затем эта таблица добавляется первой на каждой странице, а затем добавляется часть таблицы строк тела.
Результат: заголовки таблиц на каждой странице ...
Слово предупреждения: это подтверждение концепции, я проверял только код генерации таблицы из TwoPagesTableTest
. Для производственного кода вы должны применить дальнейшие тесты.
Это объявляется как [MethodImpl(MethodImplOptions.InternalCall)]
. Это означает, что это реализовано в самом CLR, как собственная процедура, не блок.NET.
Можно просмотреть подобный исходный код CLR путем рассмотрения Microsoft SSCLI (иначе Ротор).
В SSCLI 2.0 это реализовано как (в sscli20/clr/src/vm/comobject.cpp
):
FCIMPL2(FC_BOOL_RET, ObjectNative::Equals, Object *pThisRef, Object *pCompareRef)
{
CONTRACTL
{
THROWS;
DISABLED(GC_NOTRIGGER);
INJECT_FAULT(FCThrow(kOutOfMemoryException););
MODE_COOPERATIVE;
SO_TOLERANT;
}
CONTRACTL_END;
if (pThisRef == pCompareRef)
FC_RETURN_BOOL(TRUE);
// Since we are in FCALL, we must handle NULL specially.
if (pThisRef == NULL || pCompareRef == NULL)
FC_RETURN_BOOL(FALSE);
MethodTable *pThisMT = pThisRef->GetMethodTable();
// If it's not a value class, don't compare by value
if (!pThisMT->IsValueClass())
FC_RETURN_BOOL(FALSE);
// Make sure they are the same type.
if (pThisMT != pCompareRef->GetMethodTable())
FC_RETURN_BOOL(FALSE);
// Compare the contents (size - vtable - sink block index).
BOOL ret = memcmp(
(void *) (pThisRef+1),
(void *) (pCompareRef+1),
pThisRef->GetMethodTable()->GetBaseSize() - sizeof(Object) - sizeof(int)) == 0;
FC_GC_POLL_RET();
FC_RETURN_BOOL(ret);
}
FCIMPLEND