How can I exactly implement pdfjs in elixir liveview?
You’ll need to create a Hook: JavaScript interoperability — Phoenix LiveView v0.18.3
Please provide a practical example/guide
What have you tried so far? Have you got pdfjs working without liveview?
I have installed pdfjs using npm, I have made a template and created a hook in app.js.
At template
<div
id="pdfReader"
phx-hook="pdfReader"
phx-update="ignore">
</div>
<div id="pdfReader"></div>
<canvas id="myreader"></canvas>
At app.js
import Hooks from "./hooks";
At hooks.js
import pdfReader from "./hooks/pdf";
let Hooks = {
pdfReader: pdfReader
}
export default Hooks;
At pdf.js
import pdfjsLib from "pdfjs"
const pdfReader = nameRoute => {
let loadingTask = pdfjsLib.getDocument(nameRoute),
pdfDoc = null,
canvas = document.querySelector('#cnv'),
ctx = canvas.getContext('2d'),
scale = 1.5,
numPage = 1;
const GeneratePDF = numPage => {
pdfDoc.getPage(numPage).then(page => {
let viewport = page.getViewport({ scale: scale });
canvas.height = viewport.height;
canvas.width = viewport.width;
let renderContext = {
canvasContext : ctx,
viewport: viewport
}
page.render(renderContext);
})
document.querySelector('#npages').innerHTML = numPage;
}
loadingTask.promise.then(pdfDoc_ => {
pdfDoc = pdfDoc_;
document.querySelector('#npages').innerHTML = pdfDoc.numPages;
GeneratePDF(numPage)
});
}
const startPdf = () => {
pdfReader('../')
}
window.addEventListener('load', startPdf);
export default pdfReader;
But still no working
Anyone wishing to help might first want to look through this discussion of the same problem on the Elixir Discord to prevent double work and outdated code examples: Discord
Here’s a working example from my project:
To install JS dependency: npm install pdfjs-dist
JS:
- purple/pdf.js at master · knoebber/purple · GitHub
- purple/app.js at master · knoebber/purple · GitHub
LiveView:
It has simple pagination and zoom controls. It’s a bit hacky and the JS is messy but hopefully it can help others get started…
Very useful example!
Just some relevant feedback with running Purple locally.
Initially, I copied the secrets.exs.sample to secrets.exs without adding any details.
Then, I could not find any functionality for signing in a new user (just logging in an existing one). I did a “hack” by copy-pasting in the Postgres repo account (in the users table) the credentials from another repo of mine that is “compatible” with yours.
Additionally, an error was thrown in Purple.Uploads.get_file_info/2 (line 40). I “hacked” this again by merely returning %{extension: ".pdf"}
(I skipped using Mogrify.identify/1 that raised the error).
Then everything worked like a charm! The pdf can be viewed in the Liveview.
Thank you for sharing this.
Oh nice haha, I wasn’t expecting anyone to actually try running it. I create accounts from IEX, and Mogrify requires imagemagick to be installed locally. Glad it could help, cheers!
I have also tested it. Working well. I have referenced the code and added the hook at app.js and also added the pdf.js file. My liveview has this
<canvas
class="mt-1"
phx-hook="PDF"
id="pdf-canvas"
data-path={@book_path}
>
</canvas>
where by an example of book_path is ""/home/peter/Documents/2work/uploads/23-Learning Web Design _ A Beginner’s Guide to HTML, CSS, JavaScript, and Web Graphics ( PDFDrive ).pdf" "
. But am getting missing pdf error in browser console. I can see it added http://localhost
which is unintended.
Please help.
@knoebber @bdarla
` Check attached photo
Even if I hardcode the pdf_path to an online one like this https://www.africau.edu/images/default/sample.pdf
, still not working.
@knoebber @bdarla @Nicd
I would suggest studying in more details @knoebber’s project, especially if you plan to implement this functionality in a new project of your own.
There are some files that hold features related with uploading, e.g. in the config
directory.
Moreover, it is important to understand that @knoebber’s project involves a controller (FileController
) that responds to the request of the hook for the pdf file.
Of great importance is also the router.ex
that comprises the routes to the aforementioned controller.
Finally managed to refactor the code. Its working very well. The problem is that its not working on small screens such as phones.
@knoebber @bdarla
Hi - it works fine on my phone for me (iphone 12). I can try to help out if you ask a more specific question.
@knoebber
How can I enable input to a particular page and viewing of page numbers? As attached
Plus showing percentage of zoom
Thanks
It might be that for touch-screen based input, the javascript has to be configured to capture “touch” events, in addition to the corresponding “click” events.
Hi - I don’t feel like implementing that UI right now. It’s certainly possible though. You’ll have to extend the JavaScript hook that I provided with additional calls to the pdfjs API.
Check the implementation of pdfjs for more info: GitHub - mozilla/pdf.js: PDF Reader in JavaScript
@knoebber @Nicd
Managed to diplay page number. I just added
At pdfJS
var myDiv = document.getElementById('special'); myDiv.innerHTML = currentPageNum;
At liveview
<div id="special" class="">
<p>1</p></div>
It works very well. Same can be replicated to get total pages by replacing currentPageNum
with pdf.numPages
.
The next thing is allowing scrolling. Anybody who can implement that??
i implemented pdfjs in my code and it works locally fine but when i try to install the pdf from an android emulator it doesn’t work any one can help??
import { jsPDF } from “jspdf”;
import ‘jspdf-autotable’;
const data = {
items: [
{ goal: ‘Save $1000’, amount: ‘$1000’, date: ‘2024-10-20’ },
]
};
const PdfGenerator = {
async mounted() {
const pdfButton = document.getElementById(“pdfgen”);
pdfButton.addEventListener(“click”, () => this.generatePDF());
},
generatePDF() {
const mobilenumber = document.getElementById('phonenumber').value;
const doc = new jsPDF();
doc.setFontSize(20);
doc.text("ss", 90, 15);
doc.setFontSize(12);
doc.text(" Statement", 83, 20);
doc.setFontSize(12);
doc.text("Account Info", 10, 30);
doc.text(`${mobilenumber}`, 10, 40);
doc.text("Goals and dates", 10, 60);
doc.line(10, 65, 200, 65);
doc.text("Amounts", 150, 60);
doc.line(150, 65, 200, 65);
let yPosition = 75;
data.items.forEach((item, index) => {
doc.text(`${item.goal} - ${item.date}`, 10, yPosition);
doc.text(`${item.amount}`, 150, yPosition);
yPosition += 10;
});
doc.save("invoice.pdf");
}
};
export default PdfGenerator;