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 |
+
)
|