asciidoctorj 0 travis-ci

:coffee: Java bindings for Asciidoctor. Asciidoctor on the JVM!

= AsciidoctorJ: Java bindings for Asciidoctor Alex Soto https://github.com/lordofthejars[@lordofthejars]; Dan Allen https://github.com/mojavelinux[@mojavelinux] // Settings: :compat-mode!: :page-layout: base :toc: macro :toclevels: 2 ifdef::awestruct[:toclevels: 1] :experimental: //:table-caption!: :source-language: java :language: {source-language} ifdef::env-github[:badges:] // Aliases: :dagger: † // URIs: ifdef::awestruct[:uri-docs: link:/docs] ifndef::awestruct[:uri-docs: http://asciidoctor.org/docs] :uri-asciidoctor: {uri-docs}/what-is-asciidoctor :uri-repo: https://github.com/asciidoctor/asciidoctorj :uri-issues: {uri-repo}/issues :uri-discuss: http://discuss.asciidoctor.org :artifact-version: 1.5.4 :uri-maven-artifact-query: http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.asciidoctor%22%20AND%20a%3A%22asciidoctorj%22%20AND%20v%3A%22{artifact-version}%22 :uri-maven-artifact-detail: http://search.maven.org/#artifactdetails%7Corg.asciidoctor%7Casciidoctorj%7C{artifact-version}%7Cjar :uri-maven-artifact-file: http://search.maven.org/remotecontent?filepath=org/asciidoctor/asciidoctorj/{artifact-version}/asciidoctorj-{artifact-version} :uri-bintray-artifact-query: https://bintray.com/asciidoctor/maven/asciidoctorj/view/general :uri-bintray-artifact-detail: https://bintray.com/asciidoctor/maven/asciidoctorj/{artifact-version}/view :uri-bintray-artifact-file: http://dl.bintray.com/asciidoctor/maven/org/asciidoctor/asciidoctorj/{artifact-version}/asciidoctorj-{artifact-version} :uri-jruby-startup: http://github.com/jruby/jruby/wiki/Improving-startup-time :uri-maven-guide: {uri-docs}/install-and-use-asciidoctor-maven-plugin :uri-gradle-guide: {uri-docs}/install-and-use-asciidoctor-gradle-plugin :uri-tilt: https://github.com/rtomayko/tilt :uri-font-awesome: http://fortawesome.github.io/Font-Awesome :uri-gradle: https://gradle.org

{uri-repo}[AsciidoctorJ] is the official library for running {uri-asciidoctor}[Asciidoctor] on the JVM. Using AsciidoctorJ, you can convert AsciiDoc content or analyze the structure of a parsed AsciiDoc document from Java and other JVM languages.

ifdef::badges[] image:https://img.shields.io/travis/asciidoctor/asciidoctorj/master.svg[Build Status (Travis CI), link=https://travis-ci.org/asciidoctor/asciidoctorj] image:https://ci.appveyor.com/api/projects/status/syxrlv047esal4n0/branch/master?svg=true[Build Status (AppVeyor), link=https://ci.appveyor.com/project/asciidoctor/asciidoctorj] image:https://img.shields.io/badge/javadoc.io-1.5.4-blue.svg[Javadoc, link=http://www.javadoc.io/doc/org.asciidoctor/asciidoctorj/1.5.4] endif::[]

ifdef::awestruct,env-browser[] toc::[] endif::[]

== Distribution

The version of AsciidoctorJ matches the version of Asciidoctor RubyGem it bundles. AsciidoctorJ is published to Maven Central and Bintray. The artifact information can be found in the tables below.

[cols=“2,2,^2,4”] .Artifact information for AsciidoctorJ in jCenter (Bintray) |=== |Group Id |Artifact Id |Version |Download

|org.asciidoctor |{uri-bintray-artifact-query}[asciidoctorj] |{uri-bintray-artifact-detail}[{artifact-version}] |{uri-bintray-artifact-file}.pom[pom] {uri-bintray-artifact-file}.jar[jar] {uri-bintray-artifact-file}-javadoc.jar[javadoc (jar)] {uri-bintray-artifact-file}-sources.jar[sources (jar)]

|org.asciidoctor |asciidoctorj-epub3 |1.5.0-alpha.6 |{empty}

|org.asciidoctor |asciidoctorj-pdf |1.5.0-alpha.11 |{empty} |===

[cols=“2,2,^2,4”] .Artifact information for AsciidoctorJ in Maven Central |=== |Group Id |Artifact Id |Version |Download

|org.asciidoctor |{uri-maven-artifact-query}[asciidoctorj] |{uri-maven-artifact-detail}[{artifact-version}] |{uri-maven-artifact-file}.pom[pom] {uri-maven-artifact-file}.jar[jar] {uri-maven-artifact-file}-javadoc.jar[javadoc (jar)] {uri-maven-artifact-file}-sources.jar[sources (jar)]

|org.asciidoctor |asciidoctorj-epub3 |1.5.0-alpha.6 |{empty}

|org.asciidoctor |asciidoctorj-pdf |1.5.0-alpha.11 |{empty} |===

CAUTION: The artifactId changed to asciidoctorj starting in 1.5.0.

== Installation

AsciidoctorJ is a standard .jar file. To start using it, you need to add the library to your project’s classpath. To start using it under WildFly AS, you can’t just use it, you also have to modify your WildFly installation due to classpath loading issues; see <>.

// SW: Need functional tests for a java maven project and a java gradle project

[source,xml] [subs=“specialcharacters,attributes,callouts”]

.Declaring the dependency in a Maven build file (i.e., pom.xml)

org.asciidoctor asciidoctorj {artifact-version}

[source,groovy] [subs=“specialcharacters,attributes,callouts”]

.Declaring the dependency in a Gradle build file (e.g., build.gradle)

dependencies { compile ‘org.asciidoctor:asciidoctorj:{artifact-version}’

}

[source,scala] [subs=“specialcharacters,attributes,callouts”]

.Declaring the dependency in an SBT build file (e.g., build.sbt)

libraryDependencies += “org.asciidoctor” % “asciidoctorj” % “{artifact-version}” // <1>

<1> Specifying the version of AsciidoctorJ implicitly selects the version of Asciidoctor

[source,clojure] [subs=“specialcharacters,attributes,callouts”]

.Declaring the dependency in a Leiningen build file (e.g., project.clj)

:dependencies [[org.asciidoctor/asciidoctorj “{artifact-version}”]]

// DA: Should we mention how to download if you just want to use the asciidoctorj command?

[TIP]

In addition to using AsciidoctorJ directly, you can invoke it as part of your build using the Maven or Gradle plugin.

  • {uri-maven-guide}[How to Install and Use the Asciidoctor Maven Plugin]
  • {uri-gradle-guide}[How to Install and Use the Asciidoctor Gradle Plugin] ====

== Converting documents

The main entry point for AsciidoctorJ is the Asciidoctor Java interface. This interface provides four methods for converting AsciiDoc content.

  • convert
  • convertFile
  • convertFiles
  • convertDirectory

You’ll learn about these methods in the <>.

IMPORTANT: Prior to Asciidoctor 1.5.0, the term render was used in these method names instead of convert (i.e., render, renderFile, renderFiles and renderDirectory). AsciidoctorJ continues to support the old method names for backwards compatibility.

[cols=“1m,1m,2”] .Convert methods on the Asciidoctor interface |=== |Method Name |Return Type| Description

|convert |String |Parses AsciiDoc content read from a string or stream and converts it to the format specified by the backend option.

