PDF가 스캔되는지 확인하는 간단한 방법이 있습니까?


8

수천 개의 문서가 있으며 그 중 일부가 스캔되었습니다. 따라서 디렉토리에 속한 모든 PDF 파일을 테스트하는 스크립트가 필요합니다. 그렇게하는 간단한 방법이 있습니까?

  1. 대부분의 PDF는 보고서입니다. 따라서 그들은 많은 텍스트를 가지고 있습니다.
  2. 그것들은 매우 다르지만, 아래에 언급 된 바와 같이 스캔 된 것들은 스캔에 결합 된 불안정한 OCR 프로세스로 인해 일부 텍스트를 찾을 수 있습니다.

  3. 아래 주석에서 Sudodus로 인한 제안은 매우 흥미로운 것 같습니다. 스캔 한 PDF와 스캔되지 않은 PDF의 차이점을 확인하십시오.

스캔 됨 :

grep --color -a 'Image' AR-G1002.pdf
<</BitsPerComponent 8/ColorSpace/DeviceRGB/Filter[/DCTDecode]/Height 2197/Length 340615/Name/Obj13/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 40452/Name/Obj18/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 41680/Name/Obj23/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 41432/Name/Obj28/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 59084/Name/Obj33/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 8/ColorSpace/DeviceRGB/Filter[/DCTDecode]/Height 2197/Length 472681/Name/Obj38/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 8/ColorSpace/DeviceRGB/Filter[/DCTDecode]/Height 2197/Length 469340/Name/Obj43/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 8/ColorSpace/DeviceRGB/Filter[/DCTDecode]/Height 2197/Length 371863/Name/Obj48/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 8/ColorSpace/DeviceRGB/Filter[/DCTDecode]/Height 2197/Length 344092/Name/Obj53/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 59416/Name/Obj58/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 48308/Name/Obj63/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 51564/Name/Obj68/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 63184/Name/Obj73/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 40824/Name/Obj78/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 23320/Name/Obj83/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 31504/Name/Obj93/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 18996/Name/Obj98/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 8/ColorSpace/DeviceRGB/Filter[/DCTDecode]/Height 2197/Length 292932/Name/Obj103/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 27720/Name/Obj108/Subtype/Image/Type/XObject/Width 1698>>stream
               <rdf:li xml:lang="x-default">Image</rdf:li>
               <rdf:li xml:lang="x-default">Image</rdf:li>

스캔되지 않음 :

