1. John Vanleeuwe
  2. PowerBuilder
  3. Tuesday, 11 February 2020 10:55 AM UTC

Hi guys,

 

Since PB doens't yet support merging multiple PDFs files into 1 , we need to roll our own solution.

 

I have created a function , that takes 2 blobs as parms , i read them in a blob and just for testing purposes i write them again in the same folder. Both PDF files are written and can be opened without any problem.

 

My question is how do i concatenate 2 blobs into one ?

 

Can i just write ?

blob_full = blob_full + blob_file ?

 

I have tried this , but when i write my blob_full variable with the same function as i did with the 2 input files, i end up having a corrupt file which i cant open, but does have a valid filesize...

 

 

 

TIA

John

 

 

 

 

 

René Ullrich Accepted Answer Pending Moderation
  1. Tuesday, 11 February 2020 11:04 AM UTC
  2. PowerBuilder
  3. # 1

Hi John,

Yes, you can concatenate two blobs in this way. But you can't concatenate two pdf files to get a valid PDF file!

Look for external software or a library like Amyuni that supports pdf merging.

HTH,

René

Comment
There are no comments made yet.
John Vanleeuwe Accepted Answer Pending Moderation
  1. Tuesday, 11 February 2020 11:09 AM UTC
  2. PowerBuilder
  3. # 2

Vielen dank Rene !  External solution is out of the question for such a basic request IMHO.

any forecast in PBs roadmap ?

 

tia

John

 

Comment
  1. Brad Mettee
  2. Tuesday, 11 February 2020 14:40 PM UTC
Merging PDF files isn't as simple as concatenating files. There are binary markers in the PDF (descriptors, pointers, etc.) that need to be added/updated/moved/removed. Unless you read up on the PDF specs and write the functionality yourself, directly in PB, you can't do what you're asking without 3rd party tools (dll, ocx, exe). This is not a failing of PB, nor something that will likely be added. It's the nature of specialized file formats.
  1. Helpful
  1. John Vanleeuwe
  2. Tuesday, 11 February 2020 14:56 PM UTC
Thanks Brad. I agree its not a failing of PB , but i do disagree and think they should add it on their roadmap. To be honest , i was under the impression that i read somewhere they already did put this on the roadmap :)



  1. Helpful
There are no comments made yet.
Michael Kramer Accepted Answer Pending Moderation
  1. Tuesday, 11 February 2020 14:51 PM UTC
  2. PowerBuilder
  3. # 3

Hi John,

PDF like most file formats has a structure like <Intro><Actual-Content><Outro> I found this web page including schematic of PDF format. Therefore simple concatenation does not suffice.

BLOB #A PDF #B PDF Combo PDF
Content #A header
#A body
#A table
#A trailer
#B header
#B body
#B table
#B trailer