|convertFile |String |Parses AsciiDoc content read from a file and converts it to the format specified by the backend option.

|convertFiles |String[] |Parses a collection of AsciiDoc files and converts them to the format specified by the backend option.

|convertDirectory |String[] |Parses all AsciiDoc files found in the specified directory (using the provided strategy) and converts them to the format specified by the backend option. |===

// What is the ‘provided strategy’, need a link

NOTE: All the methods listed in Table 3 are overloaded to accommodate various input types and options.

You retrieve an instance of the Asciidoctor interface from the factory method provided.

[source]

.Creating an Asciidoctor instance from Asciidoctor.Factory

import static org.asciidoctor.Asciidoctor.Factory.create; import org.asciidoctor.Asciidoctor;

Asciidoctor asciidoctor = create();

Once you retrieve an instance of the Asciidoctor interface, you can use it to convert AsciiDoc content. Here’s an example of using AsciidoctorJ to convert an AsciiDoc string.

NOTE: The following convertFile or convertFiles methods will only return a converted String object or array if you disable writing to a file, which is enabled by default. To disable writing to a file, create a new Options object, disable the option to create a new file with option.setToFile(false), and then pass the object as a parameter to convertFile or convertFiles.

[source]

.Converting an AsciiDoc string

//… import java.util.HashMap; //…

String html = asciidoctor.convert( “Writing AsciiDoc is easy!”, new HashMap());

System.out.println(html);

The convertFile method will convert the contents of an AsciiDoc file.

[source]

.Converting an AsciiDoc file

//… import java.util.HashMap; //…

String html = asciidoctor.convertFile( new File(“sample.adoc”), new HashMap());

System.out.println(html);

The convertFiles method will convert a collection of AsciiDoc files:

[source]

.Converting a collection of AsciiDoc files

//… import java.util.Arrays; //…

String[] result = asciidoctor.convertFiles( Arrays.asList(new File(“sample.adoc”)), new HashMap());

for (String html : result) { System.out.println(html);

}

WARNING: If the converted content is written to files, the convertFiles method will return a String Array (i.e., String[]) with the names of all the converted documents.

Another method provided by the Asciidoctor interface is convertDirectory. This method converts all of the files with AsciiDoc extensions (.adoc (preferred), .ad, .asciidoc, .asc) that are present within a specified folder and following given strategy.

An instance of the DirectoryWalker interface, which provides a strategy for locating files to process, must be passed as the first parameter of the convertDirectory method. Currently Asciidoctor provides two built-in implementations of the DirectoryWalker interface:

[cols=“1m,2”] .Built-in DirectoryWalker implementations |=== |Class |Description

|AsciiDocDirectoryWalker |Converts all files of given folder and all its subfolders. Ignores files starting with underscore (_).

|GlobDirectoryWalker |Converts all files of given folder following a glob expression. |===

If the converted content is not written into files, convertDirectory will return an array listing all the documents converted.

// SW: Maybe provide an example of this array output?

[source]

.Converting all AsciiDoc files in a directory

//… import org.asciidoctor.AsciiDocDirectoryWalker; //…

String[] result = asciidoctor.convertDirectory( new AsciiDocDirectoryWalker(“src/asciidoc”), new HashMap());

for (String html : result) { System.out.println(html);

}

Another way to convert AsciiDoc content is by calling the convert method and providing a standard Java java.io.Reader and java.io.Writer. The Reader interface is used as the source, and the converted content is written to the Writer interface.

[source]

.Converting content read from a java.io.Reader to a java.io.Writer

//… import java.io.FileReader; import java.io.StringWriter; //…

FileReader reader = new FileReader(new File(“sample.adoc”)); StringWriter writer = new StringWriter();

asciidoctor.convert(reader, writer, options().asMap());

StringBuffer htmlBuffer = writer.getBuffer();

System.out.println(htmlBuffer.toString());

==== Safe mode and file system access Asciidoctor provides security levels that control the read and write access of attributes, the include directive, macros, and scripts while a document is processing. Each level includes the restrictions enabled in the prior security level.

When Asciidoctor (and AsciidoctorJ) is used as API, it uses SECURE safe mode by default. This mode is the most restrictive one and in summary it disallows the document from attempting to read files from the file system and including their contents into the document.

We recommend you to set SAFE safe mode when rendering AsciiDoc documents using AsciidoctorJ to have almost all Asciidoctor features such as icons, include directive or retrieving content from URIs enabled.

Safe mode is set as option when a document is rendered. For example:

[source, java]

import static org.asciidoctor.OptionsBuilder.options;

Map options = options().safe(SafeMode.SAFE) .asMap();

String outfile = asciidoctor.convertFile(new File(“sample.adoc”), options);

We are going to explain in more detail options in <>.

You can read more about safe modes in http://asciidoctor.org/docs/user-manual/#running-asciidoctor-securely

=== Conversion options

Asciidoctor supports numerous options, such as:

in_place:: Converts the output to a file adjacent to the input file.

template_dirs:: Specifies a directory of {uri-tilt}[Tilt]-compatible templates to be used instead of the default built-in templates

attributes:: A Hash (key-value pairs) of attributes to configure various aspects of the AsciiDoc processor

The second parameter of the convert method is java.util.Map. The options listed above can be set in java.util.Map.

[source]

.Using the in_place option and the backend attribute

Map attributes = new HashMap(); attributes.put(“backend”, “docbook”); // <1>

Map options = new HashMap(); options.put(“attributes”, attributes); // <2> options.put(“in_place”, true); // <3>

String outfile = asciidoctor.convertFile(new File(“sample.adoc”), options);

<1> Defines the backend attribute as docbook in the attributes map <2> Registers the attributes map as the attributes option in the options map <3> Defines the in_place option in the options map

Another way for setting options is by using org.asciidoctor.Options class. Options is a simple Java class which contains methods for setting required options. Note that related with org.asciidoctor.Options class, there is org.asciidoctor.Attributes class, which can be used for setting attributes.

The convert method is overloaded so org.asciidoctor.Options can be passed instead of a java.util.Map.

[source]

.Using the in_place option and the backend attribute

Attributes attributes = new Attributes(); attributes.setBackend(“docbook”); // <1>

Options options = new Options(); options.setAttributes(attributes); // <2> options.setInPlace(true); // <3>

String outfile = asciidoctor.convertFile(new File(“sample.adoc”), options);

<1> Defines the backend attribute as docbook in the attributes class <2> Registers the attributes class as the attributes option in the options class <3> Defines the in_place option in the options class

AsciidoctorJ also provides two builder classes to create these maps and classes in a more readable form.

AttributesBuilder:: Used to define attributes with a fluent API

OptionsBuilder:: Used to define options with a fluent API

The code below results in the same output as the previous example but uses the builder classes.

[source]

.Setting attributes and options with the builder classes

import static org.asciidoctor.AttributesBuilder.attributes; import static org.asciidoctor.OptionsBuilder.options;

//… Map attributes = attributes().backend(“docbook”) // <1> .asMap();

Map options = options().inPlace(true) .attributes(attributes) // <2> .asMap(); // <3>

String outfile = asciidoctor.convertFile(new File(“sample.adoc”), options);

<1> Defines the backend attribute as docbook using fluent API. <2> Registers the attributes map as attributes. <3> Converts options to java.util.Map instance.

[source]

.Setting attributes and options with the builder classes

import static org.asciidoctor.AttributesBuilder.attributes; import static org.asciidoctor.OptionsBuilder.options;

//… Attributes attributes = attributes().backend(“docbook”).get(); // <1> Options options = options().inPlace(true).attributes(attributes).get(); // <2>

