2. ドキュメント型
← 言語 · 目次 · 次:text:: →
すべてのスクリプトは doc 変数を自動的に受け取ります。これが解析対象の PDF
です。ここからページ、フォント、画像にたどり着きます。
2.1 doc — ドキュメント
プロパティ
| プロパティ | 型 | 内容 |
|---|---|---|
doc.page_count |
数値 | ページ数 |
doc.title |
文字列 | メタデータのタイトル(無い場合は空) |
doc.author |
文字列 | メタデータの著者(無い場合は空) |
doc.filename |
文字列 | 解析対象のファイル名 |
doc.pages |
リスト | すべてのページ |
doc.fonts |
リスト | 使用されているすべてのフォント |
doc.images |
リスト | 全ページのすべての画像 |
check "Document properties" {
print("file:", doc.filename)
print("pages:", doc.page_count)
print("title:", doc.title)
// コレクションは通常のリストです — すべてのリストメソッドが使えます
require doc.pages.length == doc.page_count
require doc.fonts.length > 0
print("images in the whole document:", doc.images.length)
}
メソッド
doc.extract_text()
ドキュメント全体のテキスト。ページは改行で区切られます。
check "Document text" {
text = doc.extract_text()
assert text.trim() != "", "PDF has no extractable text (images only?)"
require text.contains("Agreement")
print("total characters:", text.length)
}
2.2 page — ページ
ページは doc.pages(ブロック内)または page 変数(rule 内)から得ます。
プロパティ
| プロパティ | 型 | 内容 |
|---|---|---|
page.number |
数値 | ページ番号(1 起点) |
page.index |
数値 | ページ索引(0 起点) |
page.width |
数値 | 幅(ポイント) |
page.height |
数値 | 高さ(ポイント) |
page.images |
リスト | このページの画像 |
page.tac |
数値 | 推定インク総量の最大値(%) |
page.ink_coverage |
数値 | 推定インク総量の平均(%) |
page.min_stroke_width |
数値/null | 最も細い線幅(pt)。線が無ければ null |
page.has_media_box |
真偽値 | MediaBox が定義されている |
page.has_crop_box |
真偽値 | CropBox が定義されている |
page.has_trim_box |
真偽値 | TrimBox が定義されている |
page.has_bleed_box |
真偽値 | BleedBox が定義されている |
page.has_art_box |
真偽値 | ArtBox が定義されている |
check "Page format" {
doc.pages.each { |page|
// number は人が見る番号、index は内部計算用
assert page.width > 100mm,
"page #{page.number} is too narrow: #{page.width}pt"
// ボックス:印刷には必須です
assert page.has_trim_box,
"page #{page.number} has no TrimBox (trim area)"
assert page.has_bleed_box,
"page #{page.number} has no BleedBox (bleed area)"
}
}
check "Ink and strokes" {
doc.pages.each { |page|
assert page.tac <= 300,
"page #{page.number}: #{page.tac}% ink (limit 300%)"
// min_stroke_width は null になり得ます(線の無いページ)。
// null は偽なので、この書き方は安全です:
assert !page.min_stroke_width || page.min_stroke_width >= 0.25,
"page #{page.number} has a hairline stroke"
}
}
メソッド
page.extract_text()
このページだけのテキスト。
check "Blank pages" {
blank = doc.pages.filter { |p| p.extract_text().trim() == "" }
assert blank.length == 0,
"#{blank.length} blank page(s): #{blank.map { |p| p.number }.join(", ")}"
}
2.3 font — フォント
フォントは doc.fonts から得ます。
| プロパティ | 型 | 内容 |
|---|---|---|
font.name |
文字列 | フォント名 |
font.is_embedded |
真偽値 | ファイルに埋め込まれているか |
check "Embedded fonts" {
// 埋め込まれていないフォントはリーダーが代替します — 見た目が変わります
doc.fonts.each { |font|
assert font.is_embedded,
"font '#{font.name}' is not embedded in the PDF"
}
}
check "Font report" {
print("fonts in use:", doc.fonts.map { |f| f.name }.join(", "))
missing = doc.fonts.filter { |f| !f.is_embedded }
print("not embedded:", missing.length)
}
2.4 image — 画像
画像は doc.images(全体)または page.images(1ページ分)から得ます。
| プロパティ | 型 | 内容 |
|---|---|---|
image.width |
数値 | 幅(ピクセル) |
image.height |
数値 | 高さ(ピクセル) |
image.dpi |
数値 | 実効解像度(dpi_x と dpi_y の小さい方) |
image.dpi_x |
数値 | 水平方向の実効解像度 |
image.dpi_y |
数値 | 垂直方向の実効解像度 |
image.color_space |
文字列 | DeviceRGB、DeviceCMYK、Indexed… |
image.page_number |
数値 | 配置されているページ(1 起点) |
image.bits_per_pixel |
数値 | ビット深度 |
DPI は実効値です。ピクセル数 ÷ ページ上の印刷サイズで計算され、 メタデータの公称値ではありません。印刷品質に効くのはこちらです。1000 px の画像を 20 cm に引き伸ばせば、メタデータが何と言おうと DPI は低くなります。
profile "images-for-offset" {
const MIN_DPI = 300
check "Resolution" {
doc.images.each { |img|
assert img.dpi >= MIN_DPI,
"image #{img.width}x#{img.height}px on page #{img.page_number}: #{img.dpi} DPI (minimum #{MIN_DPI})"
}
}
check "Color space" {
// オフセット印刷は CMYK です。RGB は変換が必要です
doc.images.each { |img|
assert img.color_space != "DeviceRGB",
"RGB image on page #{img.page_number} — convert to CMYK"
}
}
check "Images per page" {
doc.pages.each { |page|
// page.images はそのページの画像だけを持ちます
print("page", page.number, "has", page.images.length, "image(s)")
}
}
}
2.5 region — ページ上の領域
領域は矩形でページの一部を指定し、フッター、ヘッダー、バーコード欄、医薬品の 表示帯などを個別に検証できます。
作成
// region(x, y, 幅, 高さ [, "名前"])
// 原点 (0,0) は PDF と同じく左下です。
header = region(0, 742, 595, 100, "header")
footer = region(0, 0, 595, 60, "footer")
band = region(20mm, 250mm, 60mm, 15mm, "red band")
プロパティ
| プロパティ | 内容 |
|---|---|
region.name |
作成時に付けた名前(省略時は空) |
region.x / region.y |
左下の座標 |
region.width / region.height |
寸法 |
region.right / region.top |
右辺と上辺(計算値) |
region.area |
面積(平方ポイント) |
メソッド
| メソッド | 動作 |
|---|---|
region.contains_point(x, y) |
その点は内側か |
region.intersects(other) |
2つの領域が重なるか |
region.expand(pt) |
全方向に広げた新しい領域 |
region.inset(pt) |
全方向に縮めた新しい領域 |
region.export_coordinates() |
[x0, y0, x1, y1] |
check "Working with regions" {
footer = region(0, 0, 595, 60, "footer")
require footer.name == "footer"
require footer.top == 60.0
require footer.right == 595.0
require footer.area == 35700.0
// 点がフッター内にあるか
require footer.contains_point(300, 30)
require !footer.contains_point(300, 500)
// 重なり:要素が禁止領域に侵入していないかの確認に使えます
header = region(0, 780, 595, 62)
require !footer.intersects(header)
// expand/inset は「新しい」領域を返します(元は変わりません)
slack = footer.expand(5mm) // 各辺 5mm 広い
safe = footer.inset(3mm) // 各辺 3mm 狭い
require slack.area > footer.area
require safe.area < footer.area
}
検証での使い方
profile "medicine-label" {
check "Prescription band" {
// 表示帯は上部にあり、法定文言を含む必要があります
band = region(0, 700, 595, 142, "band")
content = text::extract_from_region(1, band)
assert content.contains("PRESCRIPTION ONLY"),
"band is missing the mandatory text"
}
check "Ink in the fold area" {
// 折り目のインクが多すぎると加工で割れます
fold = region(290, 0, 15, 842, "center fold")
measured = prepress::calculate_tac_by_region(1, fold)
assert measured.first() < 240,
"too much ink on the fold: #{measured.first()}%"
}
check "Barcode in the right place" {
code_area = region(400, 20, 180, 80, "barcode area")
assert codes::validate_barcode_position(code_area),
"barcode outside the reserved area"
}
}
← 言語 · 目次 · 次:text:: →