Dokumente Scannen und als PDF speichern

  • VB.NET

Es gibt 23 Antworten in diesem Thema. Der letzte Beitrag () ist von -Franky-.

    Dokumente Scannen und als PDF speichern

    Hallo zusammen.

    Da meine Anwendung immer mehr Form annimmt, wollte ich mich nun endlich mal dem Scannen von Dokumenten widmen.
    Wir nutzen im Unternehmen ausschließlich die Blatteinzugsscanner von Fujitsu - via Twain oder ISIS (wäre mir an der Stelle erstmal egal).

    Gibt es kostenfreie Möglichkeiten, dass in meiner Anwendung zu realisieren? Wenn ja, hat jemand schon Erfahrungen gemacht
    und kann etwas empfehlen?

    Die Dokumente (ein- oder mehrseitig) sollen erstmal als PDF in einem definierten Pfad gespeichert werden. Später kann das ggf. auch gerne mal in der Datenbank landen.

    Bin für jeden Vorschlag offen ;)
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    tragl schrieb:

    Bin für jeden Vorschlag offen
    Was soll denn das Programm können, was die Scanner-Software nicht kann?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    was die Scanner-Software nicht kann?

    nichts extra.

    Ich würde nur gerne direkt vom Programm aus Scannen, ohne dass die Software dazu anspringen muss. Die Dateien sollen dann anhand Pfad und Dateiname in der Datenbank zugeordnet werden.
    z.B. Krankmeldungen einscannen, die ich dann von meinem Urlaubsplaner aus aufrufen kann und kann einsehen -> ja, Krankmeldung ist vorhanden und kannst auf doppelklick dir anzeigen lassen :)

    Ist aber nur ein Beispiel - das würde ich an vielen Stellen gebrauchen können.
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    tragl schrieb:

    das würde ich an vielen Stellen gebrauchen können.
    Dann würde ich eher überlegen, das Scanner-Programm fernzusteuern als es nachzuerfinden.
    Andere Programme fernsteuern
    externe .Net-Programme auslesen und manipulieren
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Dann würde ich eher überlegen, das Scanner-Programm fernzusteuern als es nachzuerfinden.

    Ist sicherlich ein guter Ansatz, aber nicht das was ich gerne hätte :)

    Mir wär's echt lieb, dass ich das ganze direkt in der Anwendung verankere. Dazu brauche ich ja auch nur einen Bruchteil der Funktionen aus der Scanner-Software.
    Eine Einstellung, was Farbe und Auflösung betrifft wird gebraucht, sowie das Scannen von mehreren Seiten in eine PDF-Datei - das genügt im Grunde schon.

    Mit der erzeugten Datei kann ich dann ganz normal weiter arbeiten.
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:
    @tragl Das sind mal 3 (drei) disjunkte Teile:
    1. Hardware-Ansteuerung des Scanners, Empfang des Pixelbildes,
    2. Bildverarbeitung (Minimum: Medianfilter),
    3. Pixelbild als PDF speichern.
    (2) und (3) dürfte kein Problem sein.
    Wie sieht es bei Dir mit der Hardware-Ansteuerung aus?
    Da hilft es sich schlecht, weil dazu halt ein solch Scanner vorhanden sein sollte.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Da hilft es sich schlecht, weil dazu halt ein solch Scanner vorhanden sein sollte.

    ein solcher Scanner ist bei mir zu Hause, sowie auch auf der Firma vorhanden (an beiden Plätzen programmiere ich ja nebenbei meine Anwendung) - das sollte also erstmal kein
    Problem darstellen.

    Wenn ich richtig liege, müsste ich ja aus meiner Anwendung raus den Twain-Treiber ansprechen - der regelt normalerweise den Rest (zumindest das Ansprechen der Hardware etc.).
    Ich hab sowas nur noch nie gemacht und absolut keine Planung wie sowas von Statten geht. Daher dacht ich an z.B. ein Nuget-Paket (deshalb die Frage ob irgendwer schonmal Erfahrung mit was gemacht hat) - am liebsten
    natürlich so lizenziert, dass ich das in meiner Anwendung nutzen kann / darf.

    ich hatte da mal was namens sarafftwain gesehen - ist aber a) in C# geschrieben und b) alles auf russich kommentiert, das wird mich ja vom Lerneffekt her garnix bringen.
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    tragl schrieb:

    ist aber a) in C# geschrieben und b) alles auf russich kommentiert, das wird mich ja vom Lerneffekt her garnix bringen.
    Wo ist das Problem?
    C# kannst Du mit vielen Komvertern konvertieren, einer ist in meiner Signatur.
    Russisch kannst Du mit dem Google-Translator übersetzen, das funktioniert ganz prima (ich hab mal ein chinesisches Programm so bearbeitet ;) ).
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Vielleicht hilft Dir das, hab ich einmal geschrieben. Ist sicher nicht ganz "konform", aber es funktioniert bei mir.

    VB.NET-Quellcode

    1. Option Strict On
    2. Imports System.IO
    3. Imports System.Drawing.Imaging
    4. Module mdl_scanner
    5. ''' <summary>
    6. ''' 1. zeigt den Scannerdialog zur Auswahl des Scanners
    7. ''' 2. Scannt in der angegebenen Auflösung
    8. ''' 3. Speichert die Datei an den angegebenen Zielpfad als jpg-Datei mit angegebener Kompression
    9. ''' </summary>
    10. ''' <param name="WIAZieldatei"></param>
    11. ''' <returns></returns>
    12. ''' <remarks></remarks>
    13. Public Function WIAScan(WIAZieldatei As String, JPEGCompression As Long, Optional MaxBildBreite As Integer = 1200) As Boolean
    14. Try
    15. Dim WIACommDlg As New WIA.CommonDialog()
    16. Dim WIAImage As WIA.ImageFile
    17. WIAImage = WIACommDlg.ShowAcquireImage(WIA.WiaDeviceType.ScannerDeviceType, WIA.WiaImageIntent.UnspecifiedIntent, WIA.WiaImageBias.MinimizeSize, WIA.FormatID.wiaFormatBMP, True, True, False) 'wiaFormatJPEG
    18. If WIAImage IsNot Nothing Then
    19. Dim BytePuffer As Byte() = CType(WIAImage.FileData.BinaryData, Byte())
    20. Dim MemStream As MemoryStream = New MemoryStream(BytePuffer)
    21. Dim BildZwischenspeicher As Image = Image.FromStream(MemStream)
    22. Dim BildBreite As Integer = BildZwischenspeicher.Width
    23. Dim BildHoehe As Integer = BildZwischenspeicher.Height
    24. Dim Ratio As Double = 0
    25. 'auf MaxBildBreite verkleinern
    26. If BildZwischenspeicher.Width > MaxBildBreite Then
    27. BildBreite = MaxBildBreite
    28. Ratio = 100 / BildZwischenspeicher.Width * BildBreite
    29. BildHoehe = CInt(BildZwischenspeicher.Height / 100 * Ratio)
    30. End If
    31. Dim KleinesBild As New Bitmap(BildZwischenspeicher, BildBreite, BildHoehe)
    32. If File.Exists(WIAZieldatei) Then File.Delete(WIAZieldatei)
    33. SaveJpeg(WIAZieldatei, KleinesBild, JPEGCompression)
    34. Return True
    35. Else
    36. Return False
    37. End If
    38. Catch ex As Exception
    39. If ex.Message.Contains("0x80210015") Then
    40. MessageBox.Show("Kein WIA-fähiger Scanner auf Ihrem System gefunden!", "Scannen nicht möglich", MessageBoxButtons.OK, MessageBoxIcon.Stop)
    41. Else
    42. MessageBox.Show("Fehler beim Scannen: " & vbCrLf & ex.Message & vbCrLf & Err.Number.ToString, "Allgemeiner Scannerfeler", MessageBoxButtons.OK, MessageBoxIcon.Stop)
    43. End If
    44. Return False
    45. End Try
    46. Return False
    47. End Function
    48. ''' <summary>
    49. ''' Speichert ein Image als .JPEG Datei mit angegebener Qualität von 0 bis 100 (100=beste Qualität)
    50. ''' </summary>
    51. ''' <param name="path"></param>
    52. ''' <param name="img"></param>
    53. ''' <param name="quality"></param>
    54. ''' <remarks></remarks>
    55. Private Sub SaveJpeg(ByVal path As String, ByVal img As Image, ByVal quality As Long)
    56. Dim qualityParam As New EncoderParameter(Encoder.Quality, quality)
    57. Dim jpegCodec As ImageCodecInfo = GetEncoderInfo("image/jpeg")
    58. Dim encoderParams As New EncoderParameters(1)
    59. encoderParams.Param(0) = qualityParam
    60. img.Save(path, jpegCodec, encoderParams)
    61. End Sub
    62. Private Function GetEncoderInfo(ByVal mimeType As String) As ImageCodecInfo
    63. Dim codecs As ImageCodecInfo() = ImageCodecInfo.GetImageEncoders()
    64. For i As Integer = 0 To codecs.Length - 1
    65. If (codecs(i).MimeType = mimeType) Then
    66. Return codecs(i)
    67. End If
    68. Next i
    69. Return Nothing
    70. End Function
    71. End Module
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at
    Siehe Screenshot
    Bilder
    • Unbenannt-1.jpg

      49,83 kB, 939×197, 212 mal angesehen
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at
    @RodFromGermany der c#-Converter packt leider nicht alles und ist mühselig -> am Besten alles einzeln konvertieren.
    Saraff.Twain scheint ein Großprojekt zu sein (2 oder 3 files mit über 3000 zeilen :S ) und das russisch auf deutsch übersetzen nervt echt - ABER: ich bin dran!

    @dive26: für jpeg und eine Seite funzt das super! Danke schonmal. Leider gehen da nicht mehrere Seiten hintereinander und ich muss leider vor jedem Scan die Settings vom Scanner bestätigen..
    Das soll voreingestellt werden und dann soll das Programm quasi die Scans (auch mehrseitig) einfach "durchfeuern"

    User klickt an "ja, ich will scannen" und dann soll das fluppen - höchstens einmalig den Scanner auswählen, falls keiner bereits hinterlegt ist. Wir arbeiten alle mit Fujitsu Blatteinzugs-Scannern, hier gibt's ab und an
    mal ne neuere oder ältere Modellnummer - vom Treiber her arbeiten die eigentlich alle gleich.
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:
    @RodFromGermany Ich hab das mit Saraff.Twain mal verworfen und mich an TwainDotNet bedient - das erfüllt meine Anforderungen :)
    Ich kann nun einfach durchfeuern und die gescannten Seiten (Bilder) werden dank PdfSharp in ein PDF-Dokument gespeichert.
    Einziges Problem aktuell:
    Die größe der PDF bei 2 Seiten beträgt über 3MB - das geht auf Dauer in die Hose.

    Jetzt hab ich mich ein bisschen am Code von @dive26 bedient - weiß aber grad nicht, wie ich das Quellbild (Bitmap) als Compressed JPEG programmintern bereitstelle.
    Ich will das Ganze ohne Zwischenspeichern in Dateien abhandeln.
    Hier mal mein Code: habt ihr einen Tipp für mich? (Zeile 94 + 110)

    Spoiler anzeigen

    Visual Basic-Quellcode

    1. Imports TwainDotNet, TwainDotNet.TwainNative, TwainDotNet.WinFroms
    2. Imports PdfSharp.Pdf
    3. Imports System.Drawing.Imaging
    4. Public Class dlgTwainTest
    5. Private _areaSettings As New AreaSettings(Units.Centimeters, 0.1F, 5.7F, 0.1F + 2.6F, 5.7F + 2.6F)
    6. Private _twain As Twain
    7. Private _settings As ScanSettings
    8. Private _images As List(Of Bitmap)
    9. #Region "Form"
    10. Public Sub New()
    11. InitializeComponent()
    12. Me.KeyPreview = True
    13. Me.RegisterDlgKeyHandler
    14. _twain = New Twain(New WinFormsWindowMessageHook(Me))
    15. ' Add a handler to grab each image as it comes off the scanner
    16. AddHandler _twain.TransferImage, Sub(sender As Object, args As TwainDotNet.TransferImageEventArgs)
    17. If (Not (args.Image Is Nothing)) Then
    18. PictureBox1.Image = args.Image
    19. _images.Add(args.Image)
    20. lblWidth.Text = $"Breite: {PictureBox1.Image.Width}"
    21. lblHeight.Text = $"Höhe: {PictureBox1.Image.Height}"
    22. End If
    23. End Sub
    24. ' Re-enable the form after scanning completes
    25. AddHandler _twain.ScanningComplete, Sub(sender As Object, e As TwainDotNet.ScanningCompleteEventArgs)
    26. Enabled = True
    27. End Sub
    28. PictureBox1.SizeMode = PictureBoxSizeMode.StretchImage
    29. End Sub
    30. #End Region 'Form
    31. #Region "Clickables"
    32. Private Sub btn_Click(sender As Object, e As EventArgs) Handles btnChooseScanner.Click,
    33. btnScan.Click,
    34. btnSave.Click,
    35. btnDiagnostics.Click
    36. Select Case True
    37. Case sender Is btnChooseScanner : _twain.SelectSource()
    38. Case sender Is btnScan : scan() : lblCountPages.Text = $"Anzahl Seiten: {_images.Count}"
    39. Case sender Is btnSave : savePdf()
    40. Case sender Is btnDiagnostics : Dim diagnostics As New Diagnostics(New WinFormsWindowMessageHook(Me))
    41. End Select
    42. End Sub
    43. #End Region 'Clickables
    44. Private Sub scan()
    45. Enabled = False 'Form disablen bis der Scan durchgelaufen ist
    46. _images = New List(Of Bitmap) 'alle Images vom letzten Lauf löschen
    47. 'aktuelle Settings lesen
    48. 'TODO: Settings pro User in DB persistieren
    49. _settings = New ScanSettings()
    50. _settings.UseDocumentFeeder = cbUseADF.Checked
    51. _settings.ShowTwainUI = cbUI.Checked
    52. _settings.ShowProgressIndicatorUI = cbShowProgress.Checked
    53. _settings.UseDuplex = cbDuplex.Checked
    54. _settings.Resolution = If(cbBlackWhite.Checked, ResolutionSettings.Fax, ResolutionSettings.ColourPhotocopier)
    55. _settings.Area = If(Not cbGrabArea.Checked, Nothing, _areaSettings)
    56. _settings.ShouldTransferAllPages = True
    57. _settings.Rotation = New RotationSettings With
    58. {
    59. .AutomaticRotate = cbAutorotate.Checked,
    60. .AutomaticBorderDetection = cbAutodetectBorder.Checked
    61. }
    62. Try
    63. _twain.StartScanning(_settings)
    64. Catch ex As TwainException
    65. MessageBox.Show(ex.Message)
    66. Enabled = True
    67. End Try
    68. End Sub
    69. Private Sub save()
    70. 'hier wird nur das zuletzt gescannte Bild gespeichert
    71. If (Not (PictureBox1.Image Is Nothing)) Then
    72. Dim sfd As New SaveFileDialog()
    73. If sfd.ShowDialog() = DialogResult.OK Then
    74. PictureBox1.Image.Save(sfd.FileName)
    75. End If
    76. End If
    77. End Sub
    78. Private Sub savePdf()
    79. If _images.Count = 0 Then Return
    80. Dim pdf As New PdfDocument
    81. For Each img In _images
    82. Dim test = img.RawFormat.Guid.ToString
    83. 'hier muss sich das komprimierte Bild geholt werden
    84. pdf.Pages.Add(New PdfPage)
    85. Dim page = pdf.Pages(pdf.PageCount - 1)
    86. Dim gfx = PdfSharp.Drawing.XGraphics.FromPdfPage(page)
    87. Dim xImage = PdfSharp.Drawing.XImage.FromGdiPlusImage(img)
    88. gfx.DrawImage(xImage, 0, 0, page.Width, page.Height)
    89. Next
    90. Dim file = saveAs("PDF-Datei|*.pdf")
    91. pdf.Save(file)
    92. End Sub
    93. Private Function GetJpegImage(bmp As Bitmap) As Image '?
    94. Dim qualityParam As New EncoderParameter(Encoder.Quality, 100)
    95. Dim jpegCodec As ImageCodecInfo = GetEncoderInfo("image/jpeg")
    96. Dim encoderParams As New EncoderParameters(1)
    97. encoderParams.Param(0) = qualityParam
    98. 'hier: umwandeln der Source (bmp) zu komprimierten Bild
    99. 'img.Save(Path, jpegCodec, encoderParams)
    100. End Function
    101. Private Function GetEncoderInfo(mimeType As String) As ImageCodecInfo
    102. Dim codecs As ImageCodecInfo() = ImageCodecInfo.GetImageEncoders()
    103. For i As Integer = 0 To codecs.Length - 1
    104. If (codecs(i).MimeType = mimeType) Then
    105. Return codecs(i)
    106. End If
    107. Next i
    108. Return Nothing
    109. End Function
    110. End Class


    EDIT: Hab's:

    VB.NET-Quellcode

    1. Private Function GetJpegImage(bmp As Bitmap) As MemoryStream
    2. Dim qualityParam As New EncoderParameter(Encoder.Quality, 70)
    3. Dim jpegCodec As ImageCodecInfo = GetEncoderInfo("image/jpeg")
    4. Dim encoderParams As New EncoderParameters(1)
    5. encoderParams.Param(0) = qualityParam
    6. Dim s As New MemoryStream
    7. bmp.Save(s, jpegCodec, encoderParams)
    8. Return s
    9. End Function

    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „tragl“ ()

    Das komprimieren der Scan Images ist unnötig, wenn Du das dann wieder in PDF Sharp als PDF unkomprimiert speicherst. Warum legst Du die Seiten nicht gleich als komprimierte jpg Dateien ab?

    Alles was mit PDF gut werden soll muss man entweder manuell machen (nur ein Promille aller Programmierer haben das Wissen dazu) oder mit sauteuren Komponenten.

    Ich habe mir zum Thema PDF sehr viel angesehen. PDFSharp ist dabei das datenmässig aufgeblasenste Tool das aber nicht viel kann. Spire PDF kann mehr, hakt aber auch an manchen stellen. Die anderen waren zwar gut, aber mit 3000 Dollar nicht mehr in meinem Preisrahmen.

    Images in PDF lassen sich schon komprimieren, bei PDFSharp:

    VB.NET-Quellcode

    1. document.CustomValues.CompressionMode = PdfCustomValueCompressionMode.Compressed;
    2. document.Options.FlateEncodeMode = PdfFlateEncodeMode.BestCompression;



    Ich verwende VPE (Virtual Print engine):
    VPE documents can be written compressed, this is the default now (see property „Compression“). The new compression makes VPE documents about 60 - 80% smaller.

    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „dive26“ ()

    Vollzitat eines Vorposts an dieser Stelle entfernt ~VaporiZed

    Hi!
    Ich hab den Code mal ausprobiert, da ich mich auch gerade mit der Scannerei beschäftige. Den Verweis auf WIA hab ich hinzugefügt, aber ich bekomme immer die Fehlermeldung
    Creating an instance of the COM component with CLSID {850D1D11-70F3-4BE5-9A11-77AA6B2BB201} using CoCreateInstanceFromApp failed due to the following error: 80040154 Klasse nicht registriert (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)). Please make sure your COM Object is in the allowed list of CoCreateInstanceFromApp.

    Hab schon die wia-dll's neu registriert, versucht die App als x86 und x64 zu starten aber die Meldung bleibt die gleiche.
    Hat irgendjemand eine Idee, was ich noch versuchen könnte?

    Danke und LG

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VaporiZed“ ()

    @matthias40 Siehe Post Nr. 11
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at
    Hab ich eingebunden, das hab ich gemeint mit den Verweis zu WIA hab ich hinzugefügt. Oder hab ich irgendetwas übersehen?
    EDIT: Die CLSID ist in der Registry vorhanden, also registriert sollte alles richtig sein
    Bilder
    • 2022-09-28 (3).png

      14,08 kB, 1.299×84, 101 mal angesehen

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „matthias40“ ()

    @matthias40 Könnte es sein das wenn Du im Projektmappenexplorer -> Verweise -> WIA anklickst das unten Eigenschaften -> Interoptypen einbetten auf True steht? Ich bin der Meinung das der auf False stehen müsste.
    Mfg -Franky-