String outfile = asciidoctor.convertFile(new File(“sample.adoc”), options); // <3>

<1> Defines and returns an Attributes class instead of java.util.Map by calling get() method instead of asMap(). <2> Defines and returns an Options class instead of java.util.Map by calling get() method instead of asMap(). <3> Converts the document passing Options class.

TIP: All methods used to convert content are overloaded with OptionsBuilder parameter, so it is no longer required to call get nor asMap methods.

WARNING: The icons attribute requires a String to set the value used to “draw” icons. At this time, you can use two constants org.asciidoctor.Attributes.IMAGE_ICONS for using the same approach as AsciiDoc, that is using img tags, or org.asciidoctor.Attributes.FONT_ICONS for using icons from {uri-font-awesome}[Font Awesome^].

Attributes can be specified as String or Array instead of pair key/value by using org.asciidoctor.Attributes.setAttributes(String) or [x-]org.asciidoctor.Attributes.setAttributes(String...) and AttributesBuilder methods.

[source]

.Passing attributes as a string

//… Attributes attributes = attributes().attributes(“toc numbered”).get();

Options options = options().attributes(attributes).get();

Passing attributes as a string is equivalent to passing individual attributes.

[source]

.Passing individual attributes

//… Attributes attributes = attributes().tableOfContents(true).sectionNumbers(true).get();

Options options = options().attributes(attributes).get();

You can also use an array.

[source]

.Passing attributes as an array

//… String[] attributesArray = new String[]{“toc”, “source-highlighter=coderay”}; Attributes attributes = attributes().attributes(attributesArray).sectionNumbers(true).get();

Options options = options().attributes(attributes).get();

Passing attributes as an array is equivalent to passing individual attribute.

[source]

.Passing individual attributes

//… Attributes attributes = attributes().tableOfContents(true).sectionNumbers(true).sourceHighlighter(“coderay”).get();

Options options = options().attributes(attributes).get();

== Locating files

A utility class AsciiDocDirectoryWalker is available for searching the AsciiDoc files present in a root folder and its subfolders. AsciiDocDirectoryWalker locates all files that end with .adoc, .ad, .asciidoc or .asc. Also it ignores all files starting with underscore (_).

[source]

.Locating AsciiDoc files with AsciiDocDirectoryWalker

import java.util.List; import org.asciidoctor.AsciiDocDirectoryWalker;

DirectoryWalker directoryWalker = new AsciiDocDirectoryWalker(“docs”); // <1>

List asciidocFiles = directoryWalker.scan(); // <2>

<1> Defines which parent directory is used for searching. <2> Returns a list of all AsciiDoc files found in root folder and its subfolders.

A utility class GlobDirectoryWalker is available for searching the AsciiDoc files present in a root folder and scanning using a Glob expression. GlobDirectoryWalker locates all files that end with .adoc, .ad, .asciidoc or .asc.

[source]

.Locating AsciiDoc files with GlobDirectoryWalker

import java.util.List; import org.asciidoctor.GlobDirectoryWalker;

DirectoryWalker directoryWalker = new GlobDirectoryWalker(“docs”, “*/.adoc”); // <1>

List asciidocFiles = directoryWalker.scan(); // <2>

<1> Defines which parent directory is used for searching and the glob expression. <2> Returns a list of all AsciiDoc files matching given glob expression.

== Reading the document tree

Instead of converting an AsciiDoc document, you may want to parse the document to read information it contains or navigate the document’s structure. AsciidoctorJ let’s you do this!

There are two approaches you can take to read the structure of an AsciiDoc document with AsciidoctorJ.

Using wrapper classes not connected with Ruby internal model:: The structure is copied in Java non-proxied classes so any change does not modify the original document.

// Is there something more specific we can say as opposed to ‘structure’?

Using JRuby Java wrapper classes:: The Java classes are linked to Ruby internal classes. Any modifications done here are reflected to the original document.

=== Wrapping classes

[source,asciidoc]

.Example AsciiDoc document with header information

= Sample Document Doc Writer [email protected]; John Smith [email protected] v1.0, 2013-05-20: First draft :title: Sample Document :tags: [document, example]

Preamble…

The readDocumentHeader method on the Asciidoctor interface retrieves information from the header of an AsciiDoc document without parsing or converting the entire document. This method returns an instance of org.asciidoctor.ast.DocumentHeader with all information from the header filled.

[source]

.Reading header information from the AsciiDoc document

//… import org.asciidoctor.ast.DocumentHeader;

//… DocumentHeader header = asciidoctor.readDocumentHeader( new File(“header-sample.adoc”));

System.out.println(header.getDocumentTitle().getMain()); // <1>

Author author = header.getAuthor(); // <2> System.out.println(author.getEmail()); // <3> System.out.println(author.getFullName()); // <4>

RevisionInfo revisionInfo = header.getRevisionInfo();

System.out.println(revisionInfo.getDate()); // <5> System.out.println(revisionInfo.getNumber()); // <6>

System.out.println(revisionInfo.getRemark()); // <7>

<1> prints Sample Document <2> prints Doc Writer <3> prints [x-][email protected] <4> prints Doc Writer <5> prints 2013-05-20 <6> prints 1.0 <7> prints First draft

The readDocumentHeader method can be extremely useful for building an index of documents.

[source]

import java.io.File; import java.util.HashSet; import java.util.Set; import org.asciidoctor.Asciidoctor; import org.asciidoctor.AsciiDocDirectoryWalker; import org.asciidoctor.DirectoryWalker; import org.asciidoctor.DocumentHeader;

//…

Asciidoctor asciidoctor = Asciidoctor.Factory.create(); Set documentIndex = new HashSet(); DirectoryWalker directoryWalker = new AsciiDocDirectoryWalker(“docs”); // <1>

for (File file : directoryWalker.scan()) { documentIndex.add(asciidoctor.readDocumentHeader(file));

}

<1> Converts all files in the docs folder and its subfolders.

You can also load the document inside a Document object. This object represents the whole document, including its headers. You can use it to navigate through the internals of a parsed document. To load a document, use the load or loadFile methods.

The readDocumentStructure method provides a useful way of parsing an AsciiDoc file into the structured object. First, it gathers the same information as readDocumentHeader and puts it in the header field of the StructuredDocument object. The actual content of the file is split into separate ContentParts based on blocks of the content.

This feature provides several use cases.

.AsciiDoc document with two blocks defined by section titles

[source,asciidoc]

= Sample Document

== Section one This is content of section one

== Section two And content of section two

Each section defines new content part. List of all parts can be get by getParts method on StructuredDocument. Each part will than contain of title (i.e. “Section one”) and converted text content as html.

.Print content of each part

[source]

for (ContentPart part : document.getParts()){ System.out.println(part.getTitle()); System.out.println(“—-”); System.out.println(part.getContent); System.out.println(“—-”);

}

.AsciiDoc document with two blocks defined by styles

[source]

= Sample Document

[style one] This is content of first content part

[[partId]]

[style two,role=partRole]

And content of second content part

This block can be as long as you want.


This way you can then use methods like getPartByStyle to retrieve particular content parts.

.Retrieve content part by style

[source]

ContentPart style_two = document.getPartByStyle(“style two”); // other possible way of retrieving parts: ContentPart style_two = document.getPartById(“partId”) ContentPart style_two = document.getPartByRole(“partRole”)

//and also for lists List parts = document.getPartsByStyle(“style two”); List parts = document.getPartsByRole(“partRole”);

List parts = document.getPartsByContext(“open”);

