실제 터미널 캡처: tesseract 5.5.0에 이미지 PDF scan.pdf를 그대로 넣자 Error in pixReadStream: Pdf reading is not supported와 종료 값 1이 나오고, pdftoppm으로 PNG를 만든 뒤에는 INVOICE 2026-10-17 등 OCR 글자가 나온 화면과 글 제목

Tesseract가 이미지 PDF를 거절할 때: Pdf reading is not supported

화면에서는 글자가 멀쩡히 보이는데 pdftotext 결과가 텅 비어 있다면, 먼저 그 PDF에 글자 레이어가 있는지부터 의심해 볼 만합니다. 로컬 요약 작업에 PDF 추출 도구를 붙이려면 이런 이미지 PDF에서도 문장을 꺼낼 수 있어야 하므로, 아래에는 Debian 13에서 시험용 이미지 PDF를 만들고, Poppler와 Tesseract로 그 문장을 꺼낸 순서를 그대로 실었습니다. 유료 요약 서비스에 문서를 올리거나 API 키를 마련하지 않고, 자기 컴퓨터에서 글자를 뽑아내려는 상황입니다. 추출 명령이 성공했다는 신호와 다음 단계로 넘길 내용이 쓸 만하다는 사실은 따로 확인해야 합니다. 목표는 빈 추출 결과를 재현하고, 요약에 넣을 문장을 손에 쥐는 것입니다.

Debian 13에서 진행한 순서는 빈 출력과 에러를 그대로 재현하는 것에서 시작해, pdftoppm으로 150 dpi PNG를 만들고 Tesseract 영어 모델로 읽는 데서 끝납니다. 그림에 들어간 영어 문장은 스크립트가 만든 시험 문자이고, 실제 일정 안내가 아닙니다.

시험한 환경은 먼저 표로 적어 둡니다. 같은 값은 뒤의 환경 절에서 출력으로 다시 확인합니다.

항목값
OSDebian GNU/Linux 13 (trixie)
실행일2026-10-14
로케일LC_ALL=C.UTF-8
Python3.13.5
Pillow12.3.0
pdftotext25.03.0
pdftoppm25.03.0
Tesseract5.5.0
Tesseract 언어eng, kor, osd
LibreOffice25.2.3.2 520(Build:2)

글자는 보이는데 pdftotext는 비어 있고 Tesseract는 PDF를 거절할 때

재현용 입력은 글자 레이어를 일부러 뺀 PDF입니다. Pillow로 흰 캔버스에 영어 한 줄을 그려 PDF로 저장하면, 사람 눈에는 문장이 보여도 추출기가 읽을 텍스트 객체는 하나도 없습니다. 아래 make_scan.py는 900 x 200 캔버스에 시험 문장을 그려 scan.pdf를 만듭니다. 입력을 직접 만드는 이유는 정답을 미리 알기 위해서입니다. 원래 있어야 할 문장을 알고 있으니 추출 결과가 맞는지 바로 비교됩니다.

from PIL import Image, ImageDraw, ImageFont

font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 36)
image = Image.new("RGB", (900, 200), "white")
ImageDraw.Draw(image).text((20, 60), "Reapply after December 7.", font=font, fill="black")
image.save("scan.pdf")
print("wrote scan.pdf")

파일을 만든 뒤에는 pdfinfo, pdftotext, od, tesseract를 차례로 돌립니다. 페이지가 있는 PDF인지 먼저 보고, 추출한 파일의 문자 수와 바이트를 들여다보는 흐름입니다. 여기서 종료 코드만 믿으면 안 됩니다. pdftotext는 성공한 것처럼 끝나기 때문에, 실제 내용과 Tesseract의 종료 코드까지 함께 남겨 두었습니다. 이렇게 기록해 두면 입력 문서가 문제인지, 도구가 입력을 여는 단계가 문제인지 갈라 볼 수 있습니다.

python3 make_scan.py
pdfinfo scan.pdf | grep -E 'Pages|Page size'
pdftotext scan.pdf scan.txt
wc -m scan.txt
od -An -tx1 scan.txt
tesseract scan.pdf stdout