grep --color -a 'Image' AR-G1003.pdf
<</Lang(en-US)/MarkInfo<</Marked true>>/Metadata 167 0 R/Pages 2 0 R/StructTreeR<</Contents 4 0 R/Group<</CS/DeviceRGB/S/Transparency/Type/Group>>/MediaBox[0 0 612 792]/Parent 2 0 R/Resources<</Font<</F1 5 0 R/F2 7 0 R/F3 9 0 R/F4 11 0 R/F5 13 0 R>>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI]>>/StructParents 0/Tabs/S/Type/<</Filter/FlateDecode/Length 5463>>stream
<</BaseFont/Times#20New#20Roman,Bold/Encoding/WinAnsiEncoding/FirstChar 32/FontD<</Ascent 891/AvgWidth 427/CapHeight 677/Descent -216/Flags 32/FontBBox[-558 -216 2000 677]/FontName/Times#20New#20Roman,Bold/FontWeight 700/ItalicAngle 0/Leadi<</BaseFont/Times#20New#20Roman/Encoding/WinAnsiEncoding/FirstChar 32/FontDescri<</Ascent 891/AvgWidth 401/CapHeight 693/Descent -216/Flags 32/FontBBox[-568 -216 2000 693]/FontName/Times#20New#20Roman/FontWeight 400/ItalicAngle 0/Leading 42<</BaseFont/Arial,Bold/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 10 0<</Ascent 905/AvgWidth 479/CapHeight 728/Descent -210/Flags 32/FontBBox[-628 -210 2000 728]/FontName/Arial,Bold/FontWeight 700/ItalicAngle 0/Leading 33/MaxWidth<</BaseFont/Times#20New#20Roman,Italic/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 12 0 R/LastChar 118/Name/F4/Subtype/TrueType/Type/Font/Widths 164 0 <</Ascent 891/AvgWidth 402/CapHeight 694/Descent -216/Flags 32/FontBBox[-498 -216 1333 694]/FontName/Times#20New#20Roman,Italic/FontWeight 400/ItalicAngle -16.4<</BaseFont/Arial/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 14 0 R/La<</Ascent 905/AvgWidth 441/CapHeight 728/Descent -210/Flags 32/FontBBox[-665 -210 2000 728]/FontName/Arial/FontWeight 400/ItalicAngle 0/Leading 33/MaxWidth 2665<</Contents 16 0 R/Group<</CS/DeviceRGB/S/Transparency/Type/Group>>/MediaBox[0 0 612 792]/Parent 2 0 R/Resources<</Font<</F1 5 0 R/F2 7 0 R/F5 13 0 R>>/ProcSet[<</Filter/FlateDecode/Length 7534>>streamarents 1/Tabs/S/Type/Page>>
<</Contents 18 0 R/Group<</CS/DeviceRGB/S/Transparency/Type/Group>>/MediaBox[0 0 612 792]/Parent 2 0 R/Resources<</Font<</F1 5 0 R/F2 7 0 R/F5 13 0 R>>/ProcSet[<</Filter/FlateDecode/Length 6137>>streamarents 2/Tabs/S/Type/Page>>
<</Contents 20 0 R/Group<</CS/DeviceRGB/S/Transparency/Type/Group>>/MediaBox[0 0 612 792]/Parent 2 0 R/Resources<</Font<</F1 5 0 R/F2 7 0 R/F5 13 0 R/F6 21 0 R><</Filter/FlateDecode/Length 6533>>stream>>/StructParents 3/Tabs/S/Type/Page>>
<</BaseFont/Times#20New#20Roman/DescendantFonts 22 0 R/Encoding/Identity-H/Subty<</BaseFont/Times#20New#20Roman/CIDSystemInfo 24 0 R/CIDToGIDMap/Identity/DW 100<</Ascent 891/AvgWidth 401/CapHeight 693/Descent -216/Flags 32/FontBBox[-568 -216 2000 693]/FontFile2 160 0 R/FontName/Times#20New#20Roman/FontWeight 400/Italic<</Contents 27 0 R/Group<</CS/DeviceRGB/S/Transparency/Type/Group>>/MediaBox[0 0 612 792]/Parent 2 0 R/Resources<</ExtGState<</GS28 28 0 R/GS29 29 0 R>>/Font<</F1 5 0 R/F2 7 0 R/F3 9 0 R/F5 13 0 R/F6 21 0 R>>/ProcSet[/PDF/Text/ImageB/ImageC<</Filter/FlateDecode/Length 5369>>streamge>>

페이지 당 이미지 수는 훨씬 큽니다 (페이지 당 약 1 개)!


7
텍스트인지 이미지인지를 의미합니까?
DK Bose

8
pdf 파일이 스캔되는지 여부를 알고 싶습니까? 그 정보를 어떻게 사용 하시겠습니까?
sudodus

4
@sudodus 아주 좋은 질문을합니다. 예를 들어, 대부분의 스캔 된 PDF는 OCR을 사용하여 변환 된 텍스트를 선택할 수 있습니다. 이러한 파일과 텍스트 파일을 구별합니까? PDF의 출처 를 알고 있습니까?
파이프

1
스캔 한 문서와 스캔되지 않은 문서의 메타 데이터에 차이가 있습니까? 그것은 매우 깨끗하고 쉬운 방법을 제공 할 것입니다.
디저트

1
pdf파일에 이미지 (텍스트와 함께 또는 문서 전체에 'scanned pdf'와 함께 삽입 된 이미지)가 포함 된 경우, 파일은 종종 문자열을 포함하며 /Image/,이 문자열 은 명령 행에서 찾을 수 있습니다 grep --color -a 'Image' filename.pdf. 이렇게하면 이미지가 포함 된 파일 (전체 페이지 이미지와 작은 로고가있는 텍스트 페이지 및 중간 크기의 그림 그림)이 포함 된 파일 만 분리됩니다.
sudodus

답변:


4

셸 스크립트

  • pdf파일에 이미지가 포함 된 경우 (문서와 함께 또는 전체 페이지, '스캔 된 pdf'와 함께 문서에 삽입 된 경우)에는 종종 문자열이 포함됩니다 (항상) /Image/.

  • 같은 방법으로 문자열 /Text을 검색 하여 pdf 파일에 텍스트 (스캔되지 않음)가 포함되어 있는지 확인할 수 있습니다 .

나는 shellscript를 만들었고, pdf-text-or-image대부분의 경우 파일에서 작동 할 수 있습니다. 쉘 스크립트는 텍스트 문자열을 찾습니다 /Image//Textpdf파일을 저장합니다.

#!/bin/bash

echo "shellscript $0"
ls --color --group-directories-first
read -p "Is it OK to use this shellscript in this directory? (y/N) " ans
if [ "$ans" != "y" ]
then
 exit
fi

mkdir -p scanned
mkdir -p text
mkdir -p "s-and-t"

for file in *.pdf
do
 grep -aq '/Image/' "$file"
 if [ $? -eq 0 ]
 then
  image=true
 else
  image=false
 fi
 grep -aq '/Text' "$file"
 if [ $? -eq 0 ]
 then
  text=true
 else
  text=false
 fi


 if $image && $text
 then
  mv "$file" "s-and-t"
 elif $image
 then
  mv "$file" "scanned"
 elif $text
 then
  mv "$file" "text"
 else
  echo "$file undecided"
 fi
done

쉘 스크립트를 실행 가능하게 만드십시오.

chmod ugo+x pdf-text-or-image

pdf파일 이있는 위치로 디렉토리를 변경 하고 쉘 스크립트를 실행하십시오.

식별 된 파일은 다음 서브 디렉토리로 이동됩니다.

  • scanned
  • text
  • s-and-t ([스캔?] 이미지와 텍스트 내용이 모두있는 문서)

알 수없는 파일 개체 'UFO'는 현재 디렉터리에 남아 있습니다.

테스트

나는 파일의 두 가지로 쉘 스크립트를 테스트 AR-G1002.pdf하고 AR-G1003.pdf, 일부 자신에 pdf(내가 리브레 오피스 임프레스를 사용하여 만들었다 고) 파일.

$ ./pdf-text-or-image
shellscript ./pdf-text-or-image
s-and-t                                 mkUSB-quick-start-manual-11.pdf    mkUSB-quick-start-manual-nox-11.pdf
scanned                                 mkUSB-quick-start-manual-12-0.pdf  mkUSB-quick-start-manual-nox.pdf
text                                    mkUSB-quick-start-manual-12.pdf    mkUSB-quick-start-manual.pdf
AR-G1002.pdf                            mkUSB-quick-start-manual-74.pdf    OBI-quick-start-manual.pdf
AR-G1003.pdf                            mkUSB-quick-start-manual-75.pdf    oem.pdf
DescriptionoftheOneButtonInstaller.pdf  mkUSB-quick-start-manual-8.pdf     pdf-text-or-image
GrowIt.pdf                              mkUSB-quick-start-manual-9.pdf     pdf-text-or-image0
list-files.pdf                          mkUSB-quick-start-manual-bas.pdf   README.pdf
Is it OK to use this shellscript in this directory? (y/N) y

$ ls -1 *
pdf-text-or-image
pdf-text-or-image0

s-and-t:
DescriptionoftheOneButtonInstaller.pdf
GrowIt.pdf
mkUSB-quick-start-manual-11.pdf
mkUSB-quick-start-manual-12-0.pdf
mkUSB-quick-start-manual-12.pdf
mkUSB-quick-start-manual-8.pdf
mkUSB-quick-start-manual-9.pdf
mkUSB-quick-start-manual.pdf
OBI-quick-start-manual.pdf
README.pdf

scanned:
AR-G1002.pdf

text:
AR-G1003.pdf
list-files.pdf
mkUSB-quick-start-manual-74.pdf
mkUSB-quick-start-manual-75.pdf
mkUSB-quick-start-manual-bas.pdf
mkUSB-quick-start-manual-nox-11.pdf
mkUSB-quick-start-manual-nox.pdf
oem.pdf

우리가 희망을 보자

  • 파일 세트에 UFO가 없습니다
  • 텍스트 대 스캔 / 이미지와 관련하여 정렬이 정확합니다.

/ dev / null로 리디렉션하는 대신 다음을 사용할 수 있습니다.grep -q
phuclv

1
@phuclv, 팁 주셔서 감사합니다 :-) 이것은 특히 큰 파일의 경우 다소 빠릅니다. grep -q일치하는 것이 있으면 (전체 파일을 통해 꿰매는 대신) 상태가 0으로 즉시 종료 되기 때문 입니다.
sudodus