Really nice thing about it is possibility to parse images to Image object that you can use later to embed in html page directly from your java code or manipulate in any other way.

.Define images

[source]

[Images] image::src/some{sp}image{sp}1.JPG[TODO title1,link=“link1.html”]

image::src/some{sp}image{sp}2.JPG[TODO title2,link=“link2.html”]

to get a list of images defined in the document and then to process images:

.Retrieve image information

[source]

List images = document.getPartsByContext(“image”); for (ContentPart image : images){ String src = (String) image.getAttributes().get(“target”); String alt = (String) image.getAttributes().get(“alt”); String link = (String) image.getAttributes().get(“link”);

}

As of final example consider following complete use case:

.AsciiDoc document with product definition

[source,asciidoc]

:hardbreaks:
:price: 70 pln :smallImage: photos/small/small_image.jpg

ge: photos/small/small_image.jpg

[Description] short product description

[Images] image::photos/image1.jpg[title] image::photos/image2.jpg[title]

[Detail]

Detail information about product. Note that you can use all asciidoc features here like: .simple list * lists * images * titles * further blocks

[role=text-center]

also you can also add css style by assigning role to the text.


and the way it can be than transformed to java object:

.Java method for getting product

[source]

Product product = new Product(); product.setTitle(document.getHeader().getDocumentTitle()); product.setPrice(new Price((String) document.getHeader().getAttributes().get(“price”))); product.setSmallImage(new Image((String)document.getHeader().getAttributes().get(“smallImage”),product.getTitle()));

product.setDescription(document.getPartByStyle(“description”).getContent());

List images = document.getPartsByContext(“image”); for (ContentPart image : images) { Image image = new Image(); image.setSrc((String) image.getAttributes().get(“target”)); image.setAlt((String) image.getAttributes().get(“alt”)); product.getImages().add(image); }

product.setDetail(document.getPartByStyle(“detail”).getContent());

Last feature of structure document is possibility to configure how deeply should blocks be processed. Default is one level only so if you want to have more nested structure add STRUCTURE_MAX_LEVEL parameter to processing options.

.Configuration of the structure document processing

[source]

Map parameters = new HashMap(); parameters.put(Asciidoctor.STRUCTURE_MAX_LEVEL, 2); StructuredDocument document = asciidoctor.readDocumentStructure( new File(“target/test-classes/documentblocks.asciidoc”),

parameters);

=== JRuby wrapping classes

[source]

import org.asciidoctor.ast.Document;

//…

Document document = asciidoctor.load(DOCUMENT, new HashMap()); // <1>

assertThat(document.doctitle(), is(“Document Title”)); // <2>

<1> Document from an String is loaded into Document object. <2> Title of the document is retrieved.

But also all blocks that conforms the document can be retrieved. Currently there are support for three kinds of blocks. Blocks itself, Section for sections of the document and AbstractBlock which is the base type where all kind of blocks (including those not mapped as Java class) are mapped.

[source]

import org.asciidoctor.ast.Document; import org.asciidoctor.ast.Section;

//…

Document document = asciidoctor.load(DOCUMENT, new HashMap()); // <1> Section section = (Section) document.blocks().get(1); // <2>

assertThat(section.index(), is(0)); // <3> assertThat(section.sectname(), is(“sect1”));

assertThat(section.special(), is(false));

<1> Document from an String is loaded into Document object. <2> All blocks are get and because in this example the first block is a Section block, we cast it directly. <3> Concrete methods for sections can be called.

Blocks can also be retrieved from query using findBy method.

[source]

Document document = asciidoctor.load(DOCUMENT, new HashMap()); Map selector = new HashMap(); // <1> selector.put(“context”, “:image”); // <2>

List findBy = document.findBy(selector);

assertThat(findBy, hasSize(2)); //<3>

<1> To make queries you need to use a map approach. Currently this is because of the Asciidoctor API but it will change in near future. <2> In this example all blocks with context as image is returned. Notice that the colon (:) must be added in the value part. <3> Document used as example contains two images.

== Extension API

One of the major improvements to Asciidoctor recently is the extensions API. AsciidoctorJ brings this extension API to the JVM environment. {uri-repo}[AsciidoctorJ] allows us to write extensions in Java instead of Ruby.

Asciidoctor provides seven types of extension points. Each extension point has an abstract class in Java that maps to the extension API in Ruby.

[cols=“1m,2”] .AsciidoctorJ extension APIs |=== |Name |Class

|Preprocessor |org.asciidoctor.extension.Preprocessor

|Treeprocessor |org.asciidoctor.extension.Treeprocessor

|Postprocessor |org.asciidoctor.extension.Postprocessor

|Block processor |org.asciidoctor.extension.BlockProcessor

|Block macro processor |org.asciidoctor.extension.BlockMacroProcessor

|Inline macro processor |org.asciidoctor.extension.InlineMacroProcessor

|Include processor |org.asciidoctor.extension.IncludeProcessor

|Docinfo processor |org.asciidoctor.extension.DocinfoProcessor |===

To create an extension two things are required:

. Create a class implementing an extension class (this will depend on the kind of interface being developed) . Register your class using the JavaExtensionRegistry class //^

// Is this list a sublist of the list above?

An extension can be registered by:

  • Passing as String a fully qualified class.
  • Passing a Class object of the extension.
  • Passing an instance of the extension object.

NOTE: In the first two cases, the lifecycle of the instance is managed by JRuby. In the last case, the caller is the owner of the lifecycle of the object.

TIP: Using an already created instance as an extension is useful when using CDI. For example, you can inject any extension inside the code and then register that instance as an extension.

=== Preprocessor

This extension updates an attribute value defined in a document.

[source]

.Preprocessor example

public class ChangeAttributeValuePreprocessor extends Preprocessor { // <1>

public ChangeAttributeValuePreprocessor(Map<String, Object> config) { // <2>
	super(config);
}

@Override
public PreprocessorReader process(Document document, PreprocessorReader reader) { // <3>
	document.getAttributes().put("content", "Alex");
	return reader;
}

}

<1> Class must extend from Preprocessor. <2> A constructor receiving a Map must be provided in case you want to send options to the preprocessor. <3> The process method receives a Document and PreprocessorReader.

[source]

.Register a Preprocessor

JavaExtensionRegistry extensionRegistry = this.asciidoctor.javaExtensionRegistry(); // <1>

extensionRegistry.preprocessor(ChangeAttributeValuePreprocessor.class); // <2>

String content = asciidoctor.convertFile(new File( “target/test-classes/changeattribute.adoc”),

new Options()); // <3>

<1> JavaExtensionRegistry class is created. <2> Preprocessor extension is registered by using Class approach. <3> We can call any convert method as usual; no extra parameters are required.

=== Treeprocessor

This extension detects literal blocks that contain terminal commands. It strips the prompt character and styles the command.

[source]

.Treeprocessor example

public class TerminalCommandTreeprocessor extends Treeprocessor { // <1>

private Document document;

public TerminalCommandTreeprocessor(Map config) { super(config); }

@Override public Document process(Document document) {

this.document = document;

final List<AbstractBlock> blocks = this.document.blocks(); // <2>

for (int i = 0; i < blocks.size(); i++) {
    final AbstractBlock currentBlock = blocks.get(i);
    if(currentBlock instanceof Block) {
        Block block = (Block)currentBlock;
        List<String> lines = block.lines(); // <3>
        if (lines.size() > 0 && lines.get(0).startsWith("$")) {
            blocks.set(i, convertToTerminalListing(block));
        }
    }
}

return this.document;

}

public Block convertToTerminalListing(Block block) {

Map<String, Object> attributes = block.getAttributes();
attributes.put("role", "terminal");
StringBuilder resultLines = new StringBuilder();

List<String> lines = block.lines();

for (String line : lines) {
    if (line.startsWith("$")) {
        resultLines.append("<span class=\"command\">")
                   .append(line.substring(2, line.length()))
                   .append("</command");
    }
    else {
        resultLines.append(line);
    }
}

return createBlock(this.document, "listing", Arrays.asList(resultLines.toString()), attributes,
          new HashMap<Object, Object>()); // <4>

}

}

