🔰

NimでPDFを作成

2023/03/03に公開

Nim言語については、初心者に毛が生えたぐらいの知識しかない未熟者です。
早々ですが、Nim言語でPDFを作成したいと思いませんか?、調べてみました。
nimPDFというライブラリがありましたので、今回はこれが動かせるかどうか検証していきます。

環境

私のPC環境はWindowsであるため、Nightly build版で動作させています。

  • OS: Windows11
  • Nim 1.9.1 (v2.0 RC版 2023/02/18)
  • nimPDF 0.4.3 (2年前から一切更新なし)

nimPDFの特徴

  • 画像は、PNG, JPEG, BMPが対応しています。
  • フォントは、TTF, TTCが対応しています。
  • 色彩は、RGB, CMYKが対応しています。
  • フォーム機能については、対応しているらしいが、未確認

PDFの動作確認

1. nimPDFライブラリのインストール

nimble install nimPDF

GitHubにnimPDFライブラリは存在しますが、ドキュメントは一切ありません。唯一あったのは、Adobe PDFのマニュアルのみ。

2. フォントの設定

日本語フォントについては、フリーで配布されている物は、ほぼ全て文字化けします。
唯一問題なかったのは、Lineの子会社であるLINE Seed JPが配布しているフォントのみでした。(GoogleフォントのNoto Sans JPについては、フォント形式がOTFであるため、未確認。)
TTFフォントをダウンロードした後、ソースがあるフォルダに、fontsフォルダを作成し、その中にフォントを入れます。

3. 画像の設定

画像は、ソース内にresourcesフォルダを作成し、その中に配置しておきます。

4. PDF作成プログラム

sample01.nim
#[
  nimPDFの注意点

  importは、import nimPDF/nimPDFに設定する
  日本語フォントは、フリーで配布されている日本語フォントは文字化けするが、唯一、LINE Seed JP_TTFは文字化けしない
  イタリック書体にすると文字化けする
  縦書き文字は、半角英語は問題ないが、日本語は文字が詰まって表示される
  読み込むフォントは、fontsフォルダを作成し、TTFがTTCファイルを置く
  読み込む画像は、resourcesフォルダを作成し、画像を置く
]#
import nimPDF/nimPDF

# 色の設定
const COLOR_WHITE  = initRGB(1.0, 1.0, 1.0)
const COLOR_BLACK  = initRGB(0.0, 0.0, 0.0)
const COLOR_RED    = initRGB(1.0, 0.0, 0.0)
const COLOR_GREEN  = initRGB(0.0, 0.5, 0.0)
const COLOR_YELLOW = initRGB(1.0, 1.0, 0.0)
const COLOR_BLUE   = initRGB(0.0, 0.0, 1.0)
const COLOR_GRAY   = initRGB(0.4, 0.4, 0.4)
const COLOR_LIGHT_GRAY = initRGB(0.6, 0.6, 0.6)

var doc = newPDF()          # PDFオブジェクトを生成

doc.addPage(getSizeFromName("A4"), PGO_PORTRAIT)  # ページの生成(サイズはA4)

doc.setFillColor(COLOR_BLACK)                     # 色はブラック
doc.setFont("LINE Seed JP_TTF", {FS_REGULAR}, 6, ENC_WINANSI)
doc.drawText(15, 15, "あいうえお かきくけこ さしすせそ たちつてと なにぬねの はひふへほ")
doc.drawText(15, 15+8, "アイウエオ カキクケコ サシスセソ タチツテト ナニヌネノ ハヒフヘホ")
doc.drawText(15, 15+16, "アイウエオ カキクケコ サシスセソ タチツテト ナニヌネノ ハヒフヘホ")
doc.drawText(15, 15+24, "@?%$#”!()=〇±※←")
doc.drawVText(120, 55, "あいうえお かきくけこ")     # 縦書き全角文字
doc.drawVText(100, 55, "Hello World")             # 縦書き英字

# 四角形を生成
doc.setLineWidth(1.0)             # 線の太さ
doc.setStrokeColor(COLOR_BLACK)   # 線の色
doc.drawRect(15, 50, 60, 10);     # 矩形 x y width height
doc.stroke()                      # 描画

# 四角形を生成
doc.setLineWidth(0.5)             # 線の太さ
doc.setStrokeColor(COLOR_BLACK)   # 線の色
doc.drawRect(15, 70, 60, 10);     # 矩形 x y width height
doc.setFillColor(COLOR_YELLOW)    # 矩形の色
doc.fillAndStroke()               # 描画

