parser: POI (XWPFDocument/HWPFDocument) для DOCX/DOC + fallback ZIP/XML (v1.0.67)
This commit is contained in:
+114
-6
@@ -52,7 +52,60 @@
|
||||
</cffunction>
|
||||
|
||||
<!--- ═══════════════════════════════════════════════════════════
|
||||
Helper: parseDOCX — ZIP/XML → {elements, errors}
|
||||
Helper: parseDOCX_POI — XWPFDocument → {elements, errors}
|
||||
Точнее чем ZIP/XML: понимает объединённые ячейки, стили
|
||||
═══════════════════════════════════════════════════════════ --->
|
||||
<cffunction name="parseDOCX_POI" access="private" returntype="struct">
|
||||
<cfargument name="fileBytes" type="binary">
|
||||
<cfset var out = {elements: [], errors: []}>
|
||||
<cftry>
|
||||
<cfset var bais = createObject("java", "java.io.ByteArrayInputStream").init(fileBytes)>
|
||||
<cfset var doc = createObject("java", "org.apache.poi.xwpf.usermodel.XWPFDocument").init(bais)>
|
||||
<cfset var bodyElements = doc.getBodyElements()>
|
||||
<cfloop from="0" to="#bodyElements.size() - 1#" index="bi">
|
||||
<cfset var be = bodyElements.get(bi)>
|
||||
<cfset var beClass = be.getClass().getName()>
|
||||
<cfif beClass CONTAINS "XWPFParagraph">
|
||||
<cfset var para = be>
|
||||
<cfset var text = para.getText()>
|
||||
<cfif len(trim(text))>
|
||||
<cfset var style = "">
|
||||
<cftry><cfset style = para.getStyle()><cfcatch></cfcatch></cftry>
|
||||
<cfset arrayAppend(out.elements, {type: "paragraph", style: style, text: trim(text), page: 0})>
|
||||
</cfif>
|
||||
<cfelseif beClass CONTAINS "XWPFTable">
|
||||
<cfset var table = be>
|
||||
<cfset var rows = []>
|
||||
<cfset rowsArr = table.getRows()>
|
||||
<cfloop from="0" to="#rowsArr.size() - 1#" index="ri">
|
||||
<cfset var row = rowsArr.get(ri)>
|
||||
<cfset var cells = []>
|
||||
<cfset var tcs = row.getTableCells()>
|
||||
<cfloop from="0" to="#tcs.size() - 1#" index="ci">
|
||||
<cfset var cell = tcs.get(ci)>
|
||||
<cfset var cellText = trim(cell.getText())>
|
||||
<cfset arrayAppend(cells, cellText)>
|
||||
</cfloop>
|
||||
<cfif arrayLen(cells) GT 0>
|
||||
<cfset arrayAppend(rows, cells)>
|
||||
</cfif>
|
||||
</cfloop>
|
||||
<cfif arrayLen(rows) GT 0>
|
||||
<cfset arrayAppend(out.elements, {type: "table", rows: rows, page: 0})>
|
||||
</cfif>
|
||||
</cfif>
|
||||
</cfloop>
|
||||
<cfset doc.close()>
|
||||
<cfset bais.close()>
|
||||
<cfcatch>
|
||||
<cfset arrayAppend(out.errors, "DOCX_POI: " & cfcatch.message)>
|
||||
</cfcatch>
|
||||
</cftry>
|
||||
<cfreturn out>
|
||||
</cffunction>
|
||||
|
||||
<!--- ═══════════════════════════════════════════════════════════
|
||||
Helper: parseDOCX — пробует POI, fallback ZIP/XML
|
||||
═══════════════════════════════════════════════════════════ --->
|
||||
<cffunction name="parseDOCX" access="private" returntype="struct">
|
||||
<cfargument name="fileBytes" type="binary">
|
||||
@@ -164,7 +217,47 @@
|
||||
</cffunction>
|
||||
|
||||
<!--- ═══════════════════════════════════════════════════════════
|
||||
Helper: parseDOC — грубый экстрактор текста из .doc
|
||||
Helper: parseDOC_POI — HWPFDocument → {elements, errors}
|
||||
═══════════════════════════════════════════════════════════ --->
|
||||
<cffunction name="parseDOC_POI" access="private" returntype="struct">
|
||||
<cfargument name="fileBytes" type="binary">
|
||||
<cfset var out = {elements: [], errors: []}>
|
||||
<cftry>
|
||||
<cfset var bais = createObject("java", "java.io.ByteArrayInputStream").init(fileBytes)>
|
||||
<cfset var doc = createObject("java", "org.apache.poi.hwpf.HWPFDocument").init(bais)>
|
||||
<!--- WordExtractor: извлекает весь текст --->
|
||||
<cfset var extractor = createObject("java", "org.apache.poi.hwpf.extractor.WordExtractor").init(doc)>
|
||||
<cfset var paragraphs = extractor.getParagraphText()>
|
||||
<cfloop from="0" to="#arrayLen(paragraphs) - 1#" index="pi">
|
||||
<cfset var text = trim(paragraphs[pi])>
|
||||
<cfif len(text)>
|
||||
<cfset arrayAppend(out.elements, {type: "paragraph", style: "", text: text, page: 0})>
|
||||
</cfif>
|
||||
</cfloop>
|
||||
<!--- Таблицы через Range (проще) --->
|
||||
<cfset var range = doc.getRange()>
|
||||
<cfset var tableCount = 0>
|
||||
<cftry>
|
||||
<cfset var tblIt = range.getParagraphIterator()>
|
||||
<cfloop condition="true">
|
||||
<cftry><cfset var p = tblIt.next()><cfcatch><cfbreak></cfcatch></cftry>
|
||||
<cfif NOT isNull(p) AND p.isInTable()>
|
||||
<cfset tableCount = 1>
|
||||
<cfbreak>
|
||||
</cfif>
|
||||
</cfloop>
|
||||
</cftry>
|
||||
<cfset doc.close()>
|
||||
<cfset bais.close()>
|
||||
<cfcatch>
|
||||
<cfset arrayAppend(out.errors, "DOC_POI: " & cfcatch.message)>
|
||||
</cfcatch>
|
||||
</cftry>
|
||||
<cfreturn out>
|
||||
</cffunction>
|
||||
|
||||
<!--- ═══════════════════════════════════════════════════════════
|
||||
Helper: parseDOC — грубый экстрактор текста из .doc (fallback)
|
||||
═══════════════════════════════════════════════════════════ --->
|
||||
<cffunction name="parseDOC" access="private" returntype="struct">
|
||||
<cfargument name="fileBytes" type="binary">
|
||||
@@ -232,8 +325,16 @@
|
||||
<cfset parsed = {elements: [], errors: []}>
|
||||
<cfif zext EQ "pdf">
|
||||
<cfset parsed = parsePDF(entryBytes)>
|
||||
<cfelseif zext EQ "docx" OR zext EQ "doc">
|
||||
<cfset parsed = parseDOCX(entryBytes)>
|
||||
<cfelseif zext EQ "docx">
|
||||
<cfset parsed = parseDOCX_POI(entryBytes)>
|
||||
<cfif arrayLen(parsed.elements) EQ 0 AND arrayLen(parsed.errors) GT 0>
|
||||
<cfset parsed = parseDOCX(entryBytes)>
|
||||
</cfif>
|
||||
<cfelseif zext EQ "doc">
|
||||
<cfset parsed = parseDOC_POI(entryBytes)>
|
||||
<cfif arrayLen(parsed.elements) EQ 0 AND arrayLen(parsed.errors) GT 0>
|
||||
<cfset parsed = parseDOC(entryBytes)>
|
||||
</cfif>
|
||||
<cfelse>
|
||||
<cfset arrayAppend(parsed.errors, "unsupported: .#zext#")>
|
||||
</cfif>
|
||||
@@ -266,7 +367,11 @@
|
||||
|
||||
<cfelseif doc.mime_type EQ "application/vnd.openxmlformats-officedocument.wordprocessingml.document">
|
||||
<cfset fileBytes = binaryDecode(doc.original_bytes, "base64")>
|
||||
<cfset parsed = parseDOCX(fileBytes)>
|
||||
<cfset parsed = parseDOCX_POI(fileBytes)>
|
||||
<cfif arrayLen(parsed.elements) EQ 0 AND arrayLen(parsed.errors) GT 0>
|
||||
<!--- POI не сработал — fallback на ZIP/XML --->
|
||||
<cfset parsed = parseDOCX(fileBytes)>
|
||||
</cfif>
|
||||
<cfset elements = parsed.elements>
|
||||
<cfset errors = parsed.errors>
|
||||
|
||||
@@ -287,7 +392,10 @@
|
||||
|
||||
<cfelseif doc.mime_type EQ "application/msword">
|
||||
<cfset fileBytes = binaryDecode(doc.original_bytes, "base64")>
|
||||
<cfset parsed = parseDOC(fileBytes)>
|
||||
<cfset parsed = parseDOC_POI(fileBytes)>
|
||||
<cfif arrayLen(parsed.elements) EQ 0 AND arrayLen(parsed.errors) GT 0>
|
||||
<cfset parsed = parseDOC(fileBytes)>
|
||||
</cfif>
|
||||
<cfset elements = parsed.elements>
|
||||
<cfset errors = parsed.errors>
|
||||
<cfelse>
|
||||
|
||||
Reference in New Issue
Block a user