<1> Class must extend from Treeprocessor. <2> The document instance can be used to retrieve all blocks of the current document. <3> All of the select block’s lines are retrieved. <4> To create a new block, you must use the createBlock method. Set the parent document, the context (listing), the text content, attributes and options.

[source]

.Register a Treeprocessor

JavaExtensionRegistry extensionRegistry = this.asciidoctor.javaExtensionRegistry(); // <1>

extensionRegistry.treeprocessor(“org.asciidoctor.extension.TerminalCommandTreeprocessor”); // <2>

String content = asciidoctor.convertFile(new File( “target/test-classes/sample-with-terminal-command.adoc”),

new Options()); // <3>

<1> JavaExtensionRegistry class is created. <2> Treeprocessor extension is registered using fully qualified class name as String. <3> We can call any convert method as usually, no extra parameters are required.

=== Postprocessor

This extension inserts custom footer text.

[source]

.Postprocessor example

public class CustomFooterPostProcessor extends Postprocessor { // <1>

public CustomFooterPostProcessor(Map config) { super(config); }

@Override public String process(Document document, String output) { // <2>

  String copyright  = "Copyright Acme, Inc.";

  if(document.basebackend("html")) {
      org.jsoup.nodes.Document doc = Jsoup.parse(output, "UTF-8");

      Element contentElement = doc.getElementById("footer-text");
      contentElement.append(copyright);

      output = doc.html();

  }
  return output; // <3>

}

}

<1> Class must extend from Postprocessor. <2> process method receives the Document instance and the document converted as String. <3> The content that will be written in document is returned.

[source]

.Register a Postprocessor

JavaExtensionRegistry extensionRegistry = this.asciidoctor.javaExtensionRegistry(); // <1>

extensionRegistry.postprocessor(new CustomFooterPostProcessor( new HashMap())); // <2>

String content = asciidoctor.convertFile(new File( “sample.adoc”),

options); // <3>

<1> JavaExtensionRegistry class is created. <2> Postprocessor extension is registered using an already created instance approach. <3> We can call any convert method as usually, no extra parameters are required.

=== DocinfoProcessor

This extension inserts custom content on header or footer of the document. For example can be used for adding meta tags on <head> tags.

[source]

.DocinfoProcessor example

public class MetaRobotsDocinfoProcessor extends DocinfoProcessor { //<1>

public MetaRobotsDocinfoProcessor() {
    super();
}

public MetaRobotsDocinfoProcessor(Map<String, Object> config) {
    super(config);
}

@Override
public String process(Document document) {
    return "<meta name=\"robots\" content=\"index,follow\">"; //<2>
}

}

<1> Class must extend from DocinfoProcessor. <2> In this case a meta tag is returned. By default docinfo is placed on header.

To register the DocinfoProcessor extension just use the JavaExtensionRegistry.

[source]

.Register a DocinfoProcessor

JavaExtensionRegistry javaExtensionRegistry = this.asciidoctor.javaExtensionRegistry(); // <1>

javaExtensionRegistry.docinfoProcessor(MetaRobotsDocinfoProcessor.class); // <2>

String content = asciidoctor.renderFile( classpath.getResource(“simple.adoc”), options().headerFooter(true).safe(SafeMode.SERVER).toFile(false).get());


<1> JavaExtensionRegistry class is created. <2> DocinfoProcessor extension is registered using an already created instance approach.

If you want to place docinfo on footer you need to set location option to footer.

[source]

.Register a DocinfoProcessor

JavaExtensionRegistry javaExtensionRegistry = this.asciidoctor.javaExtensionRegistry();

Map options = new HashMap(); options.put(“location”, “:footer”); // <1> MetaRobotsDocinfoProcessor metaRobotsDocinfoProcessor = new MetaRobotsDocinfoProcessor(options);

javaExtensionRegistry.docinfoProcessor(metaRobotsDocinfoProcessor);

String content = asciidoctor.renderFile( classpath.getResource(“simple.adoc”),

options().headerFooter(true).safe(SafeMode.SERVER).toFile(false).get());

<1> Sets the location option to footer. Note that a colon (:) is placed before footer.

=== Block processor

This extension registers a custom block style named yell that uppercases all the words.

[source]

.Block processor example

