Retrieve a pdf's bookmark data using VanillaJS/Node.js - javascript

I'm trying to retrieve a pdf's meta data, looking specifically for a bookmark's page number using VanillaJS/node.js with no libraries. The file is located locally on the desktop.
I found this bit of code in another answer but it only returns the length of the document. I have tried to change the regex to look for letters, but if then returns an array of 500000 letters.
Is it even possible? If libraries are required, does anyone know of one that can do this?
Thanks
const fs = require('fs').promises
let rawData = await fs.readFile(fullPath, 'utf8', (err, data) => {
if (err) {
console.error('test error', err);
return;
}
});
async function pdfDetails(data) {
return new Promise(done => {
let Pages2 = data.match(/[a-zA-Z]/g);
let regex = /<xmp.*?:(.*?)>(.*?)</g;
let meta = [{
Pages
}];
let matches = regex.exec(data);
while (matches != null) {
matches.shift();
meta.push({
[matches.shift()]: matches.shift()
});
matches = regex.exec(data);
}
done(meta);
});
}
let details = await pdfDetails(rawData)
console.log(details)

Due to the difficulty of using vanilla JS, and problems with libraries that may have worked (due to node version conflicts), I ended up using PDFTron services.

Related

Electron - write file before open save dialog

I'm using electron to develop an app. after some encryption operations are done, I need to show a dialog to the user to save the file. The filename I want to give to the file is a random hash but I have no success also with this. I'm trying with this code but the file will not be saved. How I can fix this?
const downloadPath = app.getPath('downloads')
ipcMain.on('encryptFiles', (event, data) => {
let output = [];
const password = data.password;
data.files.forEach( (file) => {
const buffer = fs.readFileSync(file.path);
const dataURI = dauria.getBase64DataURI(buffer, file.type);
const encrypted = CryptoJS.AES.encrypt(dataURI, password).toString();
output.push(encrypted);
})
const filename = hash.createHash('md5').toString('hex');
console.log(filename)
const response = output.join(' :: ');
dialog.showSaveDialog({title: 'Save encrypted file', defaultPath: downloadPath }, () => {
fs.writeFile(`${filename}.mfs`, response, (err) => console.log(err) )
})
})
The problem you're experiencing is resulting from the asynchronous nature of Electron's UI functions: They do not take callback functions, but return promises instead. Thus, you do not have to pass in a callback function, but rather handle the promise's resolution. Note that this only applies to Electron >= version 6. If you however run an older version of Electron, your code would be correct -- but then you should really update to a newer version (Electron v6 was released well over a year ago).
Adapting your code like below can be a starting point to solve your problem. However, since you do not state how you generate the hash (where does hash.createHash come from?; did you forget to declare/import hash?; did you forget to pass any message string?; are you using hash as an alias for NodeJS' crypto module?), it is (at this time) impossible to debug why you do not get any output from console.log (filename) (I assume you mean this by "in the code, the random filename will not be created"). Once you provide more details on this problem, I'd be happy to update this answer accordingly.
As for the default filename: As per the Electron documentation, you can pass a file path into dialog.showSaveDialog () to provide the user with a default filename.
The file type extension you're using should also actually be passed with the file extension into the save dialog. Also passing this file extension as a filter into the dialog will prevent users from selecting any other file type, which is ultimately what you're also currently doing by appending it to the filename.
Also, you could utilise CryptoJS for the filename generation: Given some arbitrary string, which could really be random bytes, you could do: filename = CryptoJS.MD5 ('some text here') + '.mfs'; However, remember to choose the input string wisely. MD5 has been broken and should thus no longer be used to store secrets -- using any known information which is crucial for the encryption of the files you're storing (such as data.password) is inherently insecure. There are some good examples on how to create random strings in JavaScript around the internet, along with this answer here on SO.
Taking all these issues into account, one might end up with the following code:
const downloadPath = app.getPath('downloads'),
path = require('path');
ipcMain.on('encryptFiles', (event, data) => {
let output = [];
const password = data.password;
data.files.forEach((file) => {
const buffer = fs.readFileSync(file.path);
const dataURI = dauria.getBase64DataURI(buffer, file.type);
const encrypted = CryptoJS.AES.encrypt(dataURI, password).toString();
output.push(encrypted);
})
// not working:
// const filename = hash.createHash('md5').toString('hex') + '.mfs';
// alternative requiring more research on your end
const filename = CryptoJS.MD5('replace me with some random bytes') + '.mfs';
console.log(filename);
const response = output.join(' :: ');
dialog.showSaveDialog(
{
title: 'Save encrypted file',
defaultPath: path.format ({ dir: downloadPath, base: filename }), // construct a proper path
filters: [{ name: 'Encrypted File (*.mfs)', extensions: ['mfs'] }] // filter the possible files
}
).then ((result) => {
if (result.canceled) return; // discard the result altogether; user has clicked "cancel"
else {
var filePath = result.filePath;
if (!filePath.endsWith('.mfs')) {
// This is an additional safety check which should not actually trigger.
// However, generally appending a file extension to a filename is not a
// good idea, as they would be (possibly) doubled without this check.
filePath += '.mfs';
}
fs.writeFile(filePath, response, (err) => console.log(err) )
}
}).catch ((err) => {
console.log (err);
});
})

Using `filter` lambda in gremlin with javascript

I am using OrientDb with JavaScript and I have tried with startingWith, containing, endingWith, notContaining, notEndingWith, notStartingWith predicates unsuccessfully. Maybe is a wrong implementation from my side but I have not found documentation about how to use.
I've been looking for a way to filter with lambdas to get a sql like behavior but have not been successful. I tried to use the method described in this answer, but it is not working on JavaScript. When using the predicates the answer is an error.
I've tried that too:
What is the equivalent of the gremlin queries in gremlin javascript?
My current JavaScript code:
import * as gremlin from 'gremlin';
const traversal = gremlin.process.AnonymousTraversalSource.traversal;
const DriverRemoteConnection = gremlin.driver.DriverRemoteConnection;
const TextPredicated = gremlin.process.TextP;
const authenticator = new gremlin.driver.auth.PlainTextSaslAuthenticator('usr', 'pwd');
const remote = new DriverRemoteConnection(
'ws://localhost:8182/gremlin', {
authenticator,
traversalSource: 'g'
});
remote.addListener('socketError', (error) => { console.log(`socketError: ${error}`); });
(async () => {
try {
remote.open();
const g = await traversal().withRemote(remote);
const results = await g.V()
.where('username', TextPredicated.containing('john'))
.toList();
console.log(results);
remote.close();
} catch (error) {
console.log(error);
} finally {
remote.close();
}
})();
You don't say what your error is, but I think your Gremlin should use has() rather than where():
const results = await g.V()
.has('username', TextPredicated.containing('john'))
.toList();
Also note that TextP did not become available until TinkerPop 3.4.0 so you'd need to be sure that your graph (in your case, OrientDB) supports at least this version of TinkerPop.

Asynchronous Programming with Nodejs - Iterating through an array of links, opening them and saving them to JSON once done

I have an array of tracking links (about 30), which I want to open piece by piece and find out the real URLs hidden behind them. Once that's done, I want to save the 'real' URLs to a JSON file.
The URLs look something like this before they have been "checked":
https://www.trackinglink.com/1
and something like this afterwards:
https://www.amazon.com/
I have solved the "uncovering" of the tracking links using request and it works. However, what I can't manage to get to work, is waiting with writing the JSON file until all the URLs have been "requested"/checked.
I know that the solution involves Async/Await or Promises, but I can't get it to work in node. For someone more experienced, this is probably a matter of a few minutes.
The concept of asynchronous programming is pretty much new to me, but I have spent my fair share of hours researching it. I think I have difficulties transferring the knowledge out there to my specific problem.
I'd really appreciate the help. Cheers!
const request = require('request');
const fs = require('fs');
let listWithRealUrls = [];
function grabAndSaveRealUrls() {
let Urls = ['https://www.trackinglink/1', 'https://www.trackinglink/2', 'https://www.trackinglink/3']
for (const Url of Urls) {
request.get(Url, function () {
let realUrl = this.uri.href;
listWithRealUrls.push(realUrl)
});
}
fs.writeFile('data.json', JSON.stringify(listWithRealUrls), function (err) {
if(err) {
console.log(err);
} else {
console.log('success');
}
})
}
grabAndSaveRealUrls();
Thanks to Jonas' comment and the previously linked 'duplicate question', I managed to solve this. I am sure there are more elegant ways, but here's how I did it:
const request = require('request');
const fs = require('fs');
let listHoldingPromises = [];
function grabAndSaveRealUrls() {
let Urls = ['https://www.trackinglink.com/1', 'https://www.trackinglink.com/2', 'https://www.trackinglink.com/3']
for (const Url of Urls) {
let promise = new Promise(function(resolve, reject){
request.get(Url, function () {
let realUrl = this.uri.href;
resolve(realUrl);
});
})
listHoldingPromises.push(promise);
}
}
grabAndSaveRealUrls();
Promise.all(listHoldingPromises).then(values => {
fs.writeFile('data.json', JSON.stringify(values), function (err) {
if(err) {
console.log(err);
} else {
console.log('success');
}
})
});

How do I reference my MongoDB data in NodeJS, and then add it to a Discord.JS message?

I'm trying to pull an array of data from a MongoDB database, and while the code is rusty (and I do want some corrections on it if it could be done better or is missing something or is wrong), it should be taking the array, finding the "user" and "description" objects, and then putting them into a discord.js message.
I've tried referencing the objects individually, making them strings, parsing the data, but I still cant find out how to do it. Heres the code I've been using.
module.exports.run = async (bot, message, args) => {
const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb+srv://something:something#something/test?retryWrites=true&w=majority';
const assert = require('assert');
try {
function remindersChecker() {
let mongoClientPromise = MongoClient.connect(url, function (err, client) {
const db = client.db("reminders");
let date = new Date().getTime();
let now = Math.floor(Date.now() / 1000);
db.collection("reminders").find().toArray(function (err, result) {
let data = JSON.toObject();
console.log(data);
let user = data.author
let description = data.description
console.log(user);
user.send("I have a reminder for you! " + description)
})
})
}
remindersChecker()
} catch(err) {
catchError()
}
}}
module.exports.help = {
name: "check"
}
(The command is temporary and will be used on a setTimeout later, hence the function rather than just plain old code.)
Thanks! And I hope I can get help soon.
probably some more information would be great to better understand the problem.
from what i can see here, you are receiving an object from your database and converting it into an array here:
db.collection("reminders").find().toArray(function (err, result) {...
now that array is actually that result obtained from the callback and you are not using it at all, you probably have to iterate on that.
plus i remember that I used to write
...find({})..
to search in the database as for SELECT*FROM in SQL. maybe that can help too.
hope this helps.

nodejs prepending to a file

For Node.js, what is the best way to prepend to a file in a way SIMILAR to
fs.appendFile(path.join(__dirname, 'app.log'), 'appendme', 'utf8')
Personally, the best way really revolves around a asynchronous solution to create a log where I can basically push onto the file from the top.
This solution isn't mine and I don't know where it's from but it works.
const data = fs.readFileSync('message.txt')
const fd = fs.openSync('message.txt', 'w+')
const insert = Buffer.from("text to prepend \n")
fs.writeSync(fd, insert, 0, insert.length, 0)
fs.writeSync(fd, data, 0, data.length, insert.length)
fs.close(fd, (err) => {
if (err) throw err;
});
It is impossible to add to a beginning of a file. See this question for the similar problem in C or this question for the similar problem in C#.
I suggest you do your logging in the conventional way (that is, log to the end of file).
Otherwise, there is no way around reading the file, adding the text to the start and writing it back to the file which can get really costly really fast.
It seems it is indeed possible with https://www.npmjs.com/package/prepend-file
Here is an example of how to prepend text to a file using gulp and a custom built function.
var through = require('through2');
gulp.src('somefile.js')
.pipe(insert('text to prepend with'))
.pipe(gulp.dest('Destination/Path/'))
function insert(text) {
function prefixStream(prefixText) {
var stream = through();
stream.write(prefixText);
return stream;
}
let prefixText = new Buffer(text + "\n\n"); // allocate ahead of time
// creating a stream through which each file will pass
var stream = through.obj(function (file, enc, cb) {
//console.log(file.contents.toString());
if (file.isBuffer()) {
file.contents = new Buffer(prefixText.toString() + file.contents.toString());
}
if (file.isStream()) {
throw new Error('stream files are not supported for insertion, they must be buffered');
}
// make sure the file goes through the next gulp plugin
this.push(file);
// tell the stream engine that we are done with this file
cb();
});
// returning the file stream
return stream;
}
Sources: [cole_gentry_github_dealingWithStreams][1]
Its possible by using the prepend-file node module. Do the following:
npm i prepend-file -S
import prepend-file module in your respective code.
Example:
let firstFile = 'first.txt';
let secondFile = 'second.txt';
prependFile(firstFile, secondFile, () => {
console.log('file prepend successfully');
})

Categories