PDF to PNG OCR

PDF to PNG OCR
Photo by Kelly Sikkema / Unsplash

pdf 파일을 json 으로 파싱해야하는데 하는 방법을 찾아보았다. gpt도 물어보고 claude도 물어보았다. 아래의 값을 받았다.

Mac 에서 기록

# 모든 필요한 도구 한 번에 설치
brew install ghostscript poppler tesseract tesseract-lang imagemagick

# 설치 확인
gs --version
pdftoppm -h
tesseract --list-langs
magick --version

파일생성 예시) pdf_to_ocr_v2.sh

#!/bin/bash
# pdf_to_ocr_advanced.sh
# PDF를 PNG로 변환하고 일본어 OCR을 수행하는 고급 스크립트

# 색상 코드 정의
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
WHITE='\033[0;37m'
BOLD='\033[1m'
NC='\033[0m' # No Color

# 진행 바 함수
show_progress() {
    local current=$1
    local total=$2
    local prefix="$3"
    local width=40
    local percentage=$((current * 100 / total))
    local completed=$((current * width / total))
    
    printf "\r${CYAN}${prefix}${NC} ["
    printf "${GREEN}%*s${NC}" $completed | tr ' ' '█'
    printf "${WHITE}%*s${NC}" $((width - completed)) | tr ' ' '░'
    printf "] ${BOLD}%d%%${NC} (${YELLOW}%d${NC}/${BLUE}%d${NC})" $percentage $current $total
}