public class YellBlock extends BlockProcessor { // <1>

public YellBlock(String name, Map config) { // <2> super(name, config); }

@Override public Object process(AbstractBlock parent, Reader reader, Map attributes) { // <3> List lines = reader.readLines(); String upperLines = null; for (String line : lines) { if (upperLines == null) { upperLines = line.toUpperCase(); } else { upperLines = upperLines + “\n” + line.toUpperCase(); } }

	  return createBlock(parent, "paragraph", Arrays.asList(upperLines), attributes, new HashMap<Object, Object>()); // <4>

}

}

<1> Class must extend from BlockProcessor. <2> Constructor must receive the name of the block and a Map for sending options to block. <3> process method receives the parent block, a reader, and attributes defined in block. <4> To create a new block we must use createBlock method. We must set the parent document, the context (listing), the text content, attributes and options.

[source]

.Register a Block processor

JavaExtensionRegistry extensionRegistry = this.asciidoctor.javaExtensionRegistry(); // <1>

extensionRegistry.block(“yell”, YellBlock.class); // <2>

String content = asciidoctor.convertFile(new File( “target/test-classes/sample-with-yell-block.adoc”),

new Options()); // <3>

<1> JavaExtensionRegistry class is created. <2> BlockProcessor extension is registered with the context of block. <3> We can call any convert method as usually, no extra parameters are required.

[source,asciidoc]

.Example of Block processor

[yell] <1>

The time is now. Get a move on.

<1> Note that yell is the context where block lives and is the same as the first parameter of block method of ExtensionRegistry class.

=== Block macro processor

This extension creates a block macro named gist for embedding a gist.

[source]

.Block macro processor example

public class GistMacro extends BlockMacroProcessor { // <1>

public GistMacro(String macroName, Map config) { // <2> super(macroName, config); }

@Override public Block process(AbstractBlock parent, String target, Map attributes) { // <3>

String content = "<div class=\"content\">\n" +
   		"<script src=\"https://gist.github.com/"+target+".js\"></script>\n" +
   		"</div>";

return createBlock(parent, "pass", Arrays.asList(content), attributes,
                    this.getConfig()); //<4>

}

}

<1> Class must extend from BlockMacroProcessor. <2> Constructor must receive the macro name, and a Map for sending options to block.. <3> process method receives the parent document, the content of the macro, and attributes defined in macro. <4> To create a new block we must use createBlock method. We must set the parent document, the context (listing), the text content, attributes and options.

[source]

.Register a Block macro processor

JavaExtensionRegistry extensionRegistry = this.asciidoctor.javaExtensionRegistry(); // <1>

extensionRegistry.blockMacro(“gist”, GistMacro.class); // <2>

String content = asciidoctor.convertFile(new File( “target/test-classes/sample-with-gist-macro.adoc”),

new Options()); // <3>

<1> JavaExtensionRegistry class is created. <2> BlockMacroProcessor extension is registered with the name of the macro. <3> We can call any convert method as usually, no extra parameters are required.

[source, asciidoc]

.Example of Block macro processor

.My Gist

gist::123456[] // <1>

<1> Note that gist is the name of the macro and is the same as the first parameter of blockMacro method of ExtensionRegistry class.

=== Inline macro processor

This extension creates an inline macro named man that links to a manpage.

[source]

.Inline macro processor example

public class ManpageMacro extends InlineMacroProcessor { //<1>

public ManpageMacro(String macroName, Map config) { // <2> super(macroName, config); }

@Override protected String process(AbstractBlock parent, String target, Map attributes) { // <3>

  Map<String, Object> options = new HashMap<String, Object>();
  options.put("type", ":link");
  options.put("target", target + ".html");
  return createInline(parent, "anchor", target, attributes, options).convert(); // <4>

}

}

<1> Class must extend from InlineMacroProcessor. <2> Constructor must receive the macro name, and a Map for passing options to extension. <3> process method receives the parent document, the content of the macro, and attributes defined in macro. <4> Because it is an inline macro, only a replacement string must be returned.

[source]

.Register an Inline macro processor

JavaExtensionRegistry extensionRegistry = this.asciidoctor.javaExtensionRegistry(); // <1>

extensionRegistry.inlineMacro(“man”, ManpageMacro.class); // <2>

String content = asciidoctor.convertFile(new File( “target/test-classes/sample-with-man-link.adoc”),

new Options()); // <3>

<1> ExtensionRegistry class is created. <2> InlineMacroProcessor extension is registered with the name of the macro. <3> We can call any convert method as usually, no extra parameters are required.

[source,asciidoc]

.Example of Inline macro processor

See man:gittutorial[7] to get started. <1>

<1> Note that man is the name of the macro and is the same as the first parameter of inlineMacro method of ExtensionRegistry class.

=== Include processor

Include a file from a URI.

[source]

.Include processor example

public class UriIncludeProcessor extends IncludeProcessor { // <1>

public UriIncludeProcessor(Map config) { super(config); }

@Override public boolean handles(String target) { return target.startsWith(“http://”) || target.startsWith(“https://”); // <2> }

@Override public void process(DocumentRuby document, PreprocessorReader reader, String target, Map attributes) {

StringBuilder content = readContent(target);
reader.push_include(content.toString(), target, target, 1, attributes); // <3>

}

private StringBuilder readContent(String target) {

StringBuilder content = new StringBuilder();

try {

  URL url = new URL(target);
  InputStream openStream = url.openStream();

  BufferedReader bufferedReader = new BufferedReader(
                new InputStreamReader(openStream));

  String line = null;
  while ((line = bufferedReader.readLine()) != null) {
    content.append(line);
  }

  bufferedReader.close();

  } catch (MalformedURLException e) {
      throw new IllegalArgumentException(e);
  } catch (IOException e) {
      throw new IllegalArgumentException(e);
  }
  return content;

}

}

<1> Class must extend from IncludeProcessor. <2> handles method is used by processor to decide if included element should be converted by this processor or not. target attribute is the value of include macro. <3> push_include method inserts new content (retrieved from the url) in current position of document.

[source]

.Register an Inline macro processor

JavaExtensionRegistry extensionRegistry = this.asciidoctor.javaExtensionRegistry(); // <1>

extensionRegistry.includeProcessor(UriIncludeProcessor.class); // <2>

String content = asciidoctor.convertFile(new File( “target/test-classes/sample-with-uri-include.adoc”),

new Options()); // <3>

<1> ExtensionRegistry class is created. <2> IncludeProcessor extension is registered. <3> We can call any convert method as usually; no extra parameters are required.

[source,asciidoc] .Example of include processor …. = Example of URI

.Gemfile

[source,ruby]

\include::https://raw.githubusercontent.com/asciidoctor/asciidoctor/master/Gemfile[]

….

=== Unregistering extensions

You can unregister all extensions by calling the org.asciidoctor.Asciidoctor.unregisterAllExtensions() method.

=== Ruby extensions

You can even register extensions written in Ruby using AsciidoctorJ. To register a Ruby extension you must get a RubyExtensionRegistry class instead of JavaExtensionRegistry.

[source]

.Register a Ruby extension in Java

RubyExtensionRegistry rubyExtensionRegistry = this.asciidoctor.rubyExtensionRegistry(); // <1> rubyExtensionRegistry.loadClass(Class.class.getResourceAsStream(“/YellRubyBlock.rb”)).block(“rubyyell”, “YellRubyBlock”); // <2>

String content = asciidoctor.convertFile(new File( “target/test-classes/sample-with-ruby-yell-block.ad”),

options().toFile(false).get());

<1> rubyExtensionRegistry method is called to get a rubyExtensionRegistry instance. <2> Ruby file containing a class implementing a Block extension is loaded inside the Ruby runtime. Then the block is registered with a name (rubyyell), and we pass the name of the class to be instantiated.

[source,ruby]

.YellBlock.rb

require ‘asciidoctor’ require ‘asciidoctor/extensions’

class YellRubyBlock < Asciidoctor::Extensions::BlockProcessor option :contexts, [:paragraph] option :content_model, :simple

def process parent, reader, attributes lines = reader.lines.map {|line| line.upcase.gsub(/.( |$)/, ‘!\1’) } Asciidoctor::Block.new parent, :paragraph, :source => lines, :attributes => attributes end

end

=== Extension SPI

In previous examples, the extensions were registered manually. However, AsciidoctorJ provides another way to register extensions. If any implementation of the SPI interface is present on the classpath, it will be executed.

To create an autoloadable extension you should do next steps.

Create a class that implements org.asciidoctor.extension.spi.ExtensionRegistry.

[source]

.org.asciidoctor.extension.ArrowsAndBoxesExtension.java

public class ArrowsAndBoxesExtension implements ExtensionRegistry { // <1>

@Override public void register(Asciidoctor asciidoctor) { // <2>

JavaExtensionRegistry javaExtensionRegistry = asciidoctor.javaExtensionRegistry();
javaExtensionRegistry.postprocessor(ArrowsAndBoxesIncludesPostProcessor.class); // <3>
javaExtensionRegistry.block("arrowsAndBoxes", ArrowsAndBoxesBlock.class);

}

}

<1> To autoload extensions you need to implement ExtensionRegistry. <2> AsciidoctorJ will automatically run the register method. The method is responsible for registering all extensions. <3> All required Java extensions are registered.

Next, you need to create a file called org.asciidoctor.extension.spi.ExtensionRegistry inside META-INF/services with the implementation’s full qualified name.

[source]

.META-INF/services/org.asciidoctor.extension.spi.ExtensionRegistry

org.asciidoctor.extension.ArrowsAndBoxesExtension

And that’s all. Now when a .jar file containing the previous structure is dropped inside classpath of AsciidoctorJ, the register method will be executed automatically and the extensions will be registered.

== Converting to EPUB3

The Asciidoctor EPUB3 gem (asciidoctor-epub3) is bundled inside the AsciidoctorJ EPUB3 jar (asciidoctorj-epub3). To use it, simply add the asciidoctorj-epub3 jar to your dependencies. The version of the AsciidoctorJ EPUB3 jar aligns with the version of the Asciidoctor EPUB3 gem.

Here’s how you can add the AsciidoctorJ EPUB3 jar to your Maven dependencies:

[source,xml]

org.asciidoctor asciidoctorj-epub3 1.5.0-alpha.4 runtime

Once you’ve added the AsciidoctorJ EPUB3 jar to your classpath, you can set the backend attribute to epub3. The document will be converted to the http://idpf.org/epub/30[EPUB3^] format.

CAUTION: The [app]asciidoctor-epub3 gem is alpha. While it can be used successfully, there may be bugs and its functionality may change in incompatible ways before the first stable release. In other words, by using it, you are also testing it ;)

Let’s see an example of how to use AsciidoctorJ with the EPUB3 converter.

[source,asciidoc]

.spine.adoc

= Book Title Author Name :imagesdir: images <1>

include::content-document.adoc[] <2>

<1> The EPUB3 converter requires the value of the imagesdir attribute to be images. <2> The EPUB3 converter must be run on a spine document that has at least one include directive (and no other body content) in order to function properly.

[source,asciidoc]

.content-document.adoc

= Content Title Author Name

[abstract] This is the actual content.

== First Section

And off we go.

And finally we can convert the document to EPUB3 using AsciidoctorJ.

[source]

asciidoctor.convertFile(new File(“spine.adoc”), options().safe(SafeMode.SAFE).backend(“epub3”).get()); // <1> <2>

assertThat(new File(“target/test-classes/index.epub”).exists(), is(true));

<1> Currently, the EPUB3 converter must be run in SAFE or UNSAFE mode due to a bug <2> epub3 is the name of the backend that must be set to convert to EPUB3.

// TODO document how to convert to KF8/MOBI (need to set KINDLEGEN environment variable) // TODO add section that covers using Asciidoctor PDF via the AsciidoctorJ PDF jar

== Loading Ruby libraries

Simple extensions may be fully implemented in Java, but if you want to create complex extensions you can mix Ruby and Java code. This means that you may need to execute a Ruby file or a RubyGem (i.e., gem) inside your extension.

To load a Ruby file inside the Ruby runtime, you can use org.asciidoctor.internal.RubyUtils.loadRubyClass(Ruby, InputStream). You can also load a gem using an API that wraps Ruby’s require command. The gem must be available inside the classpath. Next run org.asciidoctor.internal.RubyUtils.requireLibrary(Ruby, String), passing the name of the gem as the second argument.

// SW or AS: show an example of loading Ruby libraries or point to an external resource showing examples/further documentation.

== JRuby instance

Sometimes you may need the Ruby runtime used inside AsciidoctorJ. One reason is because you are using JRuby outside AsciidoctorJ and you want to reuse the same instance. Another reason is that you need to instantiate by yourself an Asciidoctor Ruby object.

To get this instance you can use org.asciidoctor.internal.JRubyRuntimeContext.get() to get it.

== GEM_PATH

By default, AsciidoctorJ comes with all required gems bundled within the jar. In certain circumstances, you may want to load gems from an external folder. To accomplish this scenario, create method provides a parameter to set folder where gems are present. Internally, AsciidoctorJ will set GEM_PATH environment variable to given path.

[source]

.Example of setting GEM_PATH

import static org.asciidoctor.Asciidoctor.Factory.create; import org.asciidoctor.Asciidoctor;

Asciidoctor asciidoctor = create(“/my/gem/path”); // <1>

<1> Creates an Asciidoctor instance with given GEM_PATH location.

== Using AsciidoctorJ in an OSGi environment

In a non OSGi context, the following snippet will successfully create an Asciidoctor object:

[source,java]

import static org.asciidoctor.Asciidoctor.Factory.create; import org.asciidoctor.Asciidoctor;

Asciidoctor asciidoctor = create();

In an OSGi context it will not work because JRuby needs some paths to find the gems (the Asciidoctor ones and the Ruby themselves ones). In order to make it work, you will need two more classes (RubyInstanceConfig and JavaEmbedUtils) and a small modification of the previous snippet of code. The modifications take care of the class loaders because in OSGi, which are a key point in OSGi:

[source,java]

import static org.asciidoctor.Asciidoctor.Factory.create; import org.asciidoctor.Asciidoctor;

RubyInstanceConfig config = new RubyInstanceConfig(); config.setLoader(this.getClass().getClassLoader()); <1>

JavaEmbedUtils.initialize(Arrays.asList(“META-INF/jruby.home/lib/ruby/2.0”, “gems/asciidoctor-1.5.4/lib”), config); <2><3><4>

Asciidoctor asciidoctor = create(this.getClass().getClassLoader()); <5>

<1> The RubyInstanceConfig will use the class loader of the OSGi bundle ; <2> The JavaEmbedUtils will specify the load paths of the required gems. If they are not specified, you will get JRuby exceptions ; <3> META-INF/jruby.home/lib/ruby/2.0 specifies where the Ruby gems are located. Actually this path is located inside the jruby-complete-<version>.jar file. Without having this path specified you may get an org.jruby.exceptions.RaiseException: (LoadError) no such file to load -- set error ; <4> gems/asciidoctor-<version>/lib specifies where the gems for Asciidoctor are located. Actually this path is located inside the asciidoctorj-<version>.jar file ; <5> The factory for the Asciidoctor object also specify the class loader to use.

[NOTE] We consider this code to be placed inside an OSGi bundle

This solution has pros and cons:

