PDFs zusammenführen

Es gibt viele Wege, mehrere Dokumente in einem PDF zusammenzuführen. Eine davon ist im Kontextmenü des enaio®-Clients direkt und praktisch für alle User verfügbar.

In einem Kundenprojekt der letzten Tage gab es die Herausforderung, die Ausgabe mit Scripting-Mitteln des Rich-Clients zu beeinflussen. Dies kann z. B. sinnvoll sein, wenn die Reihenfolge angepasst werden soll, bestimmte Dokumente nicht oder anders exportiert werden soll etc.

Hier für spätere Referenz und interessierte kurz notiert, wie wir dies umgesetzt haben:

Der RichClient von enaio® unterstützt externe Anwendungen. Um hier keine kompilierte Anwendung hinterlegen zu müssen, ist diese komplett in VBScript notiert und nutzt die freundliche REST-API von enaio®.

Natürlich gäbe es hier viele weitere Lösungswege. An anderer Stelle haben wir beispielsweise spezielle Converter-Services eingebunden oder Export-APIs als Java- oder Python-Service entwickelt. Es kommt einfach immer darauf an, wo und in mit welchen Schwerpunkten eine Erweiterung zum Einsatz kommt. Und natürlich wäre es besonders schön, wenn dieser Kunde auch noch die vielen Vorteile von Embedded Documents for enaio® kennenlernt. :wink:

Konfiguration der externen Anwendung

  • Die externe Anwendung muss ohne Warten im Client konfiguriert werden, da sonst Zeile 6 CreateObject("Optimal_AS.Application") auf sich selbst wartet.
  • Die externe Anwendung muss bei einem 32-bit-Client die WScript.exe im SysWOW64-Verzeichnis nutzen.
    Konfiguration der externen Anwendung

Das Script

Option Explicit
Dim ArgObj: Set ArgObj = WScript.Arguments
Dim FSO: Set FSO = CreateObject("Scripting.FileSystemObject")
Dim WShell: Set WShell = CreateObject("Wscript.Shell")
Dim AX: Set AX = CreateObject("Optimal_AS.Application")
Dim DrtSession: Set DrtSession = AX.GetDrtSession()

Main(): Function Main()
	Dim IniFilePath: IniFilePath = ArgObj(0)
	Dim osParamFile: Set osParamFile = FSO.OpenTextFile(IniFilePath)
	Dim content: content = osParamFile.ReadAll
	osParamFile.Close
	FSO.DeleteFile IniFilePath, True
    Dim DocIDs: DocIDs = Split(content, vbCrLf)

	If UBound(DocIDs) = 0 Then
		MsgBox "Es wurde nur ein Dokument zur Zusamemnfassung übergeben.", 48, "Dokumente Zusammenfassen"
		Exit Function
	End If

    ' TODO: Hier können nun weitere Filter, Verbesserungen/Anpassungen an der übergebenen Dokumentliste implementiert werden.

    Dim i: For i = 0 To UBound(DocIDs)
        DocIDs(i) = Split(DocIDs(i), ",")(0)
    Next

	Dim result: Set result = SendMergeRequest(DocIDs)

	If result.status <> 200 Then
		MsgBox "Die Dokumente konnten nicht zusammgefasst werden:" & vbCrLf & vbCrLf & "Fehler " & result.status & " " & result.ResponseText, 48, "Dokumente Zusammenfassen"
		Exit Function
	End If

	Dim stream: Set stream = CreateObject("Adodb.Stream")
	Dim tempFilepath: tempFilepath = FSO.GetSpecialFolder(2) & "\" & fso.GetTempName & ".pdf"

	With stream
		.Type = 1
		.Open
		.Write result.responseBody
		.SaveToFile tempFilepath, 2
	End With
	WShell.Run tempFilepath
End Function 

Function ReadINI(FileContent, Section, Key) 'As String
	Dim RE: Set RE = New RegExp: RE.Pattern = "\[" & Section & "\][^\[\]]*\r\n" & Key & "=(.*)\r\n"
	Dim MC: Set MC = RE.Execute(FileContent)
	If MC.Count > 0 Then ReadINI = MC(0).SubMatches(0)
End Function

Function SendMergeRequest(DocIds)
	Dim o: Set o = CreateObject("MSXML2.XMLHTTP")
    Dim url: url = Replace(GetRegValue("Services\Gateway\API") & "/osrest/api/documentfiles/pdf?sessionguid=" & DrtSession.SessionGuid, "//osrest", "/osrest")
    Dim body: body = "{""pdfname"": ""merge.pdf"", ""ids"": [ " + Join(DocIDs, ", ") + " ]}"
	o.Open "POST", url, False
    o.SetRequestHeader "Content-Type", "application/json"
	o.Send body
	Set SendMergeRequest = o
End Function

Function GetRegValue(byVal Name)
	Dim Job: Set Job = DrtSession.CreateJob("krn", "REGetRegValue")
	Job.AddInParam "Name", Name, 1
	Job.AddInParam "Flags", "0", 2
	Job.Execute
	GetRegValue = job.GetOutParamString("Value")
End Function
  • An der Stelle 'TODO können nun weitere Filter, Verbesserungen/Anpassungen an der übergebenen Dokumentliste oder was auch immer im spezifischen Fall gewünscht ist, implementiert werden.
  • Die Methode GetRegValue wird genutzt, um die für den Client (hoffentlich korrekt konfigurierte und erreichbare) API-URL direkt vom enaio-Server zu erfahren.
  • Als Session-Token wird der API das der laufenden Client-Session übergeben. Dadurch stellt enaio sicher, dass User nur Dokumente zum PDF zusammenfassen können, auf welche sie auch zugreifen dürfen.