6
  1. 모든 .pdf 파일을 한 폴더에 넣습니다.
  2. 해당 폴더에 .txt 파일이 없습니다.
  3. 터미널에서 디렉토리를 해당 폴더로 변경하십시오. cd <path to dir>
  4. 스캔되지 않은 파일을위한 디렉토리를 하나 더 만드십시오. 예:
mkdir ./x 
for file in *.pdf; do
    if [ $(pdftotext "$file")"x" == "x" ] ; then mv "$file" ./x; fi
rm *.txt
done

스캔 한 모든 pdf 파일은 폴더에 남아 있고 다른 파일은 다른 폴더로 이동합니다.


이것은 대단하다. 그러나이 파일은 다른 폴더로 이동하여 스캔됩니다. drive.google.com/open?id=12xIQdRo_cyTf27Ck6DQKvRyRvlkYEzjl 무슨 일입니까?
DanielTheRocketMan 14

8
스캔 한 PDF에는 항상 OCR 텍스트 내용이 포함되어 있으므로 간단한 테스트가 실패 할 것입니다. 더 나은 표시기는 텍스트 내용에 관계없이 페이지 당 하나의 큰 이미지 일 수 있습니다.
Joey

2
매우 명백한 결함으로 인해 하향 투표 : 파일이 처음에 스캔되는지 여부를 어떻게 알 수 있습니까? 그것이 OP가 요구하는 것입니다 : 프로그래밍 방식으로 스캔을 테스트하는 방법.
jamesqf