  • Pros: you don’t need to extract the gems located in the asciidoctorj binary ;
  • Cons: ** the version of asciidoctor is hard coded ; ** the version of ruby is hard coded.

== Optimization

JRuby may start slower than expected versus the C-based Ruby implementation (MRI). Fortunately, JRuby offers flags that can improve the start time and tune applications. Several Java flags can also be used in conjunction with or apart from the JRuby flags in order to improve the start time even more.

// SW: Need examples of JRuby and Java flags being used

For small tasks such as converting an AsciiDoc document, two JRuby flags can drastically improve the start time:

[cols=“1m,2”, width=“50%”] .JRuby flags |=== |Name |Value

|jruby.compat.version |RUBY1_9

|jruby.compile.mode |OFF |===

Both flags are set by default inside AsciidoctorJ.

The Java flags available for improving start time depend on whether your working on a 32- or 64-bit processor and your JDK version. These flags are set by using the JRUBY_OPTS environment variable. Let’s see a summary of these flags and in which environments they can be used.

[cols=“1m,2”,width=“75%”] .Java flags |=== |Name |JDK

|-client |32 bit Java

|-Xverify:none |3264 bit Java

|-XX:+TieredCompilation |3264 bit Java SE 7

|-XX:TieredStopAtLevel=1 |3264 bit Java SE 7 |===

[source,shell]

.Setting flags for Java SE 6

export JRUBY_OPTS=“-J-Xverify:none -J-client” # <1>

<1> Note that you should add -J before the flag.

You can find a full explanation on how to improve the start time of JRuby applications in <>.

[[wildfly-kludge]] == Running AsciidoctorJ on WildFly AS

If you want to use AsciidoctorJ in an application deployed on [app]WildFly AS, you have to install AsciidoctorJ as a JBoss Module.

Follow the steps below:

. Create an Asciidoctor module for [app]WildFly AS. . Create the following folder tree: [path]_$JBOSSHOME/modules/org/asciidoctor/main. . Create the module descriptor file [path]module.xml. + [source,xml]

.Asciidoctor module descriptor for WildFly AS

<?xml version=“1.0” encoding=“UTF-8”?>

. Copy the jar files into the same folder as the module.xml file. . Make sure the version numbers of the jar files agree with what’s in the current set. Restart WildFly for the new module to take effect.

. Add a dependency on your Java archive to this WildFly module using one of the following options: .. Add the dependency just into the [path]MANIFEST.MF file. +

.MANIFEST.MF file example with dependency to Asciidoctor module

Manifest-Version: 1.0 Dependencies: org.asciidoctor

.. Or, configure the dependency into the [path]pom.xml with the [app]Maven JAR/WAR plugin. + [source,xml] [subs=“specialcharacters,attributes,callouts”]

.pom.xml file example with Maven WAR plugin configuration to add a dependency

org.asciidoctor asciidoctorj {artifact-version} provided org.apache.maven.plugins maven-war-plugin 2.4 org.asciidoctor

<1> The AsciidoctorJ dependency and the transitives dependencies don’t need to be added to the final WAR since all JARs are available through the module. <2> The module dependency will be added to the [path]MANIFEST.MF file.

== Using a pre-release version

Pre-release versions of AsciidoctorJ are published to Bintray. You can find them in https://bintray.com/asciidoctor/maven/asciidoctorj/view. Final releases are released to both Maven Central and Bintray.

Here’s how to use a pre-release version in Maven:

[source, xml]

central bintray http://dl.bintray.com/asciidoctor/maven false

== Development

AsciidoctorJ is built using {uri-gradle}[Gradle]. The project is structured as a multi-module build.

=== Project layout

The root folder is the root project and there are several subproject folders, each prefixed with asciidoctorj-. Each subproject produces a primary artifact (e.g., jar or zip) and its supporting artifacts (e.g., javadoc, sources, etc).

The subprojects are as follows:

asciidoctorj:: The main Java bindings for the Asciidoctor RubyGem (asciidoctor). Also bundles optional RubyGems needed at runtime, such as coderay, tilt, haml and slim. Produces the asciidoctorj jar.

asciidoctorj-distribution:: Produces the distribution zip that provides the standalone asciidoctorj command.

asciidoctorj-epub3:: Bundles the Asciidoctor EPUB3 RubyGem (asciidoctor-pdf) and its dependencies as the asciidoctorj-epub3 jar.

asciidoctorj-pdf:: Bundles the Asciidoctor PDF RubyGem (asciidoctor-pdf) and its dependencies as the asciidoctorj-pdf jar.

The Gradle build is partitioned into the following files:

…. build.gradle gradle.properties settings.gradle gradle/ wrapper/ … deploy.gradle eclipse.gradle idea.gradle publish.gradle sign.gradle asciidoctorj-core/ build.gradle asciidoctorj-distribution/ build.gradle asciidoctorj-epub3/ build.gradle asciidoctorj-pdf/ build.gradle ….

=== Build the project

You invoke Gradle on this project using the gradlew command (i.e., the Gradle Wrapper).

TIP: We strongly recommend that you use Gradle via the https://www.timroes.de/2013/09/12/speed-up-gradle[Gradle daemon].

To clone the project, compile the source and build the artifacts (i.e., jars) locally, run:

$ git clone https://github.com/asciidoctor/asciidoctorj cd asciidoctorj ./gradlew assemble

You can find the built artifacts in the [path]asciidoctorj-*/build/libs folders.

To execute tests when running the build, use:

$ ./gradlew build

To only execute the tests, run:

$ ./gradlew check

You can also run tests for a single module:

$ cd asciidoctorj-core ../gradlew check

To run a single test in the asciidoctorj-core subproject, use:

$ ../gradlew -Dsingle.test=NameOfTestClass test

To create the distribution, run:

$ ./gradlew distZip

You can find the distribution in the [path]asciidoctorj-distribution/build/distributions folder.

=== Develop in an IDE

==== IntelliJ IDEA

To import the project into IntelliJ IDEA 14, simply import the project using the import wizard. For more information, see the https://www.jetbrains.com/idea/help/gradle.html[Gradle page] in the IntelliJ IDEA Web Help.

==== Eclipse

To open the project in Eclipse, first generate the Eclipse project files:

$ cd asciidoctorj-core ./gradlew eclipse

Then, import the project into Eclipse using menu:File[Import,General,Existing Project into Workspace].

=== Continuous integration

Continuous integration for the AsciidoctorJ project is performed by Travis CI. You can find recent build results, including the build status of pull requests, on the https://travis-ci.org/asciidoctor/asciidoctorj[asciidoctor/asciidoctorj] page.

=== Publish the artifacts

Artifacts are published to Maven Central and jCenter by way of Bintray’s Distribution as a Service platform.

Before publishing, you need to configure your gpg signing and Bintray credentials. Create the file [path]$HOME/.gradle/gradle.properties and populate the following properties.


signing.keyId= signing.password= signing.secretKeyRingFile=/home/YOUR_USERNAME/.gnupg/secring.gpg bintrayUsername=

bintrayApiKey=

To build, assemble and sign the archives (jars and distribution zip), run:

$ ./gradlew -PpublishRelease=true signJars

TIP: The publishRelease=true property is technically only required if the version is a snapshot.

To build, assemble (but not sign) and install the archives (jars and distribution zip) into the local Maven repository, run:

$ ./gradlew -PpublishRelease=true install

To build, assemble, sign and publish the archives (jars and distribution zip) to Bintray, run:

$ ./gradlew clean ./gradlew -i -x pMNPTML bintrayUpload

CAUTION: Don’t run the clean task in the same execution as the bintrayUpload because it will not upload one of the signatures.

If you want to first perform a dry run of the upload, add the dryRun=true property.

$ ./gradlew -i -PdryRun=true -x pMNPTML bintrayUpload

NOTE: The -x pMNPTML is necessary to work around a bug in the publishing plugin that prevents it from signing the archives.

IMPORTANT: Bintray does not allow you to publish snapshots. You have

Related Repositories

asciidoctorj

asciidoctorj

:coffee: Java bindings for Asciidoctor. Asciidoctor on the JVM! ...

asciiblog

asciiblog

Java EE blog application with Git and AsciidoctorJ integration ...

asciidoctorj-screenshot-example

asciidoctorj-screenshot-example

Usage example for the asciidoctorj-sreenshot extension ...

asciidoctorj-extensions-lab

asciidoctorj-extensions-lab

A collection of extensions for AsciidoctorJ that may be freely reused ...

adoc

adoc

A Clojure wrapper for asciidoctorj ...


Top Contributors

lordofthejars mojavelinux robertpanzer ancho IanDarwin mtolk lefou Mogztter aalmiray LightGuard ysb33r benevans nawroth ahus1 DavidGamba gregturn htmfilho johncarl81 ktoso smigielski madmas seanhussey sschuberth twasyl abelsromero bvandaele

Releases

-   v1.6.0-alpha.3 zip tar
-   v1.6.0-alpha.2 zip tar
-   v1.6.0-alpha.1 zip tar
-   v1.5.4.1 zip tar
-   v1.5.4 zip tar
-   v1.5.3.2 zip tar
-   v1.5.3.1 zip tar
-   v1.5.3 zip tar
-   asciidoctorj-pdf-1.5 zip tar
-   asciidoctorj-pdf-1.5 zip tar
-   asciidoctorj-pdf-1.5 zip tar
-   asciidoctorj-diagram zip tar
-   asciidoctorj-1.5.2 zip tar
-   asciidoctorj-1.5.1 zip tar
-   asciidoctorj-1.5.0 zip tar
-   asciidoctor-java-int zip tar
-   asciidoctor-java-int zip tar
-   asciidoctor-java-int zip tar
-   asciidoctor-java-int zip tar
-   asciidoctor-java-int zip tar
-   asciidoctor-java-int zip tar
-   1.6.0-alpha.1 zip tar
-   1.5.0.preview2 zip tar