Homelab, Linux, JS & ABAP (~˘▾˘)~
 

[nodejs] Merge PDFs using pdf-lib

https://github.com/Hopding/pdf-lib

const { PDFDocument } = require('pdf-lib')

// files = [{ fileName: 'test1.pdf, content: arraybuffer },{ fileName: 'test2.pdf, content: arraybuffer }]

mergePdfs: async function (files) {
        try {
            const mergedPdf = await PDFDocument.create()

            for (let file of files) {
                const pdf = await PDFDocument.load(file.content)
                const copiedPages = await mergedPdf.copyPages(pdf, pdf.getPageIndices())
                copiedPages.forEach((page) => mergedPdf.addPage(page))
            }

            const mergedPdfFile = await mergedPdf.save()
            const buffer = Buffer.from(mergedPdfFile)
            return await buffer.toString('base64') // return as buffer or base64 encoded file
        } catch (err) {
            console.error(err.message)
        }
}

[ABAP] Display PDF in HTML Control

  DATA lt_data TYPE STANDARD TABLE OF x255.
  CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
    EXPORTING
      buffer     = lv_xstring "pdf data
    TABLES
      binary_tab = lt_data.  

 DATA(o_html) = NEW cl_gui_html_viewer( parent = o_parent ).

* URL zu HTML holen
  DATA: lv_url TYPE swk_url.
  o_html->load_data( EXPORTING type         = 'BIN'
                               subtype      = 'PDF'
                     IMPORTING assigned_url = lv_url
                     CHANGING  data_table   = lt_data ).
* HTML anzeigen
  o_html->show_url( lv_url ).

[ABAP] OData – GET_STREAM implementation to return a PDF

  METHOD /iwbep/if_mgw_appl_srv_runtime~get_stream.

* This method get's called when a media file is queried with $value. A binary stream will be returned.

    TRY.
        DATA(file_id) = VALUE zfile_id( it_key_tab[ name = 'file_id' ]-value ).
      CATCH cx_sy_itab_line_not_found.
        RETURN. " leave here when no file_id provided
    ENDTRY.
  
    DATA(ls_file) = get_file( file_id ) " read your file you want to return (if it's not yet a binary stream, convert it)

    DATA(ls_stream) = VALUE ty_s_media_resource( value     = ls_file-value
                                                 mime_type = ls_file-mimetype ). " in my case it's 'application/pdf'

    " necessary to display the filename instead of $value in the viewer title
    TRY.
        " create pdf object
        DATA(lo_fp)     = cl_fp=>get_reference( ).
        DATA(lo_pdfobj) = lo_fp->create_pdf_object( connection = 'ADC' ).
        lo_pdfobj->set_document( pdfdata = ls_stream-value ).
        " set title
        lo_pdfobj->set_metadata( VALUE #( title = ls_file-filename ) ).
        lo_pdfobj->execute( ).
        " get pdf with title
        lo_pdfobj->get_document( IMPORTING pdfdata = ls_stream-value ).

      CATCH cx_fp_runtime_internal
            cx_fp_runtime_system
            cx_fp_runtime_usage INTO DATA(lo_fpex).
    ENDTRY.

    copy_data_to_ref( EXPORTING is_data = ls_stream
                      CHANGING  cr_data = er_stream ).

    " necessary for the pdf to be opened inline instead of a download (also sets the filename when downloaded)
    /iwbep/if_mgw_conv_srv_runtime~set_header( VALUE #( name  = 'content-disposition'
                                                        value = |inline; filename={ ls_file-filename }| ) ).

  ENDMETHOD

Quick way to open a PDFViewer in your UI5 App:

			const pdfViewer = new PDFViewer()
			pdfViewer.setSource("/sap/opu/odata/ZMY_SEVICE" + my_path + "/$value")  // my_path could be something like this "/PdfSet('file_id')"
			pdfViewer.setTitle("My PDFViewer Title") // title of the popup, not the viewer
			pdfViewer.open()

[PDF.js] Set light and dark theme manually

The new PDF.js viewer design Photon supports a light and a dark mode. By default, the theme is set automatically. To overwrite this, you can set the viewerCssTheme property.

I’ve embedded my PDF viewer in an iFrame like this:

<iframe id="pdf-js-viewer" src="/pdf/web/viewer.html" title="webviewer" frameborder="0" width="100%" height="700" allowfullscreen="" webkitallowfullscreen=""/>

