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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5128f12524a44fcbb19f0127e8bf0ec546f0287e80fae2bb6ccb70a1a69a2a7e
4
- data.tar.gz: fa168da5cd4a10528e3bb732467d2f129d64fdaf26d182b874ccf6a70729bc51
3
+ metadata.gz: f6a1fbcfb416a485f625f2b12c4b7c30806e0d05cdf3ecc327b2ae6b0b04003e
4
+ data.tar.gz: bb1f4811237d7478cd132474175e11fbc1fa9e4755a0888ba1bb633215174b16
5
5
  SHA512:
6
- metadata.gz: b91e25292d981f9409298c7ad49acd41e9c259e56dde1935d7e03d54326ee8aca9c0ef9a02532aab172c42da1fd7bbcad943c047a523af3990486ae05f1d2acc
7
- data.tar.gz: 3034f7607666738c74c47bca7f6400568030e100a4e2bc5ca2de268ab4c2d0ba94afc531b20c0ed72b5d04a592a22735eb95b6846d7a93c2c467799bf96d1092
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.4, 2019-09-04
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.4
27
+ :release-version: 1.5.0.beta.5
28
28
  // URIs:
29
- :uri-asciidoctor: http://asciidoctor.org
30
- :uri-gem: http://rubygems.org/gems/asciidoctor-pdf
31
- :uri-project: https://github.com/asciidoctor/asciidoctor-pdf
32
- :uri-project-repo: {uri-project}
33
- :uri-project-issues: {uri-project-repo}/issues
34
- :uri-project-list: http://discuss.asciidoctor.org
35
- :uri-prawn: http://prawnpdf.org
36
- :uri-prawn-gmagick: https://github.com/packetmonkey/prawn-gmagick
37
- :uri-prawn-svg: https://github.com/mogest/prawn-svg
38
- :uri-asciidoctor-mathematical: https://github.com/asciidoctor/asciidoctor-mathematical
39
- :uri-rvm: http://rvm.io
40
- :uri-graphicsmagick: http://www.graphicsmagick.org
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={uri-gem}]
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 {uri-asciidoctor}[Asciidoctor] and {uri-prawn}[Prawn]! +
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
- {uri-project}[{project-name}] is made possible by an amazing Ruby gem named Prawn.
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
- {uri-prawn}[Prawn] is a nimble PDF writer for Ruby.
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 {uri-prawn-svg}[prawn-svg] to embed SVGs in the PDF document, including SVGs generated by Asciidoctor Diagram.
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
- {uri-prawn}[Prawn] provides support for reading JPG and PNG images.
587
- {uri-prawn-svg}[prawn-svg] brings support for SVG images.
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 {uri-prawn-gmagick}[prawn-gmagick] (>= 0.0.9) Ruby gem.
591
- prawn-gmagick is an extension for Prawn based on {uri-graphicsmagick}[GraphicsMagick] that adds support for all the image formats recognized by that library.
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 {uri-prawn-gmagick}[README for prawn-gmagick] to learn how to install it.
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
- {uri-asciidoctor-mathematical}[Asciidoctor Mathematical] is an extension for Asciidoctor that provides alternate processing of STEM blocks and inline macros (currently only latexmath).
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 {uri-asciidoctor-mathematical}#installation[installation section] in the Asciidoctor Mathematical README to learn how to install it.
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 {uri-asciidoctor-mathematical}#readme[README] for Asciidoctor Mathematical to learn about additional settings and options.
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 {uri-project-issues}[issue tracker] or {uri-project-list}[Asciidoctor mailing list] to provide feedback or suggestions in other ways.
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 {uri-project-repo}[GitHub repository URL] and pass it to the `git clone` command:
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 {uri-rvm}[RVM], we recommend creating a new gemset to work with {project-name}:
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
 
@@ -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, separated from the location of your custom fonts by a semi-colon (e.g., `path/to/your/fonts;GEM_FONTS_DIR`).
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 styles to support AsciiDoc content properly.
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 <<Prepare a Custom Font>> to learn how to prepare your font for use with Asciidoctor PDF.
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 a hyphen or underscore.
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., `\&apos;`.
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
- if (insert_title_page = doc.doctype == 'book' || (doc.attr? 'title-page'))
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 insert_title_page
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 insert_title_page
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 insert_title_page
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
- front_matter_sig = [@theme.running_content_start_at || 'body', @theme.page_numbering_start_at || 'body', insert_toc]
234
- # NOTE start running content from title or toc, if specified (default: body)
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', true] => [0, 0],
237
- ['title', 'title', false] => [0, 0],
238
- ['title', 'toc', true] => [0, 1],
239
- ['title', 'toc', false] => [0, 1],
240
- ['title', 'body', true] => [0, body_offset],
241
- ['title', 'body', false] => [0, 1],
242
- ['toc', 'title', true] => [1, 0],
243
- ['toc', 'title', false] => [1, 0],
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
- # the indent guard will be added by the source highlighter logic
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
- # FIXME link to title page if there's a cover page (skip cover page and ensure blank page)
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 ';', -1).map do |dir|
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
- if string
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
- # NOTE decorate child fragments with styles from this element
137
- fragments << apply(pcdata).map {|fragment| build_fragment(fragment, tag_name, attributes) }
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
- # fragments << build_fragment({ text: DummyText }, tag_name, node[:attributes])
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 << { text: %(#{fragments.pop[:text]}#{LF}) }
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 << { text: %(#{fragments.pop[:text]}#{node[:value]}) }
179
+ fragments << (clone_fragment inherited, text: %(#{fragments.pop[:text]}#{node[:value]}))
176
180
  else
177
- fragments << { text: node[:value] }
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 << { text: %(#{fragments.pop[:text]}#{text}) }
195
+ fragments << (clone_fragment inherited, text: %(#{fragments.pop[:text]}#{text}))
192
196
  else
193
- fragments << { text: text }
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.flatten
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
- # NOTE prefer old value, except for styles and callback, which should be combined
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 !fragment[:color]
213
- if (rgb = attrs[:rgb])
214
- case rgb.chr
215
- when '#'
216
- fragment[:color] = rgb[1..-1]
217
- when '['
218
- # treat value as CMYK array (e.g., "[50, 100, 0, 0]")
219
- fragment[:color] = rgb[1..-1].chomp(']').split(', ').map(&:to_i)
220
- # ...or we could honor an rgb array too
221
- #case (vals = rgb[1..-1].chomp(']').split(', ')).size
222
- #when 4
223
- # fragment[:color] = vals.map(&:to_i)
224
- #when 3
225
- # fragment[:color] = vals.map {|e| '%02X' % e.to_i }.join
226
- #end
227
- else
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 !fragment[:font] && (value = attrs[:name])
239
+ if (value = attrs[:name])
239
240
  fragment[:font] = value
240
241
  end
241
- if !fragment[:size] && (value = attrs[:size])
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 !fragment[:character_spacing] && (value = attrs[:character_spacing])
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
- # NOTE prefer old value, except for styles, which should be combined
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) : oval } if @theme_settings.key? class_name
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] ||= []) << TextBackgroundAndBorderRenderer).uniq!
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.absolute_path theme_name))
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
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module Asciidoctor
3
3
  module PDF
4
- VERSION = '1.5.0.beta.4'
4
+ VERSION = '1.5.0.beta.5'
5
5
  end
6
6
  Pdf = PDF unless const_defined? :Pdf, false
7
7
  end
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
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-05 00:00:00.000000000 Z
12
+ date: 2019-09-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: asciidoctor