Convert Swagger Specs to AsciiDoc & PDF

Swagger UI is a powerful tool for visualizing and interacting with your API’s resources. While it offers a sleek default design, you might want to personalize its appearance to align with your brand. One effective way to achieve this is by customizing the fonts.

Why Customize Fonts?

Customizing fonts enhances the user experience by making the interface more visually appealing and consistent with your brand identity. Moreover, it can improve readability and accessibility for users with specific needs.

Create Swagger specifications.

Swagger specifications to AsciiDoc.

AsciiDoc is a plain text markup language designed for writing technical content. It includes rich semantic elements and offers features for modularizing and reusing content.

Adds a Maven dependency in “pom.xml”.

<!--  https://mvnrepository.com/artifact/org.openapitools/openapi-generator  -->
<dependency>
    <groupId>org.openapitools</groupId>
    <artifactId>openapi-generator</artifactId>
    <version>7.3.0</version>
</dependency>

Creates a Java class to convert Swagger specifications to AsciiDoc.

import org.openapitools.codegen.DefaultGenerator;
import org.openapitools.codegen.config.CodegenConfigurator;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

public class OpenApiToAsciidoc {

    public static void main(String[] args) {
        OpenApiToAsciidoc asciidoc = new OpenApiToAsciidoc();
        asciidoc.jsonToAsciidoc("D:/workspace/document/", "api-docs.json");
    }

    public void jsonToAsciidoc(String swaggerPath, String json) {

        System.out.println("Converting OpenAPI JSON to AsciiDoc...");

        // Input OpenAPI JSON file path
        String inputFile = swaggerPath + File.separator + json;

        // Output AsciiDoc file path
        String outputFile = swaggerPath + File.separator + "asciidoc";

        Map<String, Object> options = new HashMap<String, Object>();

        options.put("openapi", "3.0.1");
        options.put("dateLibrary", "java21");
        options.put("enumPropertyNaming", "UPPERCASE");
        options.put("hideGenerationTimestamp", true);
        options.put("version","1.0.0");
        options.put("version-label","1.0.0");
        options.put("license-id","Apache-2.0");
        options.put("developerEmail","admin@example.com");

        // Configure CodegenConfigurator
        CodegenConfigurator configurator = new CodegenConfigurator()
                .setInputSpec(inputFile)
                .setOutputDir(outputFile)
                .setGeneratorName("asciidoc")
                .setSkipOperationExample(true)
                .setAdditionalProperties(options)
                ;

        // Generate AsciiDoc file
        DefaultGenerator generator = new DefaultGenerator();
        generator.opts(configurator.toClientOptInput());
        generator.generate();


        System.out.println("Conversion completed. AsciiDoc file generated at: " + outputFile);


    }
}

Executes a Java class to create an AsciiDoc.

In the directory “D:/workspace/document/”, asciidoc A folder was created.

Swagger specifications to HTML.

Convert to HTML using a Java class with a third-party library called “asciidoctor” or create HTML from the Swagger editor website. Swagger Editor

Convert to HTML5 with a Java class.

Adds a Maven dependency in “pom.xml”.

<!--  https://mvnrepository.com/artifact/org.asciidoctor/asciidoctorj  -->
<dependency>
    <groupId>org.asciidoctor</groupId>
    <artifactId>asciidoctorj</artifactId>
    <version>2.5.11</version>
</dependency>

Creates a Java class to convert Swagger specifications to HTML.

import org.asciidoctor.Asciidoctor;
import org.asciidoctor.Options;
import org.asciidoctor.SafeMode;

import java.io.File;

public class AsciidoctorHtml5Generator {

    final static String swaggerPath = "D:/workspaces/documents/";

    public static void main(String[] args) {

        // Output HTML file path
        String outputFilePath = swaggerPath + "/asciidoc/index.html";

        // Configure Asciidoctor instance
        try (Asciidoctor asciidoctor = Asciidoctor.Factory.create()) {
            // Convert AsciiDoc to HTML5
            asciidoctor.convertFile(new File(swaggerPath + "/asciidoc/index.adoc"),
                    Options.builder().mkDirs(true)
                            .backend("html5")
                            .toFile(new File(outputFilePath))
                            .safe(SafeMode.SAFE) //allowed to generate pdf inside this directory only.
                            .build());
            System.out.println("HTML file generated successfully!");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }


    }
}

Fix this problem in 2 options.

Jul 30, 2024 3:24:09 PM org.asciidoctor.jruby.internal.JRubyAsciidoctor convertFile
SEVERE: (SecurityError) to_dir D:\workspaces\documents\asciidoc\index.html is outside of jail: D:/workspaces/swagger-converter (disallowed in safe mode)
org.jruby.exceptions.SecurityError: (SecurityError) to_dir D:\workspaces\documents\asciidoc\index.html is outside of jail: D:/workspaces/swagger-converter (disallowed in safe mode)

1. Recommend moving the asciidoc directory to the project directory.

2. Change the safe mode to UNSAFE.

