Upload
ochi-shuji
View
2.707
Download
0
Embed Size (px)
Citation preview
iOS での PDF 処理あれこれ
越智 修司
@ponpoko1968
自己紹介越智修司
• KLab( くらぶ ) 株式会社
• ソシャゲの会社でソシャゲじゃ無いもの作ってます
• アプリ・サービスのプロトタイピング
• 有名アーティスト・アイドルのファンクラブアプリ開発
• 最近はデータ解析
• python,R など
動機• 自炊始めた
• PDF リーダーをいろいろ試してみた
• 要望• 読書内容を残したい・シェア
• evernote
• 視力が落ちてきた人 ( つまり自分 ) も読みやすく
クリップリーダー• PDF リーダー
苦労したこと• メモリ消費
• 目次の抽出
• 文字列の抽出
PDF 描画 (1)
• CGContextDrawPDFPage() を使えばOK
• ビットマップコンテクストに描画して永続化すればキャッシュできる
PDF 描画 (2)CGPDFDocumentRef pdfDocument = CGPDFDocumentCreateWithURL(url);
// ページ番号からページを取得CGPDFPageRef page = CGPDFDocumentGetPage ( pdfDocument, pageNum );
CGContextDrawPDFPage( context, page );CGPDFDocumentRelease( pdfDocument );
PDF 描画 (3)
• 注意点
• メモリを消費します。
• しかもページを取得して描画するたびに消費量が増えます。
• 対策
• ページレンダリングの都度 CGPDFDocument を開放して開き直す。→パフォーマンス劣化
• didReceiveMemoryWarning を受け取ったらいったん閉じて再開。
「目次」機能
Core Graphics での PDF の構造• 階層化されたオブジェクトの集合体
• ページ• フォント• コンテントストリーム
Document management — Portable document format — Part 1: PDF 1.7 より
PDF 描画 (2)CGPDFDocumentRef pdfDocument = CGPDFDocumentCreateWithURL(url);
// ページ番号からページを取得CGPDFPageRef page = CGPDFDocumentGetPage ( pdfDocument, pageNum );
CGContextDrawPDFPage( context, page );CGPDFDocumentRelease( pdfDocument );
Core Graphics での PDF の構造• Document Catalog という辞書から文
書構造を取り出す
• オブジェクト単位の情報はCGPDFDictionary として扱われる
• 配列は CGPDFArray として扱われる場合と、 'First','Next' キーから参照できるリンクリストになっている場合がある( Lisp の car と cdr のようなもの )
• しかも Composite 構造になっている
リンクリスト
Next
First
Pages
ページ 並びの取得CGPDFDictionaryRef catalog=CGPDFDocumentGetCatalog( document_ );CGPDFDictionaryRef pages =NULL;CGPDFDictionaryGetDictionary(catalog, "Pages", &pages);
// 先頭の要素を取得CGPDFArrayRef pagesArray= NULL;CGPDFDictionaryGetDictionary(pages, "Kids", &pagesArray);
int cnt = CGPDFArrayGetCount (pagesArray );for ( int i = 0; i < cnt; i++ ){
const char *typeString;CGPDFDictionaryRef pageDict;CGPDFArrayGetDictionary(pagesArray, i, &pageDict );CGPDFDictionaryGetName(pageDict, "Type", &typeString );if(strncmp("Page",typeString,strlen("Page"))==0 ){
// pageDict オブジェクトへのポインタと、ページ番号をNSDictionary に保存[pageNumDict setValue:[NSNumber numberWithInt:pageNum]
forKey:[NSString stringWithFormat:@"%p",pageDict] ];
}
目次構造の取得
Document management — Portable document format — Part 1: PDF 1.7 より
PDF 文書構造(1)CGPDFDictionaryRef
catalog=CGPDFDocumentGetCatalog( document_ );CGPDFDictionaryRef outlines=NULL;CGPDFDictionaryGetDictionary(catalog, "Outlines",
&outlines );
// 先頭の要素を取得CGPDFDictionaryRef first = NULL;CGPDFDictionaryGetDictionary(outlines, "First", &first );
// 見出しを取得CGPDFStringRef title;CGPDFDictionaryGetString ( first, "Title", &title );
// 次の章 (cdr 部 ) を取得CGPDFDictionaryRef next = NULL;CGPDFDictionaryGetDictionary(outlines, "Next", &next);
// 小見出し( car 部)を取得CGPDFDictionaryRef children;CGPDFDictionaryGetString ( first, "First", &children);
PDF 文書構造(2)// ページオブジェクトを取得CGPDFDictionaryRef page;CGPDFDictionaryDictionary ( first, "D", &title );
// 「 D 」キーがなく、ページオブジェクトを直接参照できない場合CGPDFStringRef dest;CGPDFDictionaryGetString(dict, "Dest", &dest );
Document management — Portable document format — Part 1: PDF 1.7 より
ページ番号と文書構造のリンク
0
1
2
3
・・・
n ContentsPages
val key
文字列抽出機能
• コンテントストリームのなかから、文字列描画命令部分を取り出す
コンテントストリーム
ページ上で表現される一連の描画命令とデータ
PDF のオペレータ
• 後置記法
• パラメータは LIFO スタックに積まれる
param1 param2 param3 param4 op
param4
param3
param2
param1
PDF の文字列描画
BT % Begin Text/F1 24 Tf % フォント指定
% /F1がフォントを表現するシンボル
1 0 0 1 72 648 Tm % 描画位置の指定
(Hello World) Tj % 文字列描画 -- (と )が引用符
1 0 0 1 72 612 Tm% non-ASCII文字列
<4D53835383568362834E3234837C834383938367> Tj1 0 0 1 72 576 Tm0.5 g % グレイスケール<82BB82EA82F08A44904682C982B582BD82E082CC> TjET % End Text
「 PDF by Hand 」http://www.kobu.com/docs/pdf/pdfxhand.htmより
文字列描画オペレータ
• Tj オペレータ
文字列抽出
// コールバック関数を設定するCGPDFOperatorTableRef table_;CGPDFOperatorTableSetCallback(table_,
"BT",stringBlockBeginsCallback);CGPDFOperatorTableSetCallback(table_, "ET",
stringBlockEndedCallback);CGPDFOperatorTableSetCallback(table_, "TJ",
stringArrayCallback);CGPDFOperatorTableSetCallback(table_, "Tj", stringCallback);CGPDFOperatorTableSetCallback(table_, "Tf", fontCallback);
// ページに適用CGPDFContentStreamRef contentStream =
CGPDFContentStreamCreateWithPage(page);CGPDFScannerRef scanner = CGPDFScannerCreate(contentStream,
table_, self); // userinfo として self を指定bool ret = CGPDFScannerScan(scanner);
文字列抽出コールバックstatic void stringCallback(CGPDFScannerRef inScanner, void
*userInfo){ PDFStringExtractor *zelf = (PDFStringExtractor *)userInfo; CGPDFStringRef string=NULL;
if(CGPDFScannerPopString(inScanner, &string)) {
// ↑LIFO なのでポップする // 座標関連の命令を取り出すときは注意
NSString* s = [zelf stringWithPDFString:string];
文字列抽出コールバックstatic void stringCallback(CGPDFScannerRef inScanner, void
*userInfo){ PDFStringExtractor *zelf = (PDFStringExtractor *)userInfo; CGPDFStringRef string=NULL;
if(CGPDFScannerPopString(inScanner, &string)) {
// ↑ 全然文字列じゃない!!!
NSString* s = [zelf stringWithPDFString:string];
レンダラの気持ちになって考える
CID
• PDF における " 文字列 " は、実際には CID の列であることがある
• CID= グリフ ( 字形 ) を一意に識別するためのID
• CID と文字コードのマッピングは文字列描画に用いるフォントによって異なる
フォント指定が重要
BT % Begin Text/F1 24 Tf % フォント指定
% /F1がフォントを表現するシンボル
1 0 0 1 72 648 Tm % 描画位置の指定
(Hello World) Tj % 文字列描画 -- (と )が引用符
1 0 0 1 72 612 Tm% non-ASCII文字列
<4D53835383568362834E3234837C834383938367> Tj1 0 0 1 72 576 Tm0.5 g % グレイスケール<82BB82EA82F08A44904682C982B582BD82E082CC> TjET % End Text
「 PDF by Hand 」http://www.kobu.com/docs/pdf/pdfxhand.htmより
CGPDFDictionaryRef pageDict = CGPDFPageGetDictionary(page);CGPDFDictionaryRef resourceDict = NULL;CGPDFDictionaryRef fontDict = NULL;// フォント辞書をスキャンif(CGPDFDictionaryGetDictionary(pageDict, "Resources",
&resourceDict ) ) { if(CGPDFDictionaryGetDictionary(resourceDict, "Font",
&fontDict ) ) {
CGPDFDictionaryApplyFunction(fontDict,enumerateFontsInDictionary,self);
} }
static void enumerateFontsInDictionary(const char *key, CGPDFObjectRef value, void *info) {// フォント情報をキャッシュする
}
フォントのエンコーディング情報
• Encoding
• "Identity-H","Identity-V"
• DescendantFont
• /Registry (Adobe)
• /Ordering (Japan-1)
• /Supplement (6)
• CMAP 名
CID ファイル :EUC-H の例
100 begincidrange ← 100 個の区間があることを示す<20> <7e> 231 ← cid231 〜 313 は printable ASCII の区間<8ea0> <8edf> 326<a1a1> <a1fe> 633<a2a1> <a2ae> 727<a2ba> <a2c1> 741
.
.
.endcidrange
課題
• 対応できてないパターンがある
• テキスト領域認識
• 全文検索
参考文献• Life is Beautiful (中島聡氏 )
• CloudReaders の開発• PDF レンダリングのメモリ消費の問題
を指摘• 超巨大ページにも対応
• 木下誠氏のマイコミジャーナルの記事• Core Text を用いた CID→Unicode の簡便な解決案が提示されています
• http://news.mynavi.jp/column/iphone/039/index.html
参考文献 (2)• 通称フグ本
• CID などアドビ社の多国語対応の情報
• 鈍器としても使えます
参考文献 (3)• もっと早く出ていれ
ば。。。
PDF Voyeur
https://github.com/below/PDF-Voyeur.git