Update app.py
Browse files
app.py
CHANGED
@@ -179,6 +179,8 @@ def markdown_to_pdf_content(markdown_text, add_space_before_numbered, headings_t
|
|
179 |
return pdf_content, total_lines
|
180 |
|
181 |
def create_pdf(markdown_text, base_font_size, num_columns, add_space_before_numbered, headings_to_fonts, doc_title, longest_line_words, total_lines):
|
|
|
|
|
182 |
buffer = io.BytesIO()
|
183 |
page_width = A4[0] * 2
|
184 |
page_height = A4[1]
|
@@ -198,14 +200,14 @@ def create_pdf(markdown_text, base_font_size, num_columns, add_space_before_numb
|
|
198 |
available_font_files = glob.glob("*.ttf")
|
199 |
if not available_font_files:
|
200 |
st.error("No .ttf font files found.")
|
201 |
-
return
|
202 |
selected_font_path = next((f for f in available_font_files if "NotoEmoji-Bold" in f), None)
|
203 |
if selected_font_path:
|
204 |
pdfmetrics.registerFont(TTFont("NotoEmoji-Bold", selected_font_path))
|
205 |
pdfmetrics.registerFont(TTFont("DejaVuSans", "DejaVuSans.ttf"))
|
206 |
except Exception as e:
|
207 |
st.error(f"Font registration error: {e}")
|
208 |
-
return
|
209 |
total_chars = sum(len(line) for line in pdf_content)
|
210 |
hierarchy_weight = sum(1.5 if line.startswith("<b>") else 1 for line in pdf_content)
|
211 |
content_density = total_lines * hierarchy_weight + total_chars / 50
|
@@ -216,7 +218,7 @@ def create_pdf(markdown_text, base_font_size, num_columns, add_space_before_numb
|
|
216 |
suggested_columns = max(2, min(4, int(total_lines / ideal_lines_per_col) + 1))
|
217 |
num_columns = num_columns if num_columns != 0 else suggested_columns
|
218 |
col_width = usable_width / num_columns
|
219 |
-
min_font_size =
|
220 |
max_font_size = 16
|
221 |
lines_per_col = total_lines / num_columns if num_columns > 0 else total_lines
|
222 |
target_height_per_line = usable_height / lines_per_col if lines_per_col > 0 else usable_height
|
@@ -226,10 +228,10 @@ def create_pdf(markdown_text, base_font_size, num_columns, add_space_before_numb
|
|
226 |
adjusted_font_size = int(col_width / (avg_line_chars / 10))
|
227 |
adjusted_font_size = max(min_font_size, adjusted_font_size)
|
228 |
|
229 |
-
#
|
230 |
-
if longest_line_words > 17:
|
231 |
-
font_scale = 17 / longest_line_words
|
232 |
-
adjusted_font_size = max(min_font_size, int(
|
233 |
|
234 |
item_style = ParagraphStyle(
|
235 |
'ItemStyle', parent=styles['Normal'], fontName="DejaVuSans",
|
@@ -309,6 +311,8 @@ def create_pdf(markdown_text, base_font_size, num_columns, add_space_before_numb
|
|
309 |
return buffer.getvalue()
|
310 |
|
311 |
def pdf_to_image(pdf_bytes):
|
|
|
|
|
312 |
try:
|
313 |
doc = fitz.open(stream=pdf_bytes, filetype="pdf")
|
314 |
images = []
|
@@ -325,7 +329,8 @@ def pdf_to_image(pdf_bytes):
|
|
325 |
# PDF creation and linking functions
|
326 |
WORDS_12 = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve"]
|
327 |
WORDS_24 = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten",
|
328 |
-
"eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty",
|
|
|
329 |
|
330 |
def create_crossfile_pdfs(source_pdf="TestSource.pdf", target_pdf="TestTarget.pdf"):
|
331 |
"""Create two PDFs with cross-file linking."""
|
@@ -405,7 +410,7 @@ def create_crossfile_pdfs(source_pdf="TestSource.pdf", target_pdf="TestTarget.pd
|
|
405 |
return source_pdf, target_pdf
|
406 |
|
407 |
def create_selflinking_pdf(pdf_file="SelfLinking.pdf"):
|
408 |
-
"""Create a PDF with a TOC on page 1 linking to a 1-
|
409 |
buffer = io.BytesIO()
|
410 |
c = canvas.Canvas(buffer)
|
411 |
|
@@ -420,7 +425,7 @@ def create_selflinking_pdf(pdf_file="SelfLinking.pdf"):
|
|
420 |
toc_y_positions.append(y)
|
421 |
c.showPage()
|
422 |
|
423 |
-
# Page 2: Numbered list 1-
|
424 |
c.setFont("Helvetica", 12)
|
425 |
list_y_positions = []
|
426 |
for i, word in enumerate(WORDS_24, 1):
|
@@ -490,10 +495,10 @@ with st.sidebar:
|
|
490 |
)
|
491 |
base_font_size = st.slider("Font Size (points)", min_value=6, max_value=16, value=8, step=1)
|
492 |
|
493 |
-
add_space_before_numbered = st.checkbox("Add Space Ahead of Numbered Lines", value=
|
494 |
headings_to_fonts = st.checkbox(
|
495 |
"Headings to Fonts",
|
496 |
-
value=
|
497 |
help="Convert Markdown headings (# Heading) to styled fonts"
|
498 |
)
|
499 |
auto_columns = st.checkbox("AutoColumns", value=True)
|
@@ -502,7 +507,7 @@ with st.sidebar:
|
|
502 |
longest_line_words = 0
|
503 |
total_lines = 0
|
504 |
adjusted_font_size_display = base_font_size
|
505 |
-
if 'markdown_content' in st.session_state:
|
506 |
current_markdown = st.session_state.markdown_content
|
507 |
lines = current_markdown.strip().split('\n')
|
508 |
total_lines = len([line for line in lines if line.strip()])
|
@@ -520,20 +525,26 @@ with st.sidebar:
|
|
520 |
else:
|
521 |
recommended_columns = 3
|
522 |
# Adjust font size for one-page fit
|
523 |
-
if longest_line_words > 17:
|
524 |
-
font_scale = 17 / longest_line_words
|
525 |
-
adjusted_font_size_display = max(
|
526 |
st.markdown("**Document Stats**")
|
527 |
st.write(f"- Longest Line: {longest_line_words} words")
|
528 |
st.write(f"- Total Lines: {total_lines}")
|
529 |
st.write(f"- Recommended Columns: {recommended_columns}")
|
530 |
st.write(f"- Adjusted Font Size: {adjusted_font_size_display} points")
|
|
|
|
|
|
|
|
|
|
|
|
|
531 |
|
532 |
column_options = [2, 3, 4]
|
533 |
num_columns = st.selectbox(
|
534 |
"Number of Columns",
|
535 |
options=column_options,
|
536 |
-
index=column_options.index(recommended_columns)
|
537 |
)
|
538 |
st.info("Font size and columns adjust to fit one page.")
|
539 |
|
@@ -645,7 +656,8 @@ with st.container():
|
|
645 |
with st.sidebar:
|
646 |
st.download_button(
|
647 |
label="๐พ Save PDF",
|
648 |
-
data=pdf_bytes,
|
649 |
file_name=f"{prefix} {selected_md}.pdf" if selected_md else f"{prefix} output.pdf",
|
650 |
-
mime="application/pdf"
|
|
|
651 |
)
|
|
|
179 |
return pdf_content, total_lines
|
180 |
|
181 |
def create_pdf(markdown_text, base_font_size, num_columns, add_space_before_numbered, headings_to_fonts, doc_title, longest_line_words, total_lines):
|
182 |
+
if not markdown_text.strip():
|
183 |
+
return None # Handle empty markdown gracefully
|
184 |
buffer = io.BytesIO()
|
185 |
page_width = A4[0] * 2
|
186 |
page_height = A4[1]
|
|
|
200 |
available_font_files = glob.glob("*.ttf")
|
201 |
if not available_font_files:
|
202 |
st.error("No .ttf font files found.")
|
203 |
+
return None
|
204 |
selected_font_path = next((f for f in available_font_files if "NotoEmoji-Bold" in f), None)
|
205 |
if selected_font_path:
|
206 |
pdfmetrics.registerFont(TTFont("NotoEmoji-Bold", selected_font_path))
|
207 |
pdfmetrics.registerFont(TTFont("DejaVuSans", "DejaVuSans.ttf"))
|
208 |
except Exception as e:
|
209 |
st.error(f"Font registration error: {e}")
|
210 |
+
return None
|
211 |
total_chars = sum(len(line) for line in pdf_content)
|
212 |
hierarchy_weight = sum(1.5 if line.startswith("<b>") else 1 for line in pdf_content)
|
213 |
content_density = total_lines * hierarchy_weight + total_chars / 50
|
|
|
218 |
suggested_columns = max(2, min(4, int(total_lines / ideal_lines_per_col) + 1))
|
219 |
num_columns = num_columns if num_columns != 0 else suggested_columns
|
220 |
col_width = usable_width / num_columns
|
221 |
+
min_font_size = 5 # Reduced to allow tighter fit
|
222 |
max_font_size = 16
|
223 |
lines_per_col = total_lines / num_columns if num_columns > 0 else total_lines
|
224 |
target_height_per_line = usable_height / lines_per_col if lines_per_col > 0 else usable_height
|
|
|
228 |
adjusted_font_size = int(col_width / (avg_line_chars / 10))
|
229 |
adjusted_font_size = max(min_font_size, adjusted_font_size)
|
230 |
|
231 |
+
# Enhanced font size scaling for one-page fit
|
232 |
+
if longest_line_words > 17 or lines_per_col > 20:
|
233 |
+
font_scale = min(17 / max(longest_line_words, 17), 60 / max(lines_per_col, 20))
|
234 |
+
adjusted_font_size = max(min_font_size, int(base_font_size * font_scale))
|
235 |
|
236 |
item_style = ParagraphStyle(
|
237 |
'ItemStyle', parent=styles['Normal'], fontName="DejaVuSans",
|
|
|
311 |
return buffer.getvalue()
|
312 |
|
313 |
def pdf_to_image(pdf_bytes):
|
314 |
+
if pdf_bytes is None:
|
315 |
+
return None
|
316 |
try:
|
317 |
doc = fitz.open(stream=pdf_bytes, filetype="pdf")
|
318 |
images = []
|
|
|
329 |
# PDF creation and linking functions
|
330 |
WORDS_12 = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve"]
|
331 |
WORDS_24 = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten",
|
332 |
+
"eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty",
|
333 |
+
"twenty-one", "twenty-two", "twenty-three", "twenty-four"]
|
334 |
|
335 |
def create_crossfile_pdfs(source_pdf="TestSource.pdf", target_pdf="TestTarget.pdf"):
|
336 |
"""Create two PDFs with cross-file linking."""
|
|
|
410 |
return source_pdf, target_pdf
|
411 |
|
412 |
def create_selflinking_pdf(pdf_file="SelfLinking.pdf"):
|
413 |
+
"""Create a PDF with a TOC on page 1 linking to a 1-24 list starting on page 2."""
|
414 |
buffer = io.BytesIO()
|
415 |
c = canvas.Canvas(buffer)
|
416 |
|
|
|
425 |
toc_y_positions.append(y)
|
426 |
c.showPage()
|
427 |
|
428 |
+
# Page 2: Numbered list 1-24
|
429 |
c.setFont("Helvetica", 12)
|
430 |
list_y_positions = []
|
431 |
for i, word in enumerate(WORDS_24, 1):
|
|
|
495 |
)
|
496 |
base_font_size = st.slider("Font Size (points)", min_value=6, max_value=16, value=8, step=1)
|
497 |
|
498 |
+
add_space_before_numbered = st.checkbox("Add Space Ahead of Numbered Lines", value=True)
|
499 |
headings_to_fonts = st.checkbox(
|
500 |
"Headings to Fonts",
|
501 |
+
value=True,
|
502 |
help="Convert Markdown headings (# Heading) to styled fonts"
|
503 |
)
|
504 |
auto_columns = st.checkbox("AutoColumns", value=True)
|
|
|
507 |
longest_line_words = 0
|
508 |
total_lines = 0
|
509 |
adjusted_font_size_display = base_font_size
|
510 |
+
if 'markdown_content' in st.session_state and st.session_state.markdown_content.strip():
|
511 |
current_markdown = st.session_state.markdown_content
|
512 |
lines = current_markdown.strip().split('\n')
|
513 |
total_lines = len([line for line in lines if line.strip()])
|
|
|
525 |
else:
|
526 |
recommended_columns = 3
|
527 |
# Adjust font size for one-page fit
|
528 |
+
if longest_line_words > 17 or total_lines / max(num_columns, 1) > 20:
|
529 |
+
font_scale = min(17 / max(longest_line_words, 17), 60 / max(total_lines / max(num_columns, 1), 20))
|
530 |
+
adjusted_font_size_display = max(5, int(base_font_size * font_scale))
|
531 |
st.markdown("**Document Stats**")
|
532 |
st.write(f"- Longest Line: {longest_line_words} words")
|
533 |
st.write(f"- Total Lines: {total_lines}")
|
534 |
st.write(f"- Recommended Columns: {recommended_columns}")
|
535 |
st.write(f"- Adjusted Font Size: {adjusted_font_size_display} points")
|
536 |
+
else:
|
537 |
+
st.markdown("**Document Stats**")
|
538 |
+
st.write("- Longest Line: 0 words")
|
539 |
+
st.write("- Total Lines: 0")
|
540 |
+
st.write("- Recommended Columns: 3")
|
541 |
+
st.write(f"- Adjusted Font Size: {base_font_size} points")
|
542 |
|
543 |
column_options = [2, 3, 4]
|
544 |
num_columns = st.selectbox(
|
545 |
"Number of Columns",
|
546 |
options=column_options,
|
547 |
+
index=column_options.index(recommended_columns) if recommended_columns in column_options else 0
|
548 |
)
|
549 |
st.info("Font size and columns adjust to fit one page.")
|
550 |
|
|
|
656 |
with st.sidebar:
|
657 |
st.download_button(
|
658 |
label="๐พ Save PDF",
|
659 |
+
data=pdf_bytes if pdf_bytes else "",
|
660 |
file_name=f"{prefix} {selected_md}.pdf" if selected_md else f"{prefix} output.pdf",
|
661 |
+
mime="application/pdf",
|
662 |
+
disabled=pdf_bytes is None
|
663 |
)
|