v1.0.7: parser.cfm — DOCX через ZIP/XML (без POI)

This commit is contained in:
2026-06-18 16:50:39 +04:00
parent 3e191f83ed
commit 45b2f31e8a
2 changed files with 121 additions and 5 deletions
+120 -4
View File
@@ -72,10 +72,126 @@
</cfcatch>
</cftry>
<!--- DOCX/DOC — POI нет, пропускаем --->
<cfelseif doc.mime_type EQ "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
OR doc.mime_type EQ "application/msword">
<cfset arrayAppend(errors, "DOCX/DOC parsing not available (POI not installed)")>
<!--- DOCX — ZIP/XML (POI не нужен) --->
<cfelseif doc.mime_type EQ "application/vnd.openxmlformats-officedocument.wordprocessingml.document">
<cftry>
<cfset fileBytes = binaryDecode(doc.original_bytes, "base64")>
<cfset bais = createObject("java", "java.io.ByteArrayInputStream").init(fileBytes)>
<cfset zis = createObject("java", "java.util.zip.ZipInputStream").init(bais)>
<cfset xmlStr = "">
<cfloop condition="true">
<cfset entry = zis.getNextEntry()>
<cfif isNull(entry)><cfbreak></cfif>
<cfif entry.getName() EQ "word/document.xml">
<cftry>
<cfset xmlBytes = zis.readAllBytes()>
<cfset xmlStr = createObject("java", "java.lang.String").init(xmlBytes, "UTF-8")>
<cfcatch>
<!--- fallback: читаем через буфер --->
<cfset baos = createObject("java", "java.io.ByteArrayOutputStream").init()>
<cfloop condition="true">
<cfset b = zis.read()>
<cfif b LT 0><cfbreak></cfif>
<cfset baos.write(b)>
</cfloop>
<cfset xmlStr = createObject("java", "java.lang.String").init(baos.toByteArray(), "UTF-8")>
</cfcatch>
</cftry>
<cfbreak>
</cfif>
<cfset zis.closeEntry()>
</cfloop>
<cfset zis.close()>
<cfset bais.close()>
<cfif NOT len(xmlStr)>
<cfset arrayAppend(errors, "DOCX: word/document.xml not found in ZIP")>
<cfelse>
<cfset docXml = xmlParse(xmlStr)>
<cfset ns = "http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<!--- Ищем body: или с namespace или без --->
<cfset body = "">
<cftry>
<cfset body = xmlSearch(docXml, "//:body")>
<cfcatch>
<cfset body = xmlSearch(docXml, "//*[local-name()='body']")>
</cfcatch>
</cftry>
<cfif arrayLen(body) GT 0>
<cfset body = body[1]>
<cfloop array="#body.XmlChildren#" index="child">
<cfset localName = child.XmlName>
<cfif localName EQ "p" OR right(localName, 2) EQ ":p">
<!--- Параграф: собираем текст из всех w:t --->
<cfset paraText = "">
<cftry>
<cfset textNodes = xmlSearch(child, "//:t")>
<cfcatch>
<cfset textNodes = xmlSearch(child, "//*[local-name()='t']")>
</cfcatch>
</cftry>
<cfloop array="#textNodes#" index="tn">
<cfset paraText &= tn.XmlText>
</cfloop>
<cfset paraText = trim(paraText)>
<cfif len(paraText)>
<cfset arrayAppend(elements, {type: "paragraph", style: "", text: paraText, page: 0})>
</cfif>
<cfelseif localName EQ "tbl" OR right(localName, 4) EQ ":tbl">
<!--- Таблица --->
<cfset rows = []>
<cftry>
<cfset trNodes = xmlSearch(child, "//:tr")>
<cfcatch>
<cfset trNodes = xmlSearch(child, "//*[local-name()='tr']")>
</cfcatch>
</cftry>
<cfloop array="#trNodes#" index="trNode">
<cfset row = []>
<cftry>
<cfset tcNodes = xmlSearch(trNode, ".//:tc")>
<cfcatch>
<cfset tcNodes = xmlSearch(trNode, ".//*[local-name()='tc']")>
</cfcatch>
</cftry>
<cfloop array="#tcNodes#" index="tcNode">
<cfset cellText = "">
<cftry>
<cfset tNodes = xmlSearch(tcNode, ".//:t")>
<cfcatch>
<cfset tNodes = xmlSearch(tcNode, ".//*[local-name()='t']")>
</cfcatch>
</cftry>
<cfloop array="#tNodes#" index="tNode">
<cfset cellText &= tNode.XmlText>
</cfloop>
<cfset arrayAppend(row, trim(cellText))>
</cfloop>
<cfif arrayLen(row) GT 0>
<cfset arrayAppend(rows, row)>
</cfif>
</cfloop>
<cfif arrayLen(rows) GT 0>
<cfset arrayAppend(elements, {type: "table", rows: rows, page: 0})>
</cfif>
</cfif>
</cfloop>
<cfelse>
<cfset arrayAppend(errors, "DOCX: body not found in document.xml")>
</cfif>
</cfif>
<cfcatch>
<cfset arrayAppend(errors, "DOCX: " & cfcatch.message)>
</cfcatch>
</cftry>
<!--- .doc — старый формат, пропускаем --->
<cfelseif doc.mime_type EQ "application/msword">
<cfset arrayAppend(errors, ".doc parsing not available (use libreoffice to convert)")>
<!--- TXT — plain text --->
<cfelseif doc.mime_type EQ "text/plain" OR doc.mime_type EQ "application/octet-stream">