Contents

The Printing stage

The target destination of our article is an HTML file. We want to create those by parsing the codebase and executing steps required by the commands.

But how to render the actual HTML? Without a proper tool, that can be a very challenging task. For our purposes, we will use a library called FreeMarker[1].

Apache’s FreeMarker is a template engine – a Java library to generate text output (HTML Web pages, e-mails, configuration files, source code, etc.) based on the templates and changing data. Templates are written in a FreeMarker Template Language (FTL), which is a simple specialized language. And, usually, a general-purpose programming language (like Java) is used to prepare data. Let us see how is that implemented by looking at the source code.

Everything starts with an article. The article contains header, styles, and body declaration.

com.blaster.business.InteractorPrint::printArticle

Parameters of this function are: the source root, the output file and a list of nodes to be printed

fun printArticle(output: File, nodes: List<Node>) {

After receiving a list of nodes, we wrap them into an article template

    val article = printingManager.renderTemplate(
        "template_article.ftlh", hashMapOf("article" to printParagraphs(nodes))
    )

The result is sent to printing manager to be put into a file

    printingManager.printArticle(output, article)
}

Internally, the article printing routine will call for a method to print paragraphs:

com.blaster.business.InteractorPrint::printParagraphs

This call allows us to print the body of the article – a list of nodes. One thing to note is that this routine can be called recursively. The style of the output will look slightly differently. This fact is reflected by the additional parameter child. The result of this method is the HTML generated.

private fun printParagraphs(nodes: List<Node>, child: Boolean = false): String {

We create the variable to hold the result and then we go through the nodes one by one

    var result = ""

We also want to keep track of headers and references, which we accumulated while rendering, we will place them before and after the article

    var contentHeader = "Contents"
    val headers = mutableListOf<NodeCommand>()
    val cites = mutableListOf<NodeCommand>()
    for (node in nodes) {
        result += when (node) {

For each type we call the appropriate template

            is NodeText -> renderNodeText(node, child)
            is NodeCode -> renderNodeCode(node, child)
            is NodeCommand -> {
                if (node.cmdType == CmdType.CONTENT) {
                    contentHeader = node.subcommand
                }
                renderNodeCommand(node, child, headers, cites)
            }
            else -> TODO()
        }
    }

Now we can replace content placeholder with the actual table of contents

    if (headers.isNotEmpty()) {
        var contents = printTemplateHeader(contentHeader)
        headers.forEach {
            contents += printTemplateListItem(
                printTemplateLink(it.subcommand, "#${it.subcommand.headerId()}", child = false, newWindow = false), false)
        }
        result = if (result.contains(contentPlaceholder)) {
            result.replace(contentPlaceholder, contents)
        } else {
            contents + result
        }
    }

After all of the nodes are rendered, we can add our references

    if (cites.isNotEmpty()) {
        result += printTemplateHeader("References")
        cites.forEach { node ->
            result += printTemplateListItem(
                printTemplateLink(
                    "↑[${node.subcommand}]: ", "#${node.subcommand}_origin", child, false) +
                        printTemplateCite(node.subcommand, node.argument, node.argument1, child), child)
        }
    }

The final result is returned from the call

    return result
}

Here are the three methods, responsible for the main chunks of the work:

com.blaster.business.InteractorPrint::renderNodeText

This routine will print a text node

private fun renderNodeText(node: NodeText, child: Boolean): String {
    var result = ""
    for (ch in node.children) {
        result += when (ch) {

Depending on which children we have to render, we can select an appropriate template

            is StructListItem -> renderListItem(ch, child)
            is StructLink -> printTemplateLink(ch.text, ch.link.toString(), child)
            is StructCite -> printTemplateCiteLink(ch.id)
            is StructText -> renderTextSpans(ch.children)
            else -> TODO()
        }
    }

And then we can wrap it into a paragraph tags

    return printTemplateParagraph(result + "\n", child)
}

com.blaster.business.InteractorPrint::renderNodeCode

A relatively straightforward routine to render the code. We just pass the code into a template

private fun renderNodeCode(node: NodeCode, child: Boolean): String {
    return printTemplateCode(node.code, child) + "\n"
}

com.blaster.business.InteractorPrint::renderNodeCommand

With this routine we can include additions like headers, pictures and etc.

private fun renderNodeCommand(node: NodeCommand, child: Boolean,
                              headers: MutableList<NodeCommand>, cites: MutableList<NodeCommand>): String {
    var result = ""
    when (node.cmdType) {

It can be something related to the attributes of the page

        CmdType.HEADER -> {
            headers.add(node)
            result += printTemplateHeader(node.subcommand) + "\n"
        }

Or a picture insert

        CmdType.PICTURE -> result += printTemplatePicture(node.subcommand, node.argument, child) + "\n"

Or a cite reference

        CmdType.CITE -> cites.add(node)

Content placeholder

        CmdType.CONTENT -> result += contentPlaceholder

Else just continue

        else -> {}
    }

When we include the code and comments into the article they come as children of the command which included them

    if (node.children.isNotEmpty()) {
        result += printTemplateChild(node.argument, node.location!!.url, node.children) + "\n"
    }
    return result
}

We are printing nodes by delegating pieces of the tasks to the helper methods, like this one:

com.blaster.business.InteractorPrint::printTemplateParagraph

Here is how we print paragraph

private fun printTemplateParagraph(paragraph: String, child: Boolean): String {

If this paragraph is a child of another paragraph, appropriate style is selected

    val clz = if (child) "text_child" else "text"

Then we select a template and pass the task to the printing manager

    return printingManager.renderTemplate("template_paragraph.ftlh", hashMapOf("class" to clz, "paragraph" to paragraph))
}

Each of those will handle its piece of the overall article accordingly.

At this point, we had a look at all steps required for processing the article – starting from the scenario and until the actual HTML output. Of course, we did not cover everything. The best place to find each and every detail is the source code. But I hope, at this point, most of the high-level concepts are explained.

What's next?

Any craftsman can say that there is no tool, which works equally well for every job. The idea behind Lem is to be versatile enough to showcase some of the projects on which I am working.

In this series of articles, I have shown a full pipeline of the project: from the preparation of the material to the printing stage.

The main target for Lem is to be a handy tool in explaining the Blaster – an offline renderer, on which I am currently working. I hope this article was interesting enough and will be glad to see you soon on the pages dedicated to the actual hobby – the Blaster engine – Poyekhali![2]

References


Leave a Reply

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