It is currently Thu Nov 23, 2017 11:56 am

All times are UTC + 1 hour




Post new topic Reply to topic  [ 14 posts ] 
Author Message
 Post subject: Scan to PDF tool for Linux (only Linux)
PostPosted: Tue Aug 25, 2015 7:35 am 
Offline
Addict
Addict
User avatar

Joined: Mon Jun 06, 2005 2:35 pm
Posts: 1067
Location: germany
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):
Code:
sudo apt-get update
sudo apt-get install sane
sudo apt-get install sane-utils
sudo apt-get install imagemagick

You also need at least one SANE compatible scanner connected to your PC (http://www.sane-project.org/sane-mfgs.html).

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

The source (compile using PB 5.24 or higher):
Code:
; ScanToPDF for Linux
; The source is free to use in any non-commercial or commercial project.
; (w) 2015-2017 by V. Schmid
; PureBasic 5.45 LTS x64 (Linux only!)

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
  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
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(13)
      EndIf
    Wend
    CloseProgram(proc.i)
  EndIf
  MessageRequester("Scanner options", lst.s)
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
      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
    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)
  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
  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:6' spacing='10'>" +
         "    <hbox height='50'>" +
         "      <button name='scan' text='Scan Page(s)' />" +
         "      <button name='save' text='Save PDF' />" +
         "      <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>" +
         "      <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")
        \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")
       
        GadgetToolTip(\scan, "Scan and add one or more pages")
        GadgetToolTip(\save, "Save all pages as one PDF document")
        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
            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
              If doPDF(Foldername.s, Destination.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()

Best,

Kukulkan


Last edited by Kukulkan on Mon Sep 18, 2017 11:39 am, edited 13 times in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: Scan to PDF tool for Linux (only Linux)
PostPosted: Tue Sep 01, 2015 8:25 am 
Offline
Addict
Addict
User avatar

Joined: Mon Jun 06, 2005 2:35 pm
Posts: 1067
Location: germany
I just updated the source in the first post. It now also makes sure that the display order and the order in generated PDF's is always the same like the scanning order.


Top
 Profile  
Reply with quote  
 Post subject: Re: Scan to PDF tool for Linux (only Linux)
PostPosted: Tue Sep 08, 2015 4:32 pm 
Offline
Addict
Addict
User avatar

Joined: Mon Jun 06, 2005 2:35 pm
Posts: 1067
Location: germany
I just updated the code in the first post. Simply some optical enhancements (paper-shadow, page number).

Sadly, no Linux user seems to use or test it. So, I'm the only lonely Linux user who needs to scan to PDF :-)


Top
 Profile  
Reply with quote  
 Post subject: Re: Scan to PDF tool for Linux (only Linux)
PostPosted: Thu Oct 29, 2015 11:50 am 
Offline
Addict
Addict
User avatar

Joined: Mon Jun 06, 2005 2:35 pm
Posts: 1067
Location: germany
I just updated the source in the first post:

- Fixed issue saving PDF to a path containing space
- Enhanced scanner compatibility
- Remember last used save-folder


Top
 Profile  
Reply with quote  
 Post subject: Re: Scan to PDF tool for Linux (only Linux)
PostPosted: Tue Nov 03, 2015 4:27 pm 
Offline
Addict
Addict
User avatar

Joined: Mon Jun 06, 2005 2:35 pm
Posts: 1067
Location: germany
Just updated the first post again:

[2015-11-03: Added field for optional scanner options and button to show such scanner options]


Top
 Profile  
Reply with quote  
 Post subject: Re: Scan to PDF tool for Linux (only Linux)
PostPosted: Fri Nov 06, 2015 9:38 am 
Offline
Addict
Addict
User avatar

Joined: Mon Jun 06, 2005 2:35 pm
Posts: 1067
Location: germany
Another update:

[2015-11-06: Fixed wrong date format in default filename. Prevent error if 'save' is clicked while no image is available]


Top
 Profile  
Reply with quote  
 Post subject: Re: Scan to PDF tool for Linux (only Linux)
PostPosted: Fri Nov 06, 2015 7:36 pm 
Offline
Always Here
Always Here
User avatar

Joined: Thu Jun 24, 2004 2:44 pm
Posts: 5726
Location: Berlin - Germany
Kukulkan wrote:
Sadly, no Linux user seems to use or test it. So, I'm the only lonely Linux user who needs to scan to PDF :-)

I need it, but i can't use it :cry:
I don't use sane or can use sane, i use scangearmp (canon) and this don't work with your code.

Sorry, but a nice app!

_________________
PureBasic 5.61 | SpiderBasic 2.10 | Windows 10 Pro (x64) | Linux Mint 18.2 (x64)
I would like to change the world, but God does not release the source code.
Image


Top
 Profile  
Reply with quote  
 Post subject: Re: Scan to PDF tool for Linux (only Linux)
PostPosted: Mon Nov 09, 2015 8:49 am 
Offline
Addict
Addict
User avatar

Joined: Mon Jun 06, 2005 2:35 pm
Posts: 1067
Location: germany
Hello ts-soft,

Sadly, I found no information about scanimagemp comandline options. What options does it offer? Is it only supporting Canon scanners?

Kukulkan


Top
 Profile  
Reply with quote  
 Post subject: Re: Scan to PDF tool for Linux (only Linux)
PostPosted: Tue Nov 17, 2015 9:11 am 
Offline
Addict
Addict
User avatar

Joined: Mon Jun 06, 2005 2:35 pm
Posts: 1067
Location: germany
Another update:

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.


Top
 Profile  
Reply with quote  
 Post subject: Re: Scan to PDF tool for Linux (only Linux)
PostPosted: Mon Jan 11, 2016 3:04 pm 
Offline
Addict
Addict
User avatar

Joined: Mon Jun 06, 2005 2:35 pm
Posts: 1067
Location: germany
Another update:

2016-01-11: Fixed wrong page order and font size for page numbering if there are more than 9 pages.


Top
 Profile  
Reply with quote  
 Post subject: Re: Scan to PDF tool for Linux (only Linux)
PostPosted: Tue Feb 09, 2016 4:46 pm 
Offline
Addict
Addict
User avatar

Joined: Mon Jun 06, 2005 2:35 pm
Posts: 1067
Location: germany
Another update:

2016-02-09: Added displaying some setup information on first start to help people to get it running. Some minor fixes.


Top
 Profile  
Reply with quote  
 Post subject: Re: Scan to PDF tool for Linux (only Linux)
PostPosted: Mon Aug 01, 2016 3:43 pm 
Offline
Addict
Addict
User avatar

Joined: Mon Jun 06, 2005 2:35 pm
Posts: 1067
Location: germany
Update:

2016-08-01: Now showing scan progress (learning scan time to be more accurate)


Top
 Profile  
Reply with quote  
 Post subject: Re: Scan to PDF tool for Linux (only Linux)
PostPosted: Wed Sep 28, 2016 8:16 am 
Offline
Addict
Addict
User avatar

Joined: Mon Jun 06, 2005 2:35 pm
Posts: 1067
Location: germany
Update:

2016-09-28: Main window is now remembering its position and size for the next start


Top
 Profile  
Reply with quote  
 Post subject: Re: Scan to PDF tool for Linux (only Linux)
PostPosted: Mon Sep 18, 2017 11:42 am 
Offline
Addict
Addict
User avatar

Joined: Mon Jun 06, 2005 2:35 pm
Posts: 1067
Location: germany
Update:

2017-09-18: The scanimage options are now individually stored for every scanner in the list. So it is more easy to switch between multiple scanners and their settings. Added generic scanner entry.

Important: Settings of a scanner are lost if the scanner is removed from the system (even temporarily) and the "refresh list" button is pressed! So make sure that all your scanners are attached if you click this button.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 14 posts ] 

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  

 


Powered by phpBB © 2008 phpBB Group
subSilver+ theme by Canver Software, sponsor Sanal Modifiye