Scan to PDF tool for Linux (only Linux)
Posted: Tue Aug 25, 2015 7:35 am
Hi,
a repeating task for me is to scan documents into PDF's. On Windows, there where several tools available. After switching my desktop to Linux (Kubuntu 14.04), I found that there are also some programs available for this. After trying several sort of software for that task (Scan to PDF) I found the existing either to complex, to hard to handle, to simple or crashing. So I decided to do a quick tool for that by myself. Here it is. It is far away from being complete and definitely not perfect. But it does exactly what I need. So if you like it you may extend it? I would be happy if every enhancement is posted here so maybe I can also get benefit of it
BTW, after scanning I use the sigmoidal-contrast of ImageMagic toolset instead of the standard contrast method (http://www.imagemagick.org/Usage/color_mods/#sigmoidal). IMHO this algorithm works much better on scanned text than the standard contrast function. You may replace this if you plan to scan fotos. Or make this an option by enhancing the code...
I fixed JPG quality to 75%. You also may add a setting for this...
In order to work, the following tools must be installed:
sane and sane-utils (for the 'scanimage' tool)
imagemagick (for the 'convert' tool)
To install these prerequisites, run this in your console window (line by line, each followed by the enter key):
You also need at least one SANE compatible scanner connected to your PC (http://www.sane-project.org/sane-mfgs.html).
If you get error "Error during save. Try again...", please edit /etc/ImageMagick-6/policy.xml and ensure this line (may already be there):
Updates:
[2015-09-01: Fixed wrong order of pages during display and/or PDF creation.]
[2015-09-08: Some optical enhancements (paper-shadow, page number).]
[2015-10-29: Fixed issue saving PDF to a path containing space, enhanced scanner compatibility, remember last used save-folder.]
[2015-11-03: Added field for optional scanner options and button to show such scanner options.]
[2015-11-06: Fixed wrong date format in default filename. Prevent error if 'save' is clicked while no image is available.]
[2015-11-17: Added 'Print' button to also make it a quick copy-tool. Added some tool-tips to most buttons. Fixed bug in scanner-list handling.]
[2016-01-11: Fixed wrong page order and font size for page numbering if there are more than 9 pages.]
[2016-02-09: Added displaying some setup information on first start to help people to get it running. Some minor fixes.]
[2016-08-01: Now showing scan progress (learning scan time to be more accurate)]
[2016-09-28: Main window is now remembering its position and size for the next start]
[2017-09-18: scanimage options are now stored for every scanner in the list. Added generic scanner.]
[2018-11-06: Hint: You may have to edit /etc/ImageMagick-6/policy.xml to continue this tool.]
[2022-12-14: Added option to save password protected PDF (needs qpdf on your system)]
The source (compile using PB 5.24 or higher):
Best,
Kukulkan
a repeating task for me is to scan documents into PDF's. On Windows, there where several tools available. After switching my desktop to Linux (Kubuntu 14.04), I found that there are also some programs available for this. After trying several sort of software for that task (Scan to PDF) I found the existing either to complex, to hard to handle, to simple or crashing. So I decided to do a quick tool for that by myself. Here it is. It is far away from being complete and definitely not perfect. But it does exactly what I need. So if you like it you may extend it? I would be happy if every enhancement is posted here so maybe I can also get benefit of it
BTW, after scanning I use the sigmoidal-contrast of ImageMagic toolset instead of the standard contrast method (http://www.imagemagick.org/Usage/color_mods/#sigmoidal). IMHO this algorithm works much better on scanned text than the standard contrast function. You may replace this if you plan to scan fotos. Or make this an option by enhancing the code...
I fixed JPG quality to 75%. You also may add a setting for this...
In order to work, the following tools must be installed:
sane and sane-utils (for the 'scanimage' tool)
imagemagick (for the 'convert' tool)
To install these prerequisites, run this in your console window (line by line, each followed by the enter key):
Code: Select all
sudo apt-get update
sudo apt-get install sane
sudo apt-get install sane-utils
sudo apt-get install imagemagick
sudo apt-get install qpdf
If you get error "Error during save. Try again...", please edit /etc/ImageMagick-6/policy.xml and ensure this line (may already be there):
Code: Select all
<policy domain="coder" rights="read|write" pattern="PDF" />
[2015-09-01: Fixed wrong order of pages during display and/or PDF creation.]
[2015-09-08: Some optical enhancements (paper-shadow, page number).]
[2015-10-29: Fixed issue saving PDF to a path containing space, enhanced scanner compatibility, remember last used save-folder.]
[2015-11-03: Added field for optional scanner options and button to show such scanner options.]
[2015-11-06: Fixed wrong date format in default filename. Prevent error if 'save' is clicked while no image is available.]
[2015-11-17: Added 'Print' button to also make it a quick copy-tool. Added some tool-tips to most buttons. Fixed bug in scanner-list handling.]
[2016-01-11: Fixed wrong page order and font size for page numbering if there are more than 9 pages.]
[2016-02-09: Added displaying some setup information on first start to help people to get it running. Some minor fixes.]
[2016-08-01: Now showing scan progress (learning scan time to be more accurate)]
[2016-09-28: Main window is now remembering its position and size for the next start]
[2017-09-18: scanimage options are now stored for every scanner in the list. Added generic scanner.]
[2018-11-06: Hint: You may have to edit /etc/ImageMagick-6/policy.xml to continue this tool.]
[2022-12-14: Added option to save password protected PDF (needs qpdf on your system)]
The source (compile using PB 5.24 or higher):
Code: Select all
; ScanToPDF for Linux
; The source is free to use in any non-commercial or commercial project.
; (w) 2015-2022 by V. Schmid
; PureBasic 5.45 LTS, 5.72 x64 (Linux only!)
;
; IMPORTANT: Use gtk2 as subsystem!!!!!
; It does not work with qt because of missing canvas in scrollarea (VS, PB 5.72, 2022)
UseJPEGImageDecoder()
EnableExplicit
CompilerIf #PB_Compiler_Unicode
#XmlEncoding = #PB_UTF8
CompilerElse
#XmlEncoding = #PB_Ascii
CompilerEndIf
#preview_spacing = 10 ; spacing around the preview image
Structure strPrevImage
Name.s
PosX.i
PosY.i
Width.i
Height.i
Aspect.f
EndStructure
Structure strScanPrefs
Contrast.i
Gamma.i
DPI.i
Device.s
Options.s
EndStructure
Structure strScanner
Name.s
Device.s
Options.s
EndStructure
Structure strGui
dialog.i
window.i
scan.i
print.i
save.i
saveCrypt.i
clear.i
close.i
dpi.i
gamma.i
lastPath.s
txtGamma.i
contrast.i
txtContrast.i
scanner.i
scanRefresh.i
scrollArea.i
canvas.i
btnOptions.i
strOptions.i
lblPageCount.i
lblScanTime.i
chkAutoClick.i
EndStructure
Global NewList ScannerList.strScanner()
Global gui.strGui
Global progStart.i
Global progLastTime.i = 0
Global progPos.i
Procedure.i openProgress(parentWin.i)
Protected XML.s
XML.s = "<window id='#PB_Any' name='Please wait' text='Scanning in progress' minwidth='300' minheight='auto' flags='#PB_Window_WindowCentered|#PB_Window_Tool'>" +
" <vbox expand=''>" +
" <text name='lblMessage' text='Please wait...' />" +
" <progressbar name='progress' min='0' max='100' height='30'/>" +
" </vbox>" +
"</window>"
Protected xm.i = CatchXML(#PB_Any, @XML, StringByteLength(XML), 0, #XmlEncoding)
If XMLStatus(xm.i) = #PB_XML_Success
Protected dialog.i = CreateDialog(#PB_Any)
If OpenXMLDialog(dialog.i, xm.i, "Please wait", #PB_Ignore, #PB_Ignore, #PB_Ignore, #PB_Ignore, WindowID(parentWin))
SetGadgetState(DialogGadget(dialog.i, "progress"), 0)
StickyWindow(DialogWindow(dialog.i), #True)
Else
Debug "Window not opened :-("
End
EndIf
Else
Debug "XML Error: " + XMLError(xm.i)
End
EndIf
progPos.i = 0
progStart.i = ElapsedMilliseconds()
ProcedureReturn dialog.i
EndProcedure
; just update
Procedure doProgress(dialog.i)
Protected actTime.i = ElapsedMilliseconds() - progStart.i
Protected pos.f = (100 / progLastTime.i) * actTime.i
If pos > 100
If pos > 110
; more than 10% more time needed. Inform about learning
SetGadgetText(DialogGadget(dialog.i, "lblMessage"), "Learning scan-time...")
EndIf
pos = 100
EndIf
If pos < 0: pos = 0: EndIf
SetGadgetState(DialogGadget(dialog.i, "progress"), pos)
EndProcedure
; returns the time it needed last time
Procedure closeProgress(dialog.i)
If IsDialog(dialog.i)
FreeDialog(dialog.i)
EndIf
Protected actTime.i = ElapsedMilliseconds() - progStart.i
progLastTime.i = (actTime.i + progLastTime.i) / 2 ; count new time for 50%
dialog.i = 0
ProcedureReturn actTime.i
EndProcedure
Procedure FillScannerlist(Refresh.b = #False)
Protected lst.s, x.i, entry.s
If Refresh.b = #True Or ListSize(ScannerList()) = 0
; make a copy to preserve scanner options (if scanner stays available)
Protected NewList oldList.strScanner()
CopyList(ScannerList(), oldList())
ClearList(ScannerList())
AddElement(ScannerList())
ScannerList()\Name = "-- Generic --"
ScannerList()\Device = ""
Protected proc.i = RunProgram("scanimage", "-f "+Chr(34)+"%m|%dµ"+Chr(34), "", #PB_Program_Open | #PB_Program_Read)
If proc.i
While ProgramRunning(proc.i)
If AvailableProgramOutput(proc.i)
lst.s + ReadProgramString(proc.i) + Chr(13)
EndIf
Wend
CloseProgram(proc.i)
EndIf
For x.i = 1 To CountString(lst.s, "µ")
entry.s = StringField(lst.s, x.i, "µ")
AddElement(ScannerList())
ScannerList()\Name = StringField(entry.s, 1, "|")
ScannerList()\Device = StringField(entry.s, 2, "|")
ForEach oldList()
If oldList()\Device = ScannerList()\Device
; found previous options and keep them
ScannerList()\Options = oldList()\Options
Break
EndIf
Next
Next
EndIf
ClearGadgetItems(gui\scanner)
ForEach ScannerList()
AddGadgetItem(gui\scanner, -1, ScannerList()\Name)
Next
EndProcedure
Procedure ShowScannerOptions(*Settings.strScanPrefs)
Protected ScanimageParams.s = "-A"
If *Settings\Device <> ""
ScanimageParams.s = "--device-name=" + *Settings\Device + " " + ScanimageParams.s
EndIf
Protected proc.i = RunProgram("scanimage", ScanimageParams.s, "", #PB_Program_Open | #PB_Program_Read)
Protected lst.s = ""
If proc.i
While ProgramRunning(proc.i)
If AvailableProgramOutput(proc.i)
lst.s + ReadProgramString(proc.i) + Chr(10)
EndIf
Wend
CloseProgram(proc.i)
EndIf
Protected Lines.i = CountString(lst.s, Chr(10))
Protected x.i
If Lines.i > 45
Protected Output.s = ""
For x.i = 1 To Lines.i
Output.s + StringField(lst.s, x.i, Chr(10)) + Chr(10)
If CountString(Output.s, Chr(10)) >= 30 And
Left(Trim(StringField(lst.s, x.i+1, Chr(10))), 2) = "--"
MessageRequester("Scanner options", Output.s)
Output.s = ""
EndIf
Next
MessageRequester("Scanner options", Output.s)
Else
MessageRequester("Scanner options", lst.s)
EndIf
EndProcedure
Procedure SaveSettings()
Protected Path.s = GetHomeDirectory() + ".ScanToPDF"
If FileSize(Path.s) <> -2
If CreateDirectory(Path.s) = 0
Debug "Failed to create settings dir!!!"
End
EndIf
EndIf
Path.s + "/settings.ini"
OpenPreferences(Path.s, #PB_Preference_GroupSeparator)
PreferenceGroup("Main")
WritePreferenceInteger("dpiSel", GetGadgetState(gui\dpi))
WritePreferenceInteger("gamma", GetGadgetState(gui\gamma))
WritePreferenceInteger("contrast", GetGadgetState(gui\contrast))
WritePreferenceString("lastPath", gui\lastPath)
WritePreferenceString("options", GetGadgetText(gui\strOptions))
If progLastTime > 0
WritePreferenceInteger("lastScanDuration", progLastTime)
EndIf
WritePreferenceInteger("mainLeft", WindowX(gui\window))
WritePreferenceInteger("mainTop", WindowY(gui\window))
WritePreferenceInteger("mainWidth", WindowWidth(gui\window))
WritePreferenceInteger("mainHeight", WindowHeight(gui\window))
PreferenceGroup("Scanners")
WritePreferenceInteger("count", ListSize(ScannerList()))
WritePreferenceInteger("selected", GetGadgetState(gui\scanner))
Protected c.i = 0
ForEach ScannerList()
c.i + 1
WritePreferenceString("name"+Str(c.i), ScannerList()\Name)
WritePreferenceString("device"+Str(c.i), ScannerList()\Device)
WritePreferenceString("options"+Str(c.i), ScannerList()\Options)
Next
ClosePreferences()
EndProcedure
Procedure LoadSettings()
Protected Path.s = GetHomeDirectory() + ".ScanToPDF"
If FileSize(Path.s) <> -2
Protected ret.i = MessageRequester("ScanToPDF / first start",
"Hello. To use this tool you need the following tools installed:" + #LF$ +
"sane and sane-utils (for the 'scanimage' tool)" + #LF$ +
"imagemagick (for the 'convert' tool)" + #LF$ + #LF$ +
"Did you already install these components?",
#PB_MessageRequester_YesNo)
If ret.i <> #PB_MessageRequester_Yes
SetClipboardText("sudo apt-get update" + #LF$ +
"sudo apt-get install sane" + #LF$ +
"sudo apt-get install sane-utils" + #LF$ +
"sudo apt-get install imagemagick")
MessageRequester("ScanToPDF / install information",
"I just copied the needed apt-get commandlines to your clipboard." + #LF$ + #LF$ +
"Paste and run it in a console window or check it before in some text editor.")
End
EndIf
If CreateDirectory(Path.s) = 0
MessageRequester("ScanToPDF error", "Failed to create settings dir [" + Path.s + "]. Check permissions.")
End
EndIf
EndIf
Path.s + "/settings.ini"
OpenPreferences(Path.s, #PB_Preference_GroupSeparator)
PreferenceGroup("Main")
SetGadgetState(gui\dpi, ReadPreferenceInteger("dpiSel", 2))
SetGadgetState(gui\gamma, ReadPreferenceInteger("gamma", 0))
SetGadgetState(gui\contrast, ReadPreferenceInteger("contrast", 15))
SetGadgetText(gui\strOptions, ReadPreferenceString("options", "--mode color"))
gui\lastPath = ReadPreferenceString("lastPath", "")
progLastTime = ReadPreferenceInteger("lastScanDuration", 10000)
ResizeWindow(gui\window, ReadPreferenceInteger("mainLeft", #PB_Ignore),
ReadPreferenceInteger("mainTop", #PB_Ignore),
ReadPreferenceInteger("mainWidth", #PB_Ignore),
ReadPreferenceInteger("mainHeight", #PB_Ignore))
PreferenceGroup("Scanners")
Protected c.i = ReadPreferenceInteger("count", 0)
Protected x.i
For x.i = 1 To c.i
AddElement(ScannerList())
ScannerList()\Name = ReadPreferenceString("name"+Str(x.i), "?")
ScannerList()\Device = ReadPreferenceString("device"+Str(x.i), "?")
ScannerList()\Options = ReadPreferenceString("options"+Str(x.i), "")
Next
FillScannerlist()
SetGadgetState(gui\scanner, ReadPreferenceInteger("selected", 0))
ClosePreferences()
; update some other fields
SetGadgetText(gui\txtContrast, Str(GetGadgetState(gui\contrast)))
SetGadgetText(gui\txtGamma, Str(GetGadgetState(gui\gamma)))
EndProcedure
Procedure.i CountPNMFiles(Foldername.s)
Protected Count.i = 0
Protected dir.i = ExamineDirectory(#PB_Any, Foldername.s, "*.pnm")
If dir.i
While NextDirectoryEntry(dir.i)
If DirectoryEntryType(dir.i) = #PB_DirectoryEntry_File
Count.i + 1
EndIf
Wend
FinishDirectory(dir.i)
EndIf
ProcedureReturn Count.i
EndProcedure
Procedure doScanning(Foldername.s, *Settings.strScanPrefs, Documents.i = 1)
Protected StartID.i = CountPNMFiles(Foldername.s) + 1
Protected Res.i, x.i, pid.i, startTime.i, timeoutTime.i
Protected myStart.i = ElapsedMilliseconds()
For x.i = 1 To Documents.i
If x.i > 1
If GetGadgetState(gui\chkAutoClick) = 0
Res.i = MessageRequester("ScanToPDF", "Please prepare page " + Str(x.i) + " of " + Str(Documents.i) + "." + #LF$ + #LF$ +
"Press 'Yes' to scan. Press 'No' to cancel.",
#PB_MessageRequester_YesNo)
If Res.i <> #PB_MessageRequester_Yes
Break
EndIf
EndIf
EndIf
Protected ScanimageParams.s = "--batch " +
"--batch-start=" + Str(StartID.i) + " " +
"--batch-count=1 " +
"--batch=out%03d.pnm " +
"--format=pnm " +
"--resolution " + Str(*Settings\DPI)
If *Settings\Options <> ""
ScanimageParams.s = ScanimageParams.s + " " + *Settings\Options
EndIf
If *Settings\Device <> ""
ScanimageParams.s = "--device-name=" + *Settings\Device + " " + ScanimageParams.s
EndIf
Debug "scanimage " + ScanimageParams.s
Protected d.i = openProgress(DialogWindow(gui\dialog))
startTime.i = ElapsedMilliseconds()
timeoutTime.i = startTime.i + 30000 ; wait for max. 30 seconds
pid.i = RunProgram("scanimage", ScanimageParams.s, Foldername.s, #PB_Program_Open)
While ProgramRunning(pid.i) <> 0 And ElapsedMilliseconds() < timeoutTime.i
WaitWindowEvent(100)
doProgress(d.i)
Wend
CloseProgram(pid.i)
closeProgress(d.i)
StartID.i + 1
Protected neededTime.i = ElapsedMilliseconds() - myStart.i
Protected timePerPage.i = neededTime.i / x.i
SetGadgetText(gui\lblScanTime, "Scanning speed: " + Str(timePerPage.i / 1000) + " sec/page")
Next
EndProcedure
Procedure doConvert(Foldername.s, *Settings.strScanPrefs)
Protected options.s = "-quality 75%"
NewList files.s()
If *Settings\Contrast > 0
options.s + " -sigmoidal-contrast "+Str(*Settings\Contrast)+",70%"
EndIf
If *Settings\Gamma <> 0
options.s + " -level 0%,100%,"+StrF(1 + (*Settings\Gamma / 10), 1)+"%"
EndIf
Protected dir.i = ExamineDirectory(#PB_Any, Foldername.s, "*.pnm")
If dir.i
While NextDirectoryEntry(dir.i)
If DirectoryEntryType(dir.i) = #PB_DirectoryEntry_File
AddElement(files())
files() = DirectoryEntryName(dir.i)
EndIf
Wend
FinishDirectory(dir.i)
EndIf
SortList(files(), #PB_Sort_Ascending)
ForEach files()
Protected source.s = files()
Protected destination.s = GetFilePart(source.s, #PB_FileSystem_NoExtension) + ".jpg"
Protected Convert.s = Chr(34) + source.s + Chr(34) + " " + options.s + " " + Chr(34) + destination.s + Chr(34)
RunProgram("convert", Convert.s, Foldername.s, #PB_Program_Wait)
Next
EndProcedure
Procedure.i doPDF(Foldername.s, Destination.s, password.s)
Protected File.s
NewList files.s()
If FileSize(Destination.s) <> -1
DeleteFile(Destination.s) ; cleanup
EndIf
Protected dir.i = ExamineDirectory(#PB_Any, Foldername.s, "*.jpg")
If dir.i
While NextDirectoryEntry(dir.i)
If DirectoryEntryType(dir.i) = #PB_DirectoryEntry_File
AddElement(files())
files() = DirectoryEntryName(dir.i)
EndIf
Wend
FinishDirectory(dir.i)
EndIf
SortList(files(), #PB_Sort_Ascending)
ForEach files()
File.s + " " + Chr(34) + files() + Chr(34)
Next
If File.s = ""
ProcedureReturn #False
EndIf
File.s + " " + Chr(34) + Destination.s + Chr(34)
RunProgram("convert", File.s, Foldername.s, #PB_Program_Wait)
If FileSize(Destination.s) < 0
ProcedureReturn #False
EndIf
If password.s <> ""
File.s = Chr(34) + Destination.s + Chr(34)
RunProgram("qpdf", "--replace-input --encrypt "+Chr(34)+password.s+Chr(34)+" "+Chr(34)+password.s+Chr(34)+" 256 -- " + File.s, Foldername.s, #PB_Program_Wait)
EndIf
ProcedureReturn #True
EndProcedure
Procedure.i doPrint(Foldername.s)
Protected File.s
Protected PDFFile.s = Foldername.s + "printDummy.pdf"
If doPDF(Foldername.s, PDFFile.s, "")
File.s = Chr(34) + PDFFile.s + Chr(34)
RunProgram("lpr", File.s, "", #PB_Program_Wait)
Else
MessageRequester("ScanToLinux", "Failed to convert PDF")
EndIf
DeleteFile(PDFFile.s)
ProcedureReturn #True
EndProcedure
Procedure.s GetSelectedScannerDevice()
Protected name.s = GetGadgetText(gui\scanner)
ForEach ScannerList()
If ScannerList()\Name = name.s
ProcedureReturn ScannerList()\Device
EndIf
Next
ProcedureReturn ""
EndProcedure
Procedure.s GetSelectedScannerOptions()
Protected name.s = GetGadgetText(gui\scanner)
ForEach ScannerList()
If ScannerList()\Name = name.s
ProcedureReturn ScannerList()\Options
EndIf
Next
ProcedureReturn ""
EndProcedure
Procedure SetSelectedScannerOptions(Options.s)
Protected name.s = GetGadgetText(gui\scanner)
ForEach ScannerList()
If ScannerList()\Name = name.s
ScannerList()\Options = Options.s
EndIf
Next
EndProcedure
Procedure FillPreview(Foldername.s)
Protected pw.i = GadgetWidth(gui\scrollArea) * 0.83 ; preview width
Protected px.i = GadgetWidth(gui\scrollArea) * 0.05 ; preview left
Protected posY.i = #preview_spacing
Protected firstImage.strPrevImage, imgId.i, page.i
NewList prevImg.strPrevImage()
NewList files.s()
; calculate height and define preview positions
Protected dir.i = ExamineDirectory(#PB_Any, Foldername.s, "*.jpg")
If dir.i
While NextDirectoryEntry(dir.i)
If DirectoryEntryType(dir.i) = #PB_DirectoryEntry_File
AddElement(files())
files() = DirectoryEntryName(dir.i)
EndIf
Wend
FinishDirectory(dir.i)
SortList(files(), #PB_Sort_Ascending)
ForEach files()
; make preview
If firstImage\Name = ""
; get data from first image
imgId.i = LoadImage(#PB_Any, Foldername.s + files())
If imgId.i <> 0
firstImage\Name = Foldername.s + files()
firstImage\Height = ImageHeight(imgId.i)
firstImage\Width = ImageWidth(imgId.i)
firstImage\Aspect = firstImage\Height / firstImage\Width
EndIf
FreeImage(imgId.i)
EndIf
AddElement(prevImg())
With prevImg()
\Name = Foldername.s + files()
\PosX = px.i
\PosY = posY.i
\Width = pw.i
\Height = pw.i * firstImage\Aspect
posY.i = posY.i + \Height + #preview_spacing
EndWith
Next
EndIf
If ListSize(prevImg()) < 1
posY.i = GadgetHeight(gui\scrollArea)
EndIf
; resize canvas to fit all preview images
ResizeGadget(gui\canvas, #PB_Ignore, #PB_Ignore, GadgetWidth(gui\scrollArea), posY.i)
; draw preview
Protected font11.i = LoadFont(#PB_Any, "Arial", 11)
Protected font8.i = LoadFont(#PB_Any, "Arial", 8)
StartDrawing(CanvasOutput(gui\canvas))
Box(0, 0, GadgetWidth(gui\canvas), GadgetHeight(gui\canvas), RGB(100,100,100))
FrontColor(RGB(255,255,255))
BackColor(RGB(60,60,60))
ForEach prevImg()
With prevImg()
imgId.i = LoadImage(#PB_Any, \Name)
If imgId.i <> 0
page.i = page.i + 1
; draw some shadow
Box(\PosX + \Width, \PosY + 4, 4, \Height, RGB(60,60,60)) ; right
Box(\PosX + 4, \PosY + \Height, \Width, 4, RGB(60,60,60)) ; bottom
; draw preview image
DrawImage(ImageID(imgId.i), \PosX, \PosY, \Width, \Height)
; page number
Circle(\PosX + \Width + 5, \PosY + 8, 12, RGB(225,225,75))
Circle(\PosX + \Width + 5, \PosY + 8, 10, RGB(60,60,60))
If page.i < 10
DrawingFont(FontID(font11.i))
DrawText(\PosX + \Width + 1, \PosY, Str(page.i))
Else
DrawingFont(FontID(font8.i))
DrawText(\PosX + \Width, \PosY + 2, Str(page.i))
EndIf
EndIf
FreeImage(imgId.i)
EndWith
Next
StopDrawing()
FreeFont(font11.i)
FreeFont(font8.i)
; set scrollarea
SetGadgetAttribute(gui\scrollArea, #PB_ScrollArea_InnerHeight, posY.i)
SetGadgetText(gui\lblPageCount, "Pages: " + Str(page.i))
EndProcedure
Procedure main()
Protected XML.s, Event.i, evType.i, evGadget.i
Protected Foldername.s = GetTemporaryDirectory() + "ScanToLinux/"
Protected LastSize.i = 0, LastModify.i = 0
Protected Settings.strScanPrefs
Protected opt.s = ""
XML = "<window id='#PB_Any' name='ScanToPDF' text='ScanToPDF for Linux' minwidth='500' minheight='600' flags='#PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_SizeGadget'>" +
" <vbox expand='item:7' spacing='10'>" +
" <hbox height='50'>" +
" <button name='scan' text='Scan Page(s)' />" +
" <hbox>" +
" <button name='save' text='Save PDF' />" +
" <button name='saveCrypt' text='Save PDF+pwd' />" +
" </hbox>" +
" <button name='print' text='Print' />" +
" <button name='clear' text='Clear Document' />" +
" <button name='close' text='Close' />" +
" </hbox>" +
" <text text='Scanner settings:' />" +
" <hbox>" +
" <hbox expand='item:2'>" +
" <text text='Scanner:' />" +
" <combobox name='scanner' />" +
" <button name='scanRefresh' text='refresh list' />" +
" </hbox>" +
" <hbox expand='item:2'>" +
" <text text='Scan resolution:' />" +
" <combobox name='dpi' />" +
" </hbox>" +
" </hbox>" +
" <hbox expand='item:2'>" +
" <text text='Additional scanimage options:' />" +
" <string name='strOptions' text='' />" +
" <button name='btnOptions' text='?' />" +
" </hbox>" +
" <hbox expand='item:1'>" +
" <checkbox name='chkAutoClick' text='Automatically click <Yes> for next page (eg ADF scanners)' />" +
" </hbox>" +
" <hbox>" +
" <hbox expand='item:2'>" +
" <text text='Gamma:' />" +
" <scrollbar name='gamma' min='-10' max='10' value='0' page='1'/>" +
" <text name='txtGamma' text='0 ' />" +
" </hbox>" +
" <hbox expand='item:2'>" +
" <text text='Contrast:' />" +
" <scrollbar name='contrast' min='0' max='50' value='20' page='1'/>" +
" <text name='txtContrast' text='20 ' />" +
" </hbox>" +
" </hbox>" +
" <scrollarea name='scrollarea' scrolling='vertical' innerheight='200' flags='#PB_ScrollArea_Flat'>" +
" <!-- <canvas name='canvas'/> -->" +
" </scrollarea>" +
" <hbox>" +
" <text name='lblPageCount' text='Pages: 0' flags='#PB_Text_Border' height='20'/>" +
" <text name='lblScanTime' text='Scanning speed: ?' flags='#PB_Text_Border'/>" +
" </hbox> " +
" </vbox>" +
"</window>"
Protected xm.i = CatchXML(#PB_Any, @XML, StringByteLength(XML), 0, #XmlEncoding)
If XMLStatus(xm.i) = #PB_XML_Success
Protected dialog.i = CreateDialog(#PB_Any)
If OpenXMLDialog(dialog.i, xm.i, "ScanToPDF")
With gui
\dialog = dialog.i
\window = DialogWindow(dialog.i)
\scan = DialogGadget(dialog.i, "scan")
\save = DialogGadget(dialog.i, "save")
\saveCrypt = DialogGadget(dialog.i, "saveCrypt")
\print = DialogGadget(dialog.i, "print")
\clear = DialogGadget(dialog.i, "clear")
\close = DialogGadget(dialog.i, "close")
\dpi = DialogGadget(dialog.i, "dpi")
\gamma = DialogGadget(dialog.i, "gamma")
\contrast = DialogGadget(dialog.i, "contrast")
\scanner = DialogGadget(dialog.i, "scanner")
\scanRefresh = DialogGadget(dialog.i, "scanRefresh")
\scrollArea = DialogGadget(dialog.i, "scrollarea")
\txtContrast = DialogGadget(dialog.i, "txtContrast")
\txtGamma = DialogGadget(dialog.i, "txtGamma")
\btnOptions = DialogGadget(dialog.i, "btnOptions")
\strOptions = DialogGadget(dialog.i, "strOptions")
\lblPageCount = DialogGadget(dialog.i, "lblPageCount")
\lblScanTime = DialogGadget(dialog.i, "lblScanTime")
\chkAutoClick = DialogGadget(dialog.i, "chkAutoClick")
GadgetToolTip(\scan, "Scan and add one or more pages")
GadgetToolTip(\save, "Save all pages as one PDF document")
GadgetToolTip(\saveCrypt, "Save all pages as encrypted PDF document with password")
GadgetToolTip(\print, "Output all pages to the standard printer")
GadgetToolTip(\clear, "Deletes all previous scanned pages")
GadgetToolTip(\close, "Close program")
GadgetToolTip(\scanRefresh, "Refresh the list of available scanners")
GadgetToolTip(\scanner, "The scanner to use for scanning")
GadgetToolTip(\strOptions, "This will get added to the 'scanimage' call for individual scanner options")
GadgetToolTip(\btnOptions, "Show individual options of the chosen scanner")
GadgetToolTip(\dpi, "Chose the DPI for the next scans")
EndWith
; create canvas
OpenGadgetList(gui\scrollArea)
gui\canvas = CanvasGadget(#PB_Any, 0, 0, 1000, 1000)
CloseGadgetList()
; fill dpi list
AddGadgetItem(gui\dpi, -1, "50 DPI")
AddGadgetItem(gui\dpi, -1, "75 DPI")
AddGadgetItem(gui\dpi, -1, "100 DPI")
AddGadgetItem(gui\dpi, -1, "150 DPI")
AddGadgetItem(gui\dpi, -1, "200 DPI")
AddGadgetItem(gui\dpi, -1, "300 DPI")
AddGadgetItem(gui\dpi, -1, "600 DPI")
AddGadgetItem(gui\dpi, -1, "1200 DPI")
; preview
FillPreview(Foldername.s)
RefreshDialog(dialog.i)
LoadSettings()
Repeat
Event = WaitWindowEvent(250)
If Event = #PB_Event_Gadget
evType.i = EventType()
evGadget.i = EventGadget()
If evGadget = gui\close And evType.i = #PB_EventType_LeftClick
Event = #PB_Event_CloseWindow
EndIf
If evGadget = gui\scan And evType.i = #PB_EventType_LeftClick
; prepare
If FileSize(Foldername.s) = -1
CreateDirectory(Foldername.s) ; create folder
EndIf
; ask number of pages
Protected Pages.i = Val(InputRequester("ScanToPDF", "How many pages do you like to add?", "1"))
If Pages.i > 0
; scan
Settings\DPI = Val(GetGadgetText(gui\dpi))
Settings\Device = GetSelectedScannerDevice()
Settings\Contrast = GetGadgetState(gui\contrast)
Settings\Options = GetGadgetText(gui\strOptions)
doScanning(Foldername.s, Settings, Pages.i)
; convert
doConvert(Foldername.s, Settings)
; preview
FillPreview(Foldername.s)
EndIf
EndIf
If evGadget = gui\clear And evType.i = #PB_EventType_LeftClick
If FileSize(Foldername.s) = -2
DeleteDirectory(Foldername.s, "*.*") ; cleanup
Else
CreateDirectory(Foldername.s) ; create folder
EndIf
; update preview
FillPreview(Foldername.s)
EndIf
If evGadget = gui\save Or evGadget = gui\saveCrypt
If CountPNMFiles(Foldername.s) < 1
Continue
EndIf
Protected d.s = gui\lastPath + FormatDate("%yyyy-%mm-%dd", Date()) + "_"
savePDFagain:
Protected Destination.s = SaveFileRequester("ScanToPDF - Save file to...", d.s, "PDF|*.pdf|*|*", 0)
If Destination.s <> ""
If LCase(Right(Destination.s, 4)) <> ".pdf"
Destination.s + ".pdf"
EndIf
Protected pwd.s = ""
If evGadget = gui\saveCrypt
pwd.s = Trim(InputRequester("ScanToPDF", "Please enter password for protection:", "", #PB_InputRequester_Password))
EndIf
If doPDF(Foldername.s, Destination.s, pwd.s) = #True
MessageRequester("ScanToPDF", "Document is saved.")
gui\lastPath = GetPathPart(Destination.s)
Else
MessageRequester("ScanToPDF", "Error during save. Try again...")
d.s = Destination.s
Goto savePDFagain
EndIf
EndIf
EndIf
If evGadget = gui\scanner
opt.s = GetSelectedScannerOptions()
SetGadgetText(gui\strOptions, opt.s)
EndIf
If evGadget = gui\strOptions
opt.s = GetGadgetText(gui\strOptions)
SetSelectedScannerOptions(opt.s)
EndIf
If evGadget = gui\contrast
LastModify.i = ElapsedMilliseconds()
SetGadgetText(gui\txtContrast, Str(GetGadgetState(gui\contrast)))
EndIf
If evGadget = gui\gamma
LastModify.i = ElapsedMilliseconds()
SetGadgetText(gui\txtGamma, Str(GetGadgetState(gui\gamma)))
EndIf
If evGadget = gui\scanRefresh
FillScannerlist(#True)
EndIf
If evGadget = gui\btnOptions
Settings\Device = GetSelectedScannerDevice()
ShowScannerOptions(Settings)
EndIf
If evGadget = gui\print
If CountPNMFiles(Foldername.s) < 1
Continue
EndIf
doPrint(Foldername.s)
EndIf
EndIf
If Event = #PB_Event_SizeWindow
LastSize.i = ElapsedMilliseconds()
EndIf
If LastSize.i > 0 And ElapsedMilliseconds() > LastSize.i + 500
; set scrollarea
SetGadgetAttribute(gui\scrollArea, #PB_ScrollArea_InnerHeight, GadgetHeight(gui\canvas))
; preview
FillPreview(Foldername.s)
LastSize.i = 0
EndIf
If LastModify.i > 0 And ElapsedMilliseconds() > LastModify.i + 1000
Settings\Contrast = GetGadgetState(gui\contrast)
Settings\Gamma = GetGadgetState(gui\gamma)
doConvert(Foldername.s, Settings)
; preview
FillPreview(Foldername.s)
LastModify.i = 0
EndIf
Until Event = #PB_Event_CloseWindow
Else
Debug "Dialog error: " + DialogError(dialog.i)
EndIf
Else
Debug "XML error: " + XMLError(xm) + " (Line: " + XMLErrorLine(xm) + ")"
EndIf
SaveSettings()
FreeDialog(gui\dialog)
EndProcedure
main()
Kukulkan