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)
        }
}

[PDF.js] Get PDF Viewer when it’s available in DOM and fully initialized

https://github.com/mozilla/pdf.js/wiki/Third-party-viewer-usage

<iframe id="pdf-js-viewer" src="/pdf/web/viewer.html?file=" title="webviewer" frameborder="0" width="100%" height="700" allowfullscreen="" webkitallowfullscreen=""/>
            displayPdf: async function (id) { 
                const pdfArrayBuffer= await getPdf(id)
                const pdfViewerIFrame = await getPdfViewer()
                await pdfViewerIFrame.contentWindow.PDFViewerApplication.initializedPromise
                await pdfViewerIFrame.contentWindow.PDFViewerApplication.open(pdfArrayBuffer)
            },

            getPdfViewer: function (id = 'pdf-js-viewer') {
                return new Promise((resolve, reject) => {
                    let pdfViewerIFrame = document.getElementById(id)
                    //if already loaded in DOM, return it
                    if (pdfViewerIFrame?.contentWindow?.PDFViewerApplication) {
                        resolve(pdfViewerIFrame)
                    } else {
                        //if not loaded yet, set up eventListener
                        document.addEventListener("webviewerloaded", async () => {
                            pdfViewerIFrame = document.getElementById(id)
                            resolve(pdfViewerIFrame)
                        })
                    }
                })
            }

This logic helped me to get the PDFViewerApplication in every situation correctly, for example when reloading the page with F5 or when having the PDF viewer already loaded and just wanting to open another PDF file.

[SAPUI5] Adding Lanes to a Process Flow

Manually adding Lanes to a Process Flow Control:

https://sapui5.hana.ondemand.com/#/api/sap.suite.ui.commons.ProcessFlow
https://sapui5.hana.ondemand.com/#/api/sap.suite.ui.commons.ProcessFlowLaneHeader
https://sapui5.hana.ondemand.com/#/entity/sap.suite.ui.commons.ProcessFlow/sample/sap.suite.ui.commons.sample.ProcessFlowUpdateLanes/code

In my case, there was no way to bind the model to the view, so I did the mapping for each ProcessFlowLaneHeader in the callback function after reading the oData entity.

view.xml

<flow:ProcessFlow id="process-flow"/>

controller.js

var oProcessFlow = this.getView().byId("process-flow")

var oRequestFilter = new sap.ui.model.Filter({
    path: "myId",
    operator: sap.ui.model.FilterOperator.EQ,
    value1: myId
})

this.getView().getModel().read("/WorkflowSet", {
    filters: [oFormularIdFilter],
    success: (oData, response) => {
        for (var i = 0; i < oData.results.length; i++) {
            var oLaneHeader = new ProcessFlowLaneHeader({
                laneId: oData.results[i].LaneId,
                iconSrc: oData.results[i].IconSrc,
                text: oData.results[i].Text,
                position: oData.results[i].Position,
                state: [{state: oData.results[i].State, value: "100"}]
            });
            oProcessFlow.addLane(oLaneHeader)
        }
    },
    error: oError => {
        sap.m.MessageToast.show("An error occured while reading entity /WorkflowSet.")
    }
});

[JavaScript] Clone an object containing a file as ArrayBuffer

Use the build in function structuredClone() to copy all kind of complex JS types.

Official documentation: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#supported_types

Comparison to other copy techniques like spread or json.stringify: https://www.builder.io/blog/structured-clone

[nodejs] Create buffer from stream

Using a promise

const stream2Buffer = async () => {
        return new Promise(function (resolve, reject) {
                const chunks = []
                stream.on('data', chunk => chunks.push(chunk))
                stream.on('end', () => resolve(Buffer.concat(chunks)))
                stream.on("error", err => reject(err))
        })
}
const buffer = await stream2Buffer()

A stream is also iterable (see here), so you can also use for await...of (example)

        const chunks = []
        for await (const chunk of stream) {
            chunks.push(chunk)
        }
        const buffer = Buffer.concat(chunks)