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;