asciidoctor.convertFile(new File(swaggerPath + "/asciidoc/index.adoc"),
Options.builder().mkDirs(true)
        .backend("html5")
        .toFile(new File(outputFilePath))
        .safe(SafeMode.UNSAFE)
        .build());

Executes a Java class to create an HTML5.

HTML file generated successfully!

The directory “index.html” was created.

Swagger specifications to PDF.

The developer can convert to PDF using a Java class with a third-party library called “asciidoctor” or create PDF from the Swagger To Pdf website.

Convert to PDF with a Java class.

The developer adds a Maven dependency in “pom.xml”.

<!--  https://mvnrepository.com/artifact/org.asciidoctor/asciidoctorj  -->
<dependency>
    <groupId>org.asciidoctor</groupId>
    <artifactId>asciidoctorj</artifactId>
    <version>2.5.11</version>
</dependency>
<dependency>
    <groupId>org.asciidoctor</groupId>
    <artifactId>asciidoctorj-pdf</artifactId>
    <version>2.3.13</version>
</dependency>

The developer creates a Java class to convert Swagger specifications to PDF.

import org.asciidoctor.Asciidoctor;
import org.asciidoctor.Options;
import org.asciidoctor.SafeMode;
import org.asciidoctor.jruby.internal.JRubyAsciidoctor;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;

public class SwaggerToPDF {

    final static String swaggerPath = "D:/workspaces/swagger-converter/documents/";
    final static String json = "api-docs.json";

    public static void main(String[] args) {

        //create asciidoc file
        OpenApiToAsciidoc asciidoc = new OpenApiToAsciidoc();
        asciidoc.jsonToAsciidoc(swaggerPath, json);

        // Initialize Asciidoctor instance
        Asciidoctor asciidoctor = JRubyAsciidoctor.create();

        SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");

        //output path to a pdf file.
        File pdf = new File(swaggerPath + "/pdf/user-manual-openapi_" + df.format(new Date()) + ".pdf");
        System.out.println("Start generating pdf with Asciidoctor: index.adoc to " + pdf.getAbsolutePath());

        // Convert AsciiDoc to PDF
        asciidoctor.convertFile(new File(swaggerPath + "/asciidoc/index.adoc"),
                Options.builder().mkDirs(true)
                        .backend("pdf")
                        .toFile(pdf)
                        .safe(SafeMode.SAFE) //allowed to generate pdf inside this directory only.
                        .build());

        System.out.println("PDF is created!");
    }
}

The developer executes a Java class to create a PDF.

Converting OpenAPI JSON to AsciiDoc...
[main] INFO org.openapitools.codegen.DefaultGenerator - Generating with dryRun=false
[main] INFO org.openapitools.codegen.DefaultGenerator - OpenAPI Generator: asciidoc (documentation)
[main] INFO org.openapitools.codegen.DefaultGenerator - Generator 'asciidoc' is considered stable.
[main] WARN org.openapitools.codegen.languages.AsciidocDocumentationCodegen - base part for include markup lambda not found: null as D:\workspaces\swagger-converter\null
[main] WARN org.openapitools.codegen.languages.AsciidocDocumentationCodegen - base part for include markup lambda not found: null as D:\workspaces\swagger-converter\null
[main] INFO org.openapitools.codegen.TemplateManager - writing file D:\workspaces\swagger-converter\documents\asciidoc\index.adoc
[main] INFO org.openapitools.codegen.TemplateManager - Skipped D:\workspaces\swagger-converter\documents\asciidoc\.openapi-generator-ignore (Skipped by supportingFiles options supplied by user.)
[main] INFO org.openapitools.codegen.TemplateManager - writing file D:\workspaces\swagger-converter\documents\asciidoc\.openapi-generator\VERSION
[main] INFO org.openapitools.codegen.TemplateManager - writing file D:\workspaces\swagger-converter\documents\asciidoc\.openapi-generator\FILES
################################################################################
# Thanks for using OpenAPI Generator.                                          #
# Please consider donation to help us maintain this project 🙏                 #
# https://opencollective.com/openapi_generator/donate                          #
################################################################################
Conversion completed. AsciiDoc file generated at: D:/workspaces/swagger-converter/documents/\asciidoc
Start generating pdf with Asciidoctor: index.adoc to D:\workspaces\swagger-converter\documents\pdf\user-manual-openapi_20240730.pdf
Jul 30, 2024 4:14:47 PM uri:classloader:/gems/asciidoctor-2.0.20/lib/asciidoctor/substitutors.rb sub_macros
INFO: possible invalid reference: String
PDF is created!

The developer changed the content inside the “api-docs.json” to character UTF-8, which is not supported by the asciidoctor default font.

