Czy istnieje prosty sposób stwierdzenia, czy plik PDF jest skanowany?

8

Mam tysiące dokumentów, a niektóre z nich są skanowane. Potrzebuję więc skryptu do przetestowania wszystkich plików PDF należących do katalogu. Czy jest na to prosty sposób?

  1. Większość plików PDF to raporty. Dlatego mają dużo tekstu.
  2. Są bardzo różne, ale zeskanowane, jak wspomniano poniżej, mogą znaleźć tekst z powodu niepewnego procesu OCR połączonego ze skanem.

  3. Propozycja ze względu na Sudodus w poniższych komentarzach wydaje się bardzo interesująca. Spójrz na różnicę między zeskanowanym plikiem PDF, a nie skanowanym plikiem PDF:

Zeskanowano:

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>

Nie skanowano:

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>>

Liczba zdjęć na stronie jest znacznie większa (około jednego na stronę)!

DanielTheRocketMan
źródło
7
Masz na myśli, czy są to tekst czy obrazy?
DK Bose
8
Dlaczego chcesz wiedzieć, czy plik pdf jest skanowany czy nie? Jak zamierzasz wykorzystać te informacje?
sudodus
4
@sudodus Zadaje bardzo dobre pytanie. Na przykład większość zeskanowanych plików PDF ma do wyboru tekst przekonwertowany za pomocą OCR. Czy robisz różnicę między takimi plikami a plikami tekstowymi? Czy znasz źródło swoich plików PDF?
rura
1
Czy jest jakaś różnica w metadanych skanowanych i nie skanowanych dokumentów? To byłby bardzo czysty i łatwy sposób.
deser
1
Jeśli pdfplik zawiera obraz (wstawiony do dokumentu obok tekstu lub jako całe strony, „zeskanowany plik pdf”), plik często (może zawsze) zawiera ciąg znaków /Image/, który można znaleźć w wierszu polecenia grep --color -a 'Image' filename.pdf. Spowoduje to oddzielenie plików zawierających tylko tekst od plików zawierających obrazy (obrazy całostronicowe, a także strony tekstowe z małymi logo i obrazkami ilustrującymi średnie rozmiary).
sudodus

Odpowiedzi:

4

Skrypt powłoki

  • Jeśli pdfplik zawiera obraz (wstawiony do dokumentu obok tekstu lub jako całe strony, „zeskanowany plik pdf”), plik często (może zawsze) zawiera ciąg znaków /Image/.

  • W ten sam sposób możesz wyszukać ciąg znaków, /Textaby stwierdzić, czy plik pdf zawiera tekst (nie skanowany).

Zrobiłem skrypt shellscript pdf-text-or-imagei może on działać w większości przypadków z twoimi plikami. Shellscript wyszukuje ciągów tekstowych /Image/oraz /Textw pdfplikach.

#!/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

Spraw, by skrypt był wykonywalny,

chmod ugo+x pdf-text-or-image

Przejdź do katalogu, w którym masz pdfpliki, i uruchom skrypt shellscript.

Zidentyfikowane pliki są przenoszone do następujących podkatalogów

  • scanned
  • text
  • s-and-t (w przypadku dokumentów zawierających zarówno [zeskanowane obrazy], jak i treść tekstową)

Niezidentyfikowane obiekty plików, „UFO”, pozostają w bieżącym katalogu.

Test

Przetestowałem skrypt shellscript na dwóch twoich plikach AR-G1002.pdfi na AR-G1003.pdfniektórych własnych pdfplikach (które utworzyłem za pomocą Libre Office Impress).

$ ./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

Miejmy nadzieję, że

  • w twoim zestawie plików nie ma UFO
  • sortowanie jest poprawne w odniesieniu do tekstu w porównaniu do skanowanych / obrazów
sudodus
źródło
zamiast przekierowywać do / dev / null, możesz po prostu użyćgrep -q
phuclv
1
@phuclv, Dzięki za wskazówkę :-) To sprawia, że ​​jest to również nieco szybsze, szczególnie w przypadku dużych plików, ponieważ grep -qwychodzi natychmiast z zerowym statusem, jeśli zostanie znalezione dopasowanie (zamiast przeszukiwania całych plików).
sudodus
6
  1. Umieść wszystkie pliki .pdf w jednym folderze.
  2. W tym folderze nie ma pliku .txt.
  3. W terminalu zmień katalog na ten folder za pomocą cd <path to dir>
  4. Utwórz jeszcze jeden katalog dla plików nie przeskanowanych. Przykład:
mkdir ./x 
for file in *.pdf; do
    if [ $(pdftotext "$file")"x" == "x" ] ; then mv "$file" ./x; fi
rm *.txt
done

Wszystkie zeskanowane pliki pdf pozostaną w folderze, a inne pliki zostaną przeniesione do innego folderu.

Vijay
źródło
to jest świetne. Jednak ten plik trafia do innego folderu i jest skanowany: drive.google.com/open?id=12xIQdRo_cyTf27Ck6DQKvRyRvlkYEzjl Co się dzieje?
DanielTheRocketMan,
8
Zeskanowane pliki PDF często zawsze zawierają treść OCRed, więc zgaduję, że prosty test się na nich nie powiedzie. Lepszym wskaźnikiem może być jeden duży obraz na stronę, niezależnie od zawartości tekstu.
Joey,
2
Przegłosowany z powodu bardzo oczywistej wady: skąd wiesz, czy pliki są skanowane, czy nie? O to prosi OP: jak programowo przetestować skanowane czy nie.
jamesqf,
1
@DanielTheRocketMan Wersja pliku PDF prawdopodobnie ma wpływ na narzędzie używane do zaznaczania tekstu. Dane wyjściowe file pdf-filename.pdfwygenerują numer wersji. Nie mogłem wyszukać określonego tekstu w BR-L1411-3.pdf BR-L1411-3.pdf: Dokument PDF, wersja 1.3, ale byłem w stanie wyszukać tekst w obu podanych przez Ciebie plikach, które są w wersji 1.5 i 1.6 i zdobądź co najmniej jedno dopasowanie. Do przeszukiwania tych plików korzystałem z przeglądarki PDF XChange, ale z evince uzyskałem podobne wyniki. dokument w wersji 1.3 nic nie pasował.
Elder Geek,
1
@DanielTheRocketMan W takim przypadku możesz znaleźć sortowanie dokumentów według wersji przy użyciu danych wyjściowych filepomocnych w ukończeniu projektu. Chociaż ja, jak się wydaje, inni wciąż nie są pewni, co dokładnie próbujesz osiągnąć.
Elder Geek
2

Stworzyłem skrypt wykrywający, czy plik PDF to OCRd. Główna idea: w plikach OCRd PDF tekst jest niewidoczny.

Algorytm sprawdzający, czy dany plik PDF ( f1) był OCRd:

  1. utwórz kopię f1oznaczoną jakof2
  2. usuń cały tekst na f2
  3. tworzyć obrazy (PNG) dla wszystkich (lub tylko kilku) stron dla f1if2
  4. f1był OCRd, jeśli wszystkie obrazy f1i f2są identyczne.

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
Filtr Johannesa
źródło
1

Hobbyści oferują dobre rozwiązanie, jeśli skanowane dokumenty kolekcji dokumentów nie mają dodanego tekstu z optycznym rozpoznawaniem znaków (OCR). Jeśli jest to możliwe, możesz wykonać pewne skrypty, które odczytują dane wyjściowe pdfinfo -metai sprawdzają narzędzie użyte do utworzenia pliku, lub wykorzystują procedurę Pythona, która korzysta z jednej z bibliotek Pythona do ich badania. Wyszukiwanie tekstu za pomocą takiego narzędzia stringsbędzie zawodne, ponieważ zawartość PDF można skompresować. Sprawdzanie narzędzia do tworzenia również nie jest bezpieczne, ponieważ strony PDF można łączyć; Rutynowo łączę dokumenty tekstowe PDF ze skanowanymi obrazami, aby wszystko było razem.

Przykro mi, że nie jestem w stanie zaoferować konkretnych sugestii. Minęło trochę czasu, odkąd zająłem się wewnętrzną strukturą PDF, ale w zależności od tego, jak surowe są twoje wymagania, możesz chcieć wiedzieć, że jest to trochę skomplikowane. Powodzenia!

ichabod
źródło
2
Próbuję również używać Pythona, ale nie jest trywialne wiedzieć, czy plik PDF jest skanowany, czy nie. Chodzi o to, że nawet dokumenty, których nie można zaznaczyć, zawierają tekst po przekonwertowaniu na txt. Na przykład używam górnika pdf w Pythonie i mogę znaleźć tekst w konwersji nawet dla plików pdf, które nie wybierają narzędzia.
DanielTheRocketMan,
1