# 画像を読み込み・貼り付け
var img = doc.loadImage("king.png") # 画像の読み込み
doc.saveState()
doc.scale(0.3, 15, 30)            # 画像を0.3倍に縮小 15はx座標 30はy座標
doc.drawImage(15, 320, img)       # 画像の貼り付け 左下の座標
doc.restoreState()

# PDF作成
if not doc.writePDF("sample01.pdf"):
  echo "書き込み不可能: sample01.pdf"

プログラムの説明

  • initRGBでRGB色を取得し、setFillColorで描画色を設定します。(引数は、0.0~1.0を設定で、256じゃない理由は知りません。)
  • PDFオブジェクトを生成後に、addPageページ設定を行います。
  • フォント設定を行うsetFontは、フォントファミリー名、スタイル型、サイズ、エンコードの順に設定を行います。
  • drawVTextは縦書きで表示しますが、日本語だと文字が重なり、使い物になりません。また、英語半角は表示はされますが、何故か左寄りに表示されます。
  • 画像の読み込み後に、saveStaterestoreStateを設定していますが、それぞれ何をしている関数かわかりません。無くても画像は出力されます。

下記のようにターミナル上から実行

nim r --hints:off .\sample01.nim

出力結果

  • ソースと同一ディレクトリにsample01.pdfファイルが作成されますので、PDFReaderかブラウザで表示出来ます。

5. 格子のみのPDF作成プログラム

sample02.nim
#[
  格子状の線を引くPDFを作成
]#
import nimPDF/nimPDF
import math

var doc = newPDF()

let size = getSizeFromName("A4")
doc.addPage(size, PGO_PORTRAIT)

const gray_thin = initRGB(0.5,0.5,0.5)
const gray_bold = initRGB(0.8,0.8,0.8)
doc.setFillColor(gray_thin)
doc.setStrokeColor(gray_bold)

let width = size.width.toMM
let height = size.height.toMM

# 横線の描画 5mm間隔で線を引く
var y = 0.0
while y < height:
  if `mod`(y, 10.0) == 0.0:
    doc.setLineWidth(0.5)
  else:
    doc.setLineWidth(0.1)

  doc.moveTo(0, y)
  doc.lineTo(width, y)
  doc.stroke()
  y += 5.0

# 縦線の描画 5mm間隔で線を引く
var x = 0.0
while x < width:
  if `mod`(x, 10) == 0:
    doc.setLineWidth(0.5)
  else:
    doc.setLineWidth(0.1)

  doc.moveTo(x, 0)
  doc.lineTo(x, height)
  doc.stroke()
  x += 5.0

const COLOR_BLACK  = initRGB(0.0, 0.0, 0.0)
doc.setFillColor(COLOR_BLACK)
doc.setFont("LINE Seed JP_TTF", {FS_BOLD}, 3, ENC_WINANSI)
# 縦軸の文字
y = 0.0
while y < height:
  if (`mod`(y,10.0) == 0) and (y > 0):
    doc.drawText(5, y - 2, $y)
  y += 5.0

# 横軸の文字
x = 0
while x < width:
  if (`mod`(x,50) == 0) and (x > 0):
    doc.drawText(x, 5, $x)
    doc.drawText(x, height - 5, $x)
  x += 5.0

if not doc.writePDF("sample02.pdf"):
  echo "書き込み不可能: sample02.pdf"

プログラムの説明
getSizeFromName("A4")関数でA4サイズを求め、横軸・縦軸に5mm間隔でラインを引きます。
起点moveToから終点lineToを設定した後、strokeで線を描画するだけのプログラムです。

下記のようにターミナル上から実行

nim r --hints:off .\sample02.nim

出力結果

  • ソースと同一ディレクトリにsample02.pdfファイルが作成されますので、PDFReaderかブラウザで表示出来ます。

おわりに

簡単ではありますが、nimPDFの使い方を解説してみました。
日本語フォントについては、試行錯誤しましたが、LINE Seed JPなら出力される事はわかりました。ただ、それ以外のフリーフォントは文字化けします。多分、nimPDFのバグなんでしょうね。後、スタイル形式をイタリック文字にすると文字化けします。また縦文字も文字が重なり使い物になりませんので、注意が必要です。
nimPDFにはドキュメントが一切ありませんが、sample内のソースを見てもわかる通り、ある程度、感覚でPDFを作成する事は可能です。
ただ、壁にぶち当たると、ソース解析が必要になりますが…

Discussion