결과는 아래와 같습니다. pdfinfo는 페이지를 찾았는데 pdftotext 결과에는 시험 문장이 없습니다. 문제는 이 대비에서 드러납니다. Tesseract에 PDF를 그대로 넘긴 단계에서는 Pdf reading is not supported가 나오고 종료 값도 1로 바뀝니다.

+ cat > make_scan.py << 'PY'
+ python3 make_scan.py
wrote scan.pdf
+ pdfinfo scan.pdf | grep -E 'Pages|Page size'
Pages:           1
Page size:       900 x 200 pts
+ pdftotext scan.pdf scan.txt
pdftotext_exit:0
+ wc -m scan.txt
1 scan.txt
+ od -An -tx1 scan.txt
 0c
+ tesseract scan.pdf stdout
Error in pixReadStream: Pdf reading is not supported
Leptonica Error in pixRead: pix not read: scan.pdf
Error during processing.
tesseract_exit:1
이미지 PDF OCR 실행 화면: PDF를 받은 Tesseract가 처리 오류와 종료 값 1을 반환합니다
2026-10-14 14:47:22 KST, Debian 13 — PDF를 받은 Tesseract가 처리 오류와 종료 값 1을 반환합니다.

화면을 보면 두 도구가 서로 다른 방식으로 실패합니다. pdftotext는 종료 값 0을 돌려주면서도 문자 수는 1, 바이트는 0c뿐입니다. 조용한 실패입니다. 반면 Tesseract는 처리 오류와 종료 값 1을 분명히 남깁니다. 시끄러운 실패 쪽이 오히려 다루기 쉽습니다.

pdfinfo가 찍은 Pages 1과 Page size 900 x 200 pts는 캔버스의 가로 900, 세로 200과 정확히 같습니다. 파일 자체는 멀쩡한 한 페이지 PDF라는 뜻입니다. 그런데 pdftotext는 종료 코드 0으로 끝났고, wc -m은 1 scan.txt를 출력했습니다. od로 보면 남은 것은 16진수 0c 한 바이트, 즉 폼피드입니다. 그림 속 영어 문장은 scan.txt 어디에도 없습니다.

이 상태에서 종료 코드 0만 믿고 다음 단계로 넘기면, 요약 모델이 받는 입력은 문장이 아니라 폼피드 한 바이트입니다. 글자 레이어가 없는 이번 PDF에서 pdftotext는 에러 없이 폼피드를 돌려주었고, 그 결과는 영어 문장이 아니었습니다. 파이프라인 중간에 내용 검사를 넣는 이유가 여기 있습니다.

Tesseract에 같은 PDF를 바로 넘기면 실패하는 지점이 다릅니다. Tesseract 5.5.0은 입력을 이미지로 열다가 멈추고, 표준 에러에 Error in pixReadStream: Pdf reading is not supported 와 Leptonica Error in pixRead: pix not read: scan.pdf, 그리고 Error during processing. 을 남깁니다. 이어서 찍힌 tesseract_exit 값은 1입니다. 이번 버전은 PDF 컨테이너를 이미지 파일처럼 여는 단계를 지원하지 않으니, tesseract scan.pdf stdout은 해결 명령이 될 수 없습니다.

pdftoppm으로 PNG를 만든 뒤 Tesseract 영어 모델로 읽기

해결의 요점은 순서를 바꾸는 것입니다. PDF를 먼저 페이지 이미지로 바꾸고, 그 이미지에 OCR을 겁니다. 해결 스크립트는 같은 make_scan.py로 scan.pdf를 다시 만든 뒤 Poppler의 pdftoppm에 해상도 150과 PNG 출력을 지정합니다. 만들어진 page-1.png를 Tesseract 영어 모델로 읽고, 앞뒤 공백을 걷어 낸 문자열이 그림 속 문장과 같은지 확인합니다.

pdftoppm -r 150 -png scan.pdf page
tesseract page-1.png stdout -l eng > ocr.txt
python3 check_ocr.py

검사는 check_ocr.py가 맡습니다. ocr.txt를 읽어 앞뒤 공백을 지운 다음, 그림에 넣은 문장과 비교합니다. 다르면 OCR mismatch: 뒤에 읽힌 내용을 붙여 멈추고, 같으면 SUCCESS: 뒤에 문장을 출력합니다. 파일이 생겼는지만 보는 검사였다면 앞의 pdftotext 결과도 통과했을 것입니다. 문장을 직접 대조해야 복원 여부를 확실히 가릴 수 있습니다.

