Class: VivlioStarter::CLI::Build::PdfPageMapExtractor

Inherits:
Object
  • Object
show all
Defined in:
lib/vivlio_starter/cli/build/pdf_page_map_extractor.rb

Overview

生成済み PDF の named destinations からページマッピングを抽出する

Defined Under Namespace

Classes: IndexMappingEntry, MappingEntry, PageMapping

Constant Summary collapse

GLOSSARY_PREFIX =

vivliostyle が付ける named destination の接頭辞(アンカー ID は最初の # 以降)

'gls-src-'
INDEX_PREFIX =
'idx-'

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(pdf_path) ⇒ PdfPageMapExtractor

Returns a new instance of PdfPageMapExtractor.

Parameters:

  • pdf_path (String)

    _sections.pdf のパス



50
51
52
# File 'lib/vivlio_starter/cli/build/pdf_page_map_extractor.rb', line 50

def initialize(pdf_path)
  @pdf_path = pdf_path
end

Class Method Details

.decode_destination_name(name) ⇒ String

vivliostyle の :XXXX(UTF-16 コードユニットの 4 桁 hex)エスケープを復号する。 元名前中の :# はすべてエスケープされるため、区切りの取り違えは起きない。 想定外の入力では復号せず元文字列を返し、呼び出し側の # 判定で自然に捨てられる。

Parameters:

  • name (Symbol, String)

    PDF name

Returns:

  • (String)


118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/vivlio_starter/cli/build/pdf_page_map_extractor.rb', line 118

def self.decode_destination_name(name)
  source = name.to_s
  units = []
  i = 0
  while i < source.length
    if source[i] == ':' && source[i + 1, 4]&.match?(/\A\h{4}\z/)
      units << source[i + 1, 4].hex
      i += 5
    else
      units << source[i].ord
      i += 1
    end
  end
  units.pack('U*')
rescue StandardError
  name.to_s
end

Instance Method Details

#document_first_pagesHash{String => Integer}

スパイン文書(HTML ベース名)→ その文書が始まる通しページ番号。

/Dests の名前には元 URL が丸ごと入っているため、# の手前を見れば その destination がどの文書由来かが分かる。vivliostyle はスパイン文書ごとに 必ず改ページするので、1 文書内の最小ページ=その文書の開始ページになる。

id を 1 つも持たない文書は結果に現れない。呼び出し側は「欲しい文書の鍵が 取れたか」で判定すること——件数では判定できない。

Returns:

  • (Hash{String => Integer})

    例 { '_titlepage' => 511, '_colophon' => 513 }



81
82
83
84
85
86
87
88
89
90
# File 'lib/vivlio_starter/cli/build/pdf_page_map_extractor.rb', line 81

def document_first_pages
  return {} unless File.exist?(pdf_path)

  each_destination(::PDF::Reader.new(pdf_path)).each_with_object({}) do |(document, _anchor, page), first|
    key = File.basename(document.to_s, '.html')
    next if key.empty?

    first[key] = page if first[key].nil? || page < first[key]
  end
end

#extract!PageMapping

Returns:

Raises:

  • (RuntimeError)

    PDF が無い / named destination が 1 件も取れない場合



56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/vivlio_starter/cli/build/pdf_page_map_extractor.rb', line 56

def extract!
  raise "本文 PDF が見つかりません: #{pdf_path}" unless File.exist?(pdf_path)

  reader = ::PDF::Reader.new(pdf_path)
  anchor_to_page = build_anchor_page_map(reader)

  if anchor_to_page.empty?
    raise '本文 PDF に named destinations(/Dests)が見つかりません。' \
          'vivliostyle の出力仕様が変わった可能性があります'
  end

  Common.log_success("[backlink-dedup] ページマッピングを取得しました(#{anchor_to_page.size} 件)")
  build_page_mapping(anchor_to_page, reader.page_count)
end

#pages_for(ids) ⇒ Hash{String => Integer}

指定したアンカー ID の通しページ番号を引く。

document_first_pages が「文書 → 開始ページ」を返すのに対し、こちらは 「任意の id → その id が組まれたページ」を返す。回転テーブルの画像化が 使う(kindle-rotate-table-image-spec.md §4)。

引けなかった ID は戻り値に現れない。 /Dests に出るのはリンクの飛び先に なっている id だけなので、呼び出し側は「欲しい ID の鍵が取れたか」で判定すること。

Parameters:

  • ids (Array<String>)

    引きたいアンカー ID

Returns:

  • (Hash{String => Integer})


103
104
105
106
107
108
109
110
# File 'lib/vivlio_starter/cli/build/pdf_page_map_extractor.rb', line 103

def pages_for(ids)
  wanted = Array(ids).uniq
  return {} if wanted.empty? || !File.exist?(pdf_path)

  each_destination(::PDF::Reader.new(pdf_path)).each_with_object({}) do |(_document, anchor, page), found|
    found[anchor] = page if wanted.include?(anchor)
  end
end