1
@DanielTheRocketMan PDF 파일 버전이 텍스트 선택에 사용하는 도구에 영향을 줄 수 있습니다. 의 출력은 file pdf-filename.pdf버전 번호를 생성합니다. BR-L1411-3.pdf에서 특정 텍스트를 검색 할 수 없었습니다. BR-L1411-3.pdf : PDF 문서, 버전 1.3이지만 제공 한 다른 파일 (버전 1.5 및 둘 다)에서 텍스트를 검색 할 수있었습니다. 1.6 하나 이상의 경기를하십시오. PDF XChange 뷰어를 사용하여 이러한 파일을 검색했지만 evince와 비슷한 결과를 얻었습니다. 버전 1.3 문서는 아무것도 일치하지 않습니다.
Geek Geek

1
@DanielTheRocketMan이 경우 file프로젝트 완료 에 도움 이 되는 출력을 사용하여 문서를 버전별로 정렬 할 수 있습니다 . 나는 다른 사람들 이 아직도 당신이 성취하려고하는 것에 대해 명확하지 않은 것처럼 보이지만 .
Geek

2

PDF가 OCRd인지 감지하는 스크립트를 만들었습니다. 주요 아이디어 : OCRd PDF에서 텍스트는 보이지 않습니다.

주어진 PDF ( f1)가 OCRd 인지 테스트하는 알고리즘 :

  1. f1표시된 사본을 작성하십시오.f2
  2. 에 모든 텍스트를 삭제 f2
  3. 모든 이미지 (PNG) (또는 몇)에 대한 페이지를 작성 f1하고f2
  4. f1f1및의 모든 이미지 f2가 동일한 경우 OCRd였습니다 .

https://github.com/jfilter/pdf-scripts/blob/master/is_ocrd_pdf.sh

#!/usr/bin/env bash
set -e
set -x

################################################################################
# Check if a PDF was scanned or created digitally, works on OCRd PDFs
#
# Usage:
#   bash is_scanned_pdf.sh [-p] file
#
#   Exit 0: Yes, file is a scanned PDF
#   Exit 99: No, file was created digitally
#
# Arguments:
#   -p or --pages: pos. integer, only consider first N pages
#
# Please report issues at https://github.com/jfilter/pdf-scripts/issues
#
# GPLv3, Copyright (c) 2020 Johannes Filter
################################################################################

