PDF Analyzer

PDF內容分析

操作模式

PDF解析器上方可選擇VIEW/TRAIN兩種模式。在訓練模式(TRAIN)中可以進行程式碼編寫,在預覽模式(VIEW)中則可對該文件進行預覽,文件換頁則可透過左邊的UP/DOWN操作。

PDF

目前解析的文件,支援ISO標準的PDF/A格式,可點擊PICK選取或使用 %FILENAME% 變數。

PASSWORD

PDF文件密碼(選填)。也可以將密碼寫在文字檔並放置於工作資料夾中,此時欄位中填寫該文字檔檔名。

input輸入物件

輸入物件是將PDF檔案轉換後的資料物件,其中包含了統一坐標系後的所有文字物件,線條物件...,以及解析所需的函式。

Doc{
    totalPages,    // 文件的總頁數
    pageInfo[],    // 頁面資訊,如頁面的畫素寬度/高度
    textData[],    //文字物件陣列
    hLines[],      // 橫線陣列
    vLines[],      // 縱線陣列
    blocks[],      // 方格(由縱橫線組成)陣列
    tables[],      // 表格陣列(由方格組成)陣列
}

座標系統

input中使用Page Normalized Coordinate(PNC)座標系統。原點位於左上角,每一頁的座標範圍正規化至相鄰的整數範圍。例如第一頁的座標範圍是[0,0]~[1,1],第二頁則是[0,1]~[1,2]... 依此類推。依此整份文件可視為x: [0~1], y: [0~(N-1)],的連續坐標系。(N為頁數)

文字物件

文字物件是解析文件所使用的最小單位物件,每個文字物件都帶有以下的屬性:

textObject{
    text,            // 文字物件的內文
    LT{},            // 文字物件左上角座標
    LB{},            // 文字物件左下角座標
    RT{},            // 文字物件右上角座標 
    RB{},            // 文字物件右下角座標
    C{},             // 文字物件中央點座標
    w,               // 文字物件寬度
    h,               // 文字物件高度
    block{},         // 紀錄該文字物件與文件表格的關係
    isDeleted(),     // 檢測文字物件是否被橫線劃過
    detectLine(dir), // 檢測並回傳最近的線條, dir可為"up","down","left","right"
    detectGroup(),   // 偵測並回傳鄰近線條相同的所有的文字物件,可用於偵測同一格內的所有文字物件。    
}

解析函式

輸入物件提供了數個解析函式與工具函式能幫助使用者在空間中找出目標物件。整體的解析邏輯在於使用空間(spatial)或字詞(textual)上的條件縮小文字物件的集合,接著用相對關係找到目標物件。

textObj = input.getKeyObj( keyName, keyBounds, options )

// keyName: 文字物件的內文字串
// keyBounds: 找尋範圍的物件,格式為 {"top":num,"bottom":num,"left":num,"right":num,“page”:num}
// options: 選項物件,可提供下列選項
// “regExp”: 使用正規表達式過濾搜尋範圍
// “ignoreHeader”: 忽略每一頁上方範圍的資料,範圍由小數定義(頁高百分比)
// “ignoreFooter”: 忽略每一頁下方範圍的資料,範圍由小數定義(頁高百分比)
// “all”: 布林值,若開啟則函式回傳所有找到的文字物件(陣列形式)
textObj = input.resolve( target, options )

// target: 目標物件,包含鍵值物件(keyObj)或是(keyName/keyBounds)的組合,解析方向(valPos),解析範圍(valBounds)或是相對解析範圍(relValBounds)
// options: 選項物件,可提供下列選項
// “regExp”: 使用正規表達式過濾搜尋範圍
// “ignoreHeader”: 忽略每一頁上方範圍的資料,範圍由小數定義(頁高百分比)
// “ignoreFooter”: 忽略每一頁下方範圍的資料,範圍由小數定義(頁高百分比)
textObjs[] = input.resolveRange( target, options )
    
// target: 目標物件,包含起始鍵值物件(startKeyObj)或是(startKeyName/startKeyBounds)的組合,結尾鍵值物件(endKeyObj)或是(endKeyName/endKeyBounds)的組合,解析方向(valPos),解析範圍(valBounds)或是相對解析範圍(relValBounds)\
// options: 選項物件,可提供下列選項
// “regExp”: 使用正規表達式過濾搜尋範圍
// “ignoreHeader”: 忽略每一頁上方範圍的資料,範圍由小數定義(頁高百分比)
// “ignoreFooter”: 忽略每一頁下方範圍的資料,範圍由小數定義(頁高百分比)
[實驗性功能] textObjs[] = input.resolveTable( target, options )
	
