# Create Music Bingo Cards with iText

> This weekend we had a party and wanted to organize a music bingo. A small Java project seemed to be the best solution!

- URL: https://foojay.io/today/create-music-bingo-cards-with-itext/
- Published: 2022-06-09
- Authors: Frank Delporte
- Categories: Library
- Originally published at: https://webtechie.be/post/2022-05-30-pdf-bingo-card-with-itext/


> Let's use Java to automate a boring task and have a party!
> Warning, this can lead to noise nuisance from singing and dancing...

It has been a while since I last had to create documents in a program, and iText has been "on my radar" to try out for a while now.

This weekend we had a party and wanted to organize a music bingo.

For this, we needed a set of randomly selected songs to be printed out per person.

A small Java project seemed to be the best solution, otherwise, this would have been a boring, manual, and repetitive task.

Isn't that the goal of most of our developments? **"Automate the boring stuff!"**

### About iText

iText is a library for creating and manipulating PDF files in Java and .NET, originally written by Bruno Lowagie. The history of the project and [the company that grew out of it](https://itextpdf.com/en) is described in the book ["Entreprenerd"](https://entreprenerd.lowagie.com/). This book explains what you can expect when starting a company to generate the resources that are necessary to further develop and maintain your technology.

iText is available under open source (AGPL) as well as a commercial license. As Bruno is Belgian and the company still has a solid base in Belgium, I have to admit there is also some patriotism involved here.

### Goal of the project

We asked our guests to give us their two favorite songs. We added some extra "party songs" and ended up with a list of over 70 songs in a playlist on Spotify. Each player gets a print-out with 16 songs, of course, this value can be configured in the code.

Each print-out has the name of the player and contains the songs selected by the player. The card is filled with other random songs, so each player has a different card.

{{< gallery >}}
vik-411x510.png |  | PDF Bingo Card
frank-414x510.png |  | PDF Bingo card
{{< /gallery >}}

First goal of the game was to have a full horizontal line, and after we had three winners of those, we switched to having a full Bingo card. Some of the songs were only played for 10-15 seconds, others needed more time as some dancing and singing got involved. For that last part, I want to apologize to our neighbors...

### Exporting from Spotify

A playlist can't be exported from Spotify itself. There is a "Spotify Web API" that can be used for this (see [the Spotify Developer documentation](https://developer.spotify.com/documentation/web-api/libraries/)). I've taken a shortcut here and used [Exportify](https://github.com/watsonbox/exportify) which actually is built on top of that API. It shows you all your playlists and those can be exported with one click of a button to CSV.
![Exportify](exportify-1024x312.png)

The exported file contains a lot of info, but we only need three columns for the Bingo cards, so open the file in a spreadsheet editor, remove the unneeded columns and add one with the name of the person that has selected the song, so you end up with:

* Song title
* Artist
* Name of the person that chose the song (can be empty)

For example:

```
The Greatest Show,Hugh Jackman - The Greatest Showman Ensemble,Vik
Rode rozen in de sneeuw,Marva,Omer
Macarena,Los Del Rio,
```

### Source code

The final project is a very simple Java Maven program and the sources are available on [Github](https://github.com/FDelporte/SpotifyBingoCardGenerator).

#### Data model

As this project is based on Java 17, we can use Records for the imported songs

```
public record ImportedSong (String title, String artist, String selectedBy) {
}
```

and the generated Bingo card per person with a list of songs

```
import java.util.List; 
public record BingoCard(String forName, List<ImportedSong> songs) {
}
```

#### Reading the CSV file

The list of songs is read line by line from the CSV file with a FileReader

```
public static List<ImportedSong> loadFromFile(String fileName) {
    ...
    try {
        File f = new File(resource.getFile());
        try (BufferedReader b = new BufferedReader(new FileReader(f, StandardCharsets.UTF_8))) {
            String line;
            while ((line = b.readLine()) != null) {
                var song = processLine(line);
                if (song != null) {
                    list.add(song);
                }
            }
        }
    } catch (Exception ex) {
        logger.error("Error while importing song list: {}", ex.getMessage());
    }
    return list;
}

private static ImportedSong processLine(String data) {
    if (data == null || data.isEmpty()) {
        logger.warn("Data is empty");
        return null;
    }
    String[] csvLine = data.split(",");
    return new ImportedSong(csvLine[0], csvLine[1], csvLine.length >= 3 ? csvLine[2] : "");
}
```

#### Generate a random card for every user

The cards are generated by

* Getting a list of names of the players (distinct values from column 3 of the CSV)
* For each person generate a list of 16 songs
  * First, the ones selected by the person
  * Add a random song that is not part of the list yet, until 16 is reached
  * Randomize the order of the list with `Collections.shuffle(songsForPerson);`

#### **C**reating the PDF

As the last step, for each user, a PDF page is generated. By using iText and the example code I found in \<a href="https://www.vogella.com/tutorials/JavaPDF/article.html"\>this post by Lars Vogel\</a\>, this was a very quick and easy step. The dependency is added to the pom.xml file with

```
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>${itext.version}</version>
</dependency>
```

And the actual code is very easy to read and understand:

```
public static void createPdfWithBingoCards(List<BingoCard> cards) {
    try {
        var desktop = System.getProperty("user.home") + "/Desktop";
        var pdfFile = Paths.get(desktop, "bingo_" + System.currentTimeMillis() + ".pdf").toFile();
        Document document = new Document();
        PdfWriter.getInstance(document, new FileOutputStream(pdfFile));
        document.open();
        for (BingoCard card : cards) {
            addBingoCard(document, card);
        }
        document.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

private static void addBingoCard(Document document, BingoCard bingoCard) throws DocumentException {
    // Start a new page
    document.newPage();

    Paragraph paragraph = new Paragraph();
    paragraph.setAlignment(Element.ALIGN_CENTER);

    // We add one empty line
    addEmptyLine(paragraph, 1);

    // Name of the person for this card
    var personName =new Paragraph(bingoCard.forName(), FONT_TITLE);
    personName.setAlignment(Element.ALIGN_CENTER);
    paragraph.add(personName);

    // We add two empty lines
    addEmptyLine(paragraph, 2);

    // Add a table with the songs
    PdfPTable table = new PdfPTable(NUMBER_OF_COLUMNS);

    for (ImportedSong song : bingoCard.songs()) {
        PdfPCell tableCell = new PdfPCell(new Phrase(song.title()
                + System.lineSeparator()
                + System.lineSeparator()
                + song.artist()
                + System.lineSeparator()
                + System.lineSeparator()
                + (song.selectedBy().equals(bingoCard.forName()) ? "" : song.selectedBy()), FONT_SMALL));
        tableCell.setHorizontalAlignment(Element.ALIGN_CENTER);
        tableCell.setVerticalAlignment(Element.ALIGN_TOP);
        tableCell.setMinimumHeight(120);
        if (song.selectedBy().equals(bingoCard.forName())) {
            tableCell.setBackgroundColor(BaseColor.LIGHT_GRAY);
        }
        table.addCell(tableCell);
    }

    paragraph.add(table);

    // Add paragraph to document
    document.add(paragraph);
}

private static void addEmptyLine(Paragraph paragraph, int number) {
    for (int i = 0; i < number; i++) {
        paragraph.add(new Paragraph(" "));
    }
}
```

### Conclusion

Is this production-ready code? No, of course not, there are no unit tests 😉

But it proves iText is very easy to understand and the open-source version allows you to quickly create a PDF with a custom layout.

With the `PdfPTable` method, you can create a very basic grid layout with any text information you want to include in your document.