from pathlib import Path
import sys

text = Path("ocr.txt").read_text(encoding="utf-8").strip()
expected = "Reapply after December 7."
if text != expected:
    sys.exit("OCR mismatch: " + text)
print("SUCCESS: " + text)

이번에는 Tesseract가 받는 입력이 page-1.png이고, 출력에 그림 속 영어 문장이 그대로 나타납니다. 뒤따르는 SUCCESS: 줄은 검사 스크립트가 기대 문장과 일치한다고 확인했다는 뜻입니다.

+ cat > make_scan.py << 'PY'
+ python3 make_scan.py
wrote scan.pdf
+ pdftoppm -r 150 -png scan.pdf page
+ tesseract page-1.png stdout -l eng
+ cat ocr.txt
Reapply after December 7.
+ cat > check_ocr.py << 'PY'
+ python3 check_ocr.py
SUCCESS: Reapply after December 7.
이미지 PDF OCR 실행 화면: PNG 변환 후 OCR 결과인 Reapply after December 7.을 검증합니다
2026-10-14 14:47:27 KST, Debian 13 — PNG 변환 후 OCR 결과인 Reapply after December 7.을 검증합니다.

캡처에서 ocr.txt와 검사 스크립트는 같은 영어 문장을 출력했고, 성공 줄은 「SUCCESS: Reapply after December 7.」입니다. 150 dpi PNG를 거치자 그림 속 문장이 그대로 돌아왔으니, 이 문자열은 로컬 요약의 입력으로 쓸 수 있습니다. 다만 이 성공의 기준은 시험 문장과의 일치입니다. 실제 운영 문서라면 정답을 미리 알 수 없으므로, 읽힌 내용을 따로 점검하는 절차를 두는 편이 좋습니다.

이 방법의 장점은 추가 패키지 없이 이번 서버의 Poppler와 Tesseract만으로 영어 문장이 원문과 같게 나온다는 점, 그리고 pdftotext가 비운 입력을 종료 코드 0에 속지 않고 실제 문장으로 바꿀 수 있다는 점입니다. 단점은 PDF 경로를 Tesseract에 직접 넣으면 앞 절의 에러가 다시 난다는 것이어서, 렌더를 한 번 거쳐야 합니다.

명령이 끝난 것과 문장이 돌아온 것은 다릅니다

이번 실험에서 가장 조심해야 할 함정은, 명령이 끝났다는 사실과 기대한 결과가 생겼다는 사실을 같은 것으로 착각하는 것입니다. pdftotext는 종료 코드 0을 돌려줬지만 문장은 없었고, Tesseract 5.5.0은 PDF 직접 입력에서 멈췄습니다. 패키지나 연동 API를 갱신할 때는 같은 시험 PDF로 추출과 변환을 다시 돌리고, 종료 값과 복원된 문장을 함께 검사하는 헬스체크를 권합니다. 운영 설정의 입력 경로가 PDF를 바로 넘기는 형태가 아니라 변환된 이미지를 넘기는 흐름인지도 같이 확인해야 합니다.

다른 서버로 옮기기 전에 맞춰 둘 실행 환경과 언어 옵션

운영 환경으로 옮길 때 명령어만 복사해 가면, 나중에 결과가 달라졌을 때 비교할 기준이 없습니다. 그래서 이번 Debian GNU/Linux 13 환경에서 나온 도구 버전과 언어 목록을 글 앞머리의 표로 남겼습니다. 장애가 나면 그 표를 기준으로 패키지 구성과 OCR 언어 설정을 대조하면 됩니다.

표의 값은 아래 명령에서 나왔습니다. 운영체제와 Python, PDF 도구, OCR 도구의 정보를 한 실행 기록에 모으는 명령입니다. 이 중 tesseract –list-langs가 특히 쓸모 있습니다. 설치된 언어를 보여 주므로, 실행 옵션에 적은 언어가 실제로 있는지 바로 확인됩니다. 패키지를 갱신한 뒤에도 같은 항목을 남겨 두면 전후 환경을 비교하기 쉽습니다.