# parse arguments
# h/t https://stackoverflow.com/a/33826763/4028896
max_pages=-1
# skip over positional argument of the file(s), thus -gt 1
while [[ "$#" -gt 1 ]]; do
  case $1 in
  -p | --pages)
    max_pages="$2"
    shift
    ;;
  *)
    echo "Unknown parameter passed: $1"
    exit 1
    ;;
  esac
  shift
done

# increment to make it easier with page numbering
max_pages=$((max_pages++))

command_exists() {
  if ! [ -x $($(command -v $1 &>/dev/null)) ]; then
    echo $(error: $1 is not installed.) >&2
    exit 1
  fi
}

command_exists mutool && command_exists gs && command_exists compare
command_exists pdfinfo

orig=$PWD
num_pages=$(pdfinfo $1 | grep Pages | awk '{print $2}')

echo $num_pages

echo $max_pages

if ((($max_pages > 1) && ($max_pages < $num_pages))); then
  num_pages=$max_pages
fi

cd $(mktemp -d)

for ((i = 1; i <= num_pages; i++)); do
  mkdir -p output/$i && echo $i
done

# important to filter text on output of GS (tmp1), cuz GS alters input PDF...
gs -o tmp1.pdf -sDEVICE=pdfwrite -dLastPage=$num_pages $1 &>/dev/null
gs -o tmp2.pdf -sDEVICE=pdfwrite -dFILTERTEXT tmp1.pdf &>/dev/null
mutool convert -o output/%d/1.png tmp1.pdf 2>/dev/null
mutool convert -o output/%d/2.png tmp2.pdf 2>/dev/null

for ((i = 1; i <= num_pages; i++)); do
  echo $i
  # difference in pixels, if 0 there are the same pictures
  # discard diff image
  if ! compare -metric AE output/$i/1.png output/$i/2.png null: 2>&1; then
    echo " pixels difference, not a scanned PDF, mismatch on page $i"
    exit 99
  fi
done

1

Hobbyist는 문서 콜렉션의 스캔 된 문서에 광학 문자 인식 (OCR)이 추가 된 텍스트가없는 경우 좋은 솔루션을 제공합니다. 이것이 가능한 경우 pdfinfo -meta파일을 작성하는 데 사용 된 도구 의 출력을 읽고 확인하는 스크립트 를 작성하거나 Python 라이브러리 중 하나를 사용하여 파일을 검사하는 Python 루틴을 사용할 수 있습니다. stringsPDF 내용을 압축 할 수 있기 때문에 같은 도구로 텍스트를 검색하는 것은 신뢰할 수 없습니다. 또한 PDF 페이지를 결합 할 수 있으므로 생성 도구를 검사해도 안전하지 않습니다. 나는 정기적으로 PDF 텍스트 문서를 스캔 이미지와 결합하여 사물을 함께 유지합니다.

구체적인 제안을 제시 할 수 없어서 죄송합니다. PDF 내부 구조를 살펴본 이후 오랜 시간이 지났지 만 요구 사항이 얼마나 엄격한 지에 따라 복잡하다는 것을 알고 싶을 수도 있습니다. 행운을 빕니다!


2
나는 또한 파이썬을 사용하려고하지만 PDF가 스캔되는지 여부를 아는 것은 쉽지 않습니다. 요점은 텍스트를 선택할 수없는 문서라도 txt로 변환 될 때 일부 텍스트를 표시한다는 것입니다. 예를 들어 Python에서 pdf miner를 사용하고 있으며 선택 도구가 작동하지 않는 pdf의 경우에도 변환에서 일부 텍스트를 찾을 수 있습니다.
DanielTheRocketMan

1

pdf 대신 텍스트이미지를 스캔 하는 대신 스캔하여 PDF 가 생성 되었는지 실제로 감지하는 것에 대한 자세한 내용은 내용 뿐만 아니라 파일의 메타 데이터를 파헤쳐 야 할 수도 있습니다.

