Files
contractor/parser.cfm
T

249 lines
13 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<cfsetting showdebugoutput="no" enablecfoutputonly="true">
<cfheader name="Content-Type" value="application/json; charset=utf-8">
<cfparam name="url.doc_id" default="">
<cfset result = {ok: false, error: ""}>
<cftry>
<cfif NOT len(url.doc_id)>
<cfset result.error = "doc_id required">
<cfelse>
<!--- Читаем документ из БД --->
<cfquery name="doc" datasource="baza">
SELECT id, filename, mime_type, original_bytes, status
FROM documents
WHERE id = <cfqueryparam value="#url.doc_id#" cfsqltype="cf_sql_varchar">
</cfquery>
<cfif doc.recordCount EQ 0>
<cfset result.error = "document not found">
<cfelse>
<cfset elements = []>
<cfset errors = []>
<!--- PDF --->
<cfif doc.mime_type EQ "application/pdf">
<cftry>
<!--- Base64 → байты --->
<cfset fileBytes = binaryDecode(doc.original_bytes, "base64")>
<!--- Байты → InputStream --->
<cfset bais = createObject("java", "java.io.ByteArrayInputStream").init(fileBytes)>
<cfset bis = createObject("java", "java.io.BufferedInputStream").init(bais)>
<!--- Пробуем PDFBox 3.x Loader, иначе 2.x PDDocument.load --->
<cfset pdfDoc = 0>
<cftry>
<cfset pdfDoc = createObject("java", "org.apache.pdfbox.Loader").loadPDF(bis)>
<cfcatch>
<cftry>
<cfset pdfDoc = createObject("java", "org.apache.pdfbox.pdmodel.PDDocument").load(bis)>
<cfcatch>
<cfset arrayAppend(errors, "PDFBox: cannot open PDF — " & cfcatch.message)>
</cfcatch>
</cftry>
</cfcatch>
</cftry>
<cfif NOT isNumeric(pdfDoc)>
<cfset stripper = createObject("java", "org.apache.pdfbox.text.PDFTextStripper")>
<cfset pageCount = pdfDoc.getNumberOfPages()>
<cfloop from="1" to="#pageCount#" index="pn">
<cfset stripper.setStartPage(pn)>
<cfset stripper.setEndPage(pn)>
<cfset pageText = stripper.getText(pdfDoc)>
<cfif len(trim(pageText))>
<cfloop list="#pageText#" index="line" delimiters="#chr(10)#">
<cfset line = trim(line)>
<cfif len(line)>
<cfset arrayAppend(elements, {type: "paragraph", style: "", text: line, page: pn})>
</cfif>
</cfloop>
</cfif>
</cfloop>
<cfset pdfDoc.close()>
</cfif>
<cfset bis.close()>
<cfset bais.close()>
<cfcatch>
<cfset arrayAppend(errors, "PDFBox: " & cfcatch.message)>
</cfcatch>
</cftry>
<!--- 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">
<cftry>
<cfset fileBytes = binaryDecode(doc.original_bytes, "base64")>
<cfset text = createObject("java", "java.lang.String").init(fileBytes, "UTF-8")>
<cfloop list="#text#" index="line" delimiters="#chr(10)#">
<cfset line = trim(line)>
<cfif len(line)>
<cfset arrayAppend(elements, {type: "paragraph", style: "", text: line, page: 1})>
</cfif>
</cfloop>
<cfcatch>
<cfset arrayAppend(errors, "TXT: " & cfcatch.message)>
</cfcatch>
</cftry>
<cfelse>
<cfset arrayAppend(errors, "unsupported mime_type: " & doc.mime_type)>
</cfif>
<!--- Сохраняем результат --->
<cfset newStatus = arrayLen(elements) GT 0 ? "parsed" : "error">
<cfif arrayLen(errors) GT 0 AND arrayLen(elements) EQ 0>
<cfset newStatus = "error">
</cfif>
<cfquery datasource="baza">
UPDATE documents
SET elements_json = <cfqueryparam value="#serializeJSON(elements)#" cfsqltype="cf_sql_varchar">::jsonb,
status = <cfqueryparam value="#newStatus#" cfsqltype="cf_sql_varchar">,
error_message = <cfqueryparam value="#arrayLen(errors) ? serializeJSON(errors) : ''#" cfsqltype="cf_sql_varchar">
WHERE id = <cfqueryparam value="#url.doc_id#" cfsqltype="cf_sql_varchar">
</cfquery>
<cfset result = {
ok: true,
doc_id: url.doc_id,
filename: doc.filename,
mime_type: doc.mime_type,
status: newStatus,
element_count: arrayLen(elements),
page_count: isDefined("pageCount") ? pageCount : 0,
errors: errors
}>
</cfif>
</cfif>
<cfcatch>
<cfset result = {ok: false, error: cfcatch.message, detail: cfcatch.detail}>
</cfcatch>
</cftry>
<cfoutput>#serializeJSON(result)#</cfoutput>