Jeśli chodzi raczej o wykrycie, czy PDF został utworzony przez skanowanie, a nie pdf zawiera obrazy zamiast tekstu , może być konieczne przekopanie się do metadanych pliku, a nie tylko zawartości.

Ogólnie rzecz biorąc, w przypadku plików, które mogłem znaleźć na moim komputerze i plikach testowych, spełniono następujące warunki:

  • Zeskanowane pliki mają mniej niż 1000 znaków / stronę w porównaniu do tych, które zawsze mają więcej niż 1000 znaków / stronę
  • Wiele niezależnych zeskanowanych plików zawierało „Canon” jako twórcę plików PDF, prawdopodobnie nawiązując do oprogramowania skanera Canon
  • Pliki PDF z „Microsoft Word” jako twórcą prawdopodobnie nie zostaną zeskanowane, ponieważ są eksportowane do plików Word. Ale ktoś może zeskanować do słowa, a następnie wyeksportować do pliku PDF - niektóre osoby mają bardzo dziwny przebieg pracy .

Obecnie korzystam z systemu Windows, więc użyłem node.jsnastępującego przykładu:

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;
}

Aby go uruchomić, musisz mieć zainstalowany Node.js (powinno to być pojedyncze polecenie), a także wywołać:

npm install mz pdf-parse

Stosowanie:

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

Ten przykład nie jest uważany za gotowe rozwiązanie, ale z debugflagą można uzyskać wgląd w meta informacje pliku:

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

Naiwna funkcja, którą napisałem, ma 100% sukcesu w dokumentach, które mogłem znaleźć na moim komputerze (w tym w twoich próbkach). Nazwałem pliki na podstawie ich statusu przed uruchomieniem programu, aby umożliwić sprawdzenie, czy wyniki są poprawne.

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

Możesz użyć trybu debugowania wraz z odrobiną programowania, aby znacznie poprawić swoje wyniki. Możesz przekazać wyjście programu do innych programów, zawsze będzie miał jedną pełną ścieżkę na linię.

Tomáš Zato - Przywróć Monikę
źródło
Re „Microsoft Word” jako twórca, który będzie zależeć od źródła oryginalnych dokumentów. Jeśli na przykład są to artykuły naukowe, wiele, jeśli nie większość, zostanie stworzonych przez coś z zestawu narzędzi LaTeX.
jamesqf
0

2 sposoby na wymyślenie:

  1. Korzystanie z narzędzia Wybierz tekst: jeśli używasz zeskanowanego pliku PDF, tekstów nie można wybrać, pojawi się raczej okno. Możesz użyć tego faktu do stworzenia skryptu. Wiem, że w C ++ QT istnieje sposób, choć nie jestem pewien w Linuksie.

  2. Wyszukaj słowo w pliku: W niezeskanowanym pliku PDF wyszukiwanie będzie działać, ale nie w zeskanowanym pliku. Musisz po prostu znaleźć kilka słów wspólnych dla wszystkich plików PDF lub wolę wyszukać literę „e” we wszystkich plikach PDF. Ma najwyższy rozkład częstotliwości, więc są szanse, że znajdziesz go we wszystkich dokumentach zawierających tekst (chyba że jest to gadżet )

na przykład

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

Użyj dowolnego narzędzia do przetwarzania tekstu

swapedoc
źródło
1
skanowany plik PDF może również zawierać tekst do wyboru, ponieważ OCR nie jest w dzisiejszych czasach niczym dziwnym, a nawet wiele darmowych czytników PDF ma funkcję OCR
phuclv
@phuclv: Ale jeśli plik został przekonwertowany na tekst za pomocą OCR, nie jest to już plik „skanowany”, przynajmniej tak, jak rozumiem cel PO. Chociaż tak naprawdę masz teraz 3 typy plików pdf: tekst ab initio, tekst z OCR i „tekst”, który jest zeskanowanym obrazem.
jamesqf,
1
@jamesqf, spójrz na powyższy przykład. Są skanowane pdf. Większość tekstu nie mogę pobrać za pomocą konwencjonalnego programu pdfminer.
DanielTheRocketMan
1
myślę, że operacja musi ponownie przemyśleć / przeformułować definicję skanowanego w tym przypadku lub przestać używać programu Acrobat X, który pobiera zeskanowaną kopię i przyjmuje ją jako ocr, a nie obraz
swapedoc