일반적으로 내 컴퓨터 및 테스트 파일에서 찾을 수있는 파일의 경우 다음과 같습니다.

  • 스캔 한 파일은 페이지 당 1000chars보다 적고 스캔되지 않은 파일은 페이지 당 1000chars보다 항상
  • 여러 개의 독립적 인 스캔 파일에 "Canon"이 PDF 작성자로 표시되어 있으며 Canon 스캐너 소프트웨어를 참조하는 것 같습니다
  • "Microsoft Word"가 작성자 인 PDF는 단어 내보내기이므로 스캔되지 않을 수 있습니다. 그러나 누군가는 단어로 스캔 한 다음 PDF로 내보낼 수 있습니다. 일부 사람들에게는 매우 이상한 워크 플로가 있습니다.

현재 Windows를 사용 node.js하고 있으므로 다음 예제에 사용 했습니다.

const fs = require("mz/fs");
const pdf_parse = require("pdf-parse");
const path = require("path");


const SHOW_SCANNED_ONES = process.argv.indexOf("scanned") != -1;

const DEBUG = process.argv.indexOf("debug") != -1;
const STRICT = process.argv.indexOf("strict") != -1;

const debug = DEBUG ? console.error : () => { };

(async () => {
    const pdfs = (await fs.readdir(".")).filter((fname) => { return fname.endsWith(".pdf") });

    for (let i = 0, l = pdfs.length; i < l; ++i) {
        const pdffilename = pdfs[i];
        try {
            debug("\n\nFILE: ", pdffilename);
            const buffer = await fs.readFile(pdffilename);
            const data = await pdf_parse(buffer);

            if (!data.info)
                data.indo = {};
            if (!data.metadata) {
                data.metadata = {
                    _metadata: {}
                };
            }


            // PDF info
            debug(data.info);
            // PDF metadata
            debug(data.metadata);
            // text length
            const textLen = data.text ? data.text.length : 0;
            const textPerPage = textLen / (data.numpages);
            debug("Text length: ", textLen);
            debug("Chars per page: ", textLen / data.numpages);
            // PDF.js version
            // check https://mozilla.github.io/pdf.js/getting_started/
            debug(data.version);

            if (evalScanned(data, textLen, textPerPage) == SHOW_SCANNED_ONES) {
                console.log(path.resolve(".", pdffilename));
            }
        }
        catch (e) {
            if (strict && !debug) {
                console.error("Failed to evaluate " + item);
            }
            {
                debug("Failed to evaluate " + item);
                debug(e.stack);
            }
            if (strict) {
                process.exit(1);
            }
        }
    }
})();
const IS_CREATOR_CANON = /canon/i;
const IS_CREATOR_MS_WORD = /microsoft.*?word/i;
// just defined for better clarity or return values
const IS_SCANNED = true;
const IS_NOT_SCANNED = false;
function evalScanned(pdfdata, textLen, textPerPage) {
    if (textPerPage < 300 && pdfdata.numpages>1) {
        // really low number, definitelly not text pdf
        return IS_SCANNED;
    }
    // definitelly has enough text
    // might be scanned but OCRed
    // we return this if no 
    // suspition of scanning is found
    let implicitAssumption = textPerPage > 1000 ? IS_NOT_SCANNED : IS_SCANNED;
    if (IS_CREATOR_CANON.test(pdfdata.info.Creator)) {
        // this is always scanned, canon is brand name
        return IS_SCANNED;
    }
    return implicitAssumption;
}

이를 실행하려면 Node.js를 설치해야하며 (단일 명령이어야 함) 다음을 호출해야합니다.

npm install mz pdf-parse

용법:

node howYouNamedIt.js [scanned] [debug] [strict]

 - scanned show PDFs thought to be scanned (otherwise shows not scanned)
 - debug shows the debug info such as metadata and error stack traces
 - strict kills the program on first error

이 예제는 완성 된 솔루션으로 간주되지 않지만 debug플래그를 사용하면 파일의 메타 정보에 대한 통찰력을 얻을 수 있습니다.

FILE:  BR-L1411-3-scanned.pdf
{ PDFFormatVersion: '1.3',
  IsAcroFormPresent: false,
  IsXFAPresent: false,
  Creator: 'Canon ',
  Producer: ' ',
  CreationDate: 'D:20131212150500-03\'00\'',
  ModDate: 'D:20140709104225-03\'00\'' }
