natansalda
natansalda

Reputation: 65

How to save output from XML to PDF

I am using JAXB to unmarshall XML. Then I want to take some infos and write it to PDF format using iText. For some reason PDF is created but I can't open the file. I am also using ZFile as this should work on mainframes too, but this shouldn't be a problem here.

Probably I am doing something wrong when writing to PDF file. Here is my code:

package music;

import java.io.*;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import com.ibm.jzos.ZFile;

import java.io.FileOutputStream;

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfWriter;
import music.Music.Artist;
import music.Music.Artist.Album;
import music.Music.Artist.Album.Description;
import music.Music.Artist.Album.Song;

public class MusicXml {

    public static void main(String[] args) throws JAXBException, IOException {

        ZFile inputZ = null, outputZ = null;

        File inputW = null;
        PdfWriter outputW = null;

        PdfContentByte cb = null;
        Document pdf = new Document(PageSize.A4);
        Paragraph paragraf = new Paragraph();

        // Font
        Font fnt12n;

        JAXBContext jaxb = null;
        Unmarshaller unmarsh = null;

        String line = null, sep = " ";
        Music music;

        Date date = new Date();
        Date startDate = new Timestamp(date.getTime());
        System.out.println("Start: " + startDate);

        jaxb = JAXBContext.newInstance(ObjectFactory.class);
        unmarsh = jaxb.createUnmarshaller();

        String os = System.getProperty("os.name");
        System.out.println("System: " + os);
        boolean isWin = os.toLowerCase().contains("wind");

        if (!isWin) {
            // z/OS:
            inputZ = new ZFile(args[0], "rt"); // "rt" - readtext
            InputStream inpStream = inputZ.getInputStream();
            InputStreamReader streamRdr = new InputStreamReader(inpStream, "CP870");
            try {
                outputW = PdfWriter.getInstance(pdf, (new ZFile(args[1], "wb")).getOutputStream());
            } catch (DocumentException e) {
                e.printStackTrace();
            }

            music = (Music) unmarsh.unmarshal(streamRdr);

        } else {
            // Windows:
            inputW = new File(args[0]);
            music = (Music) unmarsh.unmarshal(inputW);
            try {
                outputW = PdfWriter.getInstance(pdf, new FileOutputStream(args[1]));
            } catch (DocumentException e) {
                e.printStackTrace();
            }
        }

        List<Artist> listaArtystow = music.getArtist();
        for (Artist artysta : listaArtystow) {
            List<Album> listaAlbumow = artysta.getAlbum();
            for (Album album : listaAlbumow) {
                Description opis = album.getDescription();
                List<Song> listaPiosenek = album.getSong();
                for (Song piosenka : listaPiosenek) {
                    String artistName = artysta.getName();
                    String albumName = album.getTitle();
                    int numberOfSongs = listaPiosenek.size();
                    String albumDescription = album.getDescription().getValue();
                    String songTitle = piosenka.getTitle();
                    String songDuration = piosenka.getLength();

                    line = songTitle + sep + songDuration;

                    FontFactory.register(args[2], "jakiesFonty");
                    Font font = FontFactory.getFont("jakiesFonty", BaseFont.CP1250, BaseFont.EMBEDDED);
                    BaseFont bf = font.getBaseFont();
                    fnt12n = new Font(bf, 12f, Font.NORMAL, BaseColor.BLACK);

                    // PDF
                    outputW.setPdfVersion(PdfWriter.VERSION_1_7);

                    pdf.addTitle("Musical collection");
                    pdf.addAuthor("Natalia Nazaruk");
                    pdf.addSubject("Cwiczenie tworzenia PDF z XML");
                    pdf.addKeywords("Metadata, Java, iText, PDF");
                    pdf.addCreator("Program: MusicXML");
                    pdf.setMargins(60, 60, 50, 40);

                    pdf.open();
                    pdf.newPage();
                    try {


                        paragraf.setAlignment(Element.ALIGN_JUSTIFIED);

                        paragraf.setSpacingAfter(16f);

                        paragraf.setLeading(14f);

                        paragraf.setFirstLineIndent(30f);

                        paragraf.setFont(fnt12n);

                        pdf.add(new Paragraph(line, fnt12n));


                    } catch (DocumentException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        date = new Date();
        Date stopDate = new Timestamp(date.getTime());
        System.out.println("Stop:  " + stopDate);
        long diffInMs = stopDate.getTime() - startDate.getTime();
        float diffInSec = diffInMs / 1000.00f;
        System.out.format("Czas przetwarzenia pliku XML: %.2f s.", diffInSec);
        System.exit(0);

        if (isWin) {
            outputW.close();
        } else
            outputZ.close();
    }
}

Upvotes: 0

Views: 3074

Answers (1)

Bruno Lowagie
Bruno Lowagie

Reputation: 77606

Apart from the fact that you chose to use an old version of iText, there are a couple of other things wrong with your code. Which documentation did you read? I don't think you've already discovered the official iText web site, otherwise you would have used iText 7 instead of iText 5, and you would have known that no valid document is created if you never close the Document object.

The short answer is that you forgot:

pdf.close();

I see that you close the output stream:

if (isWin) {
        outputW.close();
    } else
        outputZ.close();
}

That doesn't really make sense, because at that point, the PDF hasn't been finalized (for instance: no cross-reference table was created). When you close the document, the underlying output stream is closed implicitly (unless you tell iText explicitly not to do this).

There's also something awkward about the loops you are creating:

List<Artist> listaArtystow = music.getArtist();
for (Artist artysta : listaArtystow) {
    ...
    for (Album album : listaAlbumow) {
        ...
        for (Song piosenka : listaPiosenek) {
            ...
            FontFactory.register(args[2], "jakiesFonty");
            Font font = FontFactory.getFont("jakiesFonty", BaseFont.CP1250, BaseFont.EMBEDDED);
            BaseFont bf = font.getBaseFont();
            fnt12n = new Font(bf, 12f, Font.NORMAL, BaseColor.BLACK);
            // PDF
            outputW.setPdfVersion(PdfWriter.VERSION_1_7);
            pdf.addTitle("Musical collection");
            pdf.addAuthor("Natalia Nazaruk");
            pdf.addSubject("Cwiczenie tworzenia PDF z XML");
            pdf.addKeywords("Metadata, Java, iText, PDF");
            pdf.addCreator("Program: MusicXML");
            pdf.setMargins(60, 60, 50, 40);
            pdf.open();
            pdf.newPage();
            ...
        }
    }
}
output.close();

You create the same font over and over again. One PDF can only have 1 version (in your case PDF-1.7) and 1 set of metadata, yet you define that version and metadata over and over again. Finally, you open the document many times whereas you only need to open it once.

This makes more sense:

FontFactory.register(args[2], "jakiesFonty");
Font font = FontFactory.getFont("jakiesFonty", BaseFont.CP1250, BaseFont.EMBEDDED);
BaseFont bf = font.getBaseFont();
fnt12n = new Font(bf, 12f, Font.NORMAL, BaseColor.BLACK);
// PDF
outputW.setPdfVersion(PdfWriter.VERSION_1_7);
pdf.addTitle("Musical collection");
pdf.addAuthor("Natalia Nazaruk");
pdf.addSubject("Cwiczenie tworzenia PDF z XML");
pdf.addKeywords("Metadata, Java, iText, PDF");
pdf.addCreator("Program: MusicXML");
pdf.setMargins(60, 60, 50, 40);
pdf.open();
List<Artist> listaArtystow = music.getArtist();
for (Artist artysta : listaArtystow) {
    ...
    for (Album album : listaAlbumow) {
        ...
        for (Song piosenka : listaPiosenek) {
            ...
            pdf.newPage();
            ...
        }
    }
}
pdf.close();

As you can see, you open() the Document instance pdf before the loop, to write the PDF headers, and you close() the Document after the loop to write some objects (e.g. fonts), the cross-reference table, and the PDF trailer. As you don't have pdf.close() in your code, all that necessary information is missing from your PDF.

Since you are new at iText, I would highly recommend you not to use versions older than iText 7. You may have discovered that the latest iText 5 release is iText 5.5.13, but that's a maintenance release. In maintenance releases, we only provide bug fixes for our paying customers; we don't add new functionality. For instance: the new PDF specification ISO 32000-2 (aka PDF 2.0) is only available from iText 7.1 on. We won't support PDF 2.0 in older versions.

If you go to the official web site, you'll notice that iText 7.1.1 is the most recent version (iText 7 download page). Where did you find iText, and how come you selected an old version? (This isn't a rhetorical question: we'd like to know to find out how we can improve our web site. We also want to know why so many people post such bad code on Stack Overflow; it's as if they can't find the tutorials. That's sad, because we're investing plenty of time and money in those tutorials. (But if no one is reading them, what's the point???)

You can find more info about iText 7 in the Jump-Start tutorial and the Building Blocks tutorial.

As for converting XML to PDF, why don't you convert to HTML first, and then use the pdfHTML add-on? There's an example on how to do that in chapter 4 of the HTML to PDF tutorial as well as in the ZUGFeRD tutorial.

Upvotes: 2

Related Questions