Update app.py
Browse files
app.py
CHANGED
|
@@ -464,23 +464,60 @@ def create_selflinking_pdf(pdf_file="SelfLinking.pdf"):
|
|
| 464 |
|
| 465 |
# ๐ผ๏ธ 13. Image-linked PDF creator - wisdom: Link images to text, like windows opening to new worlds!
|
| 466 |
def create_pdf_with_images(source_pdf_bytes, output_pdf="ImageLinked.pdf"):
|
| 467 |
-
"""Create a PDF with links to
|
| 468 |
image_files = sorted(glob.glob("*.png"))
|
| 469 |
if not source_pdf_bytes:
|
| 470 |
st.error("No source PDF provided.")
|
| 471 |
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
| 472 |
reader = PdfReader(io.BytesIO(source_pdf_bytes))
|
| 473 |
writer = PdfWriter()
|
|
|
|
|
|
|
|
|
|
| 474 |
for page in reader.pages:
|
| 475 |
writer.add_page(page)
|
| 476 |
-
|
| 477 |
-
|
| 478 |
-
|
| 479 |
-
for
|
| 480 |
-
|
| 481 |
-
|
| 482 |
-
|
| 483 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 484 |
buffer = io.BytesIO()
|
| 485 |
c = canvas.Canvas(buffer)
|
| 486 |
c.setFont("Helvetica", 8)
|
|
@@ -490,12 +527,16 @@ def create_pdf_with_images(source_pdf_bytes, output_pdf="ImageLinked.pdf"):
|
|
| 490 |
buffer.seek(0)
|
| 491 |
text_pdf = PdfReader(buffer)
|
| 492 |
page.merge_page(text_pdf.pages[0])
|
|
|
|
|
|
|
| 493 |
link = Link(
|
| 494 |
rect=(90, y - 10, 150, y + 10),
|
| 495 |
-
|
|
|
|
| 496 |
)
|
| 497 |
writer.add_annotation(page_number=0, annotation=link)
|
| 498 |
buffer.close()
|
|
|
|
| 499 |
output_buffer = io.BytesIO()
|
| 500 |
writer.write(output_buffer)
|
| 501 |
output_buffer.seek(0)
|
|
@@ -507,45 +548,15 @@ def create_pdf_with_images(source_pdf_bytes, output_pdf="ImageLinked.pdf"):
|
|
| 507 |
md_files = [f for f in glob.glob("*.md") if os.path.basename(f) != "README.md"]
|
| 508 |
md_options = [os.path.splitext(os.path.basename(f))[0] for f in md_files]
|
| 509 |
|
| 510 |
-
# ๐ฅ๏ธ 15. Main PDF generation - wisdom: Spin up the PDF like a weaver at the loom, crafting beauty!
|
| 511 |
-
with st.spinner("Generating PDF..."):
|
| 512 |
-
# Initialize defaults for PDF generation
|
| 513 |
-
selected_md = None
|
| 514 |
-
base_font_size = 8
|
| 515 |
-
num_columns = 3
|
| 516 |
-
add_space_before_numbered = True
|
| 517 |
-
headings_to_fonts = True
|
| 518 |
-
longest_line_words = 0
|
| 519 |
-
total_lines = 0
|
| 520 |
-
|
| 521 |
-
# Calculate document stats if markdown exists
|
| 522 |
-
if 'markdown_content' in st.session_state and st.session_state.markdown_content.strip():
|
| 523 |
-
current_markdown = st.session_state.markdown_content
|
| 524 |
-
lines = current_markdown.strip().split('\n')
|
| 525 |
-
total_lines = len([line for line in lines if line.strip()])
|
| 526 |
-
for line in lines:
|
| 527 |
-
if line.strip():
|
| 528 |
-
word_count = len(line.split())
|
| 529 |
-
longest_line_words = max(longest_line_words, word_count)
|
| 530 |
-
|
| 531 |
-
pdf_bytes = create_pdf(
|
| 532 |
-
st.session_state.get('markdown_content', ''),
|
| 533 |
-
base_font_size,
|
| 534 |
-
num_columns,
|
| 535 |
-
add_space_before_numbered,
|
| 536 |
-
headings_to_fonts,
|
| 537 |
-
doc_title=selected_md if selected_md else "Untitled",
|
| 538 |
-
longest_line_words=longest_line_words,
|
| 539 |
-
total_lines=total_lines
|
| 540 |
-
)
|
| 541 |
-
|
| 542 |
with st.sidebar:
|
| 543 |
# ๐ 14.1 Markdown selector - wisdom: Offer choices like a librarian, guiding users to their story!
|
| 544 |
st.markdown("### ๐ PDF Options")
|
| 545 |
if md_options:
|
| 546 |
-
selected_md = st.selectbox("Select Markdown File", options=md_options, index=0)
|
| 547 |
-
|
| 548 |
-
|
|
|
|
|
|
|
| 549 |
else:
|
| 550 |
st.warning("No markdown file found. Please add one to your folder.")
|
| 551 |
selected_md = None
|
|
@@ -706,22 +717,18 @@ with st.sidebar:
|
|
| 706 |
mime="application/pdf"
|
| 707 |
)
|
| 708 |
|
| 709 |
-
|
| 710 |
-
|
| 711 |
-
|
| 712 |
-
|
| 713 |
-
|
| 714 |
-
|
| 715 |
-
|
| 716 |
-
|
| 717 |
-
|
| 718 |
-
|
| 719 |
-
|
| 720 |
-
|
| 721 |
-
data=linked_pdf_bytes,
|
| 722 |
-
file_name=f"{prefix} {selected_md}_image_linked.pdf" if selected_md else f"{prefix} image_linked.pdf",
|
| 723 |
-
mime="application/pdf"
|
| 724 |
-
)
|
| 725 |
|
| 726 |
# ๐ผ๏ธ 16. PDF preview - wisdom: Show the masterpiece before itโs framed, delighting the creator!
|
| 727 |
with st.container():
|
|
@@ -733,12 +740,29 @@ with st.container():
|
|
| 733 |
else:
|
| 734 |
st.info("Download the PDF to view it locally.")
|
| 735 |
|
| 736 |
-
# ๐พ 17. PDF saver - wisdom: Offer the final scroll, ready to be shared like wisdom across ages!
|
| 737 |
with st.sidebar:
|
|
|
|
| 738 |
st.download_button(
|
| 739 |
label="๐พ Save PDF",
|
| 740 |
data=pdf_bytes if pdf_bytes else "",
|
| 741 |
file_name=f"{prefix} {selected_md}.pdf" if selected_md else f"{prefix} output.pdf",
|
| 742 |
mime="application/pdf",
|
| 743 |
disabled=pdf_bytes is None
|
| 744 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 464 |
|
| 465 |
# ๐ผ๏ธ 13. Image-linked PDF creator - wisdom: Link images to text, like windows opening to new worlds!
|
| 466 |
def create_pdf_with_images(source_pdf_bytes, output_pdf="ImageLinked.pdf"):
|
| 467 |
+
"""Create a PDF with links on numbered headings to new pages with images."""
|
| 468 |
image_files = sorted(glob.glob("*.png"))
|
| 469 |
if not source_pdf_bytes:
|
| 470 |
st.error("No source PDF provided.")
|
| 471 |
return None
|
| 472 |
+
if not image_files:
|
| 473 |
+
st.error("No PNG images found in the directory.")
|
| 474 |
+
return source_pdf_bytes
|
| 475 |
+
|
| 476 |
reader = PdfReader(io.BytesIO(source_pdf_bytes))
|
| 477 |
writer = PdfWriter()
|
| 478 |
+
|
| 479 |
+
# Copy all pages from source PDF
|
| 480 |
+
original_page_count = len(reader.pages)
|
| 481 |
for page in reader.pages:
|
| 482 |
writer.add_page(page)
|
| 483 |
+
|
| 484 |
+
# Add image pages
|
| 485 |
+
image_page_indices = []
|
| 486 |
+
for image_file in image_files[:12]: # Limit to 12 images
|
| 487 |
+
buffer = io.BytesIO()
|
| 488 |
+
c = canvas.Canvas(buffer, pagesize=A4)
|
| 489 |
+
try:
|
| 490 |
+
img = Image.open(image_file)
|
| 491 |
+
img_width, img_height = img.size
|
| 492 |
+
page_width, page_height = A4
|
| 493 |
+
scale = min((page_width - 40) / img_width, (page_height - 40) / img_height)
|
| 494 |
+
new_width = img_width * scale
|
| 495 |
+
new_height = img_height * scale
|
| 496 |
+
x = (page_width - new_width) / 2
|
| 497 |
+
y = (page_height - new_height) / 2
|
| 498 |
+
c.drawImage(image_file, x, y, new_width, new_height)
|
| 499 |
+
c.showPage()
|
| 500 |
+
c.save()
|
| 501 |
+
buffer.seek(0)
|
| 502 |
+
img_pdf = PdfReader(buffer)
|
| 503 |
+
writer.add_page(img_pdf.pages[0])
|
| 504 |
+
image_page_indices.append(original_page_count + len(image_page_indices))
|
| 505 |
+
buffer.close()
|
| 506 |
+
except Exception as e:
|
| 507 |
+
st.error(f"Failed to process image {image_file}: {e}")
|
| 508 |
+
buffer.close()
|
| 509 |
+
continue
|
| 510 |
+
|
| 511 |
+
# Add links to numbered headings on first page
|
| 512 |
+
if image_page_indices:
|
| 513 |
+
page = writer.pages[0]
|
| 514 |
+
y_positions = []
|
| 515 |
+
for i in range(1, 13):
|
| 516 |
+
y = 800 - (i * 20) # Matches layout from create_pdf
|
| 517 |
+
y_positions.append(y)
|
| 518 |
+
|
| 519 |
+
for idx, (y, target_page_idx) in enumerate(zip(y_positions, image_page_indices)):
|
| 520 |
+
# Add "link" text
|
| 521 |
buffer = io.BytesIO()
|
| 522 |
c = canvas.Canvas(buffer)
|
| 523 |
c.setFont("Helvetica", 8)
|
|
|
|
| 527 |
buffer.seek(0)
|
| 528 |
text_pdf = PdfReader(buffer)
|
| 529 |
page.merge_page(text_pdf.pages[0])
|
| 530 |
+
|
| 531 |
+
# Add link annotation
|
| 532 |
link = Link(
|
| 533 |
rect=(90, y - 10, 150, y + 10),
|
| 534 |
+
target_page_index=target_page_idx,
|
| 535 |
+
fit=Fit(fit_type="/Fit")
|
| 536 |
)
|
| 537 |
writer.add_annotation(page_number=0, annotation=link)
|
| 538 |
buffer.close()
|
| 539 |
+
|
| 540 |
output_buffer = io.BytesIO()
|
| 541 |
writer.write(output_buffer)
|
| 542 |
output_buffer.seek(0)
|
|
|
|
| 548 |
md_files = [f for f in glob.glob("*.md") if os.path.basename(f) != "README.md"]
|
| 549 |
md_options = [os.path.splitext(os.path.basename(f))[0] for f in md_files]
|
| 550 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 551 |
with st.sidebar:
|
| 552 |
# ๐ 14.1 Markdown selector - wisdom: Offer choices like a librarian, guiding users to their story!
|
| 553 |
st.markdown("### ๐ PDF Options")
|
| 554 |
if md_options:
|
| 555 |
+
selected_md = st.selectbox("Select Markdown File", options=md_options, index=0, key="markdown_select")
|
| 556 |
+
if selected_md != st.session_state.get('last_selected_md'):
|
| 557 |
+
with open(f"{selected_md}.md", "r", encoding="utf-8") as f:
|
| 558 |
+
st.session_state.markdown_content = f.read()
|
| 559 |
+
st.session_state.last_selected_md = selected_md
|
| 560 |
else:
|
| 561 |
st.warning("No markdown file found. Please add one to your folder.")
|
| 562 |
selected_md = None
|
|
|
|
| 717 |
mime="application/pdf"
|
| 718 |
)
|
| 719 |
|
| 720 |
+
# ๐ฅ๏ธ 15. Main PDF generation - wisdom: Spin up the PDF like a weaver at the loom, crafting beauty!
|
| 721 |
+
with st.spinner("Generating PDF..."):
|
| 722 |
+
pdf_bytes = create_pdf(
|
| 723 |
+
st.session_state.get('markdown_content', ''),
|
| 724 |
+
base_font_size,
|
| 725 |
+
num_columns,
|
| 726 |
+
add_space_before_numbered,
|
| 727 |
+
headings_to_fonts,
|
| 728 |
+
doc_title=selected_md if selected_md else "Untitled",
|
| 729 |
+
longest_line_words=longest_line_words,
|
| 730 |
+
total_lines=total_lines
|
| 731 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 732 |
|
| 733 |
# ๐ผ๏ธ 16. PDF preview - wisdom: Show the masterpiece before itโs framed, delighting the creator!
|
| 734 |
with st.container():
|
|
|
|
| 740 |
else:
|
| 741 |
st.info("Download the PDF to view it locally.")
|
| 742 |
|
|
|
|
| 743 |
with st.sidebar:
|
| 744 |
+
# ๐พ 17. PDF saver - wisdom: Offer the final scroll, ready to be shared like wisdom across ages!
|
| 745 |
st.download_button(
|
| 746 |
label="๐พ Save PDF",
|
| 747 |
data=pdf_bytes if pdf_bytes else "",
|
| 748 |
file_name=f"{prefix} {selected_md}.pdf" if selected_md else f"{prefix} output.pdf",
|
| 749 |
mime="application/pdf",
|
| 750 |
disabled=pdf_bytes is None
|
| 751 |
+
)
|
| 752 |
+
|
| 753 |
+
if st.button("๐ผ๏ธ Generate PDF With Images"):
|
| 754 |
+
with st.spinner("Generating PDF with image links..."):
|
| 755 |
+
linked_pdf_bytes = create_pdf_with_images(pdf_bytes)
|
| 756 |
+
if linked_pdf_bytes and linked_pdf_bytes != pdf_bytes:
|
| 757 |
+
st.success("Generated PDF with image links")
|
| 758 |
+
images = pdf_to_image(linked_pdf_bytes)
|
| 759 |
+
if images:
|
| 760 |
+
st.subheader("Preview of Image-Linked PDF")
|
| 761 |
+
for i, img in enumerate(images):
|
| 762 |
+
st.image(img, caption=f"Image-Linked PDF Page {i+1}", use_container_width=True)
|
| 763 |
+
st.download_button(
|
| 764 |
+
label="๐พ Download Image-Linked PDF",
|
| 765 |
+
data=linked_pdf_bytes,
|
| 766 |
+
file_name=f"{prefix} {selected_md}_image_linked.pdf" if selected_md else f"{prefix} image_linked.pdf",
|
| 767 |
+
mime="application/pdf"
|
| 768 |
+
)
|