awacke1 commited on
Commit
811580d
Β·
verified Β·
1 Parent(s): f1f8c9c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +547 -191
app.py CHANGED
@@ -1,65 +1,21 @@
1
- import os
2
- import io
3
- import re
4
  import streamlit as st
5
-
6
- # Must be the very first Streamlit command.
7
- st.set_page_config(layout="wide", initial_sidebar_state="collapsed")
8
-
9
- from PIL import Image
10
- import fitz # PyMuPDF
11
-
12
  from reportlab.lib.pagesizes import A4
13
  from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle
14
  from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
15
  from reportlab.lib import colors
16
- from reportlab.pdfbase import pdfmetrics
17
- from reportlab.pdfbase.ttfonts import TTFont
18
-
19
- # ---------------------------------------------------------------
20
- # Define available NotoEmoji fonts (all in the base directory now)
21
- available_fonts = {
22
- "NotoEmoji Variable": "NotoEmoji-VariableFont_wght.ttf",
23
- "NotoEmoji Bold": "NotoEmoji-Bold.ttf",
24
- "NotoEmoji Light": "NotoEmoji-Light.ttf",
25
- "NotoEmoji Medium": "NotoEmoji-Medium.ttf",
26
- "NotoEmoji Regular": "NotoEmoji-Regular.ttf",
27
- "NotoEmoji SemiBold": "NotoEmoji-SemiBold.ttf"
28
- }
29
-
30
- # Sidebar: Let the user choose the desired NotoEmoji font.
31
- selected_font_name = st.sidebar.selectbox(
32
- "Select NotoEmoji Font",
33
- options=list(available_fonts.keys())
34
- )
35
- selected_font_path = available_fonts[selected_font_name]
36
-
37
- # Register the chosen emoji font with ReportLab.
38
- pdfmetrics.registerFont(TTFont(selected_font_name, selected_font_path))
39
-
40
- # ---------------------------------------------------------------
41
- # Helper function to wrap emoji characters with a font tag.
42
- def apply_emoji_font(text, emoji_font):
43
- # This regex attempts to capture many common emoji ranges.
44
- emoji_pattern = re.compile(
45
- r"([\U0001F300-\U0001F5FF"
46
- r"\U0001F600-\U0001F64F"
47
- r"\U0001F680-\U0001F6FF"
48
- r"\U0001F700-\U0001F77F"
49
- r"\U0001F780-\U0001F7FF"
50
- r"\U0001F800-\U0001F8FF"
51
- r"\U0001F900-\U0001F9FF"
52
- r"\U0001FA00-\U0001FA6F"
53
- r"\U0001FA70-\U0001FAFF"
54
- r"\u2600-\u26FF"
55
- r"\u2700-\u27BF]+)"
56
- )
57
- # Wrap found emoji with a font tag using the selected emoji font.
58
- return emoji_pattern.sub(r'<font face="{}">\1</font>'.format(emoji_font), text)
59
 
60
- # ---------------------------------------------------------------
61
- # Default markdown content with emojis.
62
- default_markdown = """# Pillow-PyMuPDF-ReportLab - Markdown to PDF One Pager
63
 
64
  ## Core ML Techniques
65
  1. 🌟 **Mixture of Experts (MoE)**
@@ -126,9 +82,12 @@ default_markdown = """# Pillow-PyMuPDF-ReportLab - Markdown to PDF One Pager
126
  - Documentation synthesis