// target: 目標物件,包含鍵值物件(keyObj)或是(keyName/keyBounds)的組合,解析方向(valPos),解析範圍(valBounds)或是相對解析範圍(relValBounds)
// valType: 解析模式,可以為 ROW,ROW(Num),COL,COL(Num),REPEAT。
// ROW/COL模式會解析整行/整列的資料
// ROW(Num)/COL(Num)模式會解析鍵值右方/下方的Num個值資料。
// REPEAT模式會解析所有相似表格中的位於鍵值位置資料。
// options: 選項物件,可提供下列選項
// “regExp”: 使用正規表達式過濾搜尋範圍
// “includeKey”: 布林值,如果為真,解行列解析的回傳值會包含target中的鍵值。
// “tableRefs”: 一個物件陣列,格式如下{“refText”:string, “R”:num, “C”:num}。此物件陣列用以定義"相似表格"需要具有那些相同內容。 

工具函式

strings[] = input.textGrouping( textObjs , option)
    
// textObjs: 需要被重整的文字物件陣列
// Option: 排列方式選項,可以是 LINES, TDLR, LRTD(default)
// Note: 排序邏輯會將行高低於1.5倍/間距小於2個字寬的文字物件群組。接著將每個群組使用TDLR,LRTD的順序排序。並依序輸出每個群組的字串
// Note2: LINES模式則會以高度位於同一行高的方式分群,並以逐行的方式回傳字串。
textObjs[] = input.textSort( textObjs, mode = 0)

// textObjs: 需要被重整的文字物件陣列
// Mode: 排序模式,目前只有左至右,上至下的物件排序。
// Note:回傳新的排序後陣列。
num = input.checkBounds( keyBounds, textObj )
// (0 為無重疊, 1 為部分重疊, 2 為物件包含於範圍內)

// keyBounds: 欲測試的範圍。格式如{top:num, bottom:num, left:num, right:num}
// textObj: 欲測試的文字物件

Viewer與CodeGen

PDF解析器的預覽模式除了可以標示文字物件與座標外,也可以透過滑鼠與鍵盤的操作來自動產生函式程式碼,使用者在完成操作後可以到編輯模式中直接貼上並進行少量修改,減少程式編輯的時間。

  • 抓取物件:

  • 產生邊界:

  • 方向解析:

  • 範圍解析:

  • 相對區域解析:

output 輸出物件

加入 output 物件中的每個 key 將被輸出成工作資料夾中的一個 TXT 檔案,檔案名稱即為 key,文字內容為該 key 相對應的 value。

範例

// 1.Resolve single value by Name:
let target = {keyName:"PRODUCTION ORDER", keyBounds = "page":1, valPos:"RIGHT" } 
let productOrderNum = input.resolve( target );
// result: productOrderNum.text = “423022”


// 2.Resolve single value by object:
let orderObj = getKeyObj("PRODUCTION ORDER", {page:1}) 
let target = {keyObj: orderObj, valPos:"RIGHT"}
let productOrderNum = input.resolve( target );
// result: productOrderNum.text = “423022”