By setting the viewerCssTheme property, you are able to change the PDF viewer theme. It can be modified using the PDFViewerApplicationOptions.set() function and there are three possible values. The property has to be set before the Viewer is initialized. To archive this, you can listen to the event webviewerloaded (read more about it here). In my case, I also had to call _forceCssTheme() after that.

                document.addEventListener("webviewerloaded", async () => {
                    let pdfViewerIFrame = document.getElementById("pdf-js-viewer")
                    // https://github.com/mozilla/pdf.js/blob/master/web/app_options.js
                    pdfViewerIFrame.contentWindow.PDFViewerApplicationOptions.set("viewerCssTheme", 1)       // 0=automatic theme, 1=light theme, 2=dark theme 
                    pdfViewerIFrame.contentWindow.PDFViewerApplication._forceCssTheme()
                })

[PDF.js] Allow PDF documents to disable copying in the viewer

With pull request 11789, the PDF Viewer respects the PDF property Content Copying, if the viewer option enablePermissions is set to true.

I’ve embedded my viewer in an iFrame like this:

<iframe id="pdf-js-viewer" src="/pdf/web/viewer.html" title="webviewer" frameborder="0" width="100%" height="700" allowfullscreen="" webkitallowfullscreen=""/>

The enablePermissions property can be modified using the PDFViewerApplicationOptions.set() function. The property has to be set before the Viewer is initialized. To archive this, you can listen to the event webviewerloaded (read more about it here).

                document.addEventListener("webviewerloaded", async () => {
                    let pdfViewerIFrame = document.getElementById("pdf-js-viewer")
                    //https://github.com/mozilla/pdf.js/blob/master/web/app_options.js
                    pdfViewerIFrame.contentWindow.PDFViewerApplicationOptions.set("enablePermissions", true) //allow PDF documents to disable copying in the viewer
                    pdfViewerIFrame.contentWindow.PDFViewerApplicationOptions.set("defaultUrl", "") //prevent loading default pdf
                    }
                })

[PDF.js] Prevent loading default pdf

This can be done by appending ?file= to the src path, like it is mentionend here.

<!-- "?file=" prevents loading the default document -->
<iframe id="pdf-js-viewer" src="/pdf/web/viewer.html?file=" title="webviewer" frameborder="0" width="100%" height="700" allowfullscreen="" webkitallowfullscreen=""/>

Or by setting the variable defaultUrl to blank using the onload event.

document.addEventListener("webviewerloaded", async () => {
   let pdfViewerIFrame = document.getElementById("pdf-js-viewer")
   //https://github.com/mozilla/pdf.js/blob/master/web/app_options.js
   pdfViewerIFrame.contentWindow.PDFViewerApplicationOptions.set("defaultUrl", "") //prevent loading default pdf
})

[nodejs] read and write a file

https://nodejs.dev/learn/reading-files-with-nodejs

https://nodejs.dev/learn/writing-files-with-nodejs

        const fs = require("fs")

        try {
            // read from local folder
            const localPDF = fs.readFileSync('PDFs/myFile.pdf')

            //write back to local folder
            fs.writeFileSync('PDFs/writtenBack.pdf', localPDF )

        } catch (err) {
            console.error(err)
        }

Converting to Base64

        try {
            // read from local folder
            const localPDF = fs.readFileSync('PDFs/myFile.pdf')
            const localBase64 = localPDF.toString('base64')

            //write back to local folder
            fs.writeFileSync(`PDFs/writtenBack.pdf`, localBase64, {encoding: 'base64'})

        } catch (err) {
            console.error(err)
        }

Reading and writing using streams with pipe

        //read and write local file
        const reader = fs.createReadStream("PDFs/myFile.pdf")
        const writer = fs.createWriteStream('PDFs/writtenBack.pdf');
        reader.pipe(writer)

[Software] Using pdfunite to merge PDF documents

First install pdfunite:

sudo apt update 
sudo apt install poppler-utils

Syntax:

pdfunite source1.pdf source2.pdf merged_output.pdf

If a pdf file is located in a different folder, you have to add the path like this: $home/Downloads/source1.pdf
If you want to merge all pdf’s of the current folder you cant type:

pdfunite *.pdf merged_output.pdf

An alternative with GUI is PDF Arranger.