asciidoctor-pdf 1.5.0.beta.4 → 1.5.0.beta.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +11 -0
- data/README.adoc +30 -30
- data/docs/theming-guide.adoc +187 -7
- data/lib/asciidoctor-pdf/converter.rb +100 -46
- data/lib/asciidoctor-pdf/formatted_text/transform.rb +57 -45
- data/lib/asciidoctor-pdf/theme_loader.rb +1 -1
- data/lib/asciidoctor-pdf/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f6a1fbcfb416a485f625f2b12c4b7c30806e0d05cdf3ecc327b2ae6b0b04003e
|
4
|
+
data.tar.gz: bb1f4811237d7478cd132474175e11fbc1fa9e4755a0888ba1bb633215174b16
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f8ed0674cbc59c95bd5408874381d24708d3d07f3c6762297e9352727ddab3a67afae2e32bd48585feac84f57f9cfc03d8cee1388003617772a457591354793a
|
7
|
+
data.tar.gz: 1d38fa4fce88891b32d15b52829244222f5a1ada6269ad6108ed38cda7ed8a202d8793a4dab1ff9eadd597c53768994e0bf844e94587752d5ce51f7801677152
|
data/CHANGELOG.adoc
CHANGED
@@ -5,6 +5,17 @@
|
|
5
5
|
This document provides a high-level view of the changes to the {project-name} by release.
|
6
6
|
For a detailed view of what has changed, refer to the {uri-repo}/commits/master[commit history] on GitHub.
|
7
7
|
|
8
|
+
== 1.5.0.beta.5 (2019-09-13) - @mojavelinux
|
9
|
+
|
10
|
+
* pass styles for inline elements downwards when parsing, allowing role to override default styles for element (#1219)
|
11
|
+
* document title in outline should point to second page if document has cover page (#1268)
|
12
|
+
* start at setting for running content and page numbering must account for disabled title page (book doctype) (#1263)
|
13
|
+
* start at setting for running content and page numbering must account for front cover (#1266)
|
14
|
+
* preserve indentation that uses tabs in verbatim blocks when tabsize is not set (#1258)
|
15
|
+
* use consistent line height for list items and toc entries if text is entirely monospace (#1204)
|
16
|
+
* fix spacing between items in qanda list
|
17
|
+
* expand home directory reference in theme name when value ends with .yml and no themedir is specified
|
18
|
+
|
8
19
|
== 1.5.0.beta.4 (2019-09-04) - @mojavelinux
|
9
20
|
|
10
21
|
* always use ; as delimiter to separate multiple font dirs to be compatible with JAR paths (#1250)
|
data/README.adoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
= Asciidoctor PDF: A native PDF converter for AsciiDoc
|
2
2
|
Dan Allen <https://github.com/mojavelinux[@mojavelinux]>; Sarah White <https://github.com/graphitefriction[@graphitefriction]>
|
3
|
-
v1.5.0.beta.
|
3
|
+
v1.5.0.beta.5, 2019-09-13
|
4
4
|
// Settings:
|
5
5
|
:experimental:
|
6
6
|
:idprefix:
|
@@ -24,25 +24,25 @@ endif::[]
|
|
24
24
|
:project-name: Asciidoctor PDF
|
25
25
|
:project-handle: asciidoctor-pdf
|
26
26
|
// Variables:
|
27
|
-
:release-version: 1.5.0.beta.
|
27
|
+
:release-version: 1.5.0.beta.5
|
28
28
|
// URIs:
|
29
|
-
:
|
30
|
-
:
|
31
|
-
:
|
32
|
-
:
|
33
|
-
:
|
34
|
-
:
|
35
|
-
:
|
36
|
-
:
|
37
|
-
:
|
38
|
-
:
|
39
|
-
:
|
40
|
-
:
|
29
|
+
:url-asciidoctor: http://asciidoctor.org
|
30
|
+
:url-gem: http://rubygems.org/gems/asciidoctor-pdf
|
31
|
+
:url-project: https://github.com/asciidoctor/asciidoctor-pdf
|
32
|
+
:url-project-repo: {url-project}
|
33
|
+
:url-project-issues: {url-project-repo}/issues
|
34
|
+
:url-project-list: http://discuss.asciidoctor.org
|
35
|
+
:url-prawn: http://prawnpdf.org
|
36
|
+
:url-prawn-gmagick: https://github.com/packetmonkey/prawn-gmagick
|
37
|
+
:url-prawn-svg: https://github.com/mogest/prawn-svg
|
38
|
+
:url-asciidoctor-mathematical: https://github.com/asciidoctor/asciidoctor-mathematical
|
39
|
+
:url-rvm: http://rvm.io
|
40
|
+
:url-graphicsmagick: http://www.graphicsmagick.org
|
41
41
|
|
42
42
|
ifdef::status[]
|
43
43
|
image:https://img.shields.io/travis/asciidoctor/asciidoctor-pdf/master.svg[Build Status (Travis CI),link=https://travis-ci.org/asciidoctor/asciidoctor-pdf]
|
44
44
|
image:https://ci.appveyor.com/api/projects/status/524bhoms3j2dp1o3/branch/master?svg=true[Build Status (AppVeyor),link=https://ci.appveyor.com/project/asciidoctor/asciidoctor-pdf]
|
45
|
-
image:https://img.shields.io/gem/v/asciidoctor-pdf.svg[Latest Release, link={
|
45
|
+
image:https://img.shields.io/gem/v/asciidoctor-pdf.svg[Latest Release, link={url-gem}]
|
46
46
|
image:https://img.shields.io/badge/license-MIT-blue.svg[MIT License, link=#copyright]
|
47
47
|
endif::[]
|
48
48
|
|
@@ -53,7 +53,7 @@ Instead, you can use it to convert directly from AsciiDoc to PDF.
|
|
53
53
|
Its aim is to take the pain out of creating PDF documents from AsciiDoc.
|
54
54
|
endif::[]
|
55
55
|
ifndef::env-site[]
|
56
|
-
_Lo and behold_, a native PDF converter for AsciiDoc built with {
|
56
|
+
_Lo and behold_, a native PDF converter for AsciiDoc built with {url-asciidoctor}[Asciidoctor] and {url-prawn}[Prawn]! +
|
57
57
|
_No more middleman._ +
|
58
58
|
_No more DocBook toolchain._ +
|
59
59
|
It's AsciiDoc straight to PDF!
|
@@ -62,10 +62,10 @@ toc::[]
|
|
62
62
|
|
63
63
|
== Prawn, the Majestic PDF Generator
|
64
64
|
|
65
|
-
{
|
65
|
+
{url-project}[{project-name}] is made possible by an amazing Ruby gem named Prawn.
|
66
66
|
And what a gem it is!
|
67
67
|
|
68
|
-
{
|
68
|
+
{url-prawn}[Prawn] is a nimble PDF writer for Ruby.
|
69
69
|
More important, it's a hackable platform that offers both high level APIs for the most common needs and low level APIs for bending the document model to accommodate special circumstances.
|
70
70
|
|
71
71
|
With Prawn, you can write text, draw lines and shapes and place images _anywhere_ on the page and add as much color as you like.
|
@@ -542,7 +542,7 @@ If an image dimension matches the height or width of the page, the positioning k
|
|
542
542
|
|
543
543
|
== Fonts in SVG Images
|
544
544
|
|
545
|
-
Asciidoctor PDF uses {
|
545
|
+
Asciidoctor PDF uses {url-prawn-svg}[prawn-svg] to embed SVGs in the PDF document, including SVGs generated by Asciidoctor Diagram.
|
546
546
|
|
547
547
|
Actually, it's not accurate to say that prawn-svg embeds the SVG.
|
548
548
|
Rather, prawn-svg is an SVG _renderer_.
|
@@ -583,17 +583,17 @@ If you're using fonts in your SVG, and you want those fonts to be preserved, tho
|
|
583
583
|
|
584
584
|
In order to embed an image into a PDF, Asciidoctor PDF must understand how to read it.
|
585
585
|
To perform this work, Asciidoctor delegates to the underlying libraries.
|
586
|
-
{
|
587
|
-
{
|
586
|
+
{url-prawn}[Prawn] provides support for reading JPG and PNG images.
|
587
|
+
{url-prawn-svg}[prawn-svg] brings support for SVG images.
|
588
588
|
Without any additional libraries, those are the only supported image file formats.
|
589
589
|
|
590
|
-
If you need support for additional image formats, such as GIF, TIFF, or interlaced PNG--and you don't want to convert those images to a supported format--you must install the {
|
591
|
-
prawn-gmagick is an extension for Prawn based on {
|
590
|
+
If you need support for additional image formats, such as GIF, TIFF, or interlaced PNG--and you don't want to convert those images to a supported format--you must install the {url-prawn-gmagick}[prawn-gmagick] (>= 0.0.9) Ruby gem.
|
591
|
+
prawn-gmagick is an extension for Prawn based on {url-graphicsmagick}[GraphicsMagick] that adds support for all the image formats recognized by that library.
|
592
592
|
prawn-gmagick has the added benefit of significantly reducing the time it takes to generate a PDF containing a lot of images.
|
593
593
|
|
594
594
|
The prawn-gmagick gem uses native extensions to compile against GraphicsMagick.
|
595
595
|
This system prerequisite limits installation to Linux and OSX.
|
596
|
-
Please refer to the {
|
596
|
+
Please refer to the {url-prawn-gmagick}[README for prawn-gmagick] to learn how to install it.
|
597
597
|
|
598
598
|
Once this gem is installed, Asciidoctor automatically switches over to it to handle embedding of all images.
|
599
599
|
In addition to support for more additional image file formats, this gem also speeds up image processing considerably, so we highly recommend using it if you can.
|
@@ -734,13 +734,13 @@ That prototype can be found in the https://github.com/asciidoctor/asciidoctor-ex
|
|
734
734
|
|
735
735
|
=== Asciidoctor Mathematical
|
736
736
|
|
737
|
-
{
|
737
|
+
{url-asciidoctor-mathematical}[Asciidoctor Mathematical] is an extension for Asciidoctor that provides alternate processing of STEM blocks and inline macros (currently only latexmath).
|
738
738
|
After the document has been parsed, the extension locates all the latexmath blocks and inline macros, converts the equations to images using Mathematical, then replaces them with image nodes.
|
739
739
|
Conversion then proceeds as normal.
|
740
740
|
|
741
741
|
Asciidoctor Mathematical is a Ruby gem that uses native extensions.
|
742
742
|
It has a few system prerequisites which limit installation to Linux and OSX.
|
743
|
-
Please refer to the {
|
743
|
+
Please refer to the {url-asciidoctor-mathematical}#installation[installation section] in the Asciidoctor Mathematical README to learn how to install it.
|
744
744
|
|
745
745
|
Once Asciidoctor Mathematical is installed, you just need to enable it when invoking Asciidoctor PDF using the `-r` flag:
|
746
746
|
|
@@ -760,7 +760,7 @@ You control this setting using the `mathematical-format` AsciiDoc attribute:
|
|
760
760
|
|
761
761
|
$ asciidoctor-pdf -r asciidoctor-mathematical -a mathematical-format=svg sample.adoc
|
762
762
|
|
763
|
-
Refer to the {
|
763
|
+
Refer to the {url-asciidoctor-mathematical}#readme[README] for Asciidoctor Mathematical to learn about additional settings and options.
|
764
764
|
|
765
765
|
== Skipping Passthrough Content
|
766
766
|
|
@@ -951,7 +951,7 @@ To contribute code, simply fork the project on GitHub, hack away and send a pull
|
|
951
951
|
*All pull requests must include a) tests that verify the code change and b) an entry in the CHANGELOG.adoc file to document what changed.*
|
952
952
|
If a pull request is missing tests or a CHANGELOG entry, *it will not be merged*.
|
953
953
|
|
954
|
-
Feel free to use the {
|
954
|
+
Feel free to use the {url-project-issues}[issue tracker] or {url-project-list}[Asciidoctor mailing list] to provide feedback or suggestions in other ways.
|
955
955
|
|
956
956
|
== Development
|
957
957
|
|
@@ -967,7 +967,7 @@ You can retrieve the source of {project-name} in one of two ways:
|
|
967
967
|
|
968
968
|
==== Option 1: Fetch Using Git
|
969
969
|
|
970
|
-
If you want to clone the git repository, simply copy the {
|
970
|
+
If you want to clone the git repository, simply copy the {url-project-repo}[GitHub repository URL] and pass it to the `git clone` command:
|
971
971
|
|
972
972
|
$ git clone https://github.com/asciidoctor/asciidoctor-pdf
|
973
973
|
|
@@ -986,7 +986,7 @@ We'll leverage the project configuration to install the necessary dependencies.
|
|
986
986
|
|
987
987
|
=== Install Dependencies
|
988
988
|
|
989
|
-
If you're using {
|
989
|
+
If you're using {url-rvm}[RVM], we recommend creating a new gemset to work with {project-name}:
|
990
990
|
|
991
991
|
$ rvm use 2.5@asciidoctor-pdf --create
|
992
992
|
|
data/docs/theming-guide.adoc
CHANGED
@@ -22,6 +22,7 @@ ifndef::icons[:conum-guard-yaml: # #]
|
|
22
22
|
ifdef::backend-pdf[:conum-guard-yaml: # #]
|
23
23
|
:url-fontforge: https://fontforge.github.io/en-US/
|
24
24
|
:url-fontforge-scripting: https://fontforge.github.io/en-US/documentation/scripting/
|
25
|
+
:url-prawn: http://prawnpdf.org
|
25
26
|
|
26
27
|
////
|
27
28
|
Topics remaining to document:
|
@@ -734,7 +735,7 @@ Cannot be styled as italic, bold or bold_italic.
|
|
734
735
|
Used as the fallback font in the `default-with-fallback-font` theme.
|
735
736
|
|
736
737
|
TIP: If you want to specify the location of custom fonts using the `pdf-fontsdir` attribute, yet still be able to use the bundled fonts, you need to refer to the bundled fonts using the `GEM_FONTS_DIR` token.
|
737
|
-
To do so, you can either a) prefix the path of the bundled font in the theme file with the segment `GEM_FONTS_DIR` (e.g., `GEM_FONTS_DIR/mplus1p-regular-fallback.ttf`, or b) include `GEM_FONT_DIR` in the value of the `pdf-fontsdir` attribute
|
738
|
+
To do so, you can either a) prefix the path of the bundled font in the theme file with the segment `GEM_FONTS_DIR` (e.g., `GEM_FONTS_DIR/mplus1p-regular-fallback.ttf`, or b) use relative paths in the theme file and include `GEM_FONT_DIR` in the value of the `pdf-fontsdir` attribute separated by the location of your custom fonts using a semi-colon (e.g., `"path/to/your/fonts;GEM_FONTS_DIR"`).
|
738
739
|
|
739
740
|
=== Custom Fonts
|
740
741
|
|
@@ -749,11 +750,13 @@ A collection typically consists of all four font styles:
|
|
749
750
|
* bold
|
750
751
|
* bold_italic
|
751
752
|
|
752
|
-
You'll need all four
|
753
|
+
You'll need all four variants to support AsciiDoc content properly.
|
754
|
+
Otherwise, the converter will likely crash.
|
755
|
+
If you don't have one of the variants, you can simply reuse the normal variant in its place.
|
753
756
|
_Asciidoctor PDF cannot italicize a font dynamically like a browser can, so the italic styles are required._
|
754
757
|
|
755
758
|
In order for a third-party font to work properly with Prawn (and hence Asciidoctor PDF), several modifications are required.
|
756
|
-
See <<
|
759
|
+
See <<Preparing a Custom Font>> to learn how to prepare your font for use with Asciidoctor PDF.
|
757
760
|
|
758
761
|
Once you've obtained the TTF files, put them in the directory inside your project where you want to store the fonts.
|
759
762
|
It's recommended that you name them consistently so it's easier to type the names in the theme file.
|
@@ -779,6 +782,9 @@ font:
|
|
779
782
|
bold_italic: roboto-bold_italic.ttf
|
780
783
|
----
|
781
784
|
|
785
|
+
CAUTION: You must declare all four variants.
|
786
|
+
If you're missing the font file for one of the variants, configure it to use the same font file as the normal variant.
|
787
|
+
|
782
788
|
You can use the key that you assign to the font in the font catalog anywhere the `font-family` property is accepted in the theme file.
|
783
789
|
For example, to use the Roboto font for all headings, use:
|
784
790
|
|
@@ -792,13 +798,17 @@ When you execute Asciidoctor PDF, specify the directory where the fonts reside u
|
|
792
798
|
|
793
799
|
$ asciidoctor-pdf -a pdf-theme=basic-theme.yml -a pdf-fontsdir=path/to/fonts document.adoc
|
794
800
|
|
795
|
-
You can specify multiple directories by separating the entries with a semi-colon:
|
801
|
+
You can specify multiple directories by separating the entries with a semi-colon and enclosing the value in double quotes:
|
796
802
|
|
797
|
-
$ asciidoctor-pdf -a pdf-theme=basic-theme.yml -a pdf-fontsdir=path/to/fonts;path/to/more-fonts document.adoc
|
803
|
+
$ asciidoctor-pdf -a pdf-theme=basic-theme.yml -a pdf-fontsdir="path/to/fonts;path/to/more-fonts" document.adoc
|
798
804
|
|
799
805
|
To include the bundled fonts in the search, use the `GEM_FONTS_DIR` token:
|
800
806
|
|
801
|
-
$ asciidoctor-pdf -a pdf-theme=basic-theme.yml -a pdf-fontsdir=path/to/fonts;GEM_FONTS_DIR document.adoc
|
807
|
+
$ asciidoctor-pdf -a pdf-theme=basic-theme.yml -a pdf-fontsdir="path/to/fonts;GEM_FONTS_DIR" document.adoc
|
808
|
+
|
809
|
+
When running Asciidoctor PDF on the JVM (perhaps using AsciidoctorJ PDF), you can refer a directory inside of any JAR file on the classpath by prefixing the path with `uri:classloader:`:
|
810
|
+
|
811
|
+
$ asciidoctorj -b pdf -a pdf-theme=basic-theme.yml -a pdf-fontsdir="uri:classloader:/path/to/fonts;GEM_FONTS_DIR" document.adoc
|
802
812
|
|
803
813
|
TIP: When Asciidoctor PDF creates the PDF, it only embeds the glyphs from the font that are needed to render the characters present in the document.
|
804
814
|
Effectively, it subsets the font.
|
@@ -941,6 +951,11 @@ If the filename is absolute, it's used as is.
|
|
941
951
|
If the filename begins with `./`, it's resolved as a theme file relative to the current theme file.
|
942
952
|
Otherwise, the filename is resolved as a theme file in the normal way (relative to the value of the `pdf-themesdir` attribute).
|
943
953
|
|
954
|
+
CAUTION: If you define the <<Custom fonts,font catalog>> in a theme that extends from `default`, you *must* redeclare any built-in font that on which the combined theme depends.
|
955
|
+
You can find those definitions in default theme.
|
956
|
+
You'll then need to include `GEM_FONTS_DIR` in the value of the `pdf-fontsdir` attribute so that the converter can find and register them.
|
957
|
+
To avoid having to do this, make sure you set the font family for any element that declares a font family in the default theme.
|
958
|
+
|
944
959
|
Currently, the base theme is always loaded first.
|
945
960
|
Then, the files referenced by the extends key are loaded in order.
|
946
961
|
Finally, the keys in the current file are loaded.
|
@@ -963,7 +978,7 @@ Each time a theme is loaded, the keys are overlaid onto the keys from the previo
|
|
963
978
|
|
964
979
|
The keys in the `role` category define custom roles for formatting.
|
965
980
|
The name of the role is the first subkey level.
|
966
|
-
The role name may not contain
|
981
|
+
The role name may not contain an underscore.
|
967
982
|
The keys under the role are the concrete theming properties.
|
968
983
|
|
969
984
|
Here's an example of a role for making text red:
|
@@ -998,6 +1013,41 @@ You'll need to define these in your theme if you'd like to make use of them when
|
|
998
1013
|
|
999
1014
|
3+|[#key-prefix-role]*Key Prefix:* <<key-prefix-role,role-<name>{zwsp}>>
|
1000
1015
|
|
1016
|
+
|background-color
|
1017
|
+
|<<colors,Color>> +
|
1018
|
+
(default: _not set_)
|
1019
|
+
|role:
|
1020
|
+
highlight:
|
1021
|
+
background-color: #ffff00
|
1022
|
+
|
1023
|
+
|border-color
|
1024
|
+
|<<colors,Color>> +
|
1025
|
+
(default: _not set_)
|
1026
|
+
|role:
|
1027
|
+
found:
|
1028
|
+
border-color: #cccccc
|
1029
|
+
|
1030
|
+
|border-offset
|
1031
|
+
|<<values,Number>> +
|
1032
|
+
(default: 0)
|
1033
|
+
|role:
|
1034
|
+
found:
|
1035
|
+
border-offset: 2
|
1036
|
+
|
1037
|
+
|border-radius
|
1038
|
+
|<<values,Number>> +
|
1039
|
+
(default: _not set_)
|
1040
|
+
|role:
|
1041
|
+
found:
|
1042
|
+
border-radius: 3
|
1043
|
+
|
1044
|
+
|border-width
|
1045
|
+
|<<values,Number>> +
|
1046
|
+
(default: _not set_)
|
1047
|
+
|role:
|
1048
|
+
found:
|
1049
|
+
border-width: 0.5
|
1050
|
+
|
1001
1051
|
|font-color
|
1002
1052
|
|<<colors,Color>> +
|
1003
1053
|
(default: _inherit_)
|
@@ -4078,6 +4128,7 @@ If `pdf-theme` ends with `.yml`, and `pdf-themesdir` is not specified, then `pdf
|
|
4078
4128
|
|
4079
4129
|
pdf-fontsdir:: The directory or directories where the fonts used by your theme, if any, are located.
|
4080
4130
|
Multiple entries must be separated by a semi-colon.
|
4131
|
+
To reference a file inside a JAR file on the classpath, prefix with the path with `uri:classloader:` (AsciidoctorJ only).
|
4081
4132
|
_Specifying an absolute path is recommended._
|
4082
4133
|
|
4083
4134
|
Let's assume that you've put your theme files inside a directory named `resources` with the following layout:
|
@@ -4120,6 +4171,8 @@ The only thing you need to add to an existing build is the attributes mentioned
|
|
4120
4171
|
* https://github.com/asciidoctor/asciidoctor-maven-examples/tree/master/asciidoctor-pdf-with-theme-example[Maven Example]
|
4121
4172
|
* https://github.com/asciidoctor/asciidoctor-gradle-examples/tree/master/asciidoc-to-pdf-with-theme-example[Gradle Example]
|
4122
4173
|
|
4174
|
+
TIP: To reference a theme file or directory of fonts inside a JAR file on the classpath, prefix the path with `uri:classloader:`.
|
4175
|
+
|
4123
4176
|
== Theme-Related Document Attributes
|
4124
4177
|
|
4125
4178
|
There are various settings in the theme you control using document attributes.
|
@@ -4363,6 +4416,133 @@ For more information about source highlighting with Rouge, refer to the http://r
|
|
4363
4416
|
* http://www.sitepoint.com/hackable-pdf-typesetting-in-ruby-with-prawn[Hackable PDF typesetting in Ruby with Prawn]
|
4364
4417
|
////
|
4365
4418
|
|
4419
|
+
== Extending the Converter
|
4420
|
+
|
4421
|
+
This converter uses {url-prawn}[Prawn] under the covers to generate the PDF.
|
4422
|
+
Prawn is a low-level PDF writer that can load fonts, ink text, embed images, add graphics, and draw lines.
|
4423
|
+
With those operations alone, this converter manages to produce a PDF from an AsciiDoc document.
|
4424
|
+
This section explains the role of theming in this process and how to extend the converter to take it further.
|
4425
|
+
|
4426
|
+
=== Going Beyond Theming
|
4427
|
+
|
4428
|
+
While creating the PDF document, there are thousands of small decisions the converter must make about how to instruct Prawn to layout the content elements on the page (so-called "`hackable typesetting`").
|
4429
|
+
But once these elements are written, they can't be moved or styled (as is the case with HTML and CSS).
|
4430
|
+
To help influence those decisions--and thus prevent the converter from becoming too opinionated, a theming system was introduced.
|
4431
|
+
That theming system is described in this document.
|
4432
|
+
|
4433
|
+
The theme support is there to provide basic customizations (fonts, colors, borders, spacing, etc.).
|
4434
|
+
But it can only go so far.
|
4435
|
+
At some point, it becomes necessary to extend the converter to meet advanced design requirements.
|
4436
|
+
|
4437
|
+
Before you dive into extending this converter, you'll need to understand how to use Prawn.
|
4438
|
+
The article https://www.sitepoint.com/hackable-pdf-typesetting-in-ruby-with-prawn/[Hackable PDF Typesetting in Ruby with Prawn] gives a crash course in how to create your first PDF document containing text, graphics, and images with Prawn.
|
4439
|
+
That article is essential reading for working with Asciidoctor PDF, which uses many of the same operations (as well as many helpful add-ons).
|
4440
|
+
Once you feel comfortable with Prawn, you're ready to extend the converter.
|
4441
|
+
|
4442
|
+
=== Tailoring Conversion
|
4443
|
+
|
4444
|
+
The methods on a converter class that handle conversion follow the pattern `convert_<name>` for block elements (e.g., `convert_example`) and `convert_inline_<name>` for inline elements (e.g., `convert_inline_anchor`), where `<name>` is the name of the element.
|
4445
|
+
|
4446
|
+
When you override a block element, you write PDF objects directly to the Prawn Document (the current context).
|
4447
|
+
When you override an inline element, you return pseudo-HTML, which is then parsed and converted into PDF objects.
|
4448
|
+
|
4449
|
+
The pseudo-HTML in Asciidoctor PDF evolved from the inline formatting in Prawn, now supporting the following elements: a, br, button, code, color, del, em, font, img, key, mark, span, strong, sub, sup.
|
4450
|
+
All decimal and hexadecimal character references are supported, as well as the named entities amp, apos, gt, lt, nbsp, and quot (e.g., `\'`.
|
4451
|
+
You can change the font color using `rgb` attribue on the `color` element (e.g., `<color rgb="#ff0000">`) and the font family and size using `name` and `size` attributes on the `font` element, respectively (e.g., `<font name="sans" size="12">`).
|
4452
|
+
You can also use the `style` attribute on `span` to control the font color, weight, and style using the relevant CSS property names.
|
4453
|
+
The pseudo-HTML in Asciidoctor PDF also supports the `class` attribute on any element for applying roles from the theme.
|
4454
|
+
(Though not recommended, you can pass this pseudo-HTML straight through to Prawn using an inline passthrough in AsciiDoc).
|
4455
|
+
|
4456
|
+
=== Defining the Extended Converter
|
4457
|
+
|
4458
|
+
Starting with Asciidoctor 2, defining an extending converter and registering it in place of the original is very straightforward.
|
4459
|
+
|
4460
|
+
.custom-pdf-converter.rb
|
4461
|
+
[source,ruby]
|
4462
|
+
----
|
4463
|
+
class CustomPDFConverter < (Asciidoctor::Converter.for 'pdf')
|
4464
|
+
register_for 'pdf'
|
4465
|
+
|
4466
|
+
# overrides go here
|
4467
|
+
end
|
4468
|
+
----
|
4469
|
+
|
4470
|
+
As it stands, the converter doesn't do anything differently than the primary converter because we haven't yet overridden any of its methods.
|
4471
|
+
Let's do that now, starting by overriding the thematic break (aka horizonal rule) to make it render like a ribbon:
|
4472
|
+
|
4473
|
+
[source,ruby]
|
4474
|
+
----
|
4475
|
+
def convert_thematic_break node
|
4476
|
+
theme_margin :thematic_break, :top
|
4477
|
+
stroke_horizontal_rule 'FF0000', line_width: 0.5, line_style: :solid
|
4478
|
+
move_down 1
|
4479
|
+
stroke_horizontal_rule 'FF0000', line_width: 1, line_style: :solid
|
4480
|
+
move_down 1
|
4481
|
+
stroke_horizontal_rule 'FF0000', line_width: 0.5, line_style: :solid
|
4482
|
+
theme_margin :thematic_break, :bottom
|
4483
|
+
end
|
4484
|
+
----
|
4485
|
+
|
4486
|
+
This converter will replace the thematic break with a red ribbon.
|
4487
|
+
|
4488
|
+
Another way to override the converter is to modify the node, then delegate back to the primary converter.
|
4489
|
+
Let's put a page break before all paragraphs unless the cursor is at the top of the page.
|
4490
|
+
We'll call `super` to let the primary converter handle the work of rendering the paragraph.
|
4491
|
+
|
4492
|
+
[source,ruby]
|
4493
|
+
----
|
4494
|
+
def convert_paragraph node
|
4495
|
+
bounds.move_past_bottom unless at_page_top?
|
4496
|
+
super
|
4497
|
+
end
|
4498
|
+
----
|
4499
|
+
|
4500
|
+
Now let's look at how to modify an inline element.
|
4501
|
+
Let's say we want to override the kbd element.
|
4502
|
+
|
4503
|
+
[source,ruby]
|
4504
|
+
----
|
4505
|
+
def convert_inline_kbd node
|
4506
|
+
%(<strong><color rgb="AA0000">#{(node.attr 'keys').join ' + '}</color></strong>)
|
4507
|
+
end
|
4508
|
+
----
|
4509
|
+
|
4510
|
+
Refer to the primary converter to discover the pseudo-HTML you can use for inline elements.
|
4511
|
+
|
4512
|
+
So far we've just been biting around the edges.
|
4513
|
+
A more realistic use case is to customize the part title page in a multi-part book.
|
4514
|
+
Since this is a specialized section element, there's a dedicated method named `layout_part_title` that you'll need to override.
|
4515
|
+
|
4516
|
+
Let's customize the part title page by making the background orange, making the font white, centering the title on the page, and disabling the running content.
|
4517
|
+
(You don't need to start a new page before and after the part title since that's already done for you).
|
4518
|
+
|
4519
|
+
[source,ruby]
|
4520
|
+
----
|
4521
|
+
def layout_part_title node, title, opts = {}
|
4522
|
+
fill_absolute_bounds 'E64C3D'
|
4523
|
+
move_down 20
|
4524
|
+
typeset_text title, (calc_line_metrics 1.5), color: 'FFFFFF', inline_format: true, align: :center, size: 42
|
4525
|
+
page.instance_variable_set :@imported_page, true
|
4526
|
+
end
|
4527
|
+
----
|
4528
|
+
|
4529
|
+
The method `typeset_text` and `calc_line_metrics` are provided by Asciidoctor PDF to make writing text easier.
|
4530
|
+
If you wanted, you could just use the low-level `text` method provided by Prawn.
|
4531
|
+
|
4532
|
+
To find all the available methods to override, consult the https://www.rubydoc.info/github/asciidoctor/asciidoctor-pdf/Asciidoctor/Pdf/Converter[API docs].
|
4533
|
+
For deeper examples of how to override the behavior of the converter, refer to the extended converter in the https://github.com/mraible/infoq-mini-book/blob/master/src/main/ruby/asciidoctor-pdf-extensions.rb[InfoQ Mini-Book template].
|
4534
|
+
|
4535
|
+
Now that you've seen some examples of how to extend the converter, let's look at how to use it.
|
4536
|
+
|
4537
|
+
=== Using the Extended Converter
|
4538
|
+
|
4539
|
+
To use this converter, register it by passing the path to the `-r` flag when calling the `asciidoctor-pdf` command:
|
4540
|
+
|
4541
|
+
$ asciidoctor-pdf -r ./custom-pdf-converter.rb document.adoc
|
4542
|
+
|
4543
|
+
That's all there is too it.
|
4544
|
+
Now you're hacking the typesetting!
|
4545
|
+
|
4366
4546
|
[appendix]
|
4367
4547
|
== Preparing a Custom Font
|
4368
4548
|
|
@@ -103,6 +103,7 @@ class Converter < ::Prawn::Document
|
|
103
103
|
BlankLineRx = /\n{2,}/
|
104
104
|
CjkLineBreakRx = /(?=[\u3000\u30a0-\u30ff\u3040-\u309f\p{Han}\uff00-\uffef])/
|
105
105
|
WhitespaceChars = ' ' + TAB + LF
|
106
|
+
ValueSeparatorRx = /;|,/
|
106
107
|
SourceHighlighters = ['coderay', 'pygments', 'rouge'].to_set
|
107
108
|
PygmentsBgColorRx = /^\.highlight +{ *background: *#([^;]+);/
|
108
109
|
ViewportWidth = ::Module.new
|
@@ -184,13 +185,15 @@ class Converter < ::Prawn::Document
|
|
184
185
|
|
185
186
|
on_page_create &(method :init_page)
|
186
187
|
|
188
|
+
marked_page_number = page_number
|
189
|
+
# NOTE a new page will already be started (page_number = 2) if the front cover image is a PDF
|
187
190
|
layout_cover_page doc, :front
|
188
|
-
|
191
|
+
has_front_cover = page_number > marked_page_number
|
192
|
+
if (use_title_page = doc.doctype == 'book' || (doc.attr? 'title-page'))
|
189
193
|
layout_title_page doc
|
190
|
-
# NOTE a new page will already be started if the cover image is a PDF
|
191
194
|
start_new_page unless page.empty?
|
195
|
+
has_title_page = page_number == (has_front_cover ? 3 : 2)
|
192
196
|
else
|
193
|
-
# NOTE a new page will already be started if the cover image is a PDF
|
194
197
|
start_new_page unless page.empty?
|
195
198
|
body_start_page_number = page_number
|
196
199
|
if doc.header? && !doc.notitle
|
@@ -214,11 +217,11 @@ class Converter < ::Prawn::Document
|
|
214
217
|
indent 0, pagenum_width do
|
215
218
|
toc_page_nums = layout_toc doc, num_toc_levels, toc_page_nums, 0, toc_start
|
216
219
|
end
|
217
|
-
move_down @theme.block_margin_bottom unless
|
220
|
+
move_down @theme.block_margin_bottom unless use_title_page
|
218
221
|
toc_end = @y
|
219
222
|
end
|
220
223
|
# NOTE reserve pages for the toc; leaves cursor on page after last page in toc
|
221
|
-
if
|
224
|
+
if use_title_page
|
222
225
|
toc_page_nums.each { start_new_page }
|
223
226
|
else
|
224
227
|
(toc_page_nums.first...toc_page_nums.last).each { start_new_page }
|
@@ -228,25 +231,27 @@ class Converter < ::Prawn::Document
|
|
228
231
|
|
229
232
|
start_new_page if @ppbook && verso_page?
|
230
233
|
|
231
|
-
if
|
234
|
+
if use_title_page
|
235
|
+
zero_page_offset = has_front_cover ? 1 : 0
|
236
|
+
first_page_offset = has_title_page ? zero_page_offset.next : zero_page_offset
|
232
237
|
body_offset = (body_start_page_number = page_number) - 1
|
233
|
-
|
234
|
-
|
238
|
+
running_content_start_at = @theme.running_content_start_at || 'body'
|
239
|
+
running_content_start_at = 'toc' if running_content_start_at == 'title' && !has_title_page
|
240
|
+
running_content_start_at = 'body' if running_content_start_at == 'toc' && !insert_toc
|
241
|
+
page_numbering_start_at = @theme.page_numbering_start_at || 'body'
|
242
|
+
page_numbering_start_at = 'toc' if page_numbering_start_at == 'title' && !has_title_page
|
243
|
+
page_numbering_start_at = 'body' if page_numbering_start_at == 'toc' && !insert_toc
|
244
|
+
front_matter_sig = [running_content_start_at, page_numbering_start_at]
|
245
|
+
# table values are number of pages to skip before starting running content and page numbering, respectively
|
235
246
|
num_front_matter_pages = {
|
236
|
-
['title', 'title'
|
237
|
-
['title', '
|
238
|
-
['title', '
|
239
|
-
['
|
240
|
-
['
|
241
|
-
['
|
242
|
-
['
|
243
|
-
['
|
244
|
-
['toc', 'toc', true] => [1, 1],
|
245
|
-
['toc', 'toc', false] => [1, 1],
|
246
|
-
['toc', 'body', true] => [1, body_offset],
|
247
|
-
['body', 'title', true] => [body_offset, 0],
|
248
|
-
['body', 'title', false] => [1, 0],
|
249
|
-
['body', 'toc', true] => [body_offset, 1],
|
247
|
+
['title', 'title'] => [zero_page_offset, zero_page_offset],
|
248
|
+
['title', 'toc'] => [zero_page_offset, first_page_offset],
|
249
|
+
['title', 'body'] => [zero_page_offset, body_offset],
|
250
|
+
['toc', 'title'] => [first_page_offset, zero_page_offset],
|
251
|
+
['toc', 'toc'] => [first_page_offset, first_page_offset],
|
252
|
+
['toc', 'body'] => [first_page_offset, body_offset],
|
253
|
+
['body', 'title'] => [body_offset, zero_page_offset],
|
254
|
+
['body', 'toc'] => [body_offset, first_page_offset],
|
250
255
|
}[front_matter_sig] || [body_offset, body_offset]
|
251
256
|
else
|
252
257
|
# Q: what if there's only a toc page, but not title?
|
@@ -279,7 +284,7 @@ class Converter < ::Prawn::Document
|
|
279
284
|
end
|
280
285
|
end
|
281
286
|
|
282
|
-
add_outline doc, (doc.attr 'outlinelevels', num_toc_levels), toc_page_nums, num_front_matter_pages[1]
|
287
|
+
add_outline doc, (doc.attr 'outlinelevels', num_toc_levels), toc_page_nums, num_front_matter_pages[1], has_front_cover
|
283
288
|
if state.pages.size > 0 && (initial_zoom = @theme.page_initial_zoom)
|
284
289
|
case initial_zoom.to_sym
|
285
290
|
when :Fit
|
@@ -1078,7 +1083,7 @@ class Converter < ::Prawn::Document
|
|
1078
1083
|
end
|
1079
1084
|
|
1080
1085
|
indent marker_width do
|
1081
|
-
convert_content_for_list_item node, :colist, margin_bottom: @theme.outline_list_item_spacing
|
1086
|
+
convert_content_for_list_item node, :colist, margin_bottom: @theme.outline_list_item_spacing, normalize_line_height: true
|
1082
1087
|
end
|
1083
1088
|
end
|
1084
1089
|
|
@@ -1113,10 +1118,10 @@ class Converter < ::Prawn::Document
|
|
1113
1118
|
else
|
1114
1119
|
term_text = term.text
|
1115
1120
|
end
|
1116
|
-
layout_prose term_text, margin_top: 0, margin_bottom: @theme.description_list_term_spacing, align: :left
|
1121
|
+
layout_prose term_text, margin_top: 0, margin_bottom: @theme.description_list_term_spacing, align: :left, normalize_line_height: true
|
1117
1122
|
end
|
1118
1123
|
indent(@theme.description_list_description_indent || 0) do
|
1119
|
-
convert_content_for_list_item desc, :dlist_desc
|
1124
|
+
convert_content_for_list_item desc, :dlist_desc, normalize_line_height: true
|
1120
1125
|
end if desc
|
1121
1126
|
end
|
1122
1127
|
end
|
@@ -1313,16 +1318,16 @@ class Converter < ::Prawn::Document
|
|
1313
1318
|
end
|
1314
1319
|
|
1315
1320
|
if complex
|
1316
|
-
convert_content_for_list_item node, list_type, opts
|
1321
|
+
convert_content_for_list_item node, list_type, (opts.merge normalize_line_height: true)
|
1317
1322
|
else
|
1318
|
-
convert_content_for_list_item node, list_type, (opts.merge margin_bottom: @theme.outline_list_item_spacing)
|
1323
|
+
convert_content_for_list_item node, list_type, (opts.merge margin_bottom: @theme.outline_list_item_spacing, normalize_line_height: true)
|
1319
1324
|
end
|
1320
1325
|
end
|
1321
1326
|
|
1322
1327
|
def convert_content_for_list_item node, list_type, opts = {}
|
1323
1328
|
if list_type == :dlist # qanda
|
1324
1329
|
terms, desc = node
|
1325
|
-
[*terms].each {|term| layout_prose %(<em>#{term.text}</em>), opts }
|
1330
|
+
[*terms].each {|term| layout_prose %(<em>#{term.text}</em>), (opts.merge margin_top: 0, margin_bottom: @theme.description_list_term_spacing) }
|
1326
1331
|
if desc
|
1327
1332
|
layout_prose desc.text, opts if desc.text?
|
1328
1333
|
convert_content_for_block desc
|
@@ -1608,17 +1613,14 @@ class Converter < ::Prawn::Document
|
|
1608
1613
|
else
|
1609
1614
|
subs.delete_all :specialcharacters, :callouts
|
1610
1615
|
end
|
1611
|
-
#
|
1612
|
-
source_string = node.content
|
1616
|
+
# NOTE indentation guards will be added by the source highlighter logic
|
1617
|
+
source_string = expand_tabs node.content
|
1613
1618
|
end
|
1614
1619
|
else
|
1615
1620
|
highlighter = nil
|
1616
|
-
prev_subs = nil
|
1617
1621
|
source_string = guard_indentation node.content
|
1618
1622
|
end
|
1619
1623
|
|
1620
|
-
bg_color_override = nil
|
1621
|
-
|
1622
1624
|
source_chunks = case highlighter
|
1623
1625
|
when 'coderay'
|
1624
1626
|
source_string, conum_mapping = extract_conums source_string
|
@@ -2239,7 +2241,7 @@ class Converter < ::Prawn::Document
|
|
2239
2241
|
end
|
2240
2242
|
text = %(#{text}, #{pagenums.join ', '})
|
2241
2243
|
end
|
2242
|
-
layout_prose text, align: :left, margin: 0
|
2244
|
+
layout_prose text, align: :left, margin: 0, normalize_line_height: true
|
2243
2245
|
|
2244
2246
|
term.subterms.each do |subterm|
|
2245
2247
|
indent @theme.description_list_description_indent do
|
@@ -2703,6 +2705,7 @@ class Converter < ::Prawn::Document
|
|
2703
2705
|
string = %(<a anchor="#{anchor}">#{string}</a>)
|
2704
2706
|
end
|
2705
2707
|
margin_top top_margin
|
2708
|
+
string = ZeroWidthSpace + string if opts.delete :normalize_line_height
|
2706
2709
|
typeset_text string, calc_line_metrics((opts.delete :line_height) || @theme.base_line_height), {
|
2707
2710
|
color: @font_color,
|
2708
2711
|
# NOTE normalize makes endlines soft (replaces "\n" with ' ')
|
@@ -2751,7 +2754,8 @@ class Converter < ::Prawn::Document
|
|
2751
2754
|
margin_top: margin[:top],
|
2752
2755
|
margin_bottom: margin[:bottom],
|
2753
2756
|
align: (@theme.caption_align || @base_align).to_sym,
|
2754
|
-
normalize: false
|
2757
|
+
normalize: false,
|
2758
|
+
normalize_line_height: true
|
2755
2759
|
}.merge(opts)
|
2756
2760
|
if side == :top && @theme.caption_border_bottom_color
|
2757
2761
|
stroke_horizontal_rule @theme.caption_border_bottom_color
|
@@ -2833,7 +2837,7 @@ class Converter < ::Prawn::Document
|
|
2833
2837
|
end
|
2834
2838
|
sections.each do |sect|
|
2835
2839
|
theme_font :toc, level: (sect.level + 1) do
|
2836
|
-
sect_title = @text_transform ? (transform_text sect.numbered_title, @text_transform) : sect.numbered_title
|
2840
|
+
sect_title = ZeroWidthSpace + (@text_transform ? (transform_text sect.numbered_title, @text_transform) : sect.numbered_title)
|
2837
2841
|
# NOTE only write section title (excluding dots and page number) if this is a dry run
|
2838
2842
|
if scratch?
|
2839
2843
|
# FIXME use layout_prose
|
@@ -3255,7 +3259,7 @@ class Converter < ::Prawn::Document
|
|
3255
3259
|
end
|
3256
3260
|
end
|
3257
3261
|
|
3258
|
-
def add_outline doc, num_levels = 2, toc_page_nums = [], num_front_matter_pages = 0
|
3262
|
+
def add_outline doc, num_levels = 2, toc_page_nums = [], num_front_matter_pages = 0, has_front_cover = false
|
3259
3263
|
if ::String === num_levels
|
3260
3264
|
if num_levels.include? ':'
|
3261
3265
|
num_levels, expand_levels = num_levels.split ':', 2
|
@@ -3281,9 +3285,8 @@ class Converter < ::Prawn::Document
|
|
3281
3285
|
|
3282
3286
|
outline.define do
|
3283
3287
|
# FIXME use sanitize: :plain_text once available
|
3284
|
-
if (doctitle = document.sanitize(doc.doctitle use_fallback: true))
|
3285
|
-
|
3286
|
-
page title: doctitle, destination: (document.dest_top 1)
|
3288
|
+
if (doctitle = document.sanitize(doc.doctitle use_fallback: true)) && document.page_count > (has_front_cover ? 2 : 1)
|
3289
|
+
page title: doctitle, destination: (document.dest_top has_front_cover ? 2 : 1)
|
3287
3290
|
end
|
3288
3291
|
unless toc_page_nums.none? || (toc_title = doc.attr 'toc-title').nil_or_empty?
|
3289
3292
|
page title: toc_title, destination: (document.dest_top toc_page_nums.first)
|
@@ -3328,7 +3331,7 @@ class Converter < ::Prawn::Document
|
|
3328
3331
|
|
3329
3332
|
def register_fonts font_catalog, fonts_dir
|
3330
3333
|
return unless font_catalog
|
3331
|
-
dirs = (fonts_dir.split
|
3334
|
+
dirs = (fonts_dir.split ValueSeparatorRx, -1).map do |dir|
|
3332
3335
|
dir == 'GEM_FONTS_DIR' || dir.empty? ? ThemeLoader::FontsDir : dir
|
3333
3336
|
end
|
3334
3337
|
font_catalog.each do |key, styles|
|
@@ -3572,14 +3575,65 @@ class Converter < ::Prawn::Document
|
|
3572
3575
|
(height_of string, leading: line_metrics.leading, final_gap: line_metrics.final_gap) + line_metrics.padding_top + line_metrics.padding_bottom
|
3573
3576
|
end
|
3574
3577
|
|
3578
|
+
# NOTE only used when tabsize attribute is not specified
|
3579
|
+
# tabs must always be replaced with spaces in order for the indentation guards to work
|
3580
|
+
def expand_tabs string
|
3581
|
+
if string.nil_or_empty?
|
3582
|
+
''
|
3583
|
+
elsif string.include? TAB
|
3584
|
+
full_tab_space = ' ' * (tab_size = 4)
|
3585
|
+
(string.split LF, -1).map do |line|
|
3586
|
+
if line.empty?
|
3587
|
+
line
|
3588
|
+
elsif (tab_idx = line.index TAB)
|
3589
|
+
if tab_idx == 0
|
3590
|
+
leading_tabs = 0
|
3591
|
+
line.each_byte do |b|
|
3592
|
+
break unless b == 9
|
3593
|
+
leading_tabs += 1
|
3594
|
+
end
|
3595
|
+
line = %(#{full_tab_space * leading_tabs}#{rest = line.slice leading_tabs, line.length})
|
3596
|
+
next line unless rest.include? TAB
|
3597
|
+
end
|
3598
|
+
# keeps track of how many spaces were added to adjust offset in match data
|
3599
|
+
spaces_added = 0
|
3600
|
+
idx = 0
|
3601
|
+
result = ''
|
3602
|
+
line.each_char do |c|
|
3603
|
+
if c == TAB
|
3604
|
+
# calculate how many spaces this tab represents, then replace tab with spaces
|
3605
|
+
if (offset = idx + spaces_added) % tab_size == 0
|
3606
|
+
spaces_added += (tab_size - 1)
|
3607
|
+
result = result + full_tab_space
|
3608
|
+
else
|
3609
|
+
unless (spaces = tab_size - offset % tab_size) == 1
|
3610
|
+
spaces_added += (spaces - 1)
|
3611
|
+
end
|
3612
|
+
result = result + (' ' * spaces)
|
3613
|
+
end
|
3614
|
+
else
|
3615
|
+
result = result + c
|
3616
|
+
end
|
3617
|
+
idx += 1
|
3618
|
+
end
|
3619
|
+
result
|
3620
|
+
else
|
3621
|
+
line
|
3622
|
+
end
|
3623
|
+
end.join LF
|
3624
|
+
else
|
3625
|
+
string
|
3626
|
+
end
|
3627
|
+
end
|
3628
|
+
|
3629
|
+
# Add an indentation guard at the start of indented lines.
|
3630
|
+
# Expand tabs to spaces if tabs are present
|
3575
3631
|
def guard_indentation string
|
3576
|
-
|
3632
|
+
unless (string = expand_tabs string).empty?
|
3577
3633
|
string[0] = GuardedIndent if string.start_with? ' '
|
3578
3634
|
string.gsub! InnerIndent, GuardedInnerIndent if string.include? InnerIndent
|
3579
|
-
string
|
3580
|
-
else
|
3581
|
-
''
|
3582
3635
|
end
|
3636
|
+
string
|
3583
3637
|
end
|
3584
3638
|
|
3585
3639
|
def guard_indentation_in_fragments fragments
|
@@ -121,8 +121,7 @@ class Transform
|
|
121
121
|
end
|
122
122
|
|
123
123
|
# FIXME pass styles downwards to child elements rather than decorating on way out of hierarchy
|
124
|
-
def apply(parsed)
|
125
|
-
fragments = []
|
124
|
+
def apply(parsed, fragments = [], inherited = nil)
|
126
125
|
previous_fragment_is_text = false
|
127
126
|
# NOTE we use each since using inject is slower than a manual loop
|
128
127
|
parsed.each do |node|
|
@@ -133,14 +132,16 @@ class Transform
|
|
133
132
|
unless (pcdata = node[:pcdata]).empty?
|
134
133
|
tag_name = node[:name]
|
135
134
|
attributes = node[:attributes]
|
136
|
-
|
137
|
-
fragments
|
135
|
+
parent = clone_fragment inherited
|
136
|
+
# NOTE decorate child fragments with inherited properties from this element
|
137
|
+
apply(pcdata, fragments, (build_fragment parent, tag_name, attributes))
|
138
138
|
previous_fragment_is_text = false
|
139
139
|
# NOTE skip element if it has no children
|
140
140
|
#else
|
141
141
|
# # NOTE handle an empty anchor element (i.e., <a ...></a>)
|
142
142
|
# if (tag_name = node[:name]) == :a
|
143
|
-
#
|
143
|
+
# seed = clone_fragment inherited, text: DummyText
|
144
|
+
# fragments << build_fragment(seed, tag_name, node[:attributes])
|
144
145
|
# previous_fragment_is_text = false
|
145
146
|
# end
|
146
147
|
end
|
@@ -149,7 +150,7 @@ class Transform
|
|
149
150
|
case node[:name]
|
150
151
|
when :br
|
151
152
|
if @merge_adjacent_text_nodes && previous_fragment_is_text
|
152
|
-
fragments <<
|
153
|
+
fragments << (clone_fragment inherited, text: %(#{fragments.pop[:text]}#{LF}))
|
153
154
|
else
|
154
155
|
fragments << { text: LF }
|
155
156
|
end
|
@@ -163,6 +164,9 @@ class Transform
|
|
163
164
|
text: (attributes[:alt].delete ZeroWidthSpace),
|
164
165
|
callback: [InlineImageRenderer],
|
165
166
|
}
|
167
|
+
if inherited && (link = inherited[:link])
|
168
|
+
fragment[:link] = link
|
169
|
+
end
|
166
170
|
if (img_w = attributes[:width])
|
167
171
|
fragment[:image_width] = img_w
|
168
172
|
end
|
@@ -172,9 +176,9 @@ class Transform
|
|
172
176
|
end
|
173
177
|
when :text
|
174
178
|
if @merge_adjacent_text_nodes && previous_fragment_is_text
|
175
|
-
fragments <<
|
179
|
+
fragments << (clone_fragment inherited, text: %(#{fragments.pop[:text]}#{node[:value]}))
|
176
180
|
else
|
177
|
-
fragments <<
|
181
|
+
fragments << (clone_fragment inherited, text: node[:value])
|
178
182
|
end
|
179
183
|
previous_fragment_is_text = true
|
180
184
|
when :charref
|
@@ -188,14 +192,14 @@ class Transform
|
|
188
192
|
text = [(node[:value].to_i 16)].pack('U1')
|
189
193
|
end
|
190
194
|
if @merge_adjacent_text_nodes && previous_fragment_is_text
|
191
|
-
fragments <<
|
195
|
+
fragments << (clone_fragment inherited, text: %(#{fragments.pop[:text]}#{text}))
|
192
196
|
else
|
193
|
-
fragments <<
|
197
|
+
fragments << (clone_fragment inherited, text: text)
|
194
198
|
end
|
195
199
|
previous_fragment_is_text = true
|
196
200
|
end
|
197
201
|
end
|
198
|
-
fragments
|
202
|
+
fragments
|
199
203
|
end
|
200
204
|
|
201
205
|
def build_fragment(fragment, tag_name, attrs = {})
|
@@ -206,39 +210,36 @@ class Transform
|
|
206
210
|
when :em
|
207
211
|
styles << :italic
|
208
212
|
when :button, :code, :key, :mark
|
209
|
-
|
210
|
-
fragment.update(@theme_settings[tag_name]) {|k, oval, nval| k == :styles ? oval.merge(nval) : (k == :callback ? oval.union(nval) : oval) }
|
213
|
+
fragment.update(@theme_settings[tag_name]) {|k, oval, nval| k == :styles ? oval.merge(nval) : (k == :callback ? oval.union(nval) : nval) }
|
211
214
|
when :color
|
212
|
-
if
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
fragment[:color] = rgb
|
229
|
-
end
|
230
|
-
# QUESTION should we even support r,g,b and c,m,y,k as individual values?
|
231
|
-
elsif (r_val = attrs[:r]) && (g_val = attrs[:g]) && (b_val = attrs[:b])
|
232
|
-
fragment[:color] = [r_val, g_val, b_val].map {|e| '%02X' % e.to_i }.join
|
233
|
-
elsif (c_val = attrs[:c]) && (m_val = attrs[:m]) && (y_val = attrs[:y]) && (k_val = attrs[:k])
|
234
|
-
fragment[:color] = [c_val.to_i, m_val.to_i, y_val.to_i, k_val.to_i]
|
215
|
+
if (rgb = attrs[:rgb])
|
216
|
+
case rgb.chr
|
217
|
+
when '#'
|
218
|
+
fragment[:color] = rgb[1..-1]
|
219
|
+
when '['
|
220
|
+
# treat value as CMYK array (e.g., "[50, 100, 0, 0]")
|
221
|
+
fragment[:color] = rgb[1..-1].chomp(']').split(', ').map(&:to_i)
|
222
|
+
# ...or we could honor an rgb array too
|
223
|
+
#case (vals = rgb[1..-1].chomp(']').split(', ')).size
|
224
|
+
#when 4
|
225
|
+
# fragment[:color] = vals.map(&:to_i)
|
226
|
+
#when 3
|
227
|
+
# fragment[:color] = vals.map {|e| '%02X' % e.to_i }.join
|
228
|
+
#end
|
229
|
+
else
|
230
|
+
fragment[:color] = rgb
|
235
231
|
end
|
232
|
+
# QUESTION should we even support r,g,b and c,m,y,k as individual values?
|
233
|
+
elsif (r_val = attrs[:r]) && (g_val = attrs[:g]) && (b_val = attrs[:b])
|
234
|
+
fragment[:color] = [r_val, g_val, b_val].map {|e| '%02X' % e.to_i }.join
|
235
|
+
elsif (c_val = attrs[:c]) && (m_val = attrs[:m]) && (y_val = attrs[:y]) && (k_val = attrs[:k])
|
236
|
+
fragment[:color] = [c_val.to_i, m_val.to_i, y_val.to_i, k_val.to_i]
|
236
237
|
end
|
237
238
|
when :font
|
238
|
-
if
|
239
|
+
if (value = attrs[:name])
|
239
240
|
fragment[:font] = value
|
240
241
|
end
|
241
|
-
if
|
242
|
+
if (value = attrs[:size])
|
242
243
|
# FIXME can we make this comparison more robust / accurate?
|
243
244
|
if %(#{f_value = value.to_f}) == value || %(#{value.to_i}) == value
|
244
245
|
fragment[:size] = f_value
|
@@ -250,10 +251,10 @@ class Transform
|
|
250
251
|
fragment[:width] = value
|
251
252
|
if (value = attrs[:align])
|
252
253
|
fragment[:align] = value.to_sym
|
253
|
-
(fragment[:callback] ||= []) << InlineTextAligner
|
254
|
+
fragment[:callback] = ((fragment[:callback] ||= []) << InlineTextAligner).uniq
|
254
255
|
end
|
255
256
|
end
|
256
|
-
#if
|
257
|
+
#if (value = attrs[:character_spacing])
|
257
258
|
# fragment[:character_spacing] = value.to_f
|
258
259
|
#end
|
259
260
|
when :a
|
@@ -273,12 +274,11 @@ class Transform
|
|
273
274
|
if (type = attrs[:type])
|
274
275
|
fragment[:type] = type.to_sym
|
275
276
|
end
|
276
|
-
(fragment[:callback] ||= []) << InlineDestinationMarker
|
277
|
+
fragment[:callback] = ((fragment[:callback] ||= []) << InlineDestinationMarker).uniq
|
277
278
|
visible = false
|
278
279
|
end
|
279
280
|
end
|
280
|
-
|
281
|
-
fragment.update(@theme_settings[:link]) {|k, oval, nval| k == :styles ? oval.merge(nval) : oval } if visible
|
281
|
+
fragment.update(@theme_settings[:link]) {|k, oval, nval| k == :styles ? oval.merge(nval) : nval } if visible
|
282
282
|
when :sub
|
283
283
|
styles << :subscript
|
284
284
|
when :sup
|
@@ -325,9 +325,9 @@ class Transform
|
|
325
325
|
when 'line-through'
|
326
326
|
styles << :strikethrough
|
327
327
|
else
|
328
|
-
fragment.update(@theme_settings[class_name]) {|k, oval, nval| k == :styles ? oval.merge(nval) :
|
328
|
+
fragment.update(@theme_settings[class_name]) {|k, oval, nval| k == :styles ? oval.merge(nval) : nval } if @theme_settings.key? class_name
|
329
329
|
if fragment[:background_color] || (fragment[:border_color] && fragment[:border_width])
|
330
|
-
((fragment[:callback]
|
330
|
+
fragment[:callback] = ((fragment[:callback] || []) << TextBackgroundAndBorderRenderer).uniq
|
331
331
|
end
|
332
332
|
end
|
333
333
|
end if attrs.key?(:class)
|
@@ -335,6 +335,18 @@ class Transform
|
|
335
335
|
fragment
|
336
336
|
end
|
337
337
|
|
338
|
+
def clone_fragment fragment, append = nil
|
339
|
+
if fragment
|
340
|
+
fragment = fragment.dup
|
341
|
+
fragment[:styles] = fragment[:styles].dup if fragment.key? :styles
|
342
|
+
fragment[:callback] = fragment[:callback].dup if fragment.key? :callback
|
343
|
+
else
|
344
|
+
fragment = {}
|
345
|
+
end
|
346
|
+
fragment.update append if append
|
347
|
+
fragment
|
348
|
+
end
|
349
|
+
|
338
350
|
def to_styles(font_style, text_decoration = nil)
|
339
351
|
case font_style
|
340
352
|
when 'bold'
|
@@ -48,7 +48,7 @@ class ThemeLoader
|
|
48
48
|
if theme_dir
|
49
49
|
theme_path = ::File.absolute_path theme_name, (theme_dir = ::File.expand_path theme_dir)
|
50
50
|
else
|
51
|
-
theme_dir = ::File.dirname(theme_path = (::File.
|
51
|
+
theme_dir = ::File.dirname(theme_path = (::File.expand_path theme_name))
|
52
52
|
end
|
53
53
|
else
|
54
54
|
theme_dir = theme_dir ? (::File.expand_path theme_dir) : ThemesDir
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: asciidoctor-pdf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.5.0.beta.
|
4
|
+
version: 1.5.0.beta.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Allen
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2019-09-
|
12
|
+
date: 2019-09-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: asciidoctor
|