Следующий фрагмент создает PDF-файл с текстом, но изображение искажено вот так
Кажется, требуемое изображение должно быть .jpg
. Когда я использовал .png
, в PDF-файле не отображалось даже искаженное изображение.
public static void createPdf() {
try {
//Image image = Image.createImage("/icon.png");
Image image = Image.createImage("/Logo.jpg");
EncodedImage encodedImage = EncodedImage.createFromImage(image, true);
byte[] imageData = encodedImage.getImageData();
StringBuilder content = new StringBuilder();
content.append("%PDF-1.4\n");
content.append("1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj\n");
content.append("2 0 obj<</Type/Pages/Kids [3 0 R]/Count 1>>endobj\n");
content.append("3 0 obj<</Type/Page/Parent 2 0 R/Resources 4 0 R/MediaBox [0 0 520 800]/Contents 6 0 R>>endobj\n");
content.append("4 0 obj<</Font<</F1 5 0 R>>/XObject<</Im0 7 0 R>>>>endobj\n");
content.append("5 0 obj<</Type/Font/Subtype/Type1/BaseFont/Helvetica>>endobj\n");
content.append("6 0 obj<</Length 219>>stream\n");
content.append("BT /F1 24 Tf 175 720 Td (Codename One)Tj ET\n");
content.append("BT /F1 20 Tf 100 700 Td (Using one codebase, build)Tj ET\n");
content.append("BT /F1 20 Tf 0 660 Td (Android apps)Tj ET\n");
content.append("BT /F1 20 Tf 0 640 Td (iOS apps)Tj ET\n");
content.append("BT /F1 20 Tf 0 600 Td (UWP apps)Tj ET\n");
content.append("512 0 0 512 4 50 cm\n");
content.append("/Im0 Do\n");
content.append("endstream\n");
content.append("endobj\n");
content.append("7 0 obj<</Type/XObject/Subtype/Image/Width 512/Height 512/ColorSpace/DeviceRGB/BitsPerComponent 8/Filter/DCTDecode/Length 132959>>stream\n");
content.append(new String(imageData));
content.append("endstream\n");
content.append("endobj\n");
content.append("\n");
content.append("xref\n");
content.append("0 8\n");
content.append("0000000000 65535 f \n");
content.append("0000000009 00000 n \n");
content.append("0000000052 00000 n \n");
content.append("0000000102 00000 n \n");
content.append("0000000197 00000 n \n");
content.append("0000000255 00000 n \n");
content.append("0000000316 00000 n \n");
content.append("0000000609 00000 n \n");
content.append("trailer\n");
content.append("<</Size 8/Root 1 0 R>>\n");
content.append("startxref\n");
content.append("133724\n");
content.append("%%EOF\n");
FileSystemStorage fss = FileSystemStorage.getInstance();
String pdfPath = fss.getAppHomePath() + "Test2.pdf";
try (Writer w = new OutputStreamWriter(fss.openOutputStream(pdfPath))) {
w.write(content.toString());
} catch (Exception e) {
Log.p("Error " + e);
}
} catch (Exception e) {
Log.p("Error " + e);
}
}
Сгенерированный PDF-файл сохраняется в папке хранилища приложения home/.cn1
Как можно извлечь из изображения необходимые необработанные данные изображения, чтобы изображение отображалось без искажений?
Обновлено: этот вопрос немного отличается от этого, поскольку мне нужно, чтобы двоичные данные изображения были сгенерированы/созданы и вставлены в PDF-файл программно, а не вручную.
РЕДАКТИРОВАТЬ
Написание содержимого PDF напрямую без добавления результатов StringBuilder
в этот Test7.pdf. То есть
private void createPdf7() {
try {
FileSystemStorage fss = FileSystemStorage.getInstance();
String pdfPath = fss.getAppHomePath() + "Test7.pdf";
try (Writer w = new OutputStreamWriter(fss.openOutputStream(pdfPath))) {
w.write("%PDF-1.4\n");
w.write("1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj\n");
w.write("2 0 obj<</Type/Pages/Kids [3 0 R]/Count 1>>endobj\n");
w.write("3 0 obj<</Type/Page/Parent 2 0 R/Resources 4 0 R/MediaBox [0 0 520 800]/Contents 6 0 R>>endobj\n");
w.write("4 0 obj<</Font<</F1 5 0 R>>/XObject<</Im0 7 0 R>>>>endobj\n");
w.write("5 0 obj<</Type/Font/Subtype/Type1/BaseFont/Helvetica>>endobj\n");
w.write("6 0 obj<</Length 219>>stream\n");
w.write("BT /F1 24 Tf 175 720 Td (Codename One)Tj ET\n");
w.write("BT /F1 20 Tf 100 700 Td (Using one codebase, build)Tj ET\n");
w.write("BT /F1 20 Tf 0 660 Td (Android apps)Tj ET\n");
w.write("BT /F1 20 Tf 0 640 Td (iOS apps)Tj ET\n");
w.write("BT /F1 20 Tf 0 600 Td (UWP apps)Tj ET\n");
w.write("512 0 0 512 4 50 cm\n");
w.write("/Im0 Do\n");
w.write("endstream\n");
w.write("endobj\n");
w.write("7 0 obj<</Type/XObject/Subtype/Image/Width 50/Height 50/ColorSpace/DeviceRGB/BitsPerComponent 8/Length 132959>>stream\n");
InputStream is = Display.getInstance().getResourceAsStream(this.getClass(), "/Logo.jpg");
//InputStream is = Display.getInstance().getResourceAsStream(this.getClass(), "/icon.jpeg");
//InputStream is = Display.getInstance().getResourceAsStream(this.getClass(), "/icon.png");
int nextChar = is.read();
if (nextChar == -1) {
//return null;
}
while (nextChar > -1) {
//Log.p("Char " + (char) nextChar);
char[] charArray = {(char) nextChar};
w.write(charArray);
nextChar = is.read();
}
w.write("\nendstream\n");
w.write("endobj\n");
w.write("\n");
w.write("xref\n");
w.write("0 8\n");
w.write("0000000000 65535 f \n");
w.write("0000000009 00000 n \n");
w.write("0000000052 00000 n \n");
w.write("0000000102 00000 n \n");
w.write("0000000197 00000 n \n");
w.write("0000000255 00000 n \n");
w.write("0000000316 00000 n \n");
w.write("0000000609 00000 n \n");
w.write("trailer\n");
w.write("<</Size 8/Root 1 0 R>>\n");
w.write("startxref\n");
w.write("133724\n");
w.write("%%EOF\n");
} catch (Exception e) {
Log.p("Error " + e);
}
} catch (Exception e) {
Log.p("Error " + e);
}
}
Чего мне еще не хватает, чтобы изображение отображалось правильно?
РЕДАКТИРОВАТЬ
Открытие этого JSjpegSample-with image.pdf с помощью текстового редактора Linux показывает следующую кодировку потока данных изображения. Обратите внимание на читаемый текст, например JFIF
Photoshop 7.0
.
В симуляторе Codename One эта кодировка близка к кодировке файла базы данных SQLite. База данных SQLite, открытая с помощью текстового редактора Linux, показывает следующее. Обратите внимание на читаемый текст, например SQLite format 3
CREATE TABLE
.
Как можно добавить .jpeg
в PDF-файл так же, как файл базы данных SQLite?
Информации о размере изображения, которое использует Эрик, мало или вообще нет. Итак, как кто-нибудь узнает, правильно ли это? Type/XObject/Subtype/Image/Width 512/Height 512
Я думаю, что @KJ прав. Я предлагаю вместо создания строки и использования добавления писать непосредственно в InputStream
. Преобразуйте начальную часть в байты UTF-8, а затем запишите байты изображения вручную. Затем добавьте остальную часть текста в виде массива байтов.
В блокноте я только что открыл эти PDF-файлы , которыми вы поделились, и сравнил их с моим последним Test7.pdf, которым я поделился выше. Кажется, данные изображения почти одинаковы. Возможно ли, что с данными изображения теперь все в порядке, а причина искажения изображения заключается в чем-то другом?
Блокнот находится в ОС Windows. Однако в текстовом редакторе Linux поток изображений, которым вы поделились, начинается с \FF\D8\FF\E0\00JFIF\00\00H\00H\00\00\FF\E1wExif\00\00MM\00*\00\00\00\00
, но то, чем я поделился, начинается с ÿØÿà JFIF H H ÿØÿà wExif MM * b j( 1
.
@Eric JFIF
— это стандартный заголовок JPEG. Судя по перечисленным вами байтам, PDF кодирует байты, а не записывает их напрямую. Я недостаточно знаком с кодировкой, но похоже, что код пишет \FF
вместо байтового значения 255
и т. д. Кажется, что он записывает допустимые символы диапазона ascii, такие как JFIF, «как есть». Это должно быть довольно просто закодировать, проверив диапазон и используя Integer.toHex()
.
@KJ Это не «ложь». Значения записываются в виде байтов, а не кодируются. Простым решением, вероятно, было бы записать каждый байт как stream.write("\"); stream.write(Integer.toHexString(byteValue & 0xff))
. Это упрощенное решение может работать и так.
@ShaiAlmog записывает байты, используя byte[] imageArr = encodedImage.getImageData(); for (int i = 0; i < imageArr.length; i++) { w.write("\\"); w.write(Integer.toHexString(imageArr[i] & 0xff)); }
результаты в этот Test8HexDecode.pdf, где изображение все еще искажено.
@KJ Я не знаю, так как понятия не имею о формате PDF. Однако похоже, что файл должен быть двоичным, и вы используете Writer вместо потока. Писатель всегда будет манипулировать данными и кодировать символы Юникода определенным образом. Я предполагаю, что вы откуда-то взяли этот код. Если у вас есть ссылка на исходный код, я мог бы помочь с эквивалентом Java.
Кодировка @KJ Windows-1252
, установленная в этом примере, пытается, но некоторые шестнадцатеричные числа читаются как ?
. Но это натолкнуло меня на мысль попробовать другие кодировки, например ISO-8859-1
, которые сработали. Спасибо, что приняли участие в обсуждении и поделились знаниями о PDF
.
И найдите ссылку (например: Как просмотреть внутреннюю структуру PDF в Adobe Acrobat?), чтобы узнать, как изображения являются частью структуры PDF.
Следующее успешно добавляет изображение в документ PDF. Требования включают в себя:
Создайте изображение jpeg
, используя
EncodedImage.createFromImage(image, true);
Это полезно, поскольку изображения png
можно конвертировать в jpeg
.
Создайте ByteArrayInputStream
из байтов изображения. Используя ByteArrayInputStream
, байты изображения напрямую записываются как символы в PDF-файл.
Установите кодировку ISO-8859-1
в OutputStreamWriter
. Эта кодировка гарантировала, что все символы в байтах изображения читаются так, как ожидалось Filter/DCTDecode
.
Кодировка Windows-1252
также пытается, но некоторые шестнадцатеричные числа читаются как ?
.
private void createPdf11() {
try {
//Image image = Image.createImage("/Logo.jpg");
Image image = Image.createImage("/android_logo.png");
//Image image = Image.createImage("/ios_logo.png");
//create a jpeg encoded image
EncodedImage encodedImage = EncodedImage.createFromImage(image, true);
byte[] imageData = encodedImage.getImageData();
//get image width, height & length
int imageWidth = encodedImage.getWidth();
int imageHeight = encodedImage.getHeight();
int imageLength = imageData.length;
//create ByteArrayInputStream from image byte array
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(imageData);
FileSystemStorage fss = FileSystemStorage.getInstance();
String pdfPath = fss.getAppHomePath() + "Test12.pdf";
try (Writer w = new OutputStreamWriter(fss.openOutputStream(pdfPath), "ISO-8859-1")) {
w.write("%PDF-1.7\n");
w.write("1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj\n");
w.write("2 0 obj<</Type/Pages/Kids [3 0 R]/Count 1>>endobj\n");
w.write("3 0 obj<</Type/Page/Parent 2 0 R/Resources 4 0 R/MediaBox [0 0 520 800]/Contents 6 0 R>>endobj\n");
w.write("4 0 obj<</Font<</F1 5 0 R>>/XObject<</Im0 7 0 R>>>>endobj\n");
w.write("5 0 obj<</Type/Font/Subtype/Type1/BaseFont/Helvetica>>endobj\n");
w.write("6 0 obj<</Length 219>>stream\n");
w.write("BT /F1 24 Tf 175 720 Td (Codename One)Tj ET\n");
w.write("BT /F1 20 Tf 100 700 Td (Using one codebase, build)Tj ET\n");
w.write("BT /F1 20 Tf 0 660 Td (Android apps)Tj ET\n");
w.write("BT /F1 20 Tf 0 640 Td (iOS apps)Tj ET\n");
w.write("BT /F1 20 Tf 0 600 Td (UWP apps)Tj ET\n");
w.write("50 0 0 50 4 540 cm\n");
w.write("/Im0 Do\n");
w.write("endstream\n");
w.write("endobj\n");
w.write("7 0 obj<</Type/XObject/Subtype/Image/Width " + imageWidth + "/Height " + imageHeight + "/ColorSpace/DeviceRGB/BitsPerComponent 8/Filter/DCTDecode/Length " + imageLength + ">>stream\n");
//read the next byte of data from ByteArrayInputStream in int range 0 - 255
int nextChar = byteArrayInputStream.read();
while (nextChar > -1) {
//convert int to char and add it to char array
char[] charArray = {(char) nextChar};
//write char array
w.write(charArray);
nextChar = byteArrayInputStream.read();
}
w.write("\nendstream\n");
w.write("endobj\n");
w.write("\n");
w.write("xref\n");
w.write("0 8\n");
w.write("0000000000 65535 f \n");
w.write("0000000009 00000 n \n");
w.write("0000000052 00000 n \n");
w.write("0000000102 00000 n \n");
w.write("0000000197 00000 n \n");
w.write("0000000255 00000 n \n");
w.write("0000000316 00000 n \n");
w.write("0000000609 00000 n \n");
w.write("trailer\n");
w.write("<</Size 8/Root 1 0 R>>\n");
w.write("startxref\n");
w.write("133724\n");
w.write("%%EOF\n");
} catch (Exception e) {
Log.p("Error " + e);
}
} catch (Exception e) {
Log.p("Error " + e);
}
}
При этом создается Test11.pdf с текстом и изображением в каталоге домашнего пути приложения home/.cn1
Чтобы добавить второе изображение, необходим другой объект, содержащий его, например 8 0 obj
. Этот номер объекта затем добавляется к 4 0 obj
как /Im1 8 0 R
. Наконец, положение второго изображения также устанавливается в 6 0 obj
как w.write("q 50 0 0 50 4 460 cm /Im1 Do Q\n");
. То есть,
private void createPdf13() {
try {
Image image = Image.createImage("/Logo.jpg");
//create a jpeg encoded image
EncodedImage encodedImage = EncodedImage.createFromImage(image, true);
byte[] imageData = encodedImage.getImageData();
int imageWidth = encodedImage.getWidth();
int imageHeight = encodedImage.getHeight();
int imageLength = imageData.length;
//create ByteArrayInputStream from image byte array
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(imageData);
Image image2 = Image.createImage("/android_logo.png");
//create a jpeg encoded image
EncodedImage encodedImage2 = EncodedImage.createFromImage(image2, true);
byte[] imageData2 = encodedImage2.getImageData();
int imageWidth2 = encodedImage2.getWidth();
int imageHeight2 = encodedImage2.getHeight();
int imageLength2 = imageData2.length;
//create ByteArrayInputStream from image byte array
ByteArrayInputStream byteArrayInputStream2 = new ByteArrayInputStream(imageData2);
FileSystemStorage fss = FileSystemStorage.getInstance();
String pdfPath = fss.getAppHomePath() + "Test13.pdf";
try (Writer w = new OutputStreamWriter(fss.openOutputStream(pdfPath), "ISO-8859-1")) {
w.write("%PDF-1.7\n");
w.write("1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj\n");
w.write("2 0 obj<</Type/Pages/Kids [3 0 R]/Count 1>>endobj\n");
w.write("3 0 obj<</Type/Page/Parent 2 0 R/Resources 4 0 R/MediaBox [0 0 520 800]/Contents 6 0 R>>endobj\n");
w.write("4 0 obj<</Font<</F1 5 0 R>>/XObject<</Im0 7 0 R/Im1 8 0 R>>>>endobj\n");
w.write("5 0 obj<</Type/Font/Subtype/Type1/BaseFont/Helvetica>>endobj\n");
w.write("6 0 obj<</Length 219>>stream\n");
w.write("q\n");
w.write("BT /F1 24 Tf 175 720 Td (Codename One)Tj ET\n");
w.write("BT /F1 20 Tf 100 700 Td (Using one codebase, build)Tj ET\n");
w.write("BT /F1 20 Tf 0 660 Td (Android apps)Tj ET\n");
w.write("BT /F1 20 Tf 0 640 Td (iOS apps)Tj ET\n");
w.write("BT /F1 20 Tf 0 600 Td (UWP apps)Tj ET\n");
w.write("q 50 0 0 50 4 540 cm /Im0 Do Q\n");
w.write("q 50 0 0 50 4 460 cm /Im1 Do Q\n");
w.write("Q\n");
w.write("endstream\n");
w.write("endobj\n");
w.write("7 0 obj<</Type/XObject/Subtype/Image/Width " + imageWidth + "/Height " + imageHeight + "/ColorSpace/DeviceRGB/BitsPerComponent 8/Filter/DCTDecode/Length " + imageLength + ">>stream\n");
//read the next byte of data from ByteArrayInputStream in int range 0 - 255
int nextChar = byteArrayInputStream.read();
while (nextChar > -1) {
//convert int to char and add it to char array
char[] charArray = {(char) nextChar};
//write char array
w.write(charArray);
nextChar = byteArrayInputStream.read();
}
w.write("\nendstream\n");
w.write("endobj\n");
w.write("8 0 obj<</Type/XObject/Subtype/Image/Width " + imageWidth2 + "/Height " + imageHeight2 + "/ColorSpace/DeviceRGB/BitsPerComponent 8/Filter/DCTDecode/Length " + imageLength2 + ">>stream\n");
//read the next byte of data from ByteArrayInputStream in int range 0 - 255
int nextChar2 = byteArrayInputStream2.read();
while (nextChar2 > -1) {
//convert int to char and add it to char array
char[] charArray2 = {(char) nextChar2};
//write char array
w.write(charArray2);
nextChar2 = byteArrayInputStream2.read();
}
w.write("\nendstream\n");
w.write("endobj\n");
w.write("\n");
w.write("xref\n");
w.write("0 9\n");
w.write("0000000000 65535 f \n");
w.write("0000000009 00000 n \n");
w.write("0000000052 00000 n \n");
w.write("0000000102 00000 n \n");
w.write("0000000197 00000 n \n");
w.write("0000000255 00000 n \n");
w.write("0000000316 00000 n \n");
w.write("0000000609 00000 n \n");
w.write("trailer\n");
w.write("<</Size 9/Root 1 0 R>>\n");
w.write("startxref\n");
w.write("133724\n");
w.write("%%EOF\n");
} catch (Exception e) {
Log.p("Error " + e);
}
} catch (Exception e) {
Log.p("Error " + e);
}
}
Что генерирует Test13.pdf
Я могу программно получить длину массива байтов изображения, равную 21223. Я также могу программно установить ее как длину потока изображений. Я также могу программно получить и установить высоту и ширину изображения.
Что такое значение 746? Что такое значение 19?
Просто чтобы подтвердить, понимаю ли я. Вы имеете в виду 746 = длина в байтах от строки w.write("%PDF-1.7\n")
до строки w.write("BT /F1 20 Tf 0 600 Td (UWP apps)Tj ET\n");
Вы имеете в виду 19 = длина байта от строки w.write("\nendstream\n")
до строки w.write("\n");
?
@KJ Мне еще предстоит рассчитать и установить реальный размер xref
. Но прямо сейчас я отредактировал ответ, установив фактическую ширину, высоту и длину изображения в сгенерированный PDF-файл. Например. Test12.pdf
Тем временем я также безуспешно пытаюсь добавить несколько изображений в один и тот же PDF-файл. Я попытался создать 2 потока изображений, но отображается только 1-е изображение. Как добавить несколько изображений?
@KJ Я следую вашим инструкциям по добавлению второго изображения, добавив 8-й объект, но отображается только первое изображение, как в этом Test13.pdf . Я также пытался добавить еще один объект перед вторым изображением, но только первое изображение отображается, как в этом Test14.pdf. Что не так в этих pdf? Можете ли вы отредактировать PDF-файл и исправить неправильные части?
Writer
класс не имеет функции для чтения длины записанных байтов. Мне придется придумать, как это прочитать.
Спасибо за помощь и совет. Оба теперь успешно генерируются с несколькими изображениями. Я отредактирую ответ, чтобы добавить эту функцию. Я также поработаю над расчетом фактической xref
длины.
@KJ Я создал этот отдельный вопрос, чтобы обсудить, как рассчитать xref
длину.
Спасибо. Я посмотрю на это Тест 20 вопросов.pdf
Этот вопрос похож на: Как вставить изображение в исходный код PDF. Если вы считаете, что это другое, отредактируйте вопрос, поясните, чем он отличается и/или как ответы на этот вопрос не помогают решить вашу проблему.