grep PRETTY_NAME /etc/os-release
date -I
python3 --version
python3 -c 'import PIL; print(PIL.__version__)'
pdftotext -v 2>&1 | head -1
pdftoppm -v 2>&1 | head -1
tesseract --version 2>&1 | head -1
tesseract --list-langs
libreoffice --version 2>&1 | head -1

기록을 보면 pdftotext와 pdftoppm은 둘 다 25.03.0, Tesseract는 5.5.0입니다. 언어 목록에는 eng, kor, osd가 있고, 응답이 기록된 문서 주소에는 200이 찍혀 있습니다. 마지막 요청 줄 뒤에는 응답 값이 붙어 있지 않으니, 읽을 때 요청 줄과 응답 줄을 구분해 보셔야 합니다.

+ grep PRETTY_NAME /etc/os-release
PRETTY_NAME="Debian GNU/Linux 13 (trixie)"
+ date -I
2026-10-14
+ echo LC_ALL=C.UTF-8
LC_ALL=C.UTF-8
+ python3 --version
Python 3.13.5
+ python3 -c 'import PIL; print(PIL.__version__)'
12.3.0
+ pdftotext -v 2>&1 | head -1
pdftotext version 25.03.0
+ pdftoppm -v 2>&1 | head -1
pdftoppm version 25.03.0
+ tesseract --version 2>&1 | head -1
tesseract 5.5.0
+ tesseract --list-langs
List of available languages in "/usr/share/tesseract-ocr/5/tessdata/" (3):
eng
kor
osd
+ libreoffice --version 2>&1 | head -1
LibreOffice 25.2.3.2 520(Build:2)
+ curl -sI -o /dev/null -w '%{http_code}' --max-time 15 https://poppler.freedesktop.org/
200 https://poppler.freedesktop.org/
+ curl -sI -o /dev/null -w '%{http_code}' --max-time 15 https://manpages.debian.org/trixie/poppler-utils/pdftotext.1.en.html
200 https://manpages.debian.org/trixie/poppler-utils/pdftotext.1.en.html
+ curl -sI -o /dev/null -w '%{http_code}' --max-time 15 https://manpages.debian.org/trixie/poppler-utils/pdfinfo.1.en.html
200 https://manpages.debian.org/trixie/poppler-utils/pdfinfo.1.en.html
+ curl -sI -o /dev/null -w '%{http_code}' --max-time 15 https://manpages.debian.org/trixie/poppler-utils/pdftoppm.1.en.html
200 https://manpages.debian.org/trixie/poppler-utils/pdftoppm.1.en.html
+ curl -sI -o /dev/null -w '%{http_code}' --max-time 15 https://github.com/tesseract-ocr/tesseract
200 https://github.com/tesseract-ocr/tesseract
+ curl -sI -o /dev/null -w '%{http_code}' --max-time 15 https://tesseract-ocr.github.io/tessdoc/Command-Line-Usage.html
200 https://tesseract-ocr.github.io/tessdoc/Command-Line-Usage.html
+ curl -sI -o /dev/null -w '%{http_code}' --max-time 15 https://manpages.debian.org/trixie/tesseract-ocr/tesseract.1.en.html
이미지 PDF OCR 실행 화면: Tesseract의 eng·kor·osd 목록과 문서 URL의 200 응답을 모읍니다
2026-10-14 14:47:31 KST, Debian 13 — Tesseract의 eng·kor·osd 목록과 문서 URL의 200 응답을 모읍니다.

이 환경에는 eng, kor, osd가 설치되어 있었고, 영어 한 줄에 -l eng를 지정한 결과는 원문과 같았습니다. 언어 옵션은 그림 속 글자에 맞춰 고르면 됩니다. 하지만 목록에 kor가 보인다고 해서 한국어 문서가 잘 읽힌다는 보장은 아닙니다. 설치 여부와 인식 품질은 따로 판단해야 합니다. 운영 설정에는 입력 경로, 출력 경로, 언어 선택을 함께 남겨 두면 재현이 쉬워집니다.

