The time has come to update my Curriculum Vitae. Going back to the office suite software and struggle with table, margins, etc. is not something I’m keen on doing. I’m a fan of anything-as-a-code approach and enthusiast of AsciiDoc markup language for quite some time already. I’ve been using it for my blog posts and invoice generation, so why not give it a try for CV as well?

Asciidoctor

Asciidoctor is the most popular processor of the AsciiDoc markup language. Written in Ruby, but has wrappers for Java and JavaScript. Rich in extensions and supported by many editors and IDEs (including Asciidoc for IDEA).

My first steps were to check the internet for something similar that already exists, but I couldn’t find anything that would suit my needs. Hence, I decided to create it from scratch.

Picking the right tools

There are plenty of options, and I think it’s a matter of personal preference up to pick the one you like the most:

  • In the JVM world, there are dedicated Gradle and Maven plugins that can do the job for you without writing a single line of code (except for the plugin configuration itself).

  • Having Java wrapper for Asciidoctor gem, you can simply code it in your favourite JVM language.

  • You can always base on Asciidoctor gems and use CLI to generate the output.

I decided to write a basic Kotlin script that will generate my CV, with Gradle as a build tool. I feel more comfortable configuring it programmatically than configuring the build tool plugin.

The code

You need two dependencies to make it work: Asciidoctor Java wrapper and the PDF extension.

implementation("org.asciidoctor:asciidoctorj:2.5.10")
implementation("org.asciidoctor:asciidoctorj-pdf:2.3.9")

The script itself is very straightforward. Most of the stuff is nicely explained in the AsciidoctorJ documentation.

fun main(args: Array<String>) {
    val attributes = Attributes.builder()
        .attribute("pdf-themesdir", "src/main/asciidoc/themes")
        .attribute("pdf-theme", "cv.yml")(2)
        .build()
    val asciidoctor = Asciidoctor.Factory.create();
    asciidoctor.convertFile(
        File("src/main/asciidoc/poznachowski.adoc"),
        Options.builder()
            .attributes(attributes)
            .backend("pdf")(1)
            .toDir(File("./cv"))
            .build()
    )
}
1 We need to tell Asciidoctor to use PDF backend.
2 Tweak the default theme a little.

PDF Theming

Asciidoctor PDF comes with a few themes out of the box. They are written in YAML and can be really easily customized. The default theme is already good-looking, but I wanted to customize few things, such as font size, headings, margins, table style and footer.

My overrides are available here. Most important part is extends: default which tells Asciidoctor to use the default theme as a base. For all other options, please refer to the Theme Keys Reference and Keys, Properties and Values sections of the documentation.

There are, however, certain limitations of the PDF theme customization:

  • You can’t define cell paddings on a per-table basis. You can only define them globally. I overcame this by using empty table columns to create the illusion of padding.

  • The second thing I found was not being able to put a page break inside a table. I think it comes from the limitation of the underlying Prawn PDF generation library. Here, to achieve the expected result, I was forced to split the table into two and put them one after another.

Result

To sum up, I’m really happy with the result. It definitely took me less time than I anticipated. I hope that this post will encourage you to give it a try as well.