Metadata {
  _metadata:
   { 'xmp:createdate': '2013-12-12T15:05-03:00',
     'xmp:creatortool': 'Canon',
     'xmp:modifydate': '2014-07-09T10:42:25-03:00',
     'xmp:metadatadate': '2014-07-09T10:42:25-03:00',
     'pdf:producer': '',
     'xmpmm:documentid': 'uuid:79a14710-88e2-4849-96b1-512e89ee8dab',
     'xmpmm:instanceid': 'uuid:1d2b2106-a13f-48c6-8bca-6795aa955ad1',
     'dc:format': 'application/pdf' } }
Text length:  772
Chars per page:  2
1.10.100
D:\web\so-odpovedi\pdf\BR-L1411-3-scanned.pdf

내가 작성한 순진한 기능은 컴퓨터에서 찾을 수있는 문서 (샘플 포함)에서 100 % 성공했습니다. 프로그램이 실행되기 전의 상태에 따라 파일 이름을 지정하여 결과가 올바른지 확인할 수있었습니다.

D:\xxxx\pdf>node detect_scanned.js scanned
D:\xxxx\pdf\AR-G1002-scanned.pdf
D:\xxxx\pdf\AR-G1002_scanned.pdf
D:\xxxx\pdf\BR-L1411-3-scanned.pdf
D:\xxxx\pdf\WHO_TRS_696-scanned.pdf

D:\xxxx\pdf>node detect_scanned.js
D:\xxxx\pdf\AR-G1003-not-scanned.pdf
D:\xxxx\pdf\ASEE_-_thermoelectric_paper_-_final-not-scanned.pdf
D:\xxxx\pdf\MULTIMODE ABSORBER-not-scanned.pdf
D:\xxxx\pdf\ReductionofOxideMineralsbyHydrogenPlasma-not-scanned.pdf

디버그 모드를 약간의 프로그래밍과 함께 사용하여 결과를 크게 향상시킬 수 있습니다. 프로그램의 출력을 다른 프로그램으로 전달할 수 있으며 항상 라인 당 하나의 전체 경로를 갖습니다.


"Microsoft Word"를 작성자로 다시 지정하십시오. 원본 문서의 소스에 따라 다릅니다. 예를 들어 그것들이 과학 논문이라면, 대부분은 아니지만 대부분 LaTeX 툴체인에 의해 만들어 졌을 것입니다.
jamesqf

0

내가 생각할 수있는 두 가지 방법 :

  1. 텍스트 선택 도구 사용 : 스캔 한 PDF를 사용하는 경우 텍스트를 선택할 수 없으며 상자가 나타납니다. 이 사실을 사용하여 스크립트를 작성할 수 있습니다. C ++ QT에는 Linux가 확실하지 않은 방법이 있다는 것을 알고 있습니다.

  2. 파일에서 단어 검색 : 스캔되지 않은 PDF에서는 검색이 가능하지만 스캔 된 파일에서는 검색되지 않습니다. 모든 PDF에 공통적 인 단어를 찾거나 모든 PDF에서 문자 'e'를 검색한다고 말합니다. 빈도가 가장 높으므로 텍스트가있는 모든 문서에서 찾을 수 있습니다 ( gadsby가 아닌 한 )

예 :

grep -rnw '/path/to/pdf/' -e 'e'

텍스트 처리 도구를 사용하십시오


1
OCR 이상한 일이 아니기 때문에 스캔 한 PDF는 현재에도 많은 무료 PDF 리더 OCR 기능이 선택 텍스트를 가질 수
phuclv

@phuclv : 그러나 파일이 OCR을 사용하여 텍스트로 변환 된 경우 적어도 OP의 목적을 이해 한 것처럼 더 이상 "스캔 된"파일이 아닙니다. 실제로는 텍스트 ab initio, OCR의 텍스트 및 스캔 된 이미지 인 "text"의 3 가지 유형의 pdf 파일이 있습니다.
jamesqf

1
@jamesqf 위의 예를보십시오. 그들은 pdf로 스캔됩니다. 기존의 pdfminer를 사용하여 검색 할 수없는 대부분의 텍스트입니다.
DanielTheRocketMan

1
나는 op가 그 경우에 스캔 된 정의를 다시 생각하거나 바꾸어야 할 필요가 있다고 생각하거나 acrobat x 사용을 중단해야한다. acrobat x는 스캔 된 사본을 가져 와서 이미지 대신 ocr로 가져 간다
swapedoc
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.