You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Streamlit应用页面导出为标准尺寸PDF文件?

解决Streamlit页面导出适配11x8.5纸张的PDF问题

问题背景

我在@MathCatsAnd帮助下创建了一个Streamlit应用,每个页面包含多个DataFrame(示例简化为每页一个)。需求是将每个页面导出为标准11x8.5英寸的PDF文件,需让26列的表格适配纸张宽度,纵向内容自动分页。但原生Ctrl+P无法正常实现,且网上找不到相关解决方案。

应用示例代码如下:

import streamlit as st
import pandas as pd
import numpy as np

# Initialize session state with dataframes
# Include initialization of "edited" slots by copying originals
if 'df1' not in st.session_state:
    st.title("**:blue[Title123]**")
    Nvals = 200
    rows = ['a{}'.format(i) for i in range(1, Nvals+1)] # a1:a19
    values = np.arange(Nvals)
    st.session_state.df1 = pd.DataFrame({
        "rows/cols": rows, 
        'A': list(range(Nvals)), 'B': list(range(Nvals)), 'C': list(range(Nvals)), 'D': list(range(Nvals)), 'E': list(range(Nvals)),
        'F': list(range(Nvals)), 'G': list(range(Nvals)), 'H': list(range(Nvals)), 'I': list(range(Nvals)), 'J': list(range(Nvals)),
        'K': list(range(Nvals)), 'L': list(range(Nvals)), 'M': list(range(Nvals)), 'N': list(range(Nvals)), 'Q': list(range(Nvals)),
        'O': list(range(Nvals)), 'P': list(range(Nvals)), 'R': list(range(Nvals)), 'S': list(range(Nvals)), 'T': list(range(Nvals)),
        'U': list(range(Nvals)), 'V': list(range(Nvals)), 'W': list(range(Nvals)), 'X': list(range(Nvals)), 'Y': list(range(Nvals)), 'Z': list(range(Nvals)),
    })
    st.session_state.edited_df1 = st.session_state.df1.copy()
    #
    st.session_state.df2 = pd.DataFrame({
        "col1": ["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "b10"], 
        "Values": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
        "col2": ["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "b10"], 
        "Values": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
        "col3": ["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "b10"], 
        "Values": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    })
    st.session_state.edited_df2 = st.session_state.df2.copy()

# Save edits by copying edited dataframes to "original" slots in session state
def on_change_callback_fcn():
    st.session_state.df1 = st.session_state.edited_df1 
    st.session_state.df2 = st.session_state.edited_df2

# Sidebar to select page and commit changes upon selection
page = st.sidebar.selectbox("Select: ", ("A","B"), on_change=on_change_callback_fcn, key='SelectedSelectboxVal') 
# Convenient shorthand notation used below in funct1 and funct2
def funct1():
    st.session_state.edited_df1 = st.data_editor(st.session_state.df1, num_rows="dynamic",height=10*len(st.session_state.edited_df1)) 

def funct2():
    st.session_state.edited_df2 = st.data_editor(st.session_state.df2, num_rows="dynamic")

if  page == "A":
    st.header("Page A")
    funct1()
elif page == "B":
    st.header("Page B")
    funct2()

print('\n ...................... \n Entire Code Ran \n ...................... \n')
print(len(st.session_state.edited_df1))

解决方案

方案1:自定义CSS优化原生打印效果

通过添加打印专用CSS,强制页面适配11x8.5英寸纸张,调整表格布局并隐藏无关元素。

修改后的代码如下:

import streamlit as st
import pandas as pd
import numpy as np

# 添加打印专用CSS
st.markdown("""
<style>
@media print {
    @page {
        size: 11in 8.5in; /* 设置纸张尺寸为横向11x8.5 */
        margin: 0.5in;
    }
    /* 隐藏Streamlit侧边栏、顶部导航等无关元素 */
    section[data-testid="stSidebar"], header[data-testid="stHeader"], footer[data-testid="stFooter"] {
        display: none !important;
    }
    /* 让主内容区域占满纸张宽度 */
    div[data-testid="stVerticalBlock"] {
        width: 100% !important;
        max-width: 100% !important;
    }
    /* 调整表格样式,让列适配宽度 */
    table {
        width: 100% !important;
        font-size: 8pt; /* 缩小字体适配多列 */
        border-collapse: collapse !important;
    }
    th, td {
        border: 1px solid #ddd !important;
        padding: 2pt !important;
        word-wrap: break-word;
    }
    /* 禁止表格横向溢出 */
    div[data-testid="stDataFrame"] {
        overflow-x: visible !important;
    }
}
</style>
""", unsafe_allow_html=True)

# Initialize session state with dataframes
# Include initialization of "edited" slots by copying originals
if 'df1' not in st.session_state:
    st.title("**:blue[Title123]**")
    Nvals = 200
    rows = ['a{}'.format(i) for i in range(1, Nvals+1)] # a1:a19
    values = np.arange(Nvals)
    st.session_state.df1 = pd.DataFrame({
        "rows/cols": rows, 
        'A': list(range(Nvals)), 'B': list(range(Nvals)), 'C': list(range(Nvals)), 'D': list(range(Nvals)), 'E': list(range(Nvals)),
        'F': list(range(Nvals)), 'G': list(range(Nvals)), 'H': list(range(Nvals)), 'I': list(range(Nvals)), 'J': list(range(Nvals)),
        'K': list(range(Nvals)), 'L': list(range(Nvals)), 'M': list(range(Nvals)), 'N': list(range(Nvals)), 'Q': list(range(Nvals)),
        'O': list(range(Nvals)), 'P': list(range(Nvals)), 'R': list(range(Nvals)), 'S': list(range(Nvals)), 'T': list(range(Nvals)),
        'U': list(range(Nvals)), 'V': list(range(Nvals)), 'W': list(range(Nvals)), 'X': list(range(Nvals)), 'Y': list(range(Nvals)), 'Z': list(range(Nvals)),
    })
    st.session_state.edited_df1 = st.session_state.df1.copy()
    #
    st.session_state.df2 = pd.DataFrame({
        "col1": ["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "b10"], 
        "Values": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
        "col2": ["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "b10"], 
        "Values": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
        "col3": ["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "b10"], 
        "Values": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    })
    st.session_state.edited_df2 = st.session_state.df2.copy()

# Save edits by copying edited dataframes to "original" slots in session state
def on_change_callback_fcn():
    st.session_state.df1 = st.session_state.edited_df1 
    st.session_state.df2 = st.session_state.edited_df2

# Sidebar to select page and commit changes upon selection
page = st.sidebar.selectbox("Select: ", ("A","B"), on_change=on_change_callback_fcn, key='SelectedSelectboxVal') 
# 添加打印按钮
if st.button(f"打印当前页面 {page}"):
    st.markdown('<script>window.print();</script>', unsafe_allow_html=True)

# Convenient shorthand notation used below in funct1 and funct2
def funct1():
    st.session_state.edited_df1 = st.data_editor(st.session_state.df1, num_rows="dynamic",height=10*len(st.session_state.edited_df1)) 

def funct2():
    st.session_state.edited_df2 = st.data_editor(st.session_state.df2, num_rows="dynamic")

if  page == "A":
    st.header("Page A")
    funct1()
elif page == "B":
    st.header("Page B")
    funct2()

使用说明:

  • 运行应用后,切换到目标页面,点击"打印当前页面"按钮触发打印
  • 在打印预览中确认纸张尺寸为11x8.5英寸(横向),调整缩放比例确保列完全适配

方案2:用Pandas直接生成适配的PDF

如果原生打印仍有问题,可将编辑后的DataFrame直接导出为格式适配的PDF,需安装pdfkit和依赖的wkhtmltopdf工具。

步骤:

  1. 安装依赖:
pip install pdfkit
  1. 下载安装wkhtmltopdf(根据系统选择对应版本)
  2. 添加导出PDF的代码:

在原代码中添加如下函数,并在页面中添加导出按钮:

import pdfkit

def export_to_pdf(df, page_name):
    # 将DataFrame转为HTML,添加样式
    html = df.to_html(index=False, border=1)
    css = """
    <style>
        table { width: 100%; font-size: 8pt; border-collapse: collapse; }
        th, td { border: 1px solid #000; padding: 3px; }
    </style>
    """
    full_html = f"<html><head>{css}</head><body><h1>{page_name}</h1>{html}</body></html>"
    
    # 设置PDF选项,指定纸张尺寸
    options = {
        'page-size': 'Letter', # Letter尺寸为8.5x11英寸,这里设置横向
        'orientation': 'Landscape',
        'margin-top': '0.5in',
        'margin-right': '0.5in',
        'margin-bottom': '0.5in',
        'margin-left': '0.5in',
        'encoding': "UTF-8",
    }
    
    # 生成PDF
    pdfkit.from_string(full_html, f"{page_name}.pdf", options=options)
    st.success(f"PDF已导出为 {page_name}.pdf")

# 在页面中添加导出按钮
if page == "A":
    st.header("Page A")
    funct1()
    if st.button("导出Page A为PDF"):
        export_to_pdf(st.session_state.edited_df1, "Page A")
elif page == "B":
    st.header("Page B")
    funct2()
    if st.button("导出Page B为PDF"):
        export_to_pdf(st.session_state.edited_df2, "Page B")

注意:需确保wkhtmltopdf已添加到系统环境变量中,或在pdfkit.from_string中指定configuration参数指向wkhtmltopdf的路径。

内容的提问来源于stack exchange,提问作者user21968603

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 19:12:01