Asciidoctor 安装

asciidoctor

为什么要安装 asciidoctor?
主要目的是由原始文档能编译出 html、docbook。

Cygwin 先安装 Ruby
cygpkg i ruby
安装 Asciidoctor:
gem install asciidoctor

检查版本:
asciidoctor -v

Asciidoctor 2.0.18 [https://asciidoctor.org]
安装语法高亮度套件
安装 Rouge:
gem install rouge

出错,提示要安装 `gem install rouge -v 3.30.0`
gem install rouge -v 3.30.0

gem install pygments.rb
gem install coderay
将 AsciiDoc 文件转换,命令格式如下:
asciidoctor 文件 [选项]
asciidoctor 选项

-b, --backend

输出格式为 html5 或 docbook5,可用别名 html、docbook;如 -b docbook。注:html5 为默认值,不需要设置。

-o, --out-file

指定输出文件名 (依据输入文件的路径),若「文件名」为 - (减号);如 -o - 则为标准输出 (屏幕)。

-D, --destination-dir

输出路径,默认值为输入文件的路径。

--safe

(asciidoctor) 默认值为 unsafe

-v, --verbose

详细模式输出。

--trace

报告错误时包括回溯信息。

-a, --attribute

文档属性。

CLI Options 参阅:asciidoctor --helpCLI Options | Asciidoctor Docs (Asciidoctor)

-a, --attribute 文档属性

以 name (属性名称)、name! 或 name=value 设置文档属性。

stylesdir

CSS 路径,相对于输出路径(-D)。

stylesheet

指定 asciidoctor 链接 CSS 的文件,文件位置相对于 CSS 路径 (stylesdir)。

source-highlighter

指定语法高亮度处理套件,除了 highlight.js 其他需安装。

linkcss

链接至样式表 (档),缺省为将样式表内嵌至网页。

copycss

链接样式 (linkcss) 时;缺省将 CSS 文件拷贝至 CSS 路径 (stylesdir),以 copycss! 取消拷贝 CSS 文件。
拷贝时则会产生 asciidoctor.css,语法高亮度若为 rouge 时则会拷贝 rouge-github.css,而 highlight.js 则链接 (CSS 及 JS) 至 CDN (不会拷贝)。

highlightjsdir

指定 highlight.js 函数库位置,网页中将链接 <highlightjsdir>/highlight.min.js、<highlightjsdir>/styles/github.min.css。

iconfont-remote

缺省使用 CDN (链接至外部站台 https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css) 来解析图标字体,以 iconfont-remote! 取消;取消后链接至 <stylesdir>/font-awesome.css。

iconfont-name

取消 CDN 解析图标字体 (iconfont-remote!) 时,可指定图标字体样式表的路径及文件名 (不可含扩展名 .css)。
注:Fontawesome 链接 CSS 一律会产生。

编译 HTML 范例
asciidoctor MyAdoc.adoc \
--verbose --trace \
-D MyDestinationDirectory \
-a experimental \
-a icons=font

asciidoctor-pdf

GitHub - asciidoctor/asciidoctor-pdf 得知 Ruby 的版本要为 2.7,但 Cygwin 只有 2.6。

检查 ruby 的版本:
ruby -v

ruby 2.6.4p104 (2019-08-28 revision 67798) [x86_64-cygwin]

Cygwin 在 Ruby 2.6 安装及运行 asciidoctor-pdf 并无问题。
注:Ubuntu 则需要 2.7。

安装 asciidoctor-pdf
安装:
gem install asciidoctor-pdf

检查版本:
asciidoctor-pdf -v

Asciidoctor PDF 2.3.4 using Asciidoctor 2.0.18 [https://asciidoctor.org]
将 AsciiDoc 文件产生 PDF,命令格式如下:
asciidoctor-pdf 文件 [选项]
asciidoctor-pdf 选项

-o, --out-file

指定输出文件名 (依据输入文件的路径),若「文件名」为 - (减号);如 -o - 则为标准输出 (屏幕)。

-D, --destination-dir

输出路径,默认值为输入文件的路径。

-r, --require

函数库

--safe

(asciidoctor-pdf) 默认值为 unsafe

-v, --verbose

详细模式输出。

--trace

报告错误时包括回溯信息。

-a, --attribute

文档属性。

CLI Options 参阅:asciidoctor-pdf --help

-a, --attribute 文档属性

以 name (属性名称)、name! 或 name=value 设置文档属性。

outlinelevels

outlinelevels=3:0 则为创建 3 层大网,不自动展开 (0), PDF Outline | Asciidoctor Docs

pdf-theme

主题名称或主题文件 (需含扩展名,可含路径)。
「主题名称」的文件要合乎 <主题名称>-theme.yml 的规则,参阅: Apply a Theme | Asciidoctor Docs

pdf-themesdir

主题文件目录。

pdf-fontsdir

主题字体目录,该目录的字体档必须完整。

scripts

设置值为 cjk,在两个中文本之间插入一个看不到的字符,让断字正确。

source-highlighter

指定语法高亮度处理套件。

编译 PDF 范例
theme=zh-TW
toctitle="内容目录"
InputFile=<InputFile>
OutputFile=<OutputFile>

asciidoctor-pdf \
$InputFile -o $OutputFile
--trace --verbose
-a pdf-theme=$theme
-a toc-title=$toctitle \
-r ./bin/pdf-svg-font.rb \
-a scripts=cjk \
-a icons=font \
-a source-highlighter=rouge \
-a experimental \
-a toc=auto \
-a sectnums=1 \
-a sectnumlevels=1 \
-a toclevels=3 \
-a outlinelevels=3:0

在产生 PDF 时,可能会因为 (Table 的) 栏宽比例无法分配,造成错误,不会产生表格。

在 Asciidoctor PDF 2.3.4 会显示 asciidoctor: ERROR: cannot fit contents of table cell into specified column width

配置 theme.yml

PDF 乱码主要的原因是 asciidoctor-pdf 缺省的 default-theme.yml 中配置的字库案不支持中文。

创建主题 (theme) 的文件名 (最好) 合乎 Apply a Theme | Asciidoctor Docs <name>-theme.yml 的规则,asciidoctor-pdf 参数 pdf-theme 设置值为 <name> (主题名称)。
若不合乎则必须指定文件名称 (含扩展名)。

zh-TW-theme.yml,pdf-theme=zh-TW 如下范例:
asciidoctor-pdf \
-a scripts=cjk \
-a pdf-theme=zh-TW
找出 asciidoctor-pdf 的安装路径:
找出安装路径:
gem which asciidoctor-pdf

/home/user/.gem/ruby/2.6.0/gems/asciidoctor-pdf-2.3.4/lib/asciidoctor-pdf.rb

一并设置 asciidoctor-pdf 的路径变量:
gemPdfPath=/home/user/.gem/ruby/2.6.0/gems/asciidoctor-pdf-2.3.4
asciidoctor-pdf 缺省路径
主题 (themes) 路径:
$gemPdfPath/data/themes

字体 (fonts) 路径:
$gemPdfPath/data/fonts

主题文件可保存在「缺省路径」。

亦可在其他路径,由下列属性来设置。
  • pdf-theme 含路径及扩展名。

  • pdf-themesdir 指定主题文件的目录。注:pdf-theme 可采用「主题名称」。


下列将依据 chloerei/asciidoctor-pdf-cjk-kai_gen_gothic 的字体创建两个主题 zh-TWzh-CN
参考来源:<asciidoctor-pdf-cjk-kai_gen_gothic>/data/themes/KaiGenGothicTW-theme.yml (github 网页或安装 asciidoctor-pdf-cjk-kai_gen_gothic 在套件的文件中)。

创建 zh-TW-theme.yml
gemPdfPath=/home/user/.gem/ruby/2.6.0/gems/asciidoctor-pdf-2.3.4

cat > $gemPdfPath/data/themes/zh-TW-theme.yml << "EOF"
extends: default
font:
  catalog:
    merge: true
    # M+ 1p supports Latin, Latin-1 Supplement, Latin Extended, Greek, Cyrillic, Vietnamese, Japanese & an assortment of symbols
    # It also provides arrows for ->, <-, => and <= replacements in case these glyphs are missing from font
    M+ 1p Fallback:
      normal: KaiGenGothicTW-Regular.ttf
      bold: KaiGenGothicTW-Bold.ttf
      italic: KaiGenGothicTW-Regular-Italic.ttf
      bold_italic: KaiGenGothicTW-Bold-Italic.ttf
    M+ 1mn:
      normal: RobotoMono-Regular.ttf
      bold: RobotoMono-Bold.ttf
      italic: RobotoMono-Italic.ttf
    Noto Emoji: notoemoji-subset.ttf
  fallbacks: [M+ 1p Fallback, Noto Emoji]

base:
  align: left # 中英混合时, 不要将英文填入空白间距

page:
  size: Letter

quote:
  font-size: 10.5

heading_h6_font_size: 11.5
EOF
创建 zh-CN-theme.yml
cat > $gemPdfPath/data/themes/zh-CN-theme.yml << "EOF"
extends: default
font:
  catalog:
    merge: true
    # M+ 1p supports Latin, Latin-1 Supplement, Latin Extended, Greek, Cyrillic, Vietnamese, Japanese & an assortment of symbols
    # It also provides arrows for ->, <-, => and <= replacements in case these glyphs are missing from font
    M+ 1p Fallback:
      normal: KaiGenGothicCN-Regular.ttf
      bold: KaiGenGothicCN-Bold.ttf
      italic: KaiGenGothicCN-Regular-Italic.ttf
      bold_italic: KaiGenGothicCN-Bold-Italic.ttf
    M+ 1mn:
      normal: RobotoMono-Regular.ttf
      bold: RobotoMono-Bold.ttf
      italic: RobotoMono-Italic.ttf
    Noto Emoji: notoemoji-subset.ttf
  fallbacks: [M+ 1p Fallback, Noto Emoji]

base:
  align: left # 中英混合时, 不要将英文填入空白间距

page:
  size: Letter

quote:
  font-size: 10.5

heading_h6_font_size: 11.5
EOF

安装中文本体

安装 asciidoctor-pdf-cjk-kai_gen_gothic 目的是要取得字体
安装 asciidoctor-pdf-cjk-kai_gen_gothic:
gem install asciidoctor-pdf-cjk-kai_gen_gothic

运行安装字体:
asciidoctor-pdf-cjk-kai_gen_gothic-install (1)

[1/20] Downloading KaiGenGothicCN-Bold-Italic.ttf
...

找出字体路径:
gem which asciidoctor-pdf-cjk-kai_gen_gothic
find /home/user/.gem/ruby/2.6.0/gems/asciidoctor-pdf-cjk-kai_gen_gothic-0.1.1 -iname '*.ttf'

/home/user/.gem/ruby/2.6.0/gems/asciidoctor-pdf-cjk-kai_gen_gothic-0.1.1/data/fonts/KaiGenGothicCN-Bold-Italic.ttf

一并设置字体套件的路径变量:
gemFontPath=/home/user/.gem/ruby/2.6.0/gems/asciidoctor-pdf-cjk-kai_gen_gothic-0.1.1
1 参考 chloerei/asciidoctor-pdf-cjk-kai_gen_gothic 说明要以 asciidoctor-pdf-cjk-kai_gen_gothic-install 安装字体。

虽然 pdf-fontsdir 属性可以指定字体路径,但该路径内字体档必须完整,由于主题使用部份缺省字体,将字体拷贝至缺省路径。

将字体拷贝到 asciidoctor-pdf
确定路径无误:
ls $gemPdfPath/data/fonts
ls $gemFontPath/data/fonts

拷贝字体:
cp $gemFontPath/data/fonts/* $gemPdfPath/data/fonts
cd $gemPdfPath/data/fonts
KaiGenGothic_fonts_url=https://github.com/chloerei/asciidoctor-pdf-cjk-kai_gen_gothic/releases/download/v0.1.0-fonts

wget $KaiGenGothic_fonts_url/KaiGenGothicCN-Bold-Italic.ttf
wget $KaiGenGothic_fonts_url/KaiGenGothicCN-Bold.ttf
wget $KaiGenGothic_fonts_url/KaiGenGothicCN-Regular-Italic.ttf
wget $KaiGenGothic_fonts_url/KaiGenGothicCN-Regular.ttf

wget $KaiGenGothic_fonts_url/KaiGenGothicJP-Bold-Italic.ttf
wget $KaiGenGothic_fonts_url/KaiGenGothicJP-Bold.ttf
wget $KaiGenGothic_fonts_url/KaiGenGothicJP-Regular-Italic.ttf
wget $KaiGenGothic_fonts_url/KaiGenGothicJP-Regular.ttf

wget $KaiGenGothic_fonts_url/KaiGenGothicKR-Bold-Italic.ttf
wget $KaiGenGothic_fonts_url/KaiGenGothicKR-Bold.ttf
wget $KaiGenGothic_fonts_url/KaiGenGothicKR-Regular-Italic.ttf
wget $KaiGenGothic_fonts_url/KaiGenGothicKR-Regular.ttf

wget $KaiGenGothic_fonts_url/KaiGenGothicTW-Bold-Italic.ttf
wget $KaiGenGothic_fonts_url/KaiGenGothicTW-Bold.ttf
wget $KaiGenGothic_fonts_url/KaiGenGothicTW-Regular-Italic.ttf
wget $KaiGenGothic_fonts_url/KaiGenGothicTW-Regular.ttf

wget $KaiGenGothic_fonts_url/RobotoMono-Bold.ttf
wget $KaiGenGothic_fonts_url/RobotoMono-BoldItalic.ttf
wget $KaiGenGothic_fonts_url/RobotoMono-Italic.ttf
wget $KaiGenGothic_fonts_url/RobotoMono-Regular.ttf

修正 PDF

修正 PDF SVG 中文乱码

将 svg 文件内的 sans-serif 改为 M+ 1p Fallback 字体。

$docs/bin/pdf-svg-font.rb
cd $docs
cat > bin/pdf-svg-font.rb << "EOF"
# 改变 svg 字体
Prawn::Svg::Font::GENERIC_CSS_FONT_MAPPING.merge!(
  'sans-serif' => 'M+ 1p Fallback'
)
EOF
修正 rouge.rb 在 ini 乱掉
将 ini.rb
state :basic do
  rule %r/[;#].*?\n/, Comment
  rule %r/\s+/, Text
  rule %r/\\\n/, Str::Escape
end
修改为下列
state :basic do
  rule %r/[;#].*?\n/, Comment

  # FixBUG: Append 修正 ini 乱掉 (1)
  rule %r/\\/, Text

  rule %r/\s+/, Text
  rule %r/\\\n/, Str::Escape
end
1 插入这两行
找出 Rouge 目前的安装位置:
以 gem which 找出安装位置:
gem which rouge

/home/user/.gem/ruby/2.6.0/gems/rouge-3.30.0/lib/rouge.rb
实际位置跟 gem 有关,可能不同。

设置 Rouge 的变量:
gemRougePath=/home/user/.gem/ruby/2.6.0/gems/rouge-3.30.0
查阅 ini.rb 的内容, 也一并确认 Rouge 的变量无误
cat $gemRougePath/lib/rouge/lexers/ini.rb | grep  'state :basic do' -A8
Cygwin 先安装 Patch
cygpkg i patch
采用 patch 修正 ini.rb
# 创建 ini.rb.patch
cat > ini.rb.patch << "EOF"
@@ -16,6 +16,10 @@

       state :basic do
         rule %r/[;#].*?\n/, Comment
+
+        # FixBUG: Append 修正 ini 乱掉
+        rule %r/\\/, Text
+
         rule %r/\s+/, Text
         rule %r/\\\n/, Str::Escape
       end
EOF

# 运行 ini.rb.patch
patch $gemRougePath/lib/rouge/lexers/ini.rb -i ini.rb.patch

# 检查 patch 后的结果
cat $gemRougePath/lib/rouge/lexers/ini.rb | grep  'state :basic do' -A8

# 删除 ini.rb.patch
rm ini.rb.patch
图像的 alt 文本输入中文时,pdf 输出多个图像。

Asciidoctor PDF 2.3.4 没有本问题。

Alternative 在 PDF 并无作用,找出原代码改成不输出 alt。

converter.rb 的文件位置
# asciidoctor-pdf-1.5.3

~/.gem/ruby/2.6.0/gems/asciidoctor-pdf-1.5.3/lib/asciidoctor/pdf/converter.rb
cygpath -w "$(ls ~/.gem/ruby/2.6.0/gems/asciidoctor-pdf-1.5.3/lib/asciidoctor/pdf/converter.rb)"
c:\apps\cygwin\home\user\.gem\ruby\2.6.0\gems\asciidoctor-pdf-1.5.3\lib\asciidoctor\pdf\converter.rb

找出原代码,将下列

img = %(<img src="#{image_path}" format="#{image_format}" alt="[#{encode_quotes node.attr 'alt'}]"#{width_attr}#{fit_attr}>)

改为

# FixBUG: Modify 修正 alt 有中文时,pdf 输出多个图像, 改成不输出 alt
# img = %(<img src="#{image_path}" format="#{image_format}" alt="[#{encode_quotes node.attr 'alt'}]"#{width_attr}#{fit_attr}>)
img = %(<img src="#{image_path}" format="#{image_format}" alt="[]"#{width_attr}#{fit_attr}>)
采用 patch 修正 asciidoctor-pdf-1.5.3 converter.rb
cat > converter.rb.patch << "EOF"
@@ -2501,7 +2501,9 @@
             if ::File.readable? image_path
               width_attr = (width = preresolve_explicit_width node.attributes) ? %( width="#{width}") : ''
               fit_attr = (fit = node.attr 'fit', nil, false) ? %( fit="#{fit}") : ''
-              img = %(<img src="#{image_path}" format="#{image_format}" alt="[#{encode_quotes node.attr 'alt'}]"#{width_attr}#{fit_attr}>)
+              # FixBUG: Modify 修正 alt 有中文时,pdf 输出多个图像, 改成不输出 alt
+              # img = %(<img src="#{image_path}" format="#{image_format}" alt="[#{encode_quotes node.attr 'alt'}]"#{width_attr}#{fit_attr}>)
+              img = %(<img src="#{image_path}" format="#{image_format}" alt="[]"#{width_attr}#{fit_attr}>)
             else
               logger.warn %(image to embed not found or not readable: #{image_path}) unless scratch?
               img = %([#{node.attr 'alt'}])
EOF

ConverterRB=~/.gem/ruby/2.6.0/gems/asciidoctor-pdf-1.5.3/lib/asciidoctor/pdf/converter.rb
patch $ConverterRB -i converter.rb.patch
grep FixBUG -n3 $ConverterRB
rm converter.rb.patch
采用 patch 修正 asciidoctor-pdf-2.0.0.dev converter.rb
cat > converter.rb.patch << "EOF"
@@ -2599,7 +2599,9 @@
               end
               width_attr = %( width="#{width}")
               fit_attr = (fit = node.attr 'fit') ? %( fit="#{fit}") : ''
-              img = %(<img src="#{image_path}" format="#{image_format}" alt="#{encode_quotes node.attr 'alt'}"#{width_attr}#{fit_attr}>)
+              # FixBUG: Modify 修正 alt 有中文时,pdf 输出多个图像, 改成不输出 alt
+              # img = %(<img src="#{image_path}" format="#{image_format}" alt="#{encode_quotes node.attr 'alt'}"#{width_attr}#{fit_attr}>)
+              img = %(<img src="#{image_path}" format="#{image_format}" alt=""#{width_attr}#{fit_attr}>)
             else
               log :warn, %(image to embed not found or not readable: #{image_path})
               img = %([#{node.attr 'alt'}])
EOF

ConverterRB=~/.gem/ruby/2.6.0/gems/asciidoctor-pdf-2.0.0.dev/lib/asciidoctor/pdf/converter.rb
patch $ConverterRB -i converter.rb.patch
cat $ConverterRB | grep FixBUG -n3
rm converter.rb.patch

Asciidoctor Web PDF

npm i -g @asciidoctor/core asciidoctor-pdf

asciidoctor-web-pdf --version
# Asciidoctor Web PDF 1.0.0-alpha.10 using Asciidoctor.js 2.2.0 (Asciidoctor 2.0.10) [https://asciidoctor.org]
# Runtime Environment (node v12.18.3 on win32)
# CLI version 3.4.0

asciidoctor-web-pdf <Your adoc file> \
-a stylesheet=<Your css file>

asciidoctor-highlight.js

asciidoctor-highlight.js 可在文档转换时嵌入高亮度的 CSS (浏览器不需要 highlight.js)。

网页上有说明版本需求
检查 Node.js 的版本:
npm -v

8.19.2

检查 @asciidoctor/core 可安装的版本:
npm view @asciidoctor/core version

2.2.6

检查 highlight.js 可安装的版本:
npm view highlight.js version

11.7

都是目前版本,没有相依旧版的问题,将模块安装至 global。

安装 asciidoctor-highlight.js (注:实际上不需要指定版本)
npm install -global @asciidoctor/core@2.2.6
npm install -global highlight.js@11.7
npm install -global asciidoctor-highlight.js@0.4.0
asciidoctor-highlight.js 测试脚本
cd ~
cat > test-hljs.js << "EOF"
// Load asciidoctor.js and asciidoctor-highlight.js.
const asciidoctor = require('@asciidoctor/core')()
const highlightJsExt = require('asciidoctor-highlight.js')

// Register the extension into global registry.
highlightJsExt.register(asciidoctor.Extensions)

// Convert the content to HTML.
const content = `
[source, js]
console.log('Hello, world!')
`
const html = asciidoctor.convert(content, {
  attributes: { 'source-highlighter': 'highlightjs-ext'}, (1)
})
console.log(html)
EOF

# 运行测试
node test-hljs.js

# 测试结束,删除测试脚本。
rm test-hljs.js
1 语法高亮度处理器设为 highlightjs-ext 时,asciidoctor-highlight.js 才会转换。

CSS 可由 asciidoctor 产生网页得知 CDN 网址 https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.18.3/styles/github.min.css。
注:github.min.css 跟 Asciidoctor.js Live Preview 的 css/highlight/github.css 雷同。

@asciidoctor/core

使用语法可参考 A quick tour | Asciidoctor Docs

const asciidoctor = require('@asciidoctor/core')() (1)
// const asciidoctor = require('asciidoctor')() (1)

asciidoctor.convertFile('MyAdoc.adoc', (2)
  { safe: 'unsafe', (3)
    to_dir: 'MyDestinationDirectory',
    verbose: true, trace: true, (4)
    attributes: (5)
     {
       linkcss: true,
       copycss: false,
       experimental: true,
       icons: 'font',
       'source-highlighter': 'highlightjs-ext',
       toclevels: 2
      }
  }
)
1 @asciidoctor/core 改用 Asciidoctor.js 功能一样,修改成 const asciidoctor = require('asciidoctor')() 即可。
2 接口方法有 convert convertFile convertFiles convertDirectory 参阅: The Asciidoctor Interface | Asciidoctor Docs (AsciidoctorJ)
3 adoc 原始文档中有使用 include 宏,需设置为 unsafe (asciidoctor.js 缺省为 secure),参阅: Convert options | Asciidoctor Docs (Asciidoctor.js)
4 可采用某些 CLI Options 参考 asciidoctor --help 或 参阅: CLI Options | Asciidoctor Docs (Asciidoctor.js),
以名称来传递参数,不能用缩写。
5 Attributes 参阅:Asciidoctor 属性Document Attributes Reference | Asciidoctor Docs (Asciidoctor.js)
亦可使用「是」或「否」值,来激活或取消属性。
linkcss: ''
同等于
linkcss: true

'linkcss!': ''
同等于
linkcss: false

增加图标集 (set) 的属性如下范例:
icon:external-link-alt[set=fas]
asciidoctor-wrap.js 脚本
cat > asciidoctor-wrap.js << "EOF"
const asciidoctor = require('@asciidoctor/core')()
// const asciidoctor = require('asciidoctor')()

const highlightJsExt = require('asciidoctor-highlight.js')
highlightJsExt.register(asciidoctor.Extensions)

class TemplateConverter {
    constructor() {
        this.baseConverter = asciidoctor.Html5Converter.$new()

        const inlineImage = (node) => {
            if (node.getType() === 'icon') {
                // Icon fonts add font set attributes
                var attrib = node.getAttribute('set')
                if (!attrib) {
                    attrib = node.getDocument().getAttribute('icon-set')
                }
                var imageElement = this.baseConverter.$convert_inline_image(node)
                if (attrib) {
                    imageElement = imageElement.replace('"fa ', '"' + attrib + ' ')
                }
                return imageElement
            } else {
                // The node is not an icon, such as a callout.
                return this.baseConverter.$convert_inline_image(node)
            }
        }
        this.templates = {
            inline_image: inlineImage
        }
    }

    convert(node, transform, opts) {
        const template = this.templates[transform || node.node_name]
        if (template) {
            return template(node)
        }
        return this.baseConverter.convert(node, transform, opts)
    }
}

asciidoctor.ConverterFactory.register(new TemplateConverter(), ['html5'])

module.exports = {
    asciidoctor
}
EOF
asciidoctor-wrap-test.js (测试 asciidoctor-wrap.js) 脚本
cat > asciidoctor-wrap-test.js << "EOF"
const content = `
icon:check-square[] check-square +
icon:check-square[set="far"] check-square(far)

:icon-set: fas
icon:check-square[] check-square(fas)

[source, js]
console.log('Hello, world!')
`
const options = {
  attributes: {
    icons: 'font',
    'source-highlighter': 'highlightjs-ext'
  }
}

const wrap = require('./asciidoctor-wrap.js')
console.log(wrap.asciidoctor.convert(content, options))
EOF

# 运行测试
node asciidoctor-wrap-test.js

# 测试结束,删除测试脚本。
rm asciidoctor-wrap-test.js
Fontawesome 补充

图标名称 fa-check-square 一样;但外观不一样,是以图标集区分(如 fasfar)。Asciidcotor 产生的图标集为 fa (V4),在 V5 中也可以采用 fa 在 CSS 中它是指向 fas 的图标集。

@FontAwesome

将 Icon Font 以 svg 嵌入至网页中。

Server Side Rendering with Font Awesome 5 网页中提到
With Font Awesome 5 icons can be rendered as SVG elements using JavaScript.
意思是 Font Awesome 5 (含以后) 才有该功能,不支持 Font Awesome 4。

网页中的 js 提供 prefix 的语法,可指定 Icon set (图标集)
icon:gitlab[prefix=fab]
安装最新版的 @FontAwesome
npm i -g \
@fortawesome/fontawesome-svg-core \
@fortawesome/free-solid-svg-icons \
@fortawesome/free-regular-svg-icons \
@fortawesome/free-brands-svg-icons
列出已安装的版本
$ npm list -g
C:\apps\cygwin\usr\local\npm
├── @asciidoctor/core@2.2.6
├── @fortawesome/fontawesome-common-types@6.2.1
├── @fortawesome/fontawesome-svg-core@6.2.1
├── @fortawesome/free-brands-svg-icons@6.2.1
├── @fortawesome/free-regular-svg-icons@6.2.1
├── @fortawesome/free-solid-svg-icons@6.2.1

Asciidocor 「外部连接」图标如为 icon:external-link[set=fas] (注:set 是 pdf 的语法), external-link 是 Fontawesome V5 的名称,V6 为 fa-up-right-from-square
可在 fa-up-right-from-square 的网页 (Up Right From Square Classic Solid Icon | Font Awesome ) 中看到 external-link-alt (Alias for This Icon),
fontawesome-svg-core V6 会 (自动) 将 external-link 转换成 fa-up-right-from-square


@FontAwesome 1.2.36

@fortawesome/fontawesome-svg-core 版本 1.3 以后 (含) 产生的 html 缺少 CSS fa-w 的类别如 fa-w-16,虽然在 Server Side Rendering | Font Awesome Docs (Version 5.15.4) 及 Server-side Rendering | Font Awesome Docs (Version 6) 的范例中有 svg-inline—​fa fa-github fa-w-16 fa-lg,但实测并没有。

显示 fontawesome-svg-core 可安装的版本:
npm view @fortawesome/fontawesome-svg-core versions

找出小于 1.3 的版本为 1.2.36

显示 icons 可安装的版本:
npm view @fortawesome/free-solid-svg-icons versions

svg core 版本 1.2 需配合 icons 版本 5, 找出版本 5 的最大值为 5.15.4

由于 npm 模块为旧版本,安装在另外的路径如用户主目录中 (~)。
运行用户主目录中 (~) 的 npm 模块,NODE_PATH 应纳入 ~/node_modules (Cygwin 需转成 Windows 的路径)。

安装 @FontAwesome 1.2.36
cd ~
npm init -y

npm install @fortawesome/fontawesome-svg-core@1.2.36

npm i \
@fortawesome/free-solid-svg-icons@5.15.4 \
@fortawesome/free-regular-svg-icons@5.15.4 \
@fortawesome/free-brands-svg-icons@5.15.4
@fortawesome/free-solid-svg-icons 5.15.4 的 external-link-alt 有 bug,数据中的 , 是错的;应为 空白,修正 external-link-alt:
# 切换至 free-solid-svg-icons 模块的路径
cd ~/node_modules/@fortawesome/free-solid-svg-icons

SRC='M432,320H400a16,16,0,0,0-16,16V448H64V128H208a16,16,0,0,0,16-16V80a16,16,0,0,0-16-16H48A48,48,0,0,0,0,112V464a48,48,0,0,0,48,48H400a48,48,0,0,0,48-48V336A16,16,0,0,0,432,320ZM488,0h-128c-21.37,0-32.05,25.91-17,41l35.73,35.73L135,320.37a24,24,0,0,0,0,34L157.67,377a24,24,0,0,0,34,0L435.28,133.32,471,169c15,15,41,4.5,41-17V24A24,24,0,0,0,488,0Z'

# 先检查一下是否有错误文件
grep -rl "$SRC"

# 应该有下列三个文件
# faExternalLinkAlt.js
# index.es.js
# index.js

# 运行修正
FIX='M432 320H400a16 16 0 0 0-16 16V448H64V128H208a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16H48A48 48 0 0 0 0 112V464a48 48 0 0 0 48 48H400a48 48 0 0 0 48-48V336A16 16 0 0 0 432 320ZM488 0h-128c-21.37 0-32.05 25.91-17 41l35.73 35.73L135 320.37a24 24 0 0 0 0 34L157.67 377a24 24 0 0 0 34 0L435.28 133.32 471 169c15 15 41 4.5 41-17V24A24 24 0 0 0 488 0Z'
grep -rl "$SRC" | xargs sed -i "s/$SRC/$FIX/"

asciidoctor-svg-embed.js 脚本
cat > asciidoctor-svg-embed.js << "EOF"
const asciidoctor = require('@asciidoctor/core')()
const highlightJsExt = require('asciidoctor-highlight.js')
highlightJsExt.register(asciidoctor.Extensions)

const fortawesome = require('@fortawesome/fontawesome-svg-core');
const library = fortawesome.library;
const fas = require('@fortawesome/free-solid-svg-icons').fas
const far = require('@fortawesome/free-regular-svg-icons').far
const fab = require('@fortawesome/free-brands-svg-icons').fab
library.add(fas, far, fab)

const icon = fortawesome.icon
class TemplateConverter {
  constructor() {
    this.baseConverter = asciidoctor.Html5Converter.$new()

    const inlineImage = (node) => {
      if (node.getType() === 'icon') {
        const transform = {}
        if (node.hasAttribute('rotate')) {
          transform.rotate = node.getAttribute('rotate')
        }
        if (node.hasAttribute('flip')) {
          const flip = node.getAttribute('flip')
          if (flip === 'vertical' || flip === 'y' || flip === 'v') {
            transform.flipY = true
          } else {
            transform.flipX = true
          }
        }
        const options = {}
        options.transform = transform
        if (node.hasAttribute('title')) {
          options.title = node.getAttribute('title')
        }
        options.classes = []
        if (node.hasAttribute('size')) {
          options.classes.push(`fa-${node.getAttribute('size')}`)
        }
        if (node.getRoles()) {
          options.classes.push(node.getRoles().map(value => value.trim()))
        }

        const meta = {}
        var attrib = node.getAttribute('set') (1)
        if (!attrib) {
          attrib = node.getDocument().getAttribute('icon-set')
        }
        if (attrib) {
          meta.prefix = attrib
        }

        meta.iconName = node.getTarget()
        const faIcon = icon(meta, options)
        if (faIcon) {
          return faIcon.html
        }
        else {
          return meta.iconName
        }
      } else {
        // The node is not an icon, such as a callout.
        return this.baseConverter.$convert_inline_image(node)
      }
    }
    this.templates = {
      inline_image: inlineImage
    }
  }

  convert(node, transform, opts) {
    const template = this.templates[transform || node.node_name]
    if (template) {
      return template(node)
    }
    return this.baseConverter.convert(node, transform, opts)
  }
}
asciidoctor.ConverterFactory.register(new TemplateConverter(), ['html5'])

const dom = fortawesome.dom;
const styles = asciidoctor.Extensions.create()
styles.docinfoProcessor(function () {
  const self = this
  // Style output to header_footer
  // header_footer needs to be set to true
  self.atLocation('head')
  self.process(function () {
    return `<style>${dom.css()}</style>`
  })
})

module.exports = {
  asciidoctor,
  styles
}
EOF
1 设置图标集为 seticon:external-link-alt[set=fas]
asciidoctor-svg-embed-test.js (测试 asciidoctor-svg-embed.js) 脚本
cat > asciidoctor-svg-embed-test.js << "EOF"
const content = `
icon:check-square[] check-square +
icon:check-square[set="far"] check-square(far)

:icon-set: fas
icon:check-square[] check-square(fas)

NOTE: Note

[source, js]
----
console.log('Hello, world!') <1>
----
<1> logging
`
const wrap = require('./asciidoctor-svg-embed.js')
console.log(wrap.asciidoctor.convert(content,
  {
    // header_footer: true, (1)
    // extension_registry: wrap.styles, (1)
    attributes: {
      icons: 'svg',
      'source-highlighter': 'highlightjs-ext'
    }
  }
))
EOF

# 运行测试
node asciidoctor-svg-embed-test.js

# 测试结束,删除测试脚本
rm asciidoctor-svg-embed-test.js
1 如果要内嵌样式表至网页,则去掉注解。

asciidoctor-stylesheet-factory

为什么要安装 asciidoctor-stylesheet-factory?
因为要修改 Asciidoctor css,如果直接修改 css 除了工程太大外弹性也不足,采用修改 sass 再编译出 css 的方式,比较完整也具弹性, 如在转换主题时可套用已修改的 sass。

Cygwin 先安装 git
cygpkg i git
安装
cd $docs # 切换至项目文件夹
git clone https://github.com/asciidoctor/asciidoctor-stylesheet-factory styles (1)
cd styles
bundle
npm i (2)
1 git 拷贝至项目文件夹中的 styles
2 npm 模块安装成项目专属。
bundle 相当于运行下列
gem install --version '0.12.7' compass
gem install --version '4.3.2' zurb-foundation

注:别用上述来安装,gem 安装路径会不同,应在 styles 路径中运行 bundle 。

bundle 安装的 gems 路径
bundle show --paths

/usr/share/gems/gems/bundler-1.17.2
/usr/share/gems/gems/chunky_png-1.3.12
/usr/share/gems/gems/compass-0.12.7
/usr/share/gems/gems/fssm-0.2.10
/usr/share/gems/gems/sass-3.2.19
/usr/share/gems/gems/zurb-foundation-4.3.2
npm i 相当于运行下列
npm i color@0.11.4 csscolormin@0.0.5 cssshrink
npm 安装的模块
npm list

styles@ C:\project\styles
├── color@0.11.4
├── csscolormin@0.0.5
└── cssshrink@0.0.5
编译 sass
cd $docs # 切换至项目目录
cd styles

# 编译 sass
./build-stylesheet.sh [主题文件名]

将 zurb-foundation 合并至项目 (但不应修改),方便 VSCode 或 ATOM 搜索,下列的 $docs 为项目目录。
cp -r /usr/share/gems/gems/zurb-foundation-4.3.2/scss/foundation $docs/styles/sass
cp /usr/share/gems/gems/zurb-foundation-4.3.2/scss/normalize.scss $docs/styles/sass/_normalize.scss
文件结构如下
styles
└── sass
    ├── foundation
    │   ├── components
    │   └─ _variables.scss
    ├─ _normalize.scss
    └─ ...
@include grid-row; 那 grid-row 是怎么来的

以 VSCode / ATOM 搜索 %docs%\styles\sass 可直接得知是在 %docs%\styles\sass\foundation\components\_grid.scss。

编译出的 css 可发送至 The W3C CSS Validation Service 验证。

CSSTidy

将 css 压缩及优化。

Cygwin 先安装 php
cygpkg i php
安装 CSSTidy
mkdir $docs/bin/lib
cd $docs/bin/lib
git clone https://github.com/Cerdic/CSSTidy.git CSSTidy (1)

cd CSSTidy

git show-branch
# [master] v1.7.2

# 显示说明
bin/pcsstidy --help (2)
1 安装在项目目录中的 bin/lib/CSSTidy
2 简要说明,详细说明参考 Ubuntu Manpage: csstidy - CSS parser and optimiser
$docs/bin/mkcss.php
cd $docs
cat > bin/mkcss.php << "EOF"
<?php
include('bin/lib/CSSTidy/class.csstidy.php');
$csstidy = new csstidy();

// optimise_shorthands=[1|2|0]
// If set to 2, csstidy will invoke all optimisations. 0 represents no optimisations, and
// 1 a safe level of optimatisations. Default = 1.
$csstidy->set_cfg('optimise_shorthands', 2);

// merge_selectors=[2|1|0]
// If selectors (including ID's and classes) have identical properties, then csstidy will
// merge them. A setting of 2 represents a high degree of merging. 0 represents no
// merging. Default = 2.
$csstidy->set_cfg('merge_selectors', 2);

// discard_invalid_properties
// If set to true, csstidy will remove invalid properties. In a sense this is like
// validation, except you get no warnings unless you study the output. As an example, if
// you misspelled the property "width" as "with", that property will simply be removed.
// Default = false.
// $csstidy->set_cfg('discard_invalid_properties', true);

// Set the output template [default|filename|low|high|highest]
$csstidy->set_cfg('template', 'highest');

// Parse the CSS
$css_code = file_get_contents($argv[1]);
$csstidy->parse($css_code);

// Get back the optimized CSS Code
echo $csstidy->print->plain();
?>
EOF
外部调用方式
cd $docs
php bin/mkcss.php $InputFile > $OutputFile
运行出错如下,缺少 ctype_space。
PHP Fatal error:  Uncaught Error: Call to undefined function ctype_space() in docs$/bin/lib/CSSTidy/class.csstidy.php:703
Cygwin 安装 php-ctype
cygpkg i php-ctype

应用程序设置

Asciidoctor.js Live Preview

预览 Asciidoctor 的源文件 (.ad.adoc.asciidoc)。

Custom attributes

toc=left source-highlighter=highlight.js experimental toclevels=2 prewrap!
参阅:Asciidoctor 属性。注:source-highlighter 只支持 highlight.js
注:打开 Asciidoctor.js Live Preview 扩充功能选项可找到 Custom attributes

Firefox 出错 Unresolved directive in <some>.adoc - include::_includes/<some>.adoc[]

当文档中有 include 时,无法正确显示,可试试:

  1. 打开 Firefox about:config 页面。

  2. security.fileuri.strict_origin_policy 设为 false

不过有个小问题,由文件列表 (file://*) 点击文档后无法显示;于网址列按下 Enter 后则正常,或者采用「用新分页打开链结」。
提外话,在编译后的 html 中 Admonition 的 icon 在激活 NoScript 后看不到 icon,那是因为 NoScript 禁止「字体」;打开即可。

Firefox 扩展不允许读取本机文件

Add a stylesheet…​ 的 css 文件中是导入其他的 css 档如 @import "file://<导入的 css 文件>";,那么预览 .adoc 源文件将会出错。

安全性错误: moz-extension://<机码>/ 的内容无法由 file://<导入的 css 文件> 加载数据或链结。

解决方案:

  1. 打开 about:debugging#/runtime/this-firefox 页面,找到 Asciidoctor.js Live Preview 套件,拷贝 内部 UUID

  2. 打开 about:config 页面并创建 3 个条目 (都是 字符串 String 类型)。

    名称: capability.policy.policynames
    数值: localfilelinks
    
    名称: capability.policy.localfilelinks.checkloaduri.enabled
    数值: allAccess
    
    名称: capability.policy.localfilelinks.sites
    数值: moz-extension://<第一步拷贝的内部 UUID>

VSCode 套件

Auto Time Stamp
在保存 adoc 文件时,自动更新 :docdatetime: 的时间,如下范例:
:docdatetime: 2022-12-13 23:44:22

[[adoc-inst]]
= Asciidoctor 安装

在套件的设置中,修改下列:

Auto Time Stamp: Filename Pattern

^(?!.*[/\\]\.vscode[/\\]settings.json$) 改为 ^.*\.(adoc|ad)$ 只处理 .adoc.ad 的文件。

Auto Time Stamp: Modified Time Start

[lL]ast[ -][mM]odified *: 改为 ^:docdatetime: (注意,尾端有空白)

Auto Time Stamp: Moment Format

YYYY/MM/DD HH:mm:ss 改为 YYYY-MM-DD HH:mm:ss

settings.json
{
    "lpubsppop01.autoTimeStamp.filenamePattern": "^.*\\.(adoc|ad)$"
    "lpubsppop01.autoTimeStamp.modifiedTimeStart": "^:docdatetime: ",
    "lpubsppop01.autoTimeStamp.momentFormat": "YYYY-MM-DD HH:mm:ss",
}

注:套件中有说明 The Line Limit setting is 5 lines from the beginning of the file …​,只会在文件开头的 5 行内才会取代。


ATOM 套件

ATOM asciidoc-preview 在 Source block 预览时看不到 bash

将 bash 取代成可显示的类型。
修改 %USERPROFILE%\.atom\packages\asciidoc-preview\lib\highlights-helper.coffee
'bash': 'source.shell' 修改成可显示的 source.perl 如 'bash': 'source.perl'

ATOM document-outline 在 bash source block 的 # 字符,解读成标题

原代码 asciidoc-model.js 中的 HEADING_REGEX 纳入了 markdown 的标题 (#) 去掉它。
不过 asciidcotor 的「注解」如 // 或 //// 内的标题字符 (=) 会解读成标题。

修改 %USERPROFILE%\.atom\packages\document-outline\lib\asciidoc-model.js
const HEADING_REGEX = /^(=={0,5}|#\#{0,5})[ \t]+(.+?)(?:[ \t]+\1)?$/gm;
改成
const HEADING_REGEX = /^(=={0,5})[ \t]+(.+?)(?:[ \t]+\1)?$/gm;