From 9d7109b60f4f3834162b1554ccff5ea58574291e Mon Sep 17 00:00:00 2001 From: Narayanan Madaswamy Date: Thu, 22 Jan 2026 21:08:50 +0530 Subject: [PATCH] structured data extraction from pdf --- backend/__pycache__/main.cpython-313.pyc | Bin 12009 -> 12724 bytes backend/main.py | 45 ++++++++++++++++------- backend/requirements.txt | 1 + frontend/src/app/ocr/ocr.component.ts | 2 +- 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/backend/__pycache__/main.cpython-313.pyc b/backend/__pycache__/main.cpython-313.pyc index 200e12e9f5aa7c61b6f0bee73d7b7754a66dd922..c17dccd75df8480809ec6d14536f528edf974d20 100644 GIT binary patch delta 3887 zcma)8du&tJ89&$eUdNBv*h!3?#39Z*&Vvx*zygG4pwuCZ=7I*rk!9j*YVaeRdoz{@ z%^htssJ0S%(5h}l&8A9Y+E&oAO01P8+OqvwC4-IVj%xj*(l)78Q-Oq3wsqfkZXhAb zq}jT^^WE=pzQ_5_IrpDKza4hpbGaM@e!tjB)92P)bO(gH7n?2>g#G{xC`57emq#mT zMTjWQSfx@N3q~ZxRY6WjvoJ)V!sEhjTAcEU z8M|T|amHn{2})#PCcmU~FPmyJPQ|QZP3xljMFUCMxJ-Ww*B=z?Iu`XU$|i8IIo7)n z?v`aXTft_Cy(Na_ZQS}#Vp!PD{w4mNeQK+5S|J5o?R(cO?6UK4vxVdYMaWqW34^w! z*_m`H*;`9UPCO*C8}_n@Vo}5%AtxP@niq7F7D5Ip2{~M^NbSIDcY(TPpq>yX36VpV zK^ME`uC*n~$)lHwh=z**|8B6wUx?I`HJr1Qw}u=oHRXgHm6%W)WLxb%yOjrP9kj8S zy^6hWuVk0(e*aS64Nv&4U?102HEF$KHAx9IO0pr z7pQ#G{)T;0nhS>R1lwnW?bi;@1lwnVnEc+A-BN<8HcbYreb6 z`*2;?01&uT{L+DkI(H*zFT<_PTd_#6NgspxL|9)y5WZ6c%U)78Fm65N!lB zY?*XQ)kYFIx*lF5>>as9SU>%V>=uLr>0(DMim0HGBFe4os@y)O&^ zObpOHT+F^{D`!`&o$RxsRR<3O%aHZ_Ni}B_WmQds<)=97e!3O4ku>eNgT45QfOd}@ zINdKq=UkqNeWv1Ga4_)HVbrWa_}{Y)pcny_d6Pl74%fuf=0qr&e#u!U_{)F_+bM1< z3j2vr*y)5;up7m-N)?i85UK&flG)gDI*Xa`0N|V;ngoCyY!=oJ9ZwU(I`8y&isrL% z(?9{Wq3Led7Z#z6HF!Q3YFMea$%@@htJrpLSp-W_FHF zV2Ej$z7JpVxTP43VM``-RZnMCLpqkvXJ`&3yAhTx=5gd9Ot6>B`mDS=j`QyLyeyh} z1*K_(ApoOzA(^;7HnPwmVcSYE(LCDln0Reg@~oi>!Yk~uuhR6aanE;r(E>w%a#~lj z8n&jM$e29@bzPwb6jzp~VLIjRS8{q9^$>7=4Ua$QHs`8I+xe}xr1CkZdm_sF&O%S~ zP~KZTPS8I89)k7&s~vak;w$5lPPp!Nf>`N{O}DtaZ)5g-p5MWeZs zx%}xIEo0W|E;DEBMLG%q&9^MFCyPM8i<%f>*hgPNnis&DN@sMqCehZAQ)#N{=ElXm z4C`<{n@#IxX1wj~S=dO}@*2BdUDAi14QpZqF4H7$V7_*A*-G&0pnoXL4`JXnF=U=6 zEiiYeT4-czLw@-(2;ZKLh9ZKnXSz@`Vewo*^+^P*0X@uo^^@Vp;Ztl~Gw(}C*%A0M zEF6d*)vpzX*uU!oB`S!*)VU* zor|_BW`MCS9O+11LBLA$lP4_Gw~@Yzz?aWGigyw}>=VZlno99w+3az< zHvF8L9L+$Rw<5C*6^n5%#zF7f>0yHAR{9a>2=Hhp*mzr8SV^TZsC>Khm)OiBKHSsE+4iE~ NnBLX?GeN=?`7iKTWdZ;I delta 3496 zcmaJ@Yit}>6`tAm+q-_Qy-vJYJGR&FXB<0m(ztGD*0Bj00wP|tOm=6TO?GECXT~i~ zqOM6Q7bB?RUg!@{s}u=U0;E=Kk@y9Ke+a1pr9x(ae@IY7g{vTe;1B2ASv$s99cjLM z=iGDddE9f(o%gaIelheU81xDFl-|Boc%ywQ6cs<%>iK5Hw-8OWLTg-*1j)b9mWn|v z)ei0aJFsvhbrg=KI-rAp2N&Y0V{j}kq+L=d?U}Gib#1~8$0Tiw4% zhXpC}844;c0wXwfEsUndV60}`aa7p7PvJN>qX((I`=}F~nnY^fKI$Z=_9Jy5J-+AO2{cEu z%wbcJ;Mu|Sq!gdFq2{4<0HYb36s^?>yEL3WDUIMenr@wNNn_;B4RJ5})3g&CjWkS+ zr%%ux2joR*q9*H*CJ)HY?8;7`*wyz5(o+YdPwh&xl07{9)MA@7gBnkzPuDCyeL!v& z<(A1}_A`64^UU5ZAK9D5v&`Z6DLe0bC)|MkCq;Un=i6i3;WKxLu_$xrYVAV$!+3!^yE0|>7O+o8z)MTCt-7~#w8E74Zg6d270 zFKR0V^)f2ZR9*RyLTow>?WUrd?vkSG@`?haUo-A z1vOjC9b6U!C%#lvPnwbXE#auJOsf%m1!3972ZYNG_HjoOTXfaCMM+d`S46fIYGk)v zerD@vXK%Thn{0!~Hy9KI)wXzC5d1=1xME|sBJGJT;h3;Y!R3!zknzw|=W^XHMY6L2 zca**AZs>F@nSv?$ZZ|v!pg|lIIag3u;+N*n&%~1+z}*G5<31%PT`+@l@<`4p09Lrl zcmk%EFhh|uMMZZZSDZim#LXs(sGK!8LnaSeDO1knA%$$S`06hqtP49fHsk#~n+b#B|4xUiRF;c{8inJ<8DSj9?ynXar>DisXN zGoI?+5*81oxz6q+>gP$b>5|LXhH^Y<`I*6ili<-I{TP_9k?M64b8ATDIhVmx&g{?U@8j~L>dD?WZ56WQcH_Cd zWRI(w5zk}#M{#Xb1so|+ldnUt=km+~qz5dt45%#HpRH}`H znh}yB-Fy0K(cphRwTr>8uc_JHbo?>0{TW}q4q;vRQ?T=K@c8=qZGU7Pe{0z92ZqE+ z<{ON6@lMKN1UaoM#!HuK1~koppCIvXY-zA1NXyA*EdNRj2JclCkz(2}UASmnZ(h7d z$E>S}l$s&^R#7fyuPddsAq`3pCL*jb|Vp+h%rFbmqET4 z3RwlHrSM-m{=a9sH7KmG&7sChCoPN-Op%n6|L5@AgJ0?VJ~JI<4GU$CxZWaQ#2U%V zx*?YfLl{Z^Kc@pfz#MUu33#ieu4dY?(5>5m${Z;1fszfTc(Kqy0Y#jL>%iq?dQO)# z^6m`$3R&pqN?&1f!voGg3W str: + # Try pdfplumber first for layout preservation + try: + with pdfplumber.open(io.BytesIO(file_bytes)) as pdf: + text = "" + for page in pdf.pages: + page_text = page.extract_text(layout=True) + if page_text: + text += page_text + "\n" + if text.strip(): + return text.strip() + except Exception as e: + print(f"pdfplumber error: {e}") + + # Fallback to pypdf + try: + reader = PdfReader(io.BytesIO(file_bytes)) + text = "" + for page in reader.pages: + page_text = page.extract_text() + if page_text: + text += page_text + "\n" + return text.strip() + except Exception as e: + print(f"Error reading PDF: {e}") + return "" + # Internal modules from database import get_db, Email from scheduler import start_scheduler, stop_scheduler @@ -57,19 +88,7 @@ class LoginResponse(BaseModel): class NERResponse(BaseModel): text: str -# 2. OCR Module -def extract_text_from_pdf(file_bytes: bytes) -> str: - try: - reader = PdfReader(io.BytesIO(file_bytes)) - text = "" - for page in reader.pages: - page_text = page.extract_text() - if page_text: - text += page_text + "\n" - return text.strip() - except Exception as e: - print(f"Error reading PDF: {e}") - return "" + def extract_text_from_image(file_bytes: bytes) -> str: try: diff --git a/backend/requirements.txt b/backend/requirements.txt index 9f0da77..8c7160e 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -9,3 +9,4 @@ psycopg2-binary imap-tools apscheduler python-dotenv +pdfplumber diff --git a/frontend/src/app/ocr/ocr.component.ts b/frontend/src/app/ocr/ocr.component.ts index 192a56d..c743cd9 100644 --- a/frontend/src/app/ocr/ocr.component.ts +++ b/frontend/src/app/ocr/ocr.component.ts @@ -51,7 +51,7 @@ import { ToastModule } from 'primeng/toast'; [(ngModel)]="extractedText" readonly class="w-full" - style="min-height: 300px; width: 100%; border-color: #d1d5db;"> + style="min-height: 300px; width: 100%; border-color: #d1d5db; font-family: monospace;">