Adding Header to Each Page of PDF in Node Puppeteer - puppeteer

I am trying to print a header section to each page of a PDF that gets generated. I'm also using pdf-merger to merge together multiple PDFs. Right now when I execute this code the PDF generated contains multiple documents, as expected. However the Header section I'm adding only seems to show up in the 2nd and 5th of the documents that are part of the full PDF. I cannot tell from looking at my code why that is happening. I would assume the Header would be added to each of the documents.
First question: why am I noticing the behavior I am. Understand that would help me know what to adjust. Second question: How can I adjust this code so that the Header is added to each of the pages of the document?
Here is the section where I pass options to page.pdf():
let doc = await page.pdf({
displayHeaderFooter: true,
format: "A4",
printBackground: true,
headerTemplate: '<span style="font-size: 30px; width: 50px; height: 50px; color:black; margin: 20px;">Header</span>',
});
And here is the full block of code:
let merger = new PDFMerger();
const browser = await puppeteer.launch();
const page = await browser.newPage();
let recordNum = 1;
for (let r of recordsArr) {
try {
let signatures = [];
signatures = await signatureService.getSignatures({}, [r.guid]);
if (signatures.length) r.signatureBase64Str = signatures[0].value;
} catch (err) {
console.log(err);
return Response.Failure("Error occurred while obtaining signature.", err);
}
r.logoBase64Str = base64Logo;
if (r.goalNotes.length) {
for (const [i, value] of r.goalNotes.entries()) {
value.number = i + 1;
}
}
try {
console.log(`processing record ${recordNum} of ${recordsArr.length}`);
const content = await compile(r);
await page.setContent(content);
await page.emulateMediaType("screen");
let doc = await page.pdf({
displayHeaderFooter: true,
format: "A4",
printBackground: true,
headerTemplate: '<span style="font-size: 30px; width: 50px; height: 50px; color:black; margin: 20px;">Header</span>',
});
merger.add(doc);
++recordNum;
} catch (error) {
console.log(error);
return Response.Failure("Unable to generate PDF by parameters passed.");
}
}

It's hard to tell what's wrong with your code since so many functions are undefined, but here's a minimal, runnable example that adds headers to all pages using the same PDF merger package as you:
const PDFMerger = require("pdf-merger-js");
const puppeteer = require("puppeteer");
const headerTemplate = `<span style="font-size: 30px; width: 200px; height: 200px; background-color: black; color: white; margin: 20px;">Header</span>`;
const mockContent = Array(10).fill().map((_, i) => `<div>page ${i}</div>`);
const filename = "merged.pdf";
const pdfSettings = {
displayHeaderFooter: true,
format: "A4",
printBackground: true,
headerTemplate,
margin: {top: "100px", bottom: "100px"},
};
let browser;
(async () => {
browser = await puppeteer.launch();
const [page] = await browser.pages();
const merger = new PDFMerger();
for (const content of mockContent) {
await page.setContent(content);
merger.add(await page.pdf(pdfSettings));
}
await merger.save(filename);
})()
.catch(err => console.error(err))
.finally(async () => await browser.close())
;

Related

Split string and add extra word at onChange in Reactjs

