Skip to content

Commit 804374d

Browse files
committed
Add PDF backend
Add a PDF backend that applies the same code chomping rules as the HTML backend and also removed any `@fold` comments. Closes gh-45
1 parent dd93dcc commit 804374d

21 files changed

+451
-51
lines changed

build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,11 @@ repositories {
4141
dependencies {
4242
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:$springJavaFormatVersion")
4343
compileOnly("org.asciidoctor:asciidoctorj:$asciidoctorjVersion")
44+
compileOnly("org.asciidoctor:asciidoctorj-pdf:$asciidoctorjPdfVersion")
4445
testImplementation("com.google.guava:guava:30.1-jre")
46+
testImplementation("org.apache.pdfbox:pdfbox:2.0.23")
4547
testImplementation("org.asciidoctor:asciidoctorj:$asciidoctorjVersion")
48+
testImplementation("org.asciidoctor:asciidoctorj-pdf:$asciidoctorjPdfVersion")
4649
testImplementation("org.assertj:assertj-core:3.11.1")
4750
testImplementation("org.jsoup:jsoup:1.13.1")
4851
testImplementation("org.junit.jupiter:junit-jupiter:5.6.0")

gradle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ org.gradle.caching=true
44
org.gradle.parallel=true
55

66
asciidoctorjVersion=2.4.1
7+
asciidoctorjPdfVersion=1.5.3
78
springJavaFormatVersion=0.0.27
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.spring.asciidoctor.backend.codetools;
18+
19+
import org.asciidoctor.ast.Block;
20+
21+
/**
22+
* {@link ListingContentConverter} to remove {@code fold} tags.
23+
*
24+
* @author Phillip Webb
25+
*/
26+
class FoldRemovalListingContentConverter implements ListingContentConverter {
27+
28+
@Override
29+
public String convert(Block listingBlock, String content) {
30+
Options<FoldOption> options = Options.get(listingBlock, "fold", FoldOption.class, FoldOption.DEFAULTS);
31+
String lang = (String) listingBlock.getAttribute("language");
32+
if ("java".equals(lang) && options.has(FoldOption.TAGS)) {
33+
return removeFoldTags(content);
34+
}
35+
return content;
36+
}
37+
38+
private String removeFoldTags(String content) {
39+
StringBuilder result = new StringBuilder();
40+
String[] lines = content.split("\n\r?");
41+
for (String line : lines) {
42+
if (!isFoldLine(line)) {
43+
result.append(line);
44+
result.append("\n");
45+
}
46+
}
47+
return result.toString();
48+
}
49+
50+
private boolean isFoldLine(String line) {
51+
return line.trim().startsWith("//") && line.contains("@fold:");
52+
}
53+
54+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.spring.asciidoctor.backend.codetools;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
22+
import org.jruby.RubyObject;
23+
24+
/**
25+
* Utility called by the Ruby code to apply modifications to HTML code listings.
26+
*
27+
* @author Phillip Webb
28+
*/
29+
public final class HtmlListingContentConverters {
30+
31+
private static final ListingContentConverters converters;
32+
static {
33+
List<ListingContentConverter> converterInstances = new ArrayList<>();
34+
converterInstances.add(new ChompListingContentConverter());
35+
converterInstances.add(new FoldListingContentConverter());
36+
converters = new ListingContentConverters(converterInstances);
37+
}
38+
39+
private HtmlListingContentConverters() {
40+
}
41+
42+
/**
43+
* Convert the content of the given node before it is added to the HTML.
44+
* @param node the node to convert
45+
* @return the converted content
46+
*/
47+
public static RubyObject content(RubyObject node) {
48+
return converters.content(node);
49+
}
50+
51+
}

src/main/java/io/spring/asciidoctor/backend/codetools/ListingContentConverter.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
import org.asciidoctor.ast.Block;
2020

2121
/**
22-
* Strategy interface used by {@link ListingContentConverters} to convert listing content.
22+
* Strategy interface used by {@link HtmlListingContentConverters} to convert listing
23+
* content.
2324
*
2425
* @author Phillip Webb
2526
*/

src/main/java/io/spring/asciidoctor/backend/codetools/ListingContentConverters.java

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package io.spring.asciidoctor.backend.codetools;
1818

19-
import java.util.ArrayList;
2019
import java.util.Collections;
2120
import java.util.List;
2221

@@ -25,32 +24,22 @@
2524
import org.jruby.RubyObject;
2625

2726
/**
28-
* Utility called by the Ruby code to apply modifications to code listings.
27+
* A collection of {@link ListingContentConverter} instances.
2928
*
3029
* @author Phillip Webb
3130
*/
32-
public final class ListingContentConverters {
33-
34-
private static final List<ListingContentConverter> converters;
35-
static {
36-
List<ListingContentConverter> converterInstances = new ArrayList<>();
37-
converterInstances.add(new ChompListingContentConverter());
38-
converterInstances.add(new FoldListingContentConverter());
39-
converters = Collections.unmodifiableList(converterInstances);
40-
}
31+
public class ListingContentConverters {
32+
33+
private final List<ListingContentConverter> converters;
4134

42-
private ListingContentConverters() {
35+
ListingContentConverters(List<ListingContentConverter> converters) {
36+
this.converters = Collections.unmodifiableList(converters);
4337
}
4438

45-
/**
46-
* Convert the content of the given node before it is added to the HTML.
47-
* @param node the node to convert
48-
* @return the converted content
49-
*/
50-
public static RubyObject content(RubyObject node) {
39+
RubyObject content(RubyObject node) {
5140
Block listingBlock = (Block) NodeConverter.createASTNode(node);
5241
String content = (String) listingBlock.getContent();
53-
for (ListingContentConverter converter : converters) {
42+
for (ListingContentConverter converter : this.converters) {
5443
content = (content != null) ? content : "";
5544
content = converter.convert(listingBlock, content);
5645
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.spring.asciidoctor.backend.codetools;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
22+
import org.jruby.RubyObject;
23+
24+
/**
25+
* Utility called by the Ruby code to apply modifications to PDF code listings.
26+
*
27+
* @author Phillip Webb
28+
*/
29+
public final class PdfListingContentConverters {
30+
31+
private static final ListingContentConverters converters;
32+
static {
33+
List<ListingContentConverter> converterInstances = new ArrayList<>();
34+
converterInstances.add(new ChompListingContentConverter());
35+
converterInstances.add(new FoldRemovalListingContentConverter());
36+
converters = new ListingContentConverters(converterInstances);
37+
}
38+
39+
private PdfListingContentConverters() {
40+
}
41+
42+
/**
43+
* Convert the content of the given node before it is added to the HTML.
44+
* @param node the node to convert
45+
* @return the converted content
46+
*/
47+
public static RubyObject content(RubyObject node) {
48+
return converters.content(node);
49+
}
50+
51+
}

src/main/ruby/lib/spring-asciidoctor-backends.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,8 @@
1717

1818
require_relative "spring-asciidoctor-backends/spring-docinfo-processor"
1919
require_relative "spring-asciidoctor-backends/spring-html5-converter"
20+
21+
begin
22+
require_relative "spring-asciidoctor-backends/spring-pdf-converter"
23+
rescue LoadError
24+
end

src/main/ruby/lib/spring-asciidoctor-backends/spring-html5-converter.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def convert_listing(node)
102102

103103
def convert_listing_content(node)
104104
begin
105-
return Java::IoSpringAsciidoctorBackendCodetools::ListingContentConverters.content node
105+
return Java::IoSpringAsciidoctorBackendCodetools::HtmlListingContentConverters.content node
106106
rescue NameError
107107
return node.content
108108
end
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#
2+
# Copyright 2021 the original author or authors.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# https://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
# coding: utf-8
17+
18+
require "asciidoctor-pdf"
19+
20+
begin
21+
require "java"
22+
rescue LoadError
23+
end
24+
25+
class SpringPdfConverter < Asciidoctor::PDF::Converter
26+
27+
register_for "spring-pdf"
28+
29+
def convert_listing_or_literal node
30+
if (node.style != 'source')
31+
return super
32+
end
33+
add_dest_for_block node if node.id
34+
source_string = guard_indentation convert_listing_content(node)
35+
source_chunks = (XMLMarkupRx.match? source_string) ? (text_formatter.format source_string) : [text: source_string]
36+
adjusted_font_size = ((node.option? 'autofit') || (node.document.attr? 'autofit-option')) ? (theme_font_size_autofit source_chunks, :code) : nil
37+
theme_margin :block, :top
38+
keep_together do |box_height = nil|
39+
caption_height = node.title? ? (layout_caption node, category: :code) : 0
40+
theme_font :code do
41+
theme_fill_and_stroke_block :code, (box_height - caption_height), background_color: nil, split_from_top: false if box_height
42+
pad_box @theme.code_padding do
43+
typeset_formatted_text source_chunks, (calc_line_metrics @theme.code_line_height || @theme.base_line_height),
44+
color: (@theme.code_font_color || @font_color),
45+
size: adjusted_font_size
46+
end
47+
end
48+
end
49+
stroke_horizontal_rule @theme.caption_border_bottom_color if node.title? && @theme.caption_border_bottom_color
50+
theme_margin :block, :bottom
51+
end
52+
53+
def convert_listing_content(node)
54+
begin
55+
return Java::IoSpringAsciidoctorBackendCodetools::PdfListingContentConverters.content node
56+
rescue NameError
57+
return node.content
58+
end
59+
end
60+
61+
62+
alias convert_listing convert_listing_or_literal
63+
64+
end

src/test/gradle/build.gradle

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
plugins {
2-
id 'org.asciidoctor.jvm.convert' version '3.1.0'
2+
id 'org.asciidoctor.jvm.convert' version '3.3.2'
33
}
44

55
repositories {
@@ -15,8 +15,10 @@ configurations {
1515

1616
dependencies {
1717
asciidoctorExtensions "io.spring.asciidoctor.backends:spring-asciidoctor-backends:" + System.getenv("spring-asciidoctor-backends.version")
18+
asciidoctorExtensions "org.asciidoctor:asciidoctorj-pdf:1.5.3"
1819
}
1920

21+
2022
asciidoctor {
2123
configurations "asciidoctorExtensions"
2224
sourceDir "src/asciidoc"
@@ -25,3 +27,13 @@ asciidoctor {
2527
backends "spring-html"
2628
}
2729
}
30+
31+
task asciidoctorPdf(type: org.asciidoctor.gradle.jvm.AsciidoctorTask) {
32+
configurations "asciidoctorExtensions"
33+
sourceDir "src/asciidoc"
34+
baseDirFollowsSourceDir()
35+
copyNoResources()
36+
outputOptions {
37+
backends "spring-pdf"
38+
}
39+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.spring.asciidoctor.backend.codetools;
18+
19+
import io.spring.asciidoctor.backend.testsupport.AsciidoctorExtension;
20+
import io.spring.asciidoctor.backend.testsupport.ConvertedPdf;
21+
import org.junit.jupiter.api.Test;
22+
import org.junit.jupiter.api.extension.ExtendWith;
23+
24+
import static org.assertj.core.api.Assertions.assertThat;
25+
26+
/**
27+
* Tests for {@link FoldRemovalListingContentConverter}.
28+
*
29+
* @author Phillip Webb
30+
*/
31+
@ExtendWith(AsciidoctorExtension.class)
32+
class FoldRemovalListingContentConverterTests {
33+
34+
@Test
35+
void convertWhenHasFoldCommentReturnsRemoved(ConvertedPdf pdf) {
36+
assertThat(pdf.toString()).doesNotContain("@fold");
37+
}
38+
39+
@Test
40+
void convertWhenHasCodeFoldingDisabledAsDocumentAttributeReturnsUnchanged(ConvertedPdf pdf) {
41+
assertThat(pdf.toString()).contains("@fold");
42+
}
43+
44+
}

0 commit comments

Comments
 (0)