Согласно комментарию, вы улучшили свой код и
получили корректно обновленный дд (т.е. поток контента, который я печатаю) с замененным текстом. Я не знаю, почему я получаю пустой pdf
. Таким образом, я предполагаю, что ваши (надеюсь репрезентативные) тестовые PDF-файлы имеют все свои представляющие интерес шрифты, закодированные в кодировках ANSI и текстовых аргументахинструкции по рисованию текста содержат целые слова или даже фразы, которые могут быть должным образом обработаны, потому что в противном случае замена текста была бы невозможна.
Таким образом, здесь приведен пример того, как можно заменить текстовые фрагменты аналогично длинными при таких благоприятных обстоятельствах. без нарушения синтаксиса потока контента. В этом примере я просто использую Map
, содержащий замещающие строки. Вы можете сделать там перевод.
Сначала фрейм загружает источник, создает штамп, перебирает страницы и вызывает помощника для создания замены потока контента:
Map<String, String> replacements = new HashMap<>();
replacements.put("Förfallodatum", "Ablaufdatum");
try ( InputStream resource = SOURCE_INPUTSTREAM;
OutputStream result = new FileOutputStream(RESULT_FILE) ) {
PdfReader pdfReader = new PdfReader(resource);
PdfStamper pdfStamper = new PdfStamper(pdfReader, result);
for (int pageNum = 1; pageNum <= pdfReader.getNumberOfPages(); pageNum++) {
PdfDictionary page = pdfReader.getPageN(pageNum);
byte[] pageContentInput = ContentByteUtils.getContentBytesForPage(pdfReader, pageNum);
page.remove(PdfName.CONTENTS);
replaceInStringArguments(pageContentInput, pdfStamper.getUnderContent(pageNum), replacements);
}
pdfStamper.close();
}
( EditPageContentSimple test testReplaceInStringArgumentsForklaringAvFakturan
)
Метод replaceInStringArguments
теперь анализирует инструкции в данном потоке содержимого, изолирует строковые аргументы и вызывает другого помощника для каждой строкиаргумент, выполняющий замену.
void replaceInStringArguments(byte[] contentBytesBefore, PdfContentByte canvas, Map<String, String> replacements) throws IOException {
PRTokeniser tokeniser = new PRTokeniser(new RandomAccessFileOrArray(new RandomAccessSourceFactory().createSource(contentBytesBefore)));
PdfContentParser ps = new PdfContentParser(tokeniser);
ArrayList<PdfObject> operands = new ArrayList<PdfObject>();
while (ps.parse(operands).size() > 0){
for (int i = 0; i < operands.size(); i++) {
PdfObject pdfObject = operands.get(i);
if (pdfObject instanceof PdfString) {
operands.set(i, replaceInString((PdfString)pdfObject, replacements));
} else if (pdfObject instanceof PdfArray) {
PdfArray pdfArray = (PdfArray) pdfObject;
for (int j = 0; j < pdfArray.size(); j++) {
PdfObject arrayObject = pdfArray.getPdfObject(j);
if (arrayObject instanceof PdfString) {
pdfArray.set(j, replaceInString((PdfString)arrayObject, replacements));
}
}
}
}
for (PdfObject object : operands)
{
object.toPdf(canvas.getPdfWriter(), canvas.getInternalBuffer());
canvas.getInternalBuffer().append((byte) ' ');
}
canvas.getInternalBuffer().append((byte) '\n');
}
}
( EditPageContentSimple вспомогательный метод)
Метод replaceInString
, в свою очередь, извлекает один строковый операнд (PdfString
экземпляр), манипулирует им и возвращает версию строки с манипулированием:
PdfString replaceInString(PdfString string, Map<String, String> replacements) {
String value = PdfEncodings.convertToString(string.getBytes(), PdfObject.TEXT_PDFDOCENCODING);
for (Map.Entry<String, String> entry : replacements.entrySet()) {
value = value.replace(entry.getKey(), entry.getValue());
}
return new PdfString(PdfEncodings.convertToBytes(value, PdfObject.TEXT_PDFDOCENCODING));
}
( EditPageContentSimple вспомогательный метод)
Вместоиз этого цикла for
вы бы назвали свою процедуру перевода и перевели value
.
Как уже упоминалось ранее, этот код работает только при определенных благоприятных обстоятельствах. Не ожидайте, что он будет работать для произвольных документов из дикой природы, в частности, не для документов с глифами, отличными от западноевропейских.