{
   "openapi":"3.0.1",
   "info":{
      "title":"OpenAPI definition",
      "version":"v0"
   },
   "servers":[
      {
         "url":"http://localhost:8080",
         "description":"Generated server url"
      }
   ],
   "tags":[
      {
         "name":"สร้างข้อมูลผู้ใช้",
         "description":"Manage user profile data."
      }
   ],
   "paths":{
      "/user":{
         "post":{
            "tags":[
               "บริการสร้างข้อมูลผู้ใช้"
            ],
            "summary":"สร้างข้อมูลผู้ใช้งานระบบ",
            "description":"สร้างข้อมูลผู้ใช้งานระบบและบันทึกลงฐานข้อมูล",
            "operationId":"create",
            "requestBody":{
               "content":{
                  "application/json":{
                     "schema":{
                        "$ref":"#/components/schemas/UserProfileBean"
                     }
                  }
               },
               "required":true
            },
....

When creating a PDF file.

The characters in the PDF file are Unrecognized characters.

The developer must use a custom font that supports UTF-8 characters.

Swagger specifications to PDF with a custom font.

The developer downloads the font and creates a font directory. Create “custom-theme.yml”.

D:/workspaces/swagger-converter/documents
└───fonts
    ├───THSarabun Bold.ttf
    ├───THSarabun BoldItalic.ttf
    ├───THSarabun Italic.ttf
    └───THSarabun.ttf
└───themes
    ├───custom-theme.yml
extends: default
base:
  font_family: THSarabun
font:
  catalog:
    THSarabun:
      normal: THSarabun.ttf
      bold: THSarabun Bold.ttf
      italic: THSarabun Italic.ttf
      bold_italic: THSarabun BoldItalic.ttf
    Noto Serif:
      normal: GEM_FONTS_DIR/notoserif-regular-subset.ttf
      bold: GEM_FONTS_DIR/notoserif-bold-subset.ttf
      italic: GEM_FONTS_DIR/notoserif-italic-subset.ttf
      bold_italic: GEM_FONTS_DIR/notoserif-bold_italic-subset.ttf
      # M+ 1mn supports ASCII and the circled numbers used for conums
    M+ 1mn:
      normal: GEM_FONTS_DIR/mplus1mn-regular-subset.ttf
      bold: GEM_FONTS_DIR/mplus1mn-bold-subset.ttf
      italic: GEM_FONTS_DIR/mplus1mn-italic-subset.ttf
      bold_italic: GEM_FONTS_DIR/mplus1mn-bold_italic-subset.ttf
  fallback:
    - THSarabun
import org.asciidoctor.Asciidoctor;
import org.asciidoctor.Attributes;
import org.asciidoctor.Options;
import org.asciidoctor.SafeMode;
import org.asciidoctor.jruby.internal.JRubyAsciidoctor;

import java.io.File;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;

public class SwaggerToPDFWithCustomFont {
    final static String swaggerPath = "D:/workspaces/swagger-converter/documents/";
    final static String json = "api-docs.json";

    public static void main(String[] args) {

        //create asciidoc file
        OpenApiToAsciidoc asciidoc = new OpenApiToAsciidoc();
        asciidoc.jsonToAsciidoc(swaggerPath, json);

        // Initialize Asciidoctor instance
        Asciidoctor asciidoctor = JRubyAsciidoctor.create();

        // Set UTF-8 encoding
        Attributes attributes = Attributes.builder()
                .attribute("pdf-fontsdir", swaggerPath + File.separator + "fonts;GEM_FONTS_DIR")
                .attribute("pdf-theme", swaggerPath + File.separator + "themes/custom-theme.yml")
                .attribute("theme", "custom-theme")
                .attribute("Encoding", "UTF-8")
                .attribute("charset", StandardCharsets.UTF_8)
                .build();

        System.out.printf("file.encoding: %s%n", Charset.defaultCharset().displayName());
        System.out.printf("defaultCharset: %s%n", Charset.defaultCharset().name());

        SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");

        File pdf = new File(swaggerPath + "/pdf/user-manual-openapi_" + df.format(new Date()) + ".pdf");

        System.out.println("Start generating pdf with Asciidoctor: index.adoc to " + pdf.getAbsolutePath());

        // Convert AsciiDoc to PDF
        asciidoctor.convertFile(new File(swaggerPath + "/asciidoc/index.adoc"),
                Options.builder().mkDirs(true)
                        .backend("pdf")
                        .toFile(pdf)
                        .safe(SafeMode.SAFE) //allowed to generate pdf inside this directory only.
                        .attributes(attributes)
                        .build());

        System.out.println("PDF is created!");
    }
}

The developer executes a Java class to create a PDF with a custom font.

The characters in the PDF file are readable.

Additional Tips

  • Fallback Fonts: Always specify fallback fonts to ensure text remains legible if the custom font fails to load.
  • Performance Considerations: Using web fonts can impact page load times. Therefore, optimize font loading by selecting only the necessary font weights and styles.
  • Accessibility: Choose fonts that are easy to read and consider users with visual impairments.

Finally

Customizing fonts in Swagger UI is a straightforward process that can significantly enhance the look and feel of your API documentation. By following the steps outlined above, you can create a more engaging and accessible user interface.

Leave a Comment

Your email address will not be published. Required fields are marked *