// 3.Resolve single value with regExp Filter:
let target = { keyName:"Coord #", keyBounds = {page:1, valPos:"RIGHT"}
let options = {“regExp”:/\d{2}\/\d{2}\/\d{2}/ } 
let issueDate = input.resolve( target, options );
// result: issueDate .text = “06/24/20”


// 4.Resolve multiples values by start/endKeyObjects:
let startObj = input.getKeyObj( "Ship Type", {page:1} )
let endObj = input.getKeyObj( "Ship Terms", {page:1} )
let target = {startKeyObj:startObj, endKeyObj:endObj, valPos:"RIGHT"}
let interData = input.resolveRange(target)
//Result:  interData[0].text = “AW”
//Result:  interData[1].text = “Ship Via"
//Result:  interData[2].text = “SEA”


// 5.Resolve multiples values by valBounds:
let sellerObj = getKeyObj("To (Seller):", {page:1})
let codeObj = getKeyObj({"Code:", {page:1})
let accObj = getKeyObj("Acc Sup:", {page:1})
let target = {valBounds:{top:sellerObj.LB.y, right:accObj.LB.x, bottom:codeObj.LT.y, page:1 } }
let address = input.resolveRange(target)
//Result:  address[0].text = “TAIEASY INTERNATIONAL. CO., LTD”
//Result:  address[1].text = “11F., NO. 1, JIHU RD., NEIHU DIST.,"
//Result:  address[2].text = “TAIPEI CITY 114,”
//Result:  address[3].text = “TAIWAN (R.O.C.)”
//Result:  address[4].text = “TAIPEI CITY, TAIWAN”


// 6.Resolve multiples values by relValBounds
let facObj = getKeyObj("Mfrag Fac:", {page:1})
let target = {relValBounds:{top: -0.01, right:0.25, bottom:0.09, left:0.0} }
let location= input.resolveRange(target)
//Result:  location[0].text = “FORMOSA VIET NAM TEXTILE INDUSTRY”
//Result:  location[1].text = “CO.,LTD"
//Result:  location[2].text = “MY XUAN A2 INDUSTRIAL ZONE, MY XUAN”
//Result:  location[3].text = “WARD,”
//Result:  location[4].text = “PHU MY TOWN, BA RIA - VUNG YAU”


// 7.Resolve value with ignore options
let buyerObj = input.getKeyObj("By Buyer",{page:1})
let target = {keyObj:buyerObj, valPos:"DOWN"}
let options = {ignoreHeader:0.22, ignoreFooter:0.85}
let downVal= input.resolveRange(target, options)
// downVal.text = ”To (Seller)”


// 8.Resolve block values in Table.
let target = {keyName:"DESC", keyBounds:{page:1}, valPos:"DOWN"}
let blockVal= input.resolve(target)
// blockVal.text = “TRAINER PANT”
let allBlockVal = input.resolveTable({keyObj:blockVal})
// allBlockVal.text = “TRAINER PANT
//                               YOUTH”


// 9.Resolve row blocks values in Table.
let target = {keyName:"Ship Via", keyBounds:{page:1}, valType:"ROW"}
let blockVals = input.resolveTable(target)
// blockVals[0].text = “Ship Type”
// blockVals[1].text = “AW”
// blockVals[2].text = “Ship Via”
// blockVals[3].text = “SEA”
// blockVals[4].text = “Ship Terms”
// blockVals[5].text = “FOB”
target.valType = "ROW"
blockVals = input.resolveTable(target);
// blockVals[0].text = “SEA”
// blockVals[1].text = “Ship Terms”
// blockVals[2].text = “FOB”
target.valType = "ROW2"
blockVals = input.resolveTable(target);
// blockVals[0].text = “SEA”
// blockVals[1].text = “Ship Terms”


// 10.Resolve Column/Row blocks values in Table.
let target = {keyName:"Coord #", keyBounds:{page:1}, valType:"COL5"}
let options = {includeKey:true}
// get 6 headers in first column
let colVals = input.resolveTable(target, options)
// get row values for each header
colVals.forEach( head => {
	let rowVals = input.resolveTable({keyObj:head, valType:"ROW0"})
})
// for “Coord #” row
// 	rowVals[0].text = “TRAINING” 
// 	rowVals[1].text = “PO Issue Date” 
// 	rowVals[2].text = “06/24/20” 
// for “Season” row
// 	rowVals[0].text = “204HO” 
// 	rowVals[1].text = “Last Revised Date” 
// 	rowVals[2].text = “06/24/20” 
// for “Payment Terms” row
// 	rowVals[0].text = “75 DAY TERMS THROUGH GT NEXUS” 
// for “Ship Type” row
// 	rowVals[0].text = “AW” 
// 	rowVals[1].text = “Ship Via” 
// 	rowVals[2].text = “SEA” 
// 	rowVals[3].text = “Ship Terms” 
// 	rowVals[4].text = “FOB” 
// for “Ref#” row
// 	rowVals[0].text = “204HO NTM056 YRR PNP A” 
// for “Bene” rows
// 	rowVals[0].text = “NTM064 TAIEASY INTERNATIONAL CO.,LTD” 


// 11.Resolve repeat blocks values in Table.
// find first origin
let target = {keyName:"Country of Origin", keyBounds:{“page":1}, “valPos”:”RIGHT”}
let originVal = input.resolve(target)
//  originVal.text = “Vietnam”
// Resolve origins in other tables
let options = { tableRefs: [ 
                {refText:"Item:", R:0, C:0},
                {refText:"Country of Origin", R:1, C:0}
                ]}
let blockVals = this.resolveTable({keyObj:originVal, valType:"RIGHT"})
// blockVals[0].text = “VietNam”
// blockVals[1].text = “Taiwan”
// blockVals[2].text = “China”

Last updated