# 스피너 함수
spinner() {
    local pid=$1
    local message="$2"
    local delay=0.1
    local spinstr='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'
    
    while kill -0 $pid 2>/dev/null; do
        for i in $(seq 0 $((${#spinstr}-1))); do
            if ! kill -0 $pid 2>/dev/null; then
                break
            fi
            printf "\r${CYAN}%s${NC} ${spinstr:$i:1} " "$message"
            sleep $delay
        done
    done
    printf "\r${GREEN}%s${NC} ✅ \n" "$message"
}

# 에러 메시지 함수
error_msg() {
    echo -e "${RED}❌ 오류:${NC} $1"
}

# 성공 메시지 함수
success_msg() {
    echo -e "${GREEN}✅ 성공:${NC} $1"
}

# 정보 메시지 함수
info_msg() {
    echo -e "${BLUE}ℹ️  정보:${NC} $1"
}

# 경고 메시지 함수
warn_msg() {
    echo -e "${YELLOW}⚠️  경고:${NC} $1"
}

# 헤더 출력 함수
print_header() {
    echo -e "${BOLD}${PURPLE}"
    echo "╔══════════════════════════════════════════════════════════════╗"
    echo "║                    PDF to OCR Converter                     ║"
    echo "║                      Advanced Version                       ║"
    echo "╚══════════════════════════════════════════════════════════════╝"
    echo -e "${NC}"
}

# 필요한 도구 확인 함수
check_dependencies() {
    local missing_tools=()
    
    info_msg "필요한 도구들을 확인하는 중..."
    
    if ! command -v magick &> /dev/null && ! command -v convert &> /dev/null; then
        missing_tools+=("ImageMagick")
    fi
    
    if ! command -v tesseract &> /dev/null; then
        missing_tools+=("Tesseract")
    fi
    
    if ! command -v gs &> /dev/null; then
        missing_tools+=("Ghostscript")
    fi
    
    if [ ${#missing_tools[@]} -ne 0 ]; then
        error_msg "다음 도구들이 설치되지 않았습니다:"
        for tool in "${missing_tools[@]}"; do
            echo "  - $tool"
        done
        echo ""
        echo "설치 명령어:"
        echo "  brew install imagemagick ghostscript tesseract tesseract-lang poppler"
        exit 1
    fi
    
    # 일본어 언어팩 확인
    if ! tesseract --list-langs | grep -q "jpn"; then
        warn_msg "일본어 언어팩이 설치되지 않았을 수 있습니다."
        echo "설치 명령어: brew install tesseract-lang"
    fi
    
    success_msg "모든 필요한 도구가 설치되어 있습니다."
}

# 메인 스크립트 시작
main() {
    print_header
    
    PDF_FILE="$1"
    
    if [ -z "$PDF_FILE" ]; then
        error_msg "PDF 파일을 지정해주세요."
        echo "사용법: $0 파일명.pdf"
        exit 1
    fi
    
    if [ ! -f "$PDF_FILE" ]; then
        error_msg "$PDF_FILE 파일이 존재하지 않습니다."
        exit 1
    fi
    
    # 의존성 확인
    check_dependencies
    echo ""
    
    # PDF 정보 확인
    info_msg "PDF 파일 분석 중..."
    file_size=$(ls -lh "$PDF_FILE" | awk '{print $5}')
    echo "  📄 파일명: $PDF_FILE"
    echo "  📏 파일크기: $file_size"
    
    # 페이지 수 확인
    if command -v pdfinfo &> /dev/null; then
        total_pages=$(pdfinfo "$PDF_FILE" 2>/dev/null | grep "Pages:" | awk '{print $2}')
        if [ -n "$total_pages" ]; then
            echo "  📃 총 페이지: $total_pages"
        fi
    fi
    echo ""
    
    # 기존 파일 정리 확인
    if ls page_*.png output_*.txt &> /dev/null; then
        warn_msg "기존 변환 파일들이 발견되었습니다."
        read -p "삭제하고 계속하시겠습니까? (y/N): " -n 1 -r
        echo
        if [[ $REPLY =~ ^[Yy]$ ]]; then
            rm -f page_*.png output_*.txt processed_*.png
            success_msg "기존 파일들을 삭제했습니다."
        else
            info_msg "기존 파일들을 유지합니다."
        fi
        echo ""
    fi
    
    # PDF to PNG 변환
    echo -e "${BOLD}${BLUE}=== 1단계: PDF에서 PNG 추출 ===${NC}"
    
    # ImageMagick 버전 확인
    if command -v magick &> /dev/null; then
        CONVERT_CMD="magick"
    else
        CONVERT_CMD="convert"
    fi
    
    info_msg "변환을 시작합니다..."
    
    # 백그라운드에서 변환 실행
    $CONVERT_CMD -density 300 -quality 100 "$PDF_FILE" page_%03d.png &
    convert_pid=$!
    
    # 스피너로 진행 상황 표시
    spinner $convert_pid "PDF 변환 중 (시간이 걸릴 수 있습니다)"
    
    # 변환 결과 확인
    png_count=$(ls page_*.png 2>/dev/null | wc -l | tr -d ' ')
    
    if [ "$png_count" -eq 0 ]; then
        warn_msg "ImageMagick 변환 실패. pdftoppm으로 재시도..."
        
        if command -v pdftoppm &> /dev/null; then
            pdftoppm -png -r 300 "$PDF_FILE" page &
            convert_pid=$!
            spinner $convert_pid "pdftoppm으로 변환 중"
            
            # 파일명 형식 통일 (page-001.png → page_001.png)
            for file in page-*.png; do
                if [ -f "$file" ]; then
                    new_name=$(echo "$file" | sed 's/-/_/')
                    mv "$file" "$new_name"
                fi
            done
            
            png_count=$(ls page_*.png 2>/dev/null | wc -l | tr -d ' ')
        else
            error_msg "pdftoppm도 설치되지 않았습니다."
            echo "설치 명령어: brew install poppler"
            exit 1
        fi
    fi
    
    if [ "$png_count" -eq 0 ]; then
        error_msg "PNG 추출에 완전히 실패했습니다."
        exit 1
    fi
    
    success_msg "$png_count 개의 PNG 파일을 추출했습니다."
    echo ""
    
    # OCR 처리
    echo -e "${BOLD}${BLUE}=== 2단계: OCR 텍스트 추출 ===${NC}"
    
    success_count=0
    total_count=0
    failed_files=()
    
    # 파일 목록을 배열로 만들기
    png_files=(page*.png)
    total_files=${#png_files[@]}
    
    for file in "${png_files[@]}"; do
        if [ -f "$file" ]; then
            filename=$(basename "$file" .png)
            total_count=$((total_count + 1))
            
            # 진행률 표시
            show_progress $total_count $total_files "OCR 처리"
            echo ""
            echo -n "  🔍 처리 중: $file ... "
            
            # 이미지 전처리
            $CONVERT_CMD "$file" \
                -colorspace Gray \
                -normalize \
                -sharpen 0x1 \
                -contrast-stretch 0 \
                "processed_$filename.png" 2>/dev/null
            
            # OCR 실행
            if tesseract "processed_$filename.png" "output_$filename" -l jpn --psm 6 --oem 3 2>/dev/null; then
                echo -e "${GREEN}✅${NC}"
                success_count=$((success_count + 1))
                
                # 텍스트 파일 크기 확인
                if [ -f "output_$filename.txt" ]; then
                    txt_size=$(wc -c < "output_$filename.txt" | tr -d ' ')
                    echo "    📝 추출된 텍스트: ${txt_size}자"
                fi
            else
                echo -e "${RED}❌${NC}"
                failed_files+=("$file")
            fi
            
            # 임시 파일 삭제
            rm -f "processed_$filename.png"
            
            # 잠시 대기 (너무 빨리 지나가지 않도록)
            sleep 0.1
        fi
    done
    
    echo ""
    echo ""
    
    # 최종 결과 출력
    echo -e "${BOLD}${PURPLE}=== 📊 최종 결과 요약 ===${NC}"
    echo ""
    
    success_rate=$((success_count * 100 / total_count))
    
    echo -e "📄 ${BOLD}처리된 파일:${NC} $total_count 개"
    echo -e "✅ ${BOLD}${GREEN}성공:${NC} $success_count 개"
    echo -e "❌ ${BOLD}${RED}실패:${NC} $((total_count - success_count)) 개"
    echo -e "📈 ${BOLD}성공률:${NC} ${success_rate}%"
    
    # 성공률에 따른 이모지
    if [ $success_rate -ge 90 ]; then
        echo -e "🎉 ${GREEN}훌륭합니다!${NC}"
    elif [ $success_rate -ge 70 ]; then
        echo -e "👍 ${YELLOW}양호합니다!${NC}"
    elif [ $success_rate -ge 50 ]; then
        echo -e "😐 ${YELLOW}보통입니다.${NC}"
    else
        echo -e "😞 ${RED}개선이 필요합니다.${NC}"
    fi
    
    echo ""
    
    # 실패한 파일들 표시
    if [ ${#failed_files[@]} -gt 0 ]; then
        warn_msg "실패한 파일들:"
        for failed_file in "${failed_files[@]}"; do
            echo "  - $failed_file"
        done
        echo ""
    fi
    
    # 생성된 파일들 정보
    if [ $success_count -gt 0 ]; then
        info_msg "생성된 텍스트 파일들:"
        txt_files=(output_*.txt)
        display_count=5
        
        for ((i=0; i<${#txt_files[@]} && i<display_count; i++)); do
            file="${txt_files[$i]}"
            if [ -f "$file" ]; then
                size=$(wc -c < "$file" | tr -d ' ')
                echo "  📝 $file (${size}자)"
            fi
        done
        
        if [ ${#txt_files[@]} -gt $display_count ]; then
            echo "  ... 그리고 $((${#txt_files[@]} - display_count))개 더"
        fi
        
        echo ""
        info_msg "모든 텍스트를 합치려면: cat output_*.txt > 전체텍스트.txt"
    fi
    
    echo ""
    echo -e "${BOLD}${GREEN}🎊 모든 작업이 완료되었습니다! 🎊${NC}"
}

# 스크립트 실행
main "$@"

스크립트 실행권한주기
chmod +x pdf_to_ocr_v2.sh

실행방법

./pdf_to_ocr_v2.sh ‘변환하고 싶은.pdf’
./pdf_to_ocr_v2.sh orc.pdf

실행화면 예시