Combo header
Combo body = {#A; #B}
Combo table = {#A; #B}
Combo trailer = {#A; #B}

So you need code to split each PDF BLOB's content apart into these major segments. Then merge them segment by segment. To merge each segment you may even need to split the segment into sub segments; then merge those sub segments.

It is great that PDF file format is publicly specified and standardized so it is possible to create merge functionality without access to black magic. However, it is more complicated than simply appending one blob to another (unfortunately).

I don't know if anyone else in the PB community has created a PDF merge and is ready to share; nor potential cost; nor potential legal restrictions on usage in this world of differing sanctions across jurisdictions.

Sorry, /Michael

Comment
There are no comments made yet.
John Fauss Accepted Answer Pending Moderation
  1. Tuesday, 11 February 2020 15:02 PM UTC
  2. PowerBuilder
  3. # 4

Hi, John -

I think your premise that two concatenated files each holding a self-contained entity can or should produce a single file containing a single entity is incorrect.

While this may be feasible for some simple data types (strings, for example) and files containing simple text (e.g., Notepad-like .txt files), the information contained in many kinds of files cannot be physically concatenated while preserving meaning. Although you can physically append the bytes comprising one file to another, it's the interpretation of the file contents that is key. The corrupt file you produced illustrates this perfectly.

The results would be the same if you tried this with Word documents, Excel spreadsheets, compiled executable programs, PowerBuilder libraries, etc.

Regards, John

Comment
  1. Michael Kramer
  2. Tuesday, 11 February 2020 15:27 PM UTC
Even text files containing 3-byte encoding marker as <intro> can't be directly concatenated. You have to translate each to "final" encoding; then concatenate without encoding markers; and finally prefix with correct encoding marker.
  1. Helpful
  1. John Fauss
  2. Tuesday, 11 February 2020 17:24 PM UTC
You are correct, Michael. Good point! The information in even so-called "simple" files contains context that has to be interpreted and understood in order to have meaning, which reinforces the point I attempted to make. Thanks for clarifying!
  1. Helpful
There are no comments made yet.
Shenn Sellers Accepted Answer Pending Moderation
  1. Tuesday, 11 February 2020 17:37 PM UTC
  2. PowerBuilder
  3. # 5

You can do this for free (using PDFs, not Blobs) using the PDFtk control.

https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/

 

long ll_row
long ll_rows
string ls_pdf_type
string ls_file
string ls_file_list
string ls_run
int li_seqnum
int li_filenum
blob b_pdf
boolean lbl_have_pdfs = false

SetPointer(HourGlass!)

//Checks if the Directory Exists
if (DirectoryExists("C:\_APPS\HazardousWaste\CombinedPDFs")) then

//Populates LB with contents of directory
lb_pdf.DirList("C:\_APPS\HazardousWaste\CombinedPDFs", 0)

//Total # of Items
ll_rows = lb_pdf.TotalItems()

//Removes all files in the directory
for ll_row = 1 to ll_rows
lb_pdf.SelectItem(ll_row)
lb_pdf.DirSelect(ls_file)
FileDelete(ls_file)
next

else

//Directory doesn't exist, so create it
CreateDirectory("C:\_APPS\HazardousWaste\CombinedPDFs")

end if

//Cursor to gather all the addtional PDFs for the Incident
declare pdf cursor for
select pdf_type, seq_num
from hwp_pdfs
where incident_num = :s_email.incident_num
order by pdf_type, seq_num;

//Open cursor
open pdf;

//Gets first row of data
fetch pdf into :ls_pdf_type, :li_seqnum;

//Loops through the rows
do while(SQLCA.SQLCode = 0)

//Gets the PDF
selectblob pdf_file
into :b_pdf
from hwp_pdfs
where incident_num = :s_email.incident_num
and pdf_type = :ls_pdf_type
and seq_num = :li_seqnum;

//Open a file for writing
li_filenum = FileOpen("C:\_APPS\HazardousWaste\CombinedPDFs\" + ls_pdf_type + string(li_seqnum) + ".pdf", StreamMode!, Write!)

//Saves the PDF as a file
FileWriteEx(li_filenum, b_pdf)

//Closes file
FileClose(li_filenum)

//Changes flag
lbl_have_pdfs = true

//Gets the next row of data
fetch pdf into :ls_pdf_type, :li_seqnum;

loop

//Close cursor
close pdf;

//We have some PDFs
if (lbl_have_pdfs) then

//Fills LB with contents of directory
lb_pdf.DirList("C:\_APPS\HazardousWaste\CombinedPDFs", 0)

//Gets the # of files
ll_rows = lb_pdf.TotalItems()

//Loops through all the files and creates a File List
for ll_row = 1 to ll_rows
lb_pdf.SelectItem(ll_row)
lb_pdf.DirSelect(ls_file)
if (ll_row = 1) then
ls_file_list = "C:\_APPS\HazardousWaste\CombinedPDFs\" + ls_file
else
ls_file_list += " " + "C:\_APPS\HazardousWaste\CombinedPDFs\" + ls_file
end if
next

//Runs the tool to combine the PDFs
ls_run = 'C:\_APPS\HazardousWaste\pdftk "\\Waste-16FS01\ENVR$\Waste Inspection\Incident Reports\PDFs\Incident_' + string(s_email.incident_num) + '.pdf" ' &
+ ls_file_list + ' cat output ' + 'C:\_APPS\HazardousWaste\CombinedPDFs\Incident_' + string(s_email.incident_num) + 'Combined.pdf'
inv_run.of_run(ls_run, Normal!)

//Specify the new PDF to email
s_email.alternate_pdf_path = "C:\_APPS\HazardousWaste\CombinedPDFs\"
s_email.alternate_pdf_file = "Incident_" + string(s_email.incident_num) + "Combined.pdf"

end if

Comment
  1. Michael Kramer
  2. Tuesday, 11 February 2020 20:13 PM UTC
Definite vote from me! :-)
  1. Helpful
  1. Miguel Leeuwe
  2. Wednesday, 12 February 2020 02:52 AM UTC
Great code Sehn, thanks for that.

For those wondering what "inv_run" is, my guess is that you've used Roland's "RunAndWait" code that can be downloaded from topwizprogramming.com.

regards,

miguelL
  1. Helpful
  1. Shenn Sellers
  2. Thursday, 13 February 2020 18:35 PM UTC
Yes, "inv_run" is using Roland's "RunAndWait" code.
  1. Helpful
There are no comments made yet.
  • Page :
  • 1


There are no replies made for this question yet.
However, you are not allowed to reply to this question.