I am trying to show on my page spotify links submitted by users. The problem is the standard Spotify share link is missing the 'embed' in part of the url that is needed to render it so I have been trying to use 'split' to adjust the url to an embed-able one, however I just cannot get it to work?
This is the function I am using to split the url and add the extra embed text
function spotify () {
const message = "urlInput";
let split = message.split(".com/");
let joined = split[0]+".com/embed/"+split[1];
}
This is the relevant part of html code I am using to get the users input
{
currentAccount ? (<textarea
placeholder={spotifyLink}
type="url"
id="urlInput"
value={messageValue}
onChange={e => {setMessageValue(e.target.value); {spotify}}} />) : null
}
<button className="waveButton" onClick={music}>
Submit
</button>
and the function attached to the button onClick
const music = async () => {
try {
const { ethereum } = window;
if (ethereum) {
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const musicPortalContract = new ethers.Contract(contractAddress, contractABI, signer);
let count = await musicPortalContract.getTotalSongs();
console.log("Retrieved total song count...", count.toNumber());
const musicTxn = await musicPortalContract.music(messageValue);
await musicTxn.wait();
count = await musicPortalContract.getTotalSongs();
console.log("Retrieved total song count...", count.toNumber());
} else {
console.log("Ethereum object doesn't exist!");
}
} catch (error) {
console.log(error);
}
}
I would like to transform the url from this:
https://open.spotify.com/track/3u5N55tHf7hXATSQrjBh2q?si=8fe4896e171e4991
to this:
https://open.spotify.com/embed/track/46q5BtHso0ECuTKeq70ZhW?si=79e6006e92104e51
There might be a better way to do this than using .split but i'm not sure?
EDIT: Adding extra code here that is used for other functions such as getting the array of user inputs, incase it is useful.
const getAllMusic = async () => {
try {
const { ethereum } = window;
if (ethereum) {
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const musicPortalContract = new ethers.Contract(contractAddress, contractABI, signer);
const music = await musicPortalContract.getAllMusic();
console.log("lets surf")
let musicArray = [];
musics.forEach(music => {
musicArray.push({
address: music.owner,
message: music.message
});
});
/* Store our data in React State*/
setAllMusic(musicArray);
} else {
console.log("Ethereum object doesn't exist!")
}
} catch (error) {
console.log(error);
}
}
and the related html to it
{allMusics.map((wave, index) => {
return (
<div key={index}>
<div><iframe src={music.message} width="300" height="80" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe></div>

How do I write an async request to get a markdown file's content? Svelte

I'm having a great time building my blog with Svelte, but I'm switching the structure to to be accessed through a JSON API.
Right now it's easy to get the markdown metadata and path, but I'd love to also get the content.
How would I modify this posts.json.js file to also get the content?
const allPostFiles = import.meta.glob('../blog/posts/*.md')
const iterablePostFiles = Object.entries(allPostFiles)
const allPosts = await Promise.all(
iterablePostFiles.map(async ([path, resolver]) => {
const { metadata } = await resolver()
const postPath = path.slice(2, -3)
return {
meta: metadata,
path: postPath
}
})
)
const sortedPosts = allPosts.sort((a, b) => {
return new Date(b.meta.date) - new Date(a.meta.date)
})
return {
body: sortedPosts
}
Install and enable the vite-plugin-markdown
// svelte.config.js
import { plugin as markdown, Mode } from "vite-plugin-markdown";
/** #type {import('#sveltejs/kit').Config} */
export default {
kit: {
vite: {
plugins: [markdown({ mode: Mode.HTML })],
},
},
};
then the content will be available as html and frontmatter data as attributes
iterablePostFiles.map(async ([path, resolver]) => {
const { attributes, html } = await resolver();
return {
attributes,
html,
path: path.slice(2, -3),
};
})
(I suggest adding the metadata into the markdown files via frontmatter )
The answer above works perfectly, but it also works to tweak the API with this code:
const allPosts = await Promise.all(
iterablePostFiles.map(async ([path, resolver]) => {
const { metadata } = await resolver()
// because we know every path will start with '..' and end with '.md', we can slice from the beginning and the end
const postPath = path.slice(2, -3)
const post = await resolver()
const content = post.default.render()
return {
meta: metadata,
path: postPath,
text: content
}
})
)
The important addition is this:
const post = await resolver()
const content = post.default.render()
using these variable chains to avoid using the JS reserved word default.

Google Document Viewer shows: the server cannot process the request because it is malformed

I try to preview the documents on my site using Google viewer. Document url I get from blob, but when I click to show docs I had an error.
In console I see this link: blob:http://localhost:8080/5372d78d-a9c6-4c50-b23b-249cc643bdd2
const url = window.URL.createObjectURL(
new Blob([response.data], { type: response.data.type })
);
const link = document.createElement("iframe");
console.log(url);
link.src = `https://docs.google.com/viewer?url=${url}&embedded=true`;
link.style.width = "100%";
link.style.height = "500px";
document.getElementById(fileId).appendChild(link);
},```
```async showDoc(file) {
this.fileId = file.Document.Id;
this.fileName = file.Document.Name;
try {
let response = await axios.get(url, {
responseType: "blob",
params: {
fileId: this.fileId,
},
});
this.forceFileShow(response, this.fileId);
console.log("удача", response);
} catch (err) {
console.log("неудача", err.response);
}
}
},
And here html:
<div class="card-icon" #click="showDoc(file)"
:data-target="`#showDoc${file.Document.Id}`"
data-toggle="modal">```
[1]: https://i.stack.imgur.com/tZlkm.png

Protractor cucumber html report is generating only after 2nd run?

When i try to run my code it is generating html report only after 2nd run.
In the first run it is generating the json file and then after the second run, by using the generated json file and creating the HTML report
Please tell me how to generate html report by running only once.
below is code i tried
hook.js
const {defineSupportCode} = require('cucumber');
defineSupportCode(function ({After}) {
After(function(scenario,done)
{
const world = this;
console.log('in after block')
if (scenario.result.status === 'failed') {
console.log('in after block inside')
browser.takeScreenshot().then(function (stream) {
let decodedImage = new Buffer(stream.replace(/^data:image\/(png|gif|jpeg);base64,/, ''), 'base64');
world.attach(decodedImage, 'image/png');
console.log('screenshot successful');
}).then(function () {
done();
});
}else {
done();
}
});
});
index.js
var reporter = require('cucumber-html-reporter');
var options = {
theme: 'bootstrap',
output: 'cucumber-report.html',
reportSuiteAsScenarios: true,
launchReport: true,
screenshotsDirectory: 'screenshots123',
takeScreenShotsOnlyForFailedSpecs: true,
//screenshotsSubfolder: 'images',
storeScreenshots: true,
};
reporter.generate(options);
Index.js
var reporter = require('cucumber-html-reporter');
var options = {
theme: 'bootstrap',
jsonFile: 'C:/Users/pc/ProtractorCucumber/htmlReport/cucumber_html_reporter/report.json',
// jsonFile: 'C:/Users/pc/ProtractorCucumber/htmlReport/cucumber_html_reporter/cucumber-report.json',
output: 'C:/Users/pc/ProtractorCucumber/htmlReport/cucumber_html_reporter/cucumber-report.html',
// output: 'report123.html',
reportSuiteAsScenarios: true,
launchReport: true,
screenshotsDirectory: 'screenshots123',
takeScreenShotsOnlyForFailedSpecs: true,
//screenshotsSubfolder: 'images',
storeScreenshots: true,
};
reporter.generate(options);
Cucumber-html-reporter will require the JSON file created by cucumber after the execution.
Please refer the following snippet which has exception handled before calling generate function of cucumber-html-report.
const Cucumber = require('cucumber');
import { browser } from 'protractor';
import * as fs from 'fs';
import { config } from '../config/config';
import { defineSupportCode } from "cucumber";
import * as reporter from 'cucumber-html-reporter';
import { mkdirp } from 'mkdirp';
defineSupportCode(function ({ registerHandler, registerListener, After, setDefaultTimeout }) {
setDefaultTimeout(10 * 1000);
let jsonReports = process.cwd() + "/reports/json";
let htmlReports = process.cwd() + "/reports/html";
let targetJson = jsonReports + "/cucumber_report.json";
//BeforeFeature
registerHandler('BeforeFeature', function (event, callback) {
browser.get(config.baseUrl);
callback();
});
After(function (scenario) {
let world = this;
if (scenario.isFailed()) {
return browser.takeScreenshot().then(function (screenShot) {
// screenShot is a base-64 encoded PNG
world.attach(screenShot, 'image/png');
});
}
})
let cucumberReporterOptions = {
theme: "bootstrap",
//theme: "foundation",
// theme: "simple",
jsonFile: targetJson,
output: htmlReports + "/cucumber_reporter.html",
reportSuiteAsScenarios: true,
launchReport: false
};
let logFn = string => {
if (!fs.existsSync(jsonReports)) {
mkdirp.sync(jsonReports);
}
try {
fs.writeFileSync(targetJson, string);
reporter.generate(cucumberReporterOptions); // invoke cucumber-html-reporter
} catch (err) {
if (err) {
console.log(`Failed to save cucumber test results to json file.
Failed to create html report.
`);
console.log(err);
}
}
};
let jsonformatter = new Cucumber.JsonFormatter({
log: logFn
});
registerListener(jsonformatter);
})

How to bypass CSP(Content-Security-Policy) using puppeteer's API page.addScriptTag?

scenario:
I use puppeteer launched chrome in headless mode, and call page.addScriptTag with an cross-domain javascript file. Now if the opening site has csp set and restricts only same origin javascript tags, how can I bypass this using puppeteer API?
Use:
await page.setBypassCSP(true)
Documentation
This is my first stackoverflow contribution so have mercy on me. I found this work around to allow you to get past CSP, Here.
The basic idea is that you intercept page requests and use a library like node-fetch to make the request and disable the CSP header when passing it back to chrome.
Here's the snippet that initially came from the github issue tracker.
Replace "example.com" with the website that needs to have CSP disabled.
const fetch = require('node-fetch')
const requestInterceptor = async (request) => {
try {
const url = request.url()
const requestHeaders = request.headers()
const acceptHeader = requestHeaders.accept || ''
if (url.includes("example.com") && (acceptHeader.includes('text/html'))) {
const cookiesList = await page.cookies(url)
const cookies = cookiesList.map(cookie => `${cookie.name}=${cookie.value}`).join('; ')
delete requestHeaders['x-devtools-emulate-network-conditions-client-id']
if (requestHeaders.Cookie) {
requestHeaders.cookie = requestHeaders.Cookie
delete requestHeaders.Cookie
}
const theseHeaders = Object.assign({'cookie': cookies}, requestHeaders, {'accept-language': 'en-US,en'})
const init = {
body: request.postData(),
headers: theseHeaders,
method: request.method(),
follow: 20,
}
const result = await fetch(
url,
init,
)
const resultHeaders = {}
result.headers.forEach((value, name) => {
if (name.toLowerCase() !== 'content-security-policy') {
resultHeaders[name] = value
} else {
console.log('CSP', `omitting CSP`, {originalCSP: value})
}
})
const buffer = await result.buffer()
await request.respond({
body: buffer,
resultHeaders,
status: result.status,
})
} else {
request.continue();
}
} catch (e) {
console.log("Error while disabling CSP", e);
request.abort();
}
}
await page.setRequestInterception(true)
page.on('request', requestInterceptor)