127
  """
128
 
129
- # ---------------------------------------------------------------
130
- # Process markdown into a two-column layout for the PDF.
131
  def markdown_to_pdf_content(markdown_text):
 
 
 
 
132
  lines = markdown_text.strip().split('\n')
133
  pdf_content = []
134
  in_list_item = False
@@ -141,230 +100,627 @@ def markdown_to_pdf_content(markdown_text):
141
  continue
142
 
143
  if line.startswith('# '):
144
- # Optionally skip the main title.
145
  pass
146
  elif line.startswith('## '):
 
147
  if current_item and sub_items:
 
148
  pdf_content.append([current_item, sub_items])
149
  sub_items = []
150
  current_item = None
 
151
  section = line.replace('## ', '').strip()
152
  pdf_content.append(f"<b>{section}</b>")
153
  in_list_item = False
154
  elif re.match(r'^\d+\.', line):
 
155
  if current_item and sub_items:
 
156
  pdf_content.append([current_item, sub_items])
157
  sub_items = []
 
158
  current_item = line.strip()
159
  in_list_item = True
160
  elif line.startswith('- ') and in_list_item:
 
161
  sub_items.append(line.strip())
162
  else:
 
163
  if not in_list_item:
164
  pdf_content.append(line.strip())
165
 
 
166
  if current_item and sub_items:
167
  pdf_content.append([current_item, sub_items])
168
 
 
169
  mid_point = len(pdf_content) // 2
170
  left_column = pdf_content[:mid_point]
171
  right_column = pdf_content[mid_point:]
172
 
173
  return left_column, right_column
174
 
175
- # ---------------------------------------------------------------
176
- # Create the PDF using ReportLab.
177
- def create_main_pdf(markdown_text, base_font_size=10, auto_size=False):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  buffer = io.BytesIO()
179
- doc = SimpleDocTemplate(
180
- buffer,
181
- pagesize=(A4[1], A4[0]),
182
- leftMargin=36,
183
- rightMargin=36,
184
- topMargin=36,
185
- bottomMargin=36
186
- )
187
 
188
- styles = getSampleStyleSheet()
189
- story = []
190
- spacer_height = 10
191
- left_column, right_column = markdown_to_pdf_content(markdown_text)
 
 
 
 
 
 
 
 
 
192
 
193
- total_items = 0
194
- for col in (left_column, right_column):
195
- for item in col:
196
- if isinstance(item, list):
 
 
 
 
 
 
 
 
 
 
 
197
  main_item, sub_items = item
198
- total_items += 1 + len(sub_items)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  else:
200
- total_items += 1
 
 
 
 
 
 
201
 
202
- if auto_size:
203
- base_font_size = max(6, min(12, 200 / total_items))
 
204
 
205
- item_font_size = base_font_size
206
- subitem_font_size = base_font_size * 0.9
207
- section_font_size = base_font_size * 1.2
208
- title_font_size = min(16, base_font_size * 1.5)
209
 
210
- # Define ParagraphStyles using Helvetica for normal text.
211
- title_style = ParagraphStyle(
212
- 'Heading1',
213
- parent=styles['Heading1'],
214
- fontName="Helvetica-Bold",
215
- textColor=colors.darkblue,
216
- alignment=1,
217
- fontSize=title_font_size
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  )
219
 
 
 
 
 
 
 
 
 
220
  section_style = ParagraphStyle(
221
  'SectionStyle',
222
  parent=styles['Heading2'],
223
- fontName="Helvetica-Bold",
224
  textColor=colors.darkblue,
225
- fontSize=section_font_size,
226
- leading=section_font_size * 1.2,
227
- spaceAfter=2
228
  )
229
 
230
  item_style = ParagraphStyle(
231
  'ItemStyle',
232
  parent=styles['Normal'],
233
- fontName="Helvetica",
234
- fontSize=item_font_size,
235
- leading=item_font_size * 1.2,
236
- spaceAfter=1
237
  )
238
 
239
  subitem_style = ParagraphStyle(
240
  'SubItemStyle',
241
  parent=styles['Normal'],
242
- fontName="Helvetica",
243
- fontSize=subitem_font_size,
244
- leading=subitem_font_size * 1.2,
245
- leftIndent=10,
246
- spaceAfter=1
247
  )
248
 
249
- story.append(Paragraph(apply_emoji_font("Cutting-Edge ML Outline (ReportLab)", selected_font_name), title_style))
250
- story.append(Spacer(1, spacer_height))
 
251
 
 
252
  left_cells = []
253
  for item in left_column:
254
  if isinstance(item, str) and item.startswith('<b>'):
255
- # Process section headings.
256
  text = item.replace('<b>', '').replace('</b>', '')
257
- left_cells.append(Paragraph(apply_emoji_font(text, selected_font_name), section_style))
258
  elif isinstance(item, list):
 
259
  main_item, sub_items = item
260
- left_cells.append(Paragraph(apply_emoji_font(main_item, selected_font_name), item_style))
 
 
261
  for sub_item in sub_items:
262
- left_cells.append(Paragraph(apply_emoji_font(sub_item, selected_font_name), subitem_style))
263
  else:
264
- left_cells.append(Paragraph(apply_emoji_font(item, selected_font_name), item_style))
 
265
 
266
  right_cells = []
267
  for item in right_column:
268
  if isinstance(item, str) and item.startswith('<b>'):
 
269
  text = item.replace('<b>', '').replace('</b>', '')
270
- right_cells.append(Paragraph(apply_emoji_font(text, selected_font_name), section_style))
271
  elif isinstance(item, list):
 
272
  main_item, sub_items = item
273
- right_cells.append(Paragraph(apply_emoji_font(main_item, selected_font_name), item_style))
 
 
274
  for sub_item in sub_items:
275
- right_cells.append(Paragraph(apply_emoji_font(sub_item, selected_font_name), subitem_style))
276
  else:
277
- right_cells.append(Paragraph(apply_emoji_font(item, selected_font_name), item_style))
 
278
 
 
279
  max_cells = max(len(left_cells), len(right_cells))
280
- left_cells.extend([""] * (max_cells - len(left_cells)))
281
- right_cells.extend([""] * (max_cells - len(right_cells)))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
 
283
- table_data = list(zip(left_cells, right_cells))
284
- col_width = (A4[1] - 72) / 2.0
285
- table = Table(table_data, colWidths=[col_width, col_width], hAlign='CENTER')
286
  table.setStyle(TableStyle([
287
  ('VALIGN', (0, 0), (-1, -1), 'TOP'),
288
- ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
 
289
  ('BACKGROUND', (0, 0), (-1, -1), colors.white),
290
- ('GRID', (0, 0), (-1, -1), 0, colors.white),
291
- ('LINEAFTER', (0, 0), (0, -1), 0.5, colors.grey),
292
- ('LEFTPADDING', (0, 0), (-1, -1), 2),
293
- ('RIGHTPADDING', (0, 0), (-1, -1), 2),
294
- ('TOPPADDING', (0, 0), (-1, -1), 1),
295
- ('BOTTOMPADDING', (0, 0), (-1, -1), 1),
296
  ]))
297
 
298
  story.append(table)
 
299
  doc.build(story)
300
  buffer.seek(0)
301
  return buffer.getvalue()
302
 
303
- # ---------------------------------------------------------------
304
- # Convert PDF bytes to an image for preview using PyMuPDF.
305
- def pdf_to_image(pdf_bytes):
306
- try:
307
- doc = fitz.open(stream=pdf_bytes, filetype="pdf")
308
- page = doc[0]
309
- pix = page.get_pixmap(matrix=fitz.Matrix(2.0, 2.0))
310
- img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
311
- doc.close()
312
- return img
313
- except Exception as e:
314
- st.error(f"Failed to render PDF preview: {e}")
315
- return None
316
-
317
- # ---------------------------------------------------------------
318
- # Sidebar options for text size.
319
- with st.sidebar:
320
- auto_size = st.checkbox("Auto-size text", value=True)
321
- if not auto_size:
322
- base_font_size = st.slider("Base Font Size (points)", min_value=6, max_value=16, value=10, step=1)
323
- else:
324
- base_font_size = 10
325
- st.info("Font size will auto-adjust between 6-12 points based on content length.")
326
 
327
- # Persist markdown content in session state.
328
- if 'markdown_content' not in st.session_state:
329
- st.session_state.markdown_content = default_markdown
330
 
331
- # ---------------------------------------------------------------
332
- # Generate the PDF.
333
- with st.spinner("Generating PDF..."):
334
- pdf_bytes = create_main_pdf(st.session_state.markdown_content, base_font_size, auto_size)
335
 
336
- # Display PDF preview.
337
- with st.container():
338
- pdf_image = pdf_to_image(pdf_bytes)
339
- if pdf_image:
340
- st.image(pdf_image, use_container_width=True)
341
- else:
342
- st.info("Download the PDF to view it locally.")
 
 
 
 
 
 
 
 
 
 
343
 
344
- # PDF Download button.
345
- st.download_button(
346
- label="Download PDF",
347
- data=pdf_bytes,
348
- file_name="ml_outline.pdf",
349
- mime="application/pdf"
350
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
 
352
- # Markdown editor.
353
- edited_markdown = st.text_area(
354
- "Modify the markdown content below:",
355
- value=st.session_state.markdown_content,
356
- height=300
357
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
358
 
359
- # Update PDF on button click.
360
- if st.button("Update PDF"):
361
- st.session_state.markdown_content = edited_markdown
362
- st.experimental_rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
363
 
364
- # Markdown Download button.
365
- st.download_button(
366
- label="Save Markdown",
367
- data=st.session_state.markdown_content,
368
- file_name="ml_outline.md",
369
- mime="text/markdown"
370
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
+ import base64
 
 
 
 
 
 
3
  from reportlab.lib.pagesizes import A4
4
  from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle
5
  from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
6
  from reportlab.lib import colors
7
+ import pikepdf
8
+ import fpdf
9
+ import fitz # pymupdf
10
+ import cv2
11
+ import numpy as np
12
+ from PIL import Image
13
+ import io
14
+ import os
15
+ import re
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
+ # Define the ML outline as a markdown string for multilevel content
18
+ ml_markdown = """# Cutting-Edge ML Outline
 