Demo

Aufruf im Client

3 „Gefällt mir“

Das ist echt auch ein cooler Weg! Ich hab solche Anforderungen bisher immer über Python umgesetzt, was auch gut funktioniert hat, aber ich werde im Hinterkopf behalten, dass es auch so ginge.

Ja, mit einem eigenen (Python-) Backend kann man halt noch mehr machen, z. B. Inhaltsverzeichnisse generieren, Paginieren etc.

Hallo zusammen,
ich habe versucht diese spannenden Funktion bei uns zu implementieren.
Leider läuft es immer auf eine Fehlermeldung raus:

Habt ihr vielleicht einen Lösungsansatz für mich?

Danke und beste Grüße,
Christian

Hallo @cschulze ,

ich war über den Fehler etwas erstaunt, aber es sieht so aus, als ob der enaio RichClient in den neueren Versionen am Ende der Datei einen zusätzlichen Zeilenumbruch hinzugefügt hat. Um dieses Problem zu umgehen, habe ich den Code von @rk etwas umgebaut:

Option Explicit
Dim ArgObj: Set ArgObj = WScript.Arguments
Dim FSO: Set FSO = CreateObject("Scripting.FileSystemObject")
Dim WShell: Set WShell = CreateObject("Wscript.Shell")
Dim AX: Set AX = CreateObject("Optimal_AS.Application")
Dim DrtSession: Set DrtSession = AX.GetDrtSession()

Main(): Function Main()
	Dim DocIDs: DocIDs = Array()
	Dim IniFilePath: IniFilePath = ArgObj(0)
	Dim osParamFile: Set osParamFile = FSO.OpenTextFile(IniFilePath)
	
    ' TODO: Hier können nun weitere Filter, Verbesserungen/Anpassungen an der übergebenen Dokumentliste implementiert werden.
	Do Until osParamFile.AtEndOfStream
      Dim Line: Line = osParamFile.ReadLine
      If Line <> "" Then
		ReDim Preserve DocIDs(UBound(DocIDs) + 1)
		DocIDs(UBound(DocIDs)) = Split(Line, ",")(0)
	  End If
    Loop
	osParamFile.Close
	FSO.DeleteFile IniFilePath, True
	
	If UBound(DocIDs) = 0 Then
		MsgBox "Es wurde nur ein Dokument zur Zusamemnfassung übergeben.", 48, "Dokumente Zusammenfassen"
		Exit Function
	End If

	Dim result: Set result = SendMergeRequest(DocIDs)

	If result.status <> 200 Then
		MsgBox "Die Dokumente konnten nicht zusammgefasst werden:" & vbCrLf & vbCrLf & "Fehler " & result.status & " " & result.ResponseText, 48, "Dokumente Zusammenfassen"
		Exit Function
	End If

	Dim stream: Set stream = CreateObject("Adodb.Stream")
	Dim tempFilepath: tempFilepath = FSO.GetSpecialFolder(2) & "\" & fso.GetTempName & ".pdf"

	With stream
		.Type = 1
		.Open
		.Write result.responseBody
		.SaveToFile tempFilepath, 2
	End With
	WShell.Run tempFilepath
End Function 

Function ReadINI(FileContent, Section, Key) 'As String
	Dim RE: Set RE = New RegExp: RE.Pattern = "\[" & Section & "\][^\[\]]*\r\n" & Key & "=(.*)\r\n"
	Dim MC: Set MC = RE.Execute(FileContent)
	If MC.Count > 0 Then ReadINI = MC(0).SubMatches(0)
End Function

Function SendMergeRequest(DocIds)
	Dim o: Set o = CreateObject("MSXML2.XMLHTTP")
    Dim url: url = Replace(GetRegValue("Services\Gateway\API") & "/osrest/api/documentfiles/pdf?sessionguid=" & DrtSession.SessionGuid, "//osrest", "/osrest")
    Dim body: body = "{""pdfname"": ""merge.pdf"", ""ids"": [ " + Join(DocIDs, ", ") + " ]}"
	o.Open "POST", url, False
    o.SetRequestHeader "Content-Type", "application/json"
	o.Send body
	Set SendMergeRequest = o
End Function

Function GetRegValue(byVal Name)
	Dim Job: Set Job = DrtSession.CreateJob("krn", "REGetRegValue")
	Job.AddInParam "Name", Name, 1
	Job.AddInParam "Flags", "0", 2
	Job.Execute
	GetRegValue = job.GetOutParamString("Value")
End Function

In Zeile 24 muß im Schleifenkopf hinter UBound(DocIDs) ein -1 noch geschrieben werden.

Dim i: For i = 0 To UBound(DocIDs)-1

Ich hatte den gleichen Fehler.

VG

2 „Gefällt mir“

Hi @uw

besten Dank für die schnelle Anpassung.
Jetzt läuft es einwandfrei!

Danke und viele Grüße aus Heinsberg,
Christian

Sehr gerne.

Danke auch an @Warnower für den Hinweis. Wusste doch, dass wir das Thema schon mal hatten :).
Die neue Version ist etwas sauberer.

Der Workaround mit Dim i: For i = 0 To UBound(DocIDs)-1 löst nicht das Thema mit der folgenden Meldung:

	If UBound(DocIDs) = 0 Then
		MsgBox "Es wurde nur ein Dokument zur Zusamemnfassung übergeben.", 48, "Dokumente Zusammenfassen"
		Exit Function
	End If