A true print to PDF feature
Problem
I use streamlit to create tools for non-technical users to do analytical jobs. The resulting pages of their analyses would ideally be shared with others in a static means, such as PDF. However, right now there is no way to do this well or efficiently.
What I've tried:
- Chrome: print to PDF, which normally doesn't work since it'll do something like cut off part of the current page.
- Save outputs to some other file, such as JSON and use those results in something like pandadoc, google slides, etc. This can create nice visuals, but if the viz in streamlit is good, it seems like it's unnecessary to replicate in another medium.
- Using snip to save an image, which is obviously inefficient.
Solution
MVP: Allow for a true print to PDF functionality built into streamlit so the user can print all of the current page to PDF as it looks. This would mean capturing everything on the page as it is at the time of printing and potentially allow it to print to multiple PDF pages if the streamlit page is long.
Preferred solution: Ideally, it would be possible to print multiple streamlit pages to PDF at the same time to make this even more efficient. I think this could be accomplished by letting all user selections be saved to some global location, similar to how st.session_state works, and then looping over the pages, applying the variables and printing.
Additional context
Is there anywhere we can get transparency into whether requests are on the streamlit team's radar and the priority of them? I ask because it seems like this topic has been brought up multiple times and almost just ignored.
The only "solution" I see is making sure the browser print to PDF functionality works or that images can be saved to S3, but that isn't really what I/others are requesting since it doesn't work in my/other's use cases. https://github.com/streamlit/streamlit/issues/297 https://github.com/streamlit/streamlit/pull/566 https://github.com/CodeForPhilly/chime/issues/25
I also see a hacky work around, but it basically requires recreating the entire set of outputs through FPDF and at that point it's just as efficient and more "pretty" to use something like pandadoc, google slides, etc. https://discuss.streamlit.io/t/creating-a-pdf-file-generator/7613
Finally, for completeness, a few threads that have just been closed without any comments, so I'm guessing labelled as "won't do"? https://discuss.streamlit.io/t/is-there-a-way-to-convert-the-page-that-is-viewed-to-a-pdf/651 https://discuss.streamlit.io/t/hack-to-save-page-as-html-or-pdf/4481
Hey @msquaredds,
we are aware of this request and we want to find a solution. But it's never been at the top of our priority list so far, unfortunately. (There are just too many cool features!!) I'll have a quick chat with the engineers today how much effort this would be to see if it's something we can easily slot it or if we need to plan some dedicated time for it.
You can have a look at roadmap.streamlit.io to see a list of features we are currently working on or planning for the next few months.
Ah digging through the issues now. Seems like printing to PDF (via the browser) used to work and we fixed it at several points in time (see the issue/PR you linked). But I guess we just broke it again at some point. I'll add an item to our roadmap to at least fix that. Do you think you could live with that if printing through the browser works well, or does that not solve your problem?
(Btw posts on the forum get closed automatically after 1 year, so closed != won't do).
@jrieke Is there any update on this, I saw that the feature is planned for the future but any idea by when it would be picked up?
@jrieke I wanted to contribute myself here and say that it would be hugely convenient to have the feature to save the front-end app state as a static report or PDF. I took a look at the roadmap you mentioned and it all looks very exciting. That said, being able to print to pdf is now something that I've needed for a few apps and every time I have to either pray that printing to pdf works (as of thus, 1/4), then having to botch something together using plotly and/or fdpf.
This is an ultra experimental CSS Hack, just to see if it works on some other browsers and apps than mine, plus it doesn't take into account theme background, the dimensions and margins are not correct, plotly charts are reset before printing because there's a rerun implied, the sidebar is missing and probably a lot of other buggy stuff...here's a link to my current experiment.
- It uses html2canvas to save an HTML Element into a canvas, that can be printed into a PDF.
- It uses jspdf to split the canvas into a multipage PDF file
The goal is to use html2canvas to render the Streamlit app as is, and then jspdf to create the pdf
I'd love to hear back from you if you're testing this on your side and improving on the snippet.
- Step 1
At the beginning of your script:
@st.experimental_memo
def load_unpkg(src: str) -> str:
return requests.get(src).text
HTML_2_CANVAS = load_unpkg("https://unpkg.com/[email protected]/dist/html2canvas.js")
JSPDF = load_unpkg("https://unpkg.com/jspdf@latest/dist/jspdf.umd.min.js")
BUTTON_TEXT = "Create PDF"
This will load the js source code of the necessary packages
- Step 2
Somewhere in your app:
with st.sidebar:
st.slider("Slide to reset ability to pdf", 0, 100)
if st.button(BUTTON_TEXT):
components.html(
f"""
<script>{HTML_2_CANVAS}</script>
<script>{JSPDF}</script>
<script>
const html2canvas = window.html2canvas
const {{ jsPDF }} = window.jspdf
const streamlitDoc = window.parent.document;
const stApp = streamlitDoc.querySelector('.main > .block-container');
const buttons = Array.from(streamlitDoc.querySelectorAll('.stButton > button'));
const pdfButton = buttons.find(el => el.innerText === '{BUTTON_TEXT}');
const docHeight = stApp.scrollHeight;
const docWidth = stApp.scrollWidth;
let topLeftMargin = 15;
let pdfWidth = docHeight + (topLeftMargin * 2);
let pdfHeight = (pdfWidth * 1.5) + (topLeftMargin * 2);
let canvasImageWidth = docWidth;
let canvasImageHeight = docHeight;
let totalPDFPages = Math.ceil(docHeight / pdfHeight)-1;
pdfButton.innerText = 'Creating PDF...';
html2canvas(stApp, {{ allowTaint: true }}).then(function (canvas) {{
canvas.getContext('2d');
let imgData = canvas.toDataURL("image/jpeg", 1.0);
let pdf = new jsPDF('p', 'px', [pdfWidth, pdfHeight]);
pdf.addImage(imgData, 'JPG', topLeftMargin, topLeftMargin, canvasImageWidth, canvasImageHeight);
for (var i = 1; i <= totalPDFPages; i++) {{
pdf.addPage();
pdf.addImage(imgData, 'JPG', topLeftMargin, -(pdfHeight * i) + (topLeftMargin*4), canvasImageWidth, canvasImageHeight);
}}
pdf.save('test.pdf');
pdfButton.innerText = '{BUTTON_TEXT}';
}})
</script>
""",
height=0,
width=0,
)
In the html2canvas(stApp... you notice we are injecting the scrollable element of Streamlit, which doesn't have the background color nor the sidebar, but is the only way to get the true scrollable height and the full contents of the app.
If one wants to go further into this (even the dev team!), I suspect you need to html2canvas the sidebar and main content separately and organize them as you wish on the PDF with jspdf. It looks doable, but a pain to do. Printing HTML as PDF is just a pain 😆 actually PDF is just a pain...
I'll keep digging a bit (and maybe make a Youtube video about this) but absolutely can't guarantee further results than this.
Happy Streamlitin', Fanilo
Resources:
- https://www.freakyjolly.com/multipage-canvas-pdf-using-jspdf/
Hi Johannes,
Thanks for getting back here. I think if streamlit pages were able to print directly from chrome (print to PDF) that would be helpful - my question would be whether the print to PDF would allow for printing just what the user can see or the entire streamlit page (if the page is long). The first will probably work but feels a little hacky, while the latter seems pretty good.
Thanks for sharing the link to the roadmap too, I hadn't seen that before but it's definitely useful. In fact, the item "Save front end app state as a static report" might be a good substitute for what I'm looking for, but I'll have to ask others whether that's as good as and/or better than the PDF option (and I know that it doesn't have a specific timeline yet, so not immediately an option). One question/thought there is around the permissioning - if the report is static, is it implied that the person receiving that report can't do anything with it or does it imply that they will start with a pre-loaded state and can then still make updates? I can imagine both being useful for different cases/users.
I appreciate the detail on closing forum items after 1 year too, I saw a name associated with the closing so I assumed someone was going in and doing that.
On Wed, Jul 27, 2022 at 9:42 AM Johannes Rieke @.***> wrote:
Ah digging through the issues now. Seems like printing to PDF (via the browser) used to work and we fixed it at several points in time (see the issue/PR you linked). But I guess we just broke it again at some point. I'll add an item to our roadmap to at least fix that. Do you think you could live with that if printing through the browser works well, or does that not solve your problem?
(Btw posts on the forum get closed automatically after 1 year, so closed != won't do).
— Reply to this email directly, view it on GitHub https://github.com/streamlit/streamlit/issues/5020#issuecomment-1196779474, or unsubscribe https://github.com/notifications/unsubscribe-auth/AYMTQIP4O6YERO25DKTU5RTVWE4DTANCNFSM54MLCE7A . You are receiving this because you were mentioned.Message ID: @.***>
Update
Forgot to update here but we did a bunch of work on this issue earlier this year:
- We added a "Print..." option to the app menu in the top right (docs, see image below). Clicking this will send the correct data to the print dialog, so you can either print the page or save it to PDF.
- Printing through the browser itself (e.g. by pressing ctrl+P or going to the browser's menu and clicking "Print") does not work for apps on Community Cloud. There's unfortunately no way to make this work since the page on Community Cloud is iframed, so the browser will only pick up a part of the app. Please use the option in the app menu described above.
- We fixed all of the visual issues that we had in the past when printing. All elements should be properly printed to the page now. If you see any issues, please open a bug report on GitHub!
We don't have any plans to implement a true "Export as PDF" feature right into Streamlit since the solution above should work in most cases. We might still do this in the future so leaving this issue open.
Thanks for the update!
I'm just looking at this for the first time and while it's definitely better than before, I'm seeing some weird alignment when I go to print:
- The title of my page starts on the bottom of page 1 in the printed view
- Even when I have the sidebar hidden and go to landscape, the center of my web app is to the right of center on the printed page, so some is getting cut off on the right
Are other people seeing the same thing or is there a different way to get things aligned correctly?
@msquaredds Can you open separate GitHub issues for these things and mark them as bugs? If it's just a comment in here, we'll lose track of it.
Yeah will do that now