19
 
20
  ## Core ML Techniques
21
  1. 🌟 **Mixture of Experts (MoE)**
 
82
  - Documentation synthesis
83
  """
84
 
85
+ # Process multilevel markdown for PDF output
 
86
  def markdown_to_pdf_content(markdown_text):
87
+ """Convert markdown text to a format suitable for PDF generation"""
88
+ import re
89
+
90
+ # Convert markdown headers to styled text for PDF
91
  lines = markdown_text.strip().split('\n')
92
  pdf_content = []
93
  in_list_item = False
 
100
  continue
101
 
102
  if line.startswith('# '):
103
+ # Main header - will be handled separately in the PDF generation
104
  pass
105
  elif line.startswith('## '):
106
+ # Section header - add as a bold item
107
  if current_item and sub_items:
108
+ # Store the previous item with its sub-items
109
  pdf_content.append([current_item, sub_items])
110
  sub_items = []
111
  current_item = None
112
+
113
  section = line.replace('## ', '').strip()
114
  pdf_content.append(f"<b>{section}</b>")
115
  in_list_item = False
116
  elif re.match(r'^\d+\.', line):
117
+ # Numbered list item
118
  if current_item and sub_items:
119
+ # Store the previous item with its sub-items
120
  pdf_content.append([current_item, sub_items])
121
  sub_items = []
122
+
123
  current_item = line.strip()
124
  in_list_item = True
125
  elif line.startswith('- ') and in_list_item:
126
+ # Sub-item under a numbered list item
127
  sub_items.append(line.strip())
128
  else:
129
+ # Regular line
130
  if not in_list_item:
131
  pdf_content.append(line.strip())
132
 
133
+ # Add the last item if there is one
134
  if current_item and sub_items:
135
  pdf_content.append([current_item, sub_items])
136
 
137
+ # Split the content for two columns
138
  mid_point = len(pdf_content) // 2
139
  left_column = pdf_content[:mid_point]
140
  right_column = pdf_content[mid_point:]
141
 
142
  return left_column, right_column
143
 
144
+ # Demo functions for PDF libraries
145
+ def demo_pikepdf():
146
+ """Create a two-column PDF with the markdown outline using pikepdf"""
147
+ # Process markdown content
148
+ left_column, right_column = markdown_to_pdf_content(ml_markdown)
149
+
150
+ # We'll use pymupdf (fitz) to create the content, then save with pikepdf
151
+ doc = fitz.open()
152
+ page = doc.new_page(width=842, height=595) # A4 Landscape
153
+
154
+ # Set up fonts and colors
155
+ title_font = "helv-b"
156
+ section_font = "helv-b"
157
+ item_font = "helv-b"
158
+ subitem_font = "helv"
159
+ blue_color = (0, 0, 0.8)
160
+ black_color = (0, 0, 0)
161
+
162
+ # Add title
163
+ page.insert_text((50, 40), "Cutting-Edge ML Outline (PikePDF Demo)", fontname=title_font, fontsize=16, color=blue_color)
164
+
165
+ # First column
166
+ x1, y1 = 50, 80
167
+ current_y = y1
168
+
169
+ for item in left_column:
170
+ if isinstance(item, str) and item.startswith('<b>'):
171
+ # Section header
172
+ # Add extra space before sections (except the first one)
173
+ if current_y > y1:
174
+ current_y += 10
175
+
176
+ text = item.replace('<b>', '').replace('</b>', '')
177
+ page.insert_text((x1, current_y), text, fontname=section_font, fontsize=14, color=blue_color)
178
+ current_y += 25
179
+ elif isinstance(item, list):
180
+ # Main item with sub-items
181
+ main_item, sub_items = item
182
+ page.insert_text((x1, current_y), main_item, fontname=item_font, fontsize=12, color=black_color)
183
+ current_y += 20
184
+
185
+ # Add sub-items
186
+ for sub_item in sub_items:
187
+ page.insert_text((x1 + 20, current_y), sub_item, fontname=subitem_font, fontsize=10, color=black_color)
188
+ current_y += 15
189
+
190
+ current_y += 5 # Extra space after a group
191
+ else:
192
+ # Regular item
193
+ page.insert_text((x1, current_y), item, fontname=item_font, fontsize=12, color=black_color)
194
+ current_y += 20
195
+
196
+ # Second column
197
+ x2, y2 = 450, 80
198
+ current_y = y2
199
+
200
+ for item in right_column:
201
+ if isinstance(item, str) and item.startswith('<b>'):
202
+ # Section header
203
+ # Add extra space before sections (except the first one)
204
+ if current_y > y2:
205
+ current_y += 10
206
+
207
+ text = item.replace('<b>', '').replace('</b>', '')
208
+ page.insert_text((x2, current_y), text, fontname=section_font, fontsize=14, color=blue_color)
209
+ current_y += 25
210
+ elif isinstance(item, list):
211
+ # Main item with sub-items
212
+ main_item, sub_items = item
213
+ page.insert_text((x2, current_y), main_item, fontname=item_font, fontsize=12, color=black_color)
214
+ current_y += 20
215
+
216
+ # Add sub-items
217
+ for sub_item in sub_items:
218
+ page.insert_text((x2 + 20, current_y), sub_item, fontname=subitem_font, fontsize=10, color=black_color)
219
+ current_y += 15
220
+
221
+ current_y += 5 # Extra space after a group
222
+ else:
223
+ # Regular item
224
+ page.insert_text((x2, current_y), item, fontname=item_font, fontsize=12, color=black_color)
225
+ current_y += 20
226
+
227
+ # Draw a dividing line
228
+ page.draw_line((421, 70), (421, 550))
229
+
230
+ # Convert to pikepdf
231
+ temp_buffer = io.BytesIO()
232
+ doc.save(temp_buffer)
233
+ temp_buffer.seek(0)
234
+
235
+ pdf = pikepdf.Pdf.open(temp_buffer)
236
+
237
+ # Save to buffer
238
  buffer = io.BytesIO()
239
+ pdf.save(buffer)
240
+ buffer.seek(0)
241
+ return buffer.getvalue()
242
+
243
+ def demo_fpdf():
244
+ """Create a two-column PDF with the markdown outline using FPDF"""
245
+ # Process markdown content
246
+ left_column, right_column = markdown_to_pdf_content(ml_markdown)
247
 
248
+ pdf = fpdf.FPDF(orientation='L') # Landscape
249
+ pdf.add_page()
250
+
251
+ # Set title
252
+ pdf.set_font("Arial", 'B', size=16)
253
+ pdf.set_text_color(0, 0, 128) # Dark blue
254
+ pdf.cell(0, 10, txt="Cutting-Edge ML Outline (FPDF Demo)", ln=True, align='C')
255
+ pdf.ln(10)
256
+
257
+ # Define coordinates for columns
258
+ x_col1 = 20
259
+ x_col2 = pdf.w / 2 + 10
260
+ y_start = pdf.get_y()
261
 
262
+ # Function to render a column
263
+ def render_column(items, x_start, y_start):
264
+ y_pos = y_start
265
+
266
+ for item in items:
267
+ if isinstance(item, str) and item.startswith('<b>'):
268
+ # Section header
269
+ text = item.replace('<b>', '').replace('</b>', '')
270
+ pdf.set_font("Arial", 'B', size=14)
271
+ pdf.set_text_color(0, 0, 128) # Dark blue
272
+ pdf.set_xy(x_start, y_pos)
273
+ pdf.cell(0, 10, txt=text, ln=True)
274
+ y_pos += 10
275
+ elif isinstance(item, list):
276
+ # Main item with sub-items
277
  main_item, sub_items = item
278
+
279
+ # Main item
280
+ pdf.set_font("Arial", 'B', size=11)
281
+ pdf.set_text_color(0, 0, 0) # Black
282
+ pdf.set_xy(x_start, y_pos)
283
+ pdf.multi_cell(180, 6, txt=main_item, align='L')
284
+ main_height = pdf.get_y() - y_pos
285
+ y_pos += main_height + 2
286
+
287
+ # Sub-items
288
+ pdf.set_font("Arial", size=10)
289
+ for sub_item in sub_items:
290
+ pdf.set_xy(x_start + 10, y_pos)
291
+ pdf.multi_cell(170, 5, txt=sub_item, align='L')
292
+ sub_height = pdf.get_y() - y_pos
293
+ y_pos += sub_height + 1
294
+
295
+ y_pos += 2 # Extra space after a group
296
  else:
297
+ # Regular item
298
+ pdf.set_font("Arial", 'B', size=11)
299
+ pdf.set_text_color(0, 0, 0) # Black
300
+ pdf.set_xy(x_start, y_pos)
301
+ pdf.multi_cell(180, 6, txt=item, align='L')
302
+ item_height = pdf.get_y() - y_pos
303
+ y_pos += item_height + 4
304
 
305
+ # Render both columns
306
+ render_column(left_column, x_col1, y_start)
307
+ render_column(right_column, x_col2, y_start)
308
 
309
+ # Draw a dividing line
310
+ pdf.line(pdf.w/2, 30, pdf.w/2, 280)
 
 
311
 
312
+ buffer = io.BytesIO()
313
+ pdf.output(buffer)
314
+ buffer.seek(0)
315
+ return buffer.getvalue()
316
+
317
+ def demo_pymupdf():
318
+ """Create a two-column PDF with the markdown outline using PyMuPDF"""
319
+ # Process markdown content
320
+ left_column, right_column = markdown_to_pdf_content(ml_markdown)
321
+
322
+ doc = fitz.open()
323
+ page = doc.new_page(width=842, height=595) # A4 Landscape
324
+
325
+ # Set up fonts and colors
326
+ title_font = "helv-b"
327
+ section_font = "helv-b"
328
+ item_font = "helv-b"
329
+ subitem_font = "helv"
330
+ blue_color = (0, 0, 0.8)
331
+ black_color = (0, 0, 0)
332
+
333
+ # Add title
334
+ page.insert_text((300, 40), "Cutting-Edge ML Outline (PyMuPDF Demo)", fontname=title_font, fontsize=16, color=blue_color)
335
+
336
+ # First column
337
+ x1, y1 = 50, 80
338
+ current_y = y1
339
+
340
+ for item in left_column:
341
+ if isinstance(item, str) and item.startswith('<b>'):
342
+ # Section header
343
+ # Add extra space before sections (except the first one)
344
+ if current_y > y1:
345
+ current_y += 10
346
+
347
+ text = item.replace('<b>', '').replace('</b>', '')
348
+ page.insert_text((x1, current_y), text, fontname=section_font, fontsize=14, color=blue_color)
349
+ current_y += 25
350
+ elif isinstance(item, list):
351
+ # Main item with sub-items
352
+ main_item, sub_items = item
353
+ page.insert_text((x1, current_y), main_item, fontname=item_font, fontsize=12, color=black_color)
354
+ current_y += 20
355
+
356
+ # Add sub-items
357
+ for sub_item in sub_items:
358
+ page.insert_text((x1 + 20, current_y), sub_item, fontname=subitem_font, fontsize=10, color=black_color)
359
+ current_y += 15
360
+
361
+ current_y += 5 # Extra space after a group
362
+ else:
363
+ # Regular item
364
+ page.insert_text((x1, current_y), item, fontname=item_font, fontsize=12, color=black_color)
365
+ current_y += 20
366
+
367
+ # Second column
368
+ x2, y2 = 450, 80
369
+ current_y = y2
370
+
371
+ for item in right_column:
372
+ if isinstance(item, str) and item.startswith('<b>'):
373
+ # Section header
374
+ # Add extra space before sections (except the first one)
375
+ if current_y > y2:
376
+ current_y += 10
377
+
378
+ text = item.replace('<b>', '').replace('</b>', '')
379
+ page.insert_text((x2, current_y), text, fontname=section_font, fontsize=14, color=blue_color)
380
+ current_y += 25
381
+ elif isinstance(item, list):
382
+ # Main item with sub-items
383
+ main_item, sub_items = item
384
+ page.insert_text((x2, current_y), main_item, fontname=item_font, fontsize=12, color=black_color)
385
+ current_y += 20
386
+
387
+ # Add sub-items
388
+ for sub_item in sub_items:
389
+ page.insert_text((x2 + 20, current_y), sub_item, fontname=subitem_font, fontsize=10, color=black_color)
390
+ current_y += 15
391
+
392
+ current_y += 5 # Extra space after a group
393
+ else:
394
+ # Regular item
395
+ page.insert_text((x2, current_y), item, fontname=item_font, fontsize=12, color=black_color)
396
+ current_y += 20
397
+
398
+ # Draw a dividing line
399
+ page.draw_line((421, 70), (421, 550))
400
+
401
+ buffer = io.BytesIO()
402
+ doc.save(buffer)
403
+ buffer.seek(0)
404
+ return buffer.getvalue()
405
+
406
+ # Demo function for image capture
407
+ def demo_image_capture():
408
+ """Generate a demo image (fake capture) since we can't access the camera in this environment"""
409
+ # Create a simple gradient image using numpy and PIL
410
+ width, height = 640, 480
411
+
412
+ # Create a gradient array
413
+ x = np.linspace(0, 1, width)
414
+ y = np.linspace(0, 1, height)
415
+ xx, yy = np.meshgrid(x, y)
416
+ gradient = (xx + yy) / 2
417
+
418
+ # Convert to RGB image
419
+ img_array = (gradient * 255).astype(np.uint8)
420
+ rgb_array = np.stack([img_array, img_array//2, img_array*2], axis=2)
421
+
422
+ # Create PIL Image
423
+ img = Image.fromarray(rgb_array)
424
+
425
+ # Add text to the image
426
+ from PIL import ImageDraw, ImageFont
427
+ draw = ImageDraw.Draw(img)
428
+ try:
429
+ font = ImageFont.truetype("arial.ttf", 30)
430
+ except:
431
+ font = ImageFont.load_default()
432
+
433
+ draw.text((width//4, height//2), "OpenCV Demo Image", fill=(255, 255, 255), font=font)
434
+
435
+ # Save to buffer
436
+ buffer = io.BytesIO()
437
+ img.save(buffer, format="JPEG")
438
+ buffer.seek(0)
439
+ return buffer.getvalue()
440
+
441
+ # Main PDF creation using ReportLab
442
+ def create_main_pdf(markdown_text):
443
+ """Create a single-page landscape PDF with the outline in two columns"""
444
+ from reportlab.platypus import Table, TableStyle, Paragraph, Spacer
445
+ from reportlab.lib import pagesizes
446
+ from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
447
+
448
+ # Process markdown content
449
+ left_column, right_column = markdown_to_pdf_content(markdown_text)
450
+
451
+ buffer = io.BytesIO()
452
+ doc = SimpleDocTemplate(
453
+ buffer,
454
+ pagesize=(A4[1], A4[0]), # Landscape
455
+ leftMargin=50,
456
+ rightMargin=50,
457
+ topMargin=50,
458
+ bottomMargin=50
459
  )
460
 
461
+ styles = getSampleStyleSheet()
462
+ story = []
463
+
464
+ # Create custom styles
465
+ title_style = styles['Heading1']
466
+ title_style.textColor = colors.darkblue
467
+ title_style.alignment = 1 # Center alignment
468
+
469
  section_style = ParagraphStyle(
470
  'SectionStyle',
471
  parent=styles['Heading2'],
 
472
  textColor=colors.darkblue,
473
+ spaceAfter=6
 
 
474
  )
475
 
476
  item_style = ParagraphStyle(
477
  'ItemStyle',
478
  parent=styles['Normal'],
479
+ fontSize=11,
480
+ leading=14,
481
+ fontName='Helvetica-Bold'
 
482
  )
483
 
484
  subitem_style = ParagraphStyle(
485
  'SubItemStyle',
486
  parent=styles['Normal'],
487
+ fontSize=10,
488
+ leading=12,
489
+ leftIndent=20
 
 
490
  )
491
 
492
+ # Add title
493
+ story.append(Paragraph("Cutting-Edge ML Outline (ReportLab)", title_style))
494
+ story.append(Spacer(1, 20))
495
 
496
+ # Prepare data for table
497
  left_cells = []
498
  for item in left_column:
499
  if isinstance(item, str) and item.startswith('<b>'):
500
+ # Section header
501
  text = item.replace('<b>', '').replace('</b>', '')
502
+ left_cells.append(Paragraph(text, section_style))
503
  elif isinstance(item, list):
504
+ # Main item with sub-items
505
  main_item, sub_items = item
506
+ left_cells.append(Paragraph(main_item, item_style))
507
+
508
+ # Sub items
509
  for sub_item in sub_items:
510
+ left_cells.append(Paragraph(sub_item, subitem_style))
511
  else:
512
+ # Regular item
513
+ left_cells.append(Paragraph(item, item_style))
514
 
515
  right_cells = []
516
  for item in right_column:
517
  if isinstance(item, str) and item.startswith('<b>'):
518
+ # Section header
519
  text = item.replace('<b>', '').replace('</b>', '')
520
+ right_cells.append(Paragraph(text, section_style))
521
  elif isinstance(item, list):
522
+ # Main item with sub-items
523
  main_item, sub_items = item
524
+ right_cells.append(Paragraph(main_item, item_style))
525
+
526
+ # Sub items
527
  for sub_item in sub_items:
528
+ right_cells.append(Paragraph(sub_item, subitem_style))
529
  else:
530
+ # Regular item
531
+ right_cells.append(Paragraph(item, item_style))
532
 
533
+ # Make sure both columns have the same number of rows by adding empty cells
534
  max_cells = max(len(left_cells), len(right_cells))
535
+ if len(left_cells) < max_cells:
536
+ for i in range(max_cells - len(left_cells)):
537
+ left_cells.append("")
538
+ if len(right_cells) < max_cells:
539
+ for i in range(max_cells - len(right_cells)):
540
+ right_cells.append("")
541
+
542
+ # Create table data (one row per cell)
543
+ table_data = []
544
+ for i in range(max_cells):
545
+ table_data.append([left_cells[i], right_cells[i]])
546
+
547
+ # Calculate column widths
548
+ col_width = (A4[1] - 120) / 2.0 # Page width minus margins divided by 2
549
+
550
+ # Create the table with the data
551
+ table = Table(table_data, colWidths=[col_width, col_width])
552
 
553
+ # Style the table
 
 
554
  table.setStyle(TableStyle([
555
  ('VALIGN', (0, 0), (-1, -1), 'TOP'),
556
+ ('ALIGN', (0, 0), (0, -1), 'LEFT'),
557
+ ('ALIGN', (1, 0), (1, -1), 'LEFT'),
558
  ('BACKGROUND', (0, 0), (-1, -1), colors.white),
559
+ ('GRID', (0, 0), (-1, -1), 0.5, colors.white),
560
+ ('LINEAFTER', (0, 0), (0, -1), 1, colors.grey),
 
 
 
 
561
  ]))
562
 
563
  story.append(table)
564
+
565
  doc.build(story)
566
  buffer.seek(0)
567
  return buffer.getvalue()
568
 
569
+ def get_binary_file_downloader_html(bin_data, file_label='File'):
570
+ """Create a download link for binary data"""
571
+ bin_str = base64.b64encode(bin_data).decode()
572
+ href = f'<a href="data:application/octet-stream;base64,{bin_str}" download="{file_label}">Download {file_label}</a>'
573
+ return href
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
574
 
575
+ # Streamlit UI
576
+ st.title("πŸš€ Cutting-Edge ML Outline Generator")
 
577
 
578
+ col1, col2 = st.columns(2)
 
 
 
579
 
580
+ with col1:
581
+ st.header("πŸ“ Markdown Outline")
582
+
583
+ # Display the markdown content
584
+ st.markdown(ml_markdown)
585
+
586
+ # Create a download button for the markdown file
587
+ st.download_button(
588
+ label="Download Markdown",
589
+ data=ml_markdown,
590
+ file_name="ml_outline.md",
591
+ mime="text/markdown"
592
+ )
593
+
594
+ # Show the markdown source code in an expandable section
595
+ with st.expander("View Markdown Source"):
596
+ st.code(ml_markdown, language="markdown")
597
 
598
+ with col2:
599
+ st.header("πŸ“‘ PDF Preview & Demos")
600
+
601
+ # Library Demos
602
+ st.subheader("Library Demos")
603
+
604
+ # PikePDF demo
605
+ if st.button("Generate PikePDF Demo"):
606
+ with st.spinner("Generating PikePDF demo..."):
607
+ pike_pdf = demo_pikepdf()
608
+ st.download_button("Download PikePDF Demo", pike_pdf, "pikepdf_demo.pdf")
609
+ st.success("PikePDF demo created successfully!")
610
+ st.info("This PDF contains the multilevel markdown outline in a two-column layout.")
611
+
612
+ # FPDF demo
613
+ if st.button("Generate FPDF Demo"):
614
+ with st.spinner("Generating FPDF demo..."):
615
+ fpdf_pdf = demo_fpdf()
616
+ st.download_button("Download FPDF Demo", fpdf_pdf, "fpdf_demo.pdf")
617
+ st.success("FPDF demo created successfully!")
618
+ st.info("This PDF contains the multilevel markdown outline in a two-column layout.")
619
+
620
+ # PyMuPDF demo
621
+ if st.button("Generate PyMuPDF Demo"):
622
+ with st.spinner("Generating PyMuPDF demo..."):
623
+ pymupdf_pdf = demo_pymupdf()
624
+ st.download_button("Download PyMuPDF Demo", pymupdf_pdf, "pymupdf_demo.pdf")
625
+ st.success("PyMuPDF demo created successfully!")
626
+ st.info("This PDF contains the multilevel markdown outline in a two-column layout.")
627
+
628
+ # Image demo
629
+ if st.button("Generate Demo Image"):
630
+ with st.spinner("Generating demo image..."):
631
+ img_data = demo_image_capture()
632
+ st.image(img_data, caption="Demo Image (Camera simulation)")
633
+
634
+ # Add download button for the image
635
+ st.download_button(
636
+ label="Download Image",
637
+ data=img_data,
638
+ file_name="demo_image.jpg",
639
+ mime="image/jpeg"
640
+ )
641
 
642
+ # Main PDF Generation
643
+ st.subheader("Main Outline PDF")
644
+ if st.button("Generate Main PDF"):
645
+ with st.spinner("Generating PDF..."):
646
+ try:
647
+ pdf_bytes = create_main_pdf(ml_markdown)
648
+
649
+ st.download_button(
650
+ label="Download Main PDF",
651
+ data=pdf_bytes,
652
+ file_name="ml_outline.pdf",
653
+ mime="application/pdf"
654
+ )
655
+
656
+ # Display the PDF in the app
657
+ base64_pdf = base64.b64encode(pdf_bytes).decode('utf-8')
658
+ pdf_display = f'''
659
+ <embed
660
+ src="data:application/pdf;base64,{base64_pdf}"
661
+ width="100%"
662
+ height="400px"
663
+ type="application/pdf">
664
+ '''
665
+ st.markdown(pdf_display, unsafe_allow_html=True)
666
+
667
+ st.success("PDF generated successfully! The PDF displays the multilevel markdown outline in a two-column layout.")
668
+ except Exception as e:
669
+ st.error(f"Error generating PDF: {str(e)}")
670
 
671
+ # Show the PDF rendering code in an expandable section
672
+ with st.expander("View PDF Rendering Code"):
673
+ st.code("""
674
+ # Process multilevel markdown for PDF output
675
+ def markdown_to_pdf_content(markdown_text):
676
+ # Convert markdown headers to styled text for PDF
677
+ lines = markdown_text.strip().split('\\n')
678
+ pdf_content = []
679
+
680
+ for line in lines:
681
+ if line.startswith('# '):
682
+ # Main header - will be handled separately
683
+ pass
684
+ elif line.startswith('## '):
685
+ # Section header - add as a bold item
686
+ section = line.replace('## ', '').strip()
687
+ pdf_content.append(f"<b>{section}</b>")
688
+ elif re.match(r'^\\d+\\.', line):
689
+ # Numbered list item
690
+ item = line.strip()
691
+ pdf_content.append(item)
692
+ elif line.startswith('- '):
693
+ # Sub-item under a numbered list item
694
+ sub_item = line.strip()
695
+ pdf_content.append(" " + sub_item)
696
+
697
+ # Split the content for two columns
698
+ mid_point = len(pdf_content) // 2
699
+ left_column = pdf_content[:mid_point]
700
+ right_column = pdf_content[mid_point:]
701
+
702
+ return left_column, right_column
703
+ """, language="python")
704
 
705
+ # Add custom CSS for better appearance
706
+ st.markdown("""
707
+ <style>
708
+ .stButton>button {
709
+ background-color: #4CAF50;
710
+ color: white;
711
+ font-weight: bold;
712
+ }
713
+ .stTabs [data-baseweb="tab-list"] {
714
+ gap: 2px;
715
+ }
716
+ .stTabs [data-baseweb="tab"] {
717
+ height: 50px;
718
+ white-space: pre-wrap;
719
+ background-color: #f0f2f6;
720
+ border-radius: 4px 4px 0px 0px;
721
+ gap: 1px;
722
+ padding-top: 10px;
723
+ padding-bottom: 10px;
724
+ }
725
+ </style>
726
+ """, unsafe_allow_html=True)