언어 데이터는 출력에 나온 /usr/share/tesseract-ocr/5/tessdata/ 경로와 설치 목록을 기준으로 봅니다. 환경을 옮길 때 이 경로를 그대로 박아 넣기보다, 새 환경의 tesseract –list-langs 결과에 맞춰 설정 경로와 언어 선택을 고치는 편이 안전합니다. 이번 기록은 버전에 따라 API가 폐기되거나 옵션이 바뀌는지를 비교한 자료가 아닙니다. 그래서 갱신한 뒤에도 같은 명령이 같은 문장을 내는지 확인하는 절차 자체를 운영 기준으로 삼았습니다.

pdftotext, PDF 직접 입력, PNG OCR이 낸 세 가지 결과

실행 결과가 있는 선택지만 표로 모았습니다. 같은 이미지 PDF인데도 텍스트 추출, PDF 직접 입력, PNG를 거친 OCR은 세 가지 다른 결과를 냈습니다. 요약 단계로 넘기기 전에 문장이 어느 단계에서 생겼는지만 확인해도 실패 원인은 금방 좁혀집니다.

입력명령이번 결과
900 x 200 이미지 PDFpdftotext scan.pdf scan.txt종료 코드 0, 문자 수 1, 바이트 0c, 영어 문장 없음
같은 PDFtesseract scan.pdf stdoutPdf reading is not supported, 종료 코드 1
같은 PDFpdftoppm -r 150 -png scan.pdf page → tesseract page-1.png stdout -l eng > ocr.txtSUCCESS: Reapply after December 7.

그림만 있는 영어 PDF를 로컬 요약 입력으로 만들려면 pdftoppm으로 이미지를 만든 뒤 Tesseract 영어 모델을 돌리는 구성을 권장합니다. 150 dpi PNG를 거친 결과가 시험 문장과 같았으니, 이 순서를 기본 처리 흐름으로 삼을 만하다고 판단합니다.

운영에서는 추출과 요약 사이에 내용 검사를 하나 끼워 둡니다. 공백을 정리하고도 읽을 문장이 남았는지 보고, 비어 있으면 이미지 변환과 OCR 쪽으로 넘기는 식입니다.

변환된 이미지와 OCR 결과는 작업마다 경로를 나눠 두십시오. 실패한 입력과 처리 기록이 연결되어 있으면, 파일이 바뀐 탓인지 도구 구성 탓인지 나중에 추적하기가 훨씬 수월합니다.

실시간 서비스에 붙일 계획이라면 PDF 렌더링과 OCR을 별도 작업으로 떼어 내고, 실제 문서로 처리 시간과 메모리를 재서 서버 사양을 정해야 합니다. 요청을 붙잡고 변환이 끝나기를 기다리게 하기보다, 처리 결과를 나중에 전달하는 방식도 검토할 만합니다.

결국 제가 이 구성에서 믿는 것은 성공 로그가 아니라 복원된 문장 그 자체입니다. 시험 문서로 정상 동작을 점검하는 흐름만 유지하면, 환경을 갱신한 뒤에도 요약 단계에 쓸 만한 텍스트가 넘어가는지 판단할 수 있습니다.

옵시디언 AI 플러그인 자동 요약, KeyError와 model not found 해결 글은 text-generator KeyError와 llama3 model not found 원인, 로컬 Ollama 요약을 설명합니다. Ollama pull redirect target not allowed, 로컬 GGUF로 모델 만들기 글은 그 오류의 원인과 Modelfile create 해결 방법을 설명합니다.

PDF 추출과 Tesseract OCR 공식 문서

아래 문서 주소는 실행 로그에 HTTP 200으로 남아 있습니다. 도구 동작은 이번 실행 결과를 우선으로 설명했고, 링크는 각 프로젝트의 문서 위치입니다.

공식 문서 주소는 환경 점검 때 아래 형태로 요청해 HTTP 상태를 기록했습니다. 당시 그 주소가 응답했는지를 남기는 용도입니다. 문서 서버가 응답한다는 것과 로컬 OCR이 성공한다는 것은 전혀 다른 점검 항목입니다. 운영 헬스체크에는 실제 변환 결과까지 함께 넣는 구성을 권합니다.

curl -sI -o /dev/null -w "%{http_code}" --max-time 15 "$url"

ainewsnote.com이 어떤 기준으로 글을 쓰는지는 제작 방식 안내에 정리해 두었습니다. 본문에서 사실과 다른 부분을 찾으시면 연락처로 알려 주십시오.

Similar Posts

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다