awacke1 commited on
Commit
cd13cab
·
verified ·
1 Parent(s): 089f999

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +477 -50
app.py CHANGED
@@ -1,4 +1,130 @@
1
- import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  import base64
3
  from reportlab.lib.pagesizes import A4
4
  from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
@@ -13,7 +139,75 @@ from PIL import Image
13
  import io
14
  import os
15
 
16
- # Define the 12-point ML outline with emojis
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  ml_outline = [
18
  "🌟 1. Mixture of Experts (MoE)",
19
  "🔥 2. Supervised Fine-Tuning (SFT) using PyTorch",
@@ -29,24 +223,93 @@ ml_outline = [
29
  "💻 12. ML Code Generation with Streamlit/Gradio/HTML5+JS"
30
  ]
31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  # Demo functions for PDF libraries
33
  def demo_pikepdf():
34
- """Create a simple PDF with pikepdf"""
35
- pdf = pikepdf.Pdf.new()
36
- # Create a proper pikepdf Page object
37
- page_dict = pikepdf.Dictionary(
38
- Type=pikepdf.Name.Page,
39
- MediaBox=[0, 0, 595, 842],
40
- Contents=pdf.make_stream(b"BT /F1 12 Tf 100 700 Td (PikePDF Demo) Tj ET")
41
- )
42
- # Create a proper Pages dictionary
43
- pages_dict = pikepdf.Dictionary(
44
- Type=pikepdf.Name.Pages,
45
- Count=1,
46
- Kids=[pdf.make_indirect(page_dict)]
47
- )
48
- # Set the root
49
- pdf.Root.Pages = pdf.make_indirect(pages_dict)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  # Save to buffer
51
  buffer = io.BytesIO()
52
  pdf.save(buffer)
@@ -54,21 +317,127 @@ def demo_pikepdf():
54
  return buffer.getvalue()
55
 
56
  def demo_fpdf():
57
- """Create a simple PDF with fpdf"""
58
- pdf = fpdf.FPDF()
 
 
 
59
  pdf.add_page()
60
- pdf.set_font("Arial", size=12)
61
- pdf.cell(200, 10, txt="FPDF Demo", ln=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  buffer = io.BytesIO()
63
  pdf.output(buffer)
64
  buffer.seek(0)
65
  return buffer.getvalue()
66
 
67
  def demo_pymupdf():
68
- """Create a simple PDF with pymupdf"""
 
 
 
69
  doc = fitz.open()
70
- page = doc.new_page()
71
- page.insert_text((100, 100), "PyMuPDF Demo")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  buffer = io.BytesIO()
73
  doc.save(buffer)
74
  buffer.seek(0)
@@ -110,38 +479,96 @@ def demo_image_capture():
110
  return buffer.getvalue()
111
 
112
  # Main PDF creation using ReportLab
113
- def create_main_pdf(outline_items):
114
- """Create a two-page landscape PDF with the outline split between pages"""
 
 
 
 
 
 
 
115
  buffer = io.BytesIO()
116
- doc = SimpleDocTemplate(buffer, pagesize=(A4[1], A4[0])) # Landscape
 
 
 
 
 
 
 
 
117
  styles = getSampleStyleSheet()
118
  story = []
119
 
120
- # Title style
121
  title_style = styles['Heading1']
122
  title_style.textColor = colors.darkblue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
 
124
- # Normal style
125
- normal_style = styles['Normal']
126
- normal_style.fontSize = 12
127
- normal_style.leading = 14
128
-
129
- # Page 1: Items 1-6
130
- story.append(Paragraph("Cutting-Edge ML Areas (1-6)", title_style))
131
- story.append(Spacer(1, 12))
132
- for item in outline_items[:6]:
133
- story.append(Paragraph(item, normal_style))
134
- story.append(Spacer(1, 6))
135
-
136
- # Page break
137
- story.append(Spacer(1, 500)) # Force new page
138
-
139
- # Page 2: Items 7-12
140
- story.append(Paragraph("Cutting-Edge ML Areas (7-12)", title_style))
141
- story.append(Spacer(1, 12))
142
- for item in outline_items[6:]:
143
- story.append(Paragraph(item, normal_style))
144
- story.append(Spacer(1, 6))
145
 
146
  doc.build(story)
147
  buffer.seek(0)
 
1
+ # Streamlit UI
2
+ st.title("🚀 Cutting-Edge ML Outline Generator")
3
+
4
+ col1, col2 = st.columns(2)
5
+
6
+ with col1:
7
+ st.header("📝 Markdown Outline")
8
+
9
+ # Display the markdown content
10
+ st.markdown(ml_markdown)
11
+
12
+ # Create a download button for the markdown file
13
+ st.download_button(
14
+ label="Download Markdown",
15
+ data=ml_markdown,
16
+ file_name="ml_outline.md",
17
+ mime="text/markdown"
18
+ )
19
+
20
+ # Show the markdown source code in an expandable section
21
+ with st.expander("View Markdown Source"):
22
+ st.code(ml_markdown, language="markdown")
23
+
24
+ with col2:
25
+ st.header("📑 PDF Preview & Demos")
26
+
27
+ # Library Demos
28
+ st.subheader("Library Demos")
29
+ if st.button("Run PDF Library Demos"):
30
+ with st.spinner("Running demos..."):
31
+ # Create tabs for each demo
32
+ demo_tabs = st.tabs(["PikePDF", "FPDF", "PyMuPDF", "Image Demo"])
33
+
34
+ with demo_tabs[0]:
35
+ # pikepdf demo
36
+ pike_pdf = demo_pikepdf()
37
+ st.download_button("Download pikepdf Demo", pike_pdf, "pikepdf_demo.pdf")
38
+ st.write("PikePDF demo created successfully!")
39
+ st.info("This PDF contains the multilevel markdown outline in a two-column layout.")
40
+
41
+ with demo_tabs[1]:
42
+ # fpdf demo
43
+ fpdf_pdf = demo_fpdf()
44
+ st.download_button("Download fpdf Demo", fpdf_pdf, "fpdf_demo.pdf")
45
+ st.write("FPDF demo created successfully!")
46
+ st.info("This PDF contains the multilevel markdown outline in a two-column layout.")
47
+
48
+ with demo_tabs[2]:
49
+ # pymupdf demo
50
+ pymupdf_pdf = demo_pymupdf()
51
+ st.download_button("Download pymupdf Demo", pymupdf_pdf, "pymupdf_demo.pdf")
52
+ st.write("PyMuPDF demo created successfully!")
53
+ st.info("This PDF contains the multilevel markdown outline in a two-column layout.")
54
+
55
+ with demo_tabs[3]:
56
+ # Image demo
57
+ img_data = demo_image_capture()
58
+ st.image(img_data, caption="Demo Image (Camera simulation)")
59
+
60
+ # Main PDF Generation
61
+ st.subheader("Main Outline PDF")
62
+ if st.button("Generate Main PDF"):
63
+ with st.spinner("Generating PDF..."):
64
+ try:
65
+ pdf_bytes = create_main_pdf(ml_markdown)
66
+
67
+ st.download_button(
68
+ label="Download Main PDF",
69
+ data=pdf_bytes,
70
+ file_name="ml_outline.pdf",
71
+ mime="application/pdf"
72
+ )
73
+
74
+ # Display the PDF in the app
75
+ base64_pdf = base64.b64encode(pdf_bytes).decode('utf-8')
76
+ pdf_display = f'''
77
+ <embed
78
+ src="data:application/pdf;base64,{base64_pdf}"
79
+ width="100%"
80
+ height="400px"
81
+ type="application/pdf">
82
+ '''
83
+ st.markdown(pdf_display, unsafe_allow_html=True)
84
+
85
+ st.success("PDF generated successfully! The PDF displays the multilevel markdown outline in a two-column layout.")
86
+ except Exception as e:
87
+ st.error(f"Error generating PDF: {str(e)}")
88
+
89
+ # Show the PDF rendering code in an expandable section
90
+ with st.expander("View PDF Rendering Code"):
91
+ st.code("""
92
+ # Process multilevel markdown for PDF output
93
+ def markdown_to_pdf_content(markdown_text):
94
+ # Convert markdown headers to styled text for PDF
95
+ lines = markdown_text.strip().split('\\n')
96
+ pdf_content = []
97
+
98
+ for line in lines:
99
+ if line.startswith('# '):
100
+ # Main header - will be handled separately
101
+ pass
102
+ elif line.startswith('## '):
103
+ # Section header - add as a bold item
104
+ section = line.replace('## ', '').strip()
105
+ pdf_content.append(f"<b>{section}</b>")
106
+ elif line.startswith('- '):
107
+ # List item - add as a normal item
108
+ item = line.replace('- ', '').strip()
109
+ pdf_content.append(item)
110
+
111
+ # Split the content for two columns
112
+ mid_point = len(pdf_content) // 2
113
+ left_column = pdf_content[:mid_point]
114
+ right_column = pdf_content[mid_point:]
115
+
116
+ return left_column, right_column
117
+ """, language="python")
118
+
119
+ # Add custom CSS for better appearance
120
+ st.markdown("""
121
+ <style>
122
+ .stButton>button {
123
+ background-color: #4CAF50;
124
+ color: white;
125
+ font-weight: bold;
126
+ }
127
+ .stTabsimport streamlit as st
128
  import base64
129
  from reportlab.lib.pagesizes import A4
130
  from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
 
139
  import io
140
  import os
141
 
142
+ # Define the ML outline as a markdown string for multilevel content
143
+ ml_markdown = """# Cutting-Edge ML Outline
144
+
145
+ ## Core ML Techniques
146
+ - 🌟 **1. Mixture of Experts (MoE)**
147
+ - Conditional computation techniques
148
+ - Sparse gating mechanisms
149
+ - Training specialized sub-models
150
+
151
+ - 🔥 **2. Supervised Fine-Tuning (SFT) using PyTorch**
152
+ - Loss function customization
153
+ - Gradient accumulation strategies
154
+ - Learning rate schedulers
155
+
156
+ - 🤖 **3. Large Language Models (LLM) using Transformers**
157
+ - Attention mechanisms
158
+ - Tokenization strategies
159
+ - Position encodings
160
+
161
+ ## Training Methods
162
+ - 📊 **4. Self-Rewarding Learning using NPS 0-10 and Verbatims**
163
+ - Custom reward functions
164
+ - Feedback categorization
165
+ - Signal extraction from text
166
+
167
+ - 👍 **5. Reinforcement Learning from Human Feedback (RLHF)**
168
+ - Preference datasets
169
+ - PPO implementation
170
+ - KL divergence constraints
171
+
172
+ - 🔗 **6. MergeKit: Merging Models to Same Embedding Space**
173
+ - TIES merging
174
+ - Task arithmetic
175
+ - SLERP interpolation
176
+
177
+ ## Optimization & Deployment
178
+ - 📏 **7. DistillKit: Model Size Reduction with Spectrum Analysis**
179
+ - Knowledge distillation
180
+ - Quantization techniques
181
+ - Model pruning strategies
182
+
183
+ - 🧠 **8. Agentic RAG Agents using Document Inputs**
184
+ - Vector database integration
185
+ - Query planning
186
+ - Self-reflection mechanisms
187
+
188
+ - ⏳ **9. Longitudinal Data Summarization from Multiple Docs**
189
+ - Multi-document compression
190
+ - Timeline extraction
191
+ - Entity tracking
192
+
193
+ ## Knowledge Representation
194
+ - 📑 **10. Knowledge Extraction using Markdown Knowledge Graphs**
195
+ - Entity recognition
196
+ - Relationship mapping
197
+ - Hierarchical structuring
198
+
199
+ - 🗺️ **11. Knowledge Mapping with Mermaid Diagrams**
200
+ - Flowchart generation
201
+ - Sequence diagram creation
202
+ - State diagrams
203
+
204
+ - 💻 **12. ML Code Generation with Streamlit/Gradio/HTML5+JS**
205
+ - Code completion
206
+ - Unit test generation
207
+ - Documentation synthesis
208
+ """
209
+
210
+ # For compatibility with previous code, also maintain the list version
211
  ml_outline = [
212
  "🌟 1. Mixture of Experts (MoE)",
213
  "🔥 2. Supervised Fine-Tuning (SFT) using PyTorch",
 
223
  "💻 12. ML Code Generation with Streamlit/Gradio/HTML5+JS"
224
  ]
225
 
226
+ # Process multilevel markdown for PDF output
227
+ def markdown_to_pdf_content(markdown_text):
228
+ """Convert markdown text to a format suitable for PDF generation"""
229
+ import re
230
+
231
+ # Convert markdown headers to styled text for PDF
232
+ lines = markdown_text.strip().split('\n')
233
+ pdf_content = []
234
+
235
+ for line in lines:
236
+ if line.startswith('# '):
237
+ # Main header - will be handled separately in the PDF generation
238
+ pass
239
+ elif line.startswith('## '):
240
+ # Section header - add as a bold item
241
+ section = line.replace('## ', '').strip()
242
+ pdf_content.append(f"<b>{section}</b>")
243
+ elif line.startswith('- '):
244
+ # List item - add as a normal item
245
+ item = line.replace('- ', '').strip()
246
+ pdf_content.append(item)
247
+ elif line.strip() == '':
248
+ # Add a small spacer for empty lines
249
+ pass
250
+
251
+ # Remove empty items
252
+ pdf_content = [item for item in pdf_content if item.strip()]
253
+
254
+ # Split the content for two columns
255
+ mid_point = len(pdf_content) // 2
256
+ left_column = pdf_content[:mid_point]
257
+ right_column = pdf_content[mid_point:]
258
+
259
+ return left_column, right_column
260
+
261
  # Demo functions for PDF libraries
262
  def demo_pikepdf():
263
+ """Create a two-column PDF with the markdown outline using pikepdf"""
264
+ # Process markdown content
265
+ left_column, right_column = markdown_to_pdf_content(ml_markdown)
266
+
267
+ # We'll use pymupdf (fitz) to create the content, then save with pikepdf
268
+ doc = fitz.open()
269
+ page = doc.new_page(width=842, height=595) # A4 Landscape
270
+
271
+ # Set up fonts and colors
272
+ title_font = "helv-b"
273
+ section_font = "helv-b"
274
+ normal_font = "helv"
275
+ blue_color = (0, 0, 0.8)
276
+ black_color = (0, 0, 0)
277
+
278
+ # Add title
279
+ page.insert_text((50, 40), "Cutting-Edge ML Outline (PikePDF Demo)", fontname=title_font, fontsize=16, color=blue_color)
280
+
281
+ # First column
282
+ x1, y1 = 50, 80
283
+ for i, item in enumerate(left_column):
284
+ if item.startswith('<b>'):
285
+ # Section header
286
+ text = item.replace('<b>', '').replace('</b>', '')
287
+ page.insert_text((x1, y1 + i*25), text, fontname=section_font, fontsize=14, color=blue_color)
288
+ else:
289
+ # Normal item
290
+ page.insert_text((x1, y1 + i*25), item, fontname=normal_font, fontsize=11, color=black_color)
291
+
292
+ # Second column
293
+ x2, y2 = 450, 80
294
+ for i, item in enumerate(right_column):
295
+ if item.startswith('<b>'):
296
+ # Section header
297
+ text = item.replace('<b>', '').replace('</b>', '')
298
+ page.insert_text((x2, y2 + i*25), text, fontname=section_font, fontsize=14, color=blue_color)
299
+ else:
300
+ # Normal item
301
+ page.insert_text((x2, y2 + i*25), item, fontname=normal_font, fontsize=11, color=black_color)
302
+
303
+ # Draw a dividing line
304
+ page.draw_line((421, 70), (421, 550))
305
+
306
+ # Convert to pikepdf
307
+ temp_buffer = io.BytesIO()
308
+ doc.save(temp_buffer)
309
+ temp_buffer.seek(0)
310
+
311
+ pdf = pikepdf.Pdf.open(temp_buffer)
312
+
313
  # Save to buffer
314
  buffer = io.BytesIO()
315
  pdf.save(buffer)
 
317
  return buffer.getvalue()
318
 
319
  def demo_fpdf():
320
+ """Create a two-column PDF with the markdown outline using FPDF"""
321
+ # Process markdown content
322
+ left_column, right_column = markdown_to_pdf_content(ml_markdown)
323
+
324
+ pdf = fpdf.FPDF(orientation='L') # Landscape
325
  pdf.add_page()
326
+
327
+ # Set title
328
+ pdf.set_font("Arial", 'B', size=16)
329
+ pdf.set_text_color(0, 0, 128) # Dark blue
330
+ pdf.cell(0, 10, txt="Cutting-Edge ML Outline (FPDF Demo)", ln=True, align='C')
331
+ pdf.ln(10)
332
+
333
+ # First column
334
+ x_col1 = 20
335
+ y_start = pdf.get_y()
336
+
337
+ for item in left_column:
338
+ if item.startswith('<b>'):
339
+ # Section header
340
+ text = item.replace('<b>', '').replace('</b>', '')
341
+ pdf.set_font("Arial", 'B', size=14)
342
+ pdf.set_text_color(0, 0, 128) # Dark blue
343
+ else:
344
+ # Normal item
345
+ text = item
346
+ pdf.set_font("Arial", size=11)
347
+ pdf.set_text_color(0, 0, 0) # Black
348
+
349
+ pdf.set_x(x_col1)
350
+ pdf.multi_cell(180, 10, txt=text, align='L')
351
+ pdf.ln(2)
352
+
353
+ # Second column
354
+ x_col2 = pdf.w / 2 + 10
355
+ pdf.set_y(y_start)
356
+
357
+ for item in right_column:
358
+ if item.startswith('<b>'):
359
+ # Section header
360
+ text = item.replace('<b>', '').replace('</b>', '')
361
+ pdf.set_font("Arial", 'B', size=14)
362
+ pdf.set_text_color(0, 0, 128) # Dark blue
363
+ else:
364
+ # Normal item
365
+ text = item
366
+ pdf.set_font("Arial", size=11)
367
+ pdf.set_text_color(0, 0, 0) # Black
368
+
369
+ pdf.set_x(x_col2)
370
+ pdf.multi_cell(180, 10, txt=text, align='L')
371
+ pdf.ln(2)
372
+
373
+ # Draw a dividing line
374
+ pdf.line(pdf.w/2, 30, pdf.w/2, 280)
375
+
376
  buffer = io.BytesIO()
377
  pdf.output(buffer)
378
  buffer.seek(0)
379
  return buffer.getvalue()
380
 
381
  def demo_pymupdf():
382
+ """Create a two-column PDF with the markdown outline using PyMuPDF"""
383
+ # Process markdown content
384
+ left_column, right_column = markdown_to_pdf_content(ml_markdown)
385
+
386
  doc = fitz.open()
387
+ page = doc.new_page(width=842, height=595) # A4 Landscape
388
+
389
+ # Set up fonts and colors
390
+ title_font = "helv-b"
391
+ section_font = "helv-b"
392
+ normal_font = "helv"
393
+ blue_color = (0, 0, 0.8)
394
+ black_color = (0, 0, 0)
395
+
396
+ # Add title
397
+ page.insert_text((300, 40), "Cutting-Edge ML Outline (PyMuPDF Demo)", fontname=title_font, fontsize=16, color=blue_color)
398
+
399
+ # First column
400
+ x1, y1 = 50, 80
401
+ line_height = 25
402
+ current_y = y1
403
+
404
+ for item in left_column:
405
+ if item.startswith('<b>'):
406
+ # Add extra space before sections (except the first one)
407
+ if current_y > y1:
408
+ current_y += 10
409
+
410
+ # Section header
411
+ text = item.replace('<b>', '').replace('</b>', '')
412
+ page.insert_text((x1, current_y), text, fontname=section_font, fontsize=14, color=blue_color)
413
+ else:
414
+ # Normal item
415
+ page.insert_text((x1 + 10, current_y), item, fontname=normal_font, fontsize=11, color=black_color)
416
+
417
+ current_y += line_height
418
+
419
+ # Second column
420
+ x2, y2 = 450, 80
421
+ current_y = y2
422
+
423
+ for item in right_column:
424
+ if item.startswith('<b>'):
425
+ # Add extra space before sections (except the first one)
426
+ if current_y > y2:
427
+ current_y += 10
428
+
429
+ # Section header
430
+ text = item.replace('<b>', '').replace('</b>', '')
431
+ page.insert_text((x2, current_y), text, fontname=section_font, fontsize=14, color=blue_color)
432
+ else:
433
+ # Normal item
434
+ page.insert_text((x2 + 10, current_y), item, fontname=normal_font, fontsize=11, color=black_color)
435
+
436
+ current_y += line_height
437
+
438
+ # Draw a dividing line
439
+ page.draw_line((421, 70), (421, 550))
440
+
441
  buffer = io.BytesIO()
442
  doc.save(buffer)
443
  buffer.seek(0)
 
479
  return buffer.getvalue()
480
 
481
  # Main PDF creation using ReportLab
482
+ def create_main_pdf(markdown_text):
483
+ """Create a single-page landscape PDF with the outline in two columns"""
484
+ from reportlab.platypus import Table, TableStyle, Paragraph, Spacer
485
+ from reportlab.lib import pagesizes
486
+ from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
487
+
488
+ # Process markdown content
489
+ left_column, right_column = markdown_to_pdf_content(markdown_text)
490
+
491
  buffer = io.BytesIO()
492
+ doc = SimpleDocTemplate(
493
+ buffer,
494
+ pagesize=(A4[1], A4[0]), # Landscape
495
+ leftMargin=50,
496
+ rightMargin=50,
497
+ topMargin=50,
498
+ bottomMargin=50
499
+ )
500
+
501
  styles = getSampleStyleSheet()
502
  story = []
503
 
504
+ # Create custom styles
505
  title_style = styles['Heading1']
506
  title_style.textColor = colors.darkblue
507
+ title_style.alignment = 1 # Center alignment
508
+
509
+ section_style = ParagraphStyle(
510
+ 'SectionStyle',
511
+ parent=styles['Heading2'],
512
+ textColor=colors.darkblue,
513
+ spaceAfter=6
514
+ )
515
+
516
+ item_style = ParagraphStyle(
517
+ 'ItemStyle',
518
+ parent=styles['Normal'],
519
+ fontSize=11,
520
+ leading=14,
521
+ leftIndent=10
522
+ )
523
+
524
+ # Add title
525
+ story.append(Paragraph("Cutting-Edge ML Outline (ReportLab)", title_style))
526
+ story.append(Spacer(1, 20))
527
+
528
+ # Prepare data for table
529
+ table_data = []
530
+ max_rows = max(len(left_column), len(right_column))
531
+
532
+ # Process left and right columns into paragraphs
533
+ left_paragraphs = []
534
+ for item in left_column:
535
+ if item.startswith('<b>'):
536
+ text = item.replace('<b>', '').replace('</b>', '')
537
+ left_paragraphs.append(Paragraph(text, section_style))
538
+ else:
539
+ left_paragraphs.append(Paragraph(item, item_style))
540
+
541
+ right_paragraphs = []
542
+ for item in right_column:
543
+ if item.startswith('<b>'):
544
+ text = item.replace('<b>', '').replace('</b>', '')
545
+ right_paragraphs.append(Paragraph(text, section_style))
546
+ else:
547
+ right_paragraphs.append(Paragraph(item, item_style))
548
+
549
+ # Create one row per paragraph
550
+ for i in range(max_rows):
551
+ left_p = left_paragraphs[i] if i < len(left_paragraphs) else ""
552
+ right_p = right_paragraphs[i] if i < len(right_paragraphs) else ""
553
+ table_data.append([left_p, right_p])
554
+
555
+ # Calculate column widths
556
+ col_width = (A4[1] - 120) / 2.0 # Page width minus margins divided by 2
557
+
558
+ # Create the table with the data
559
+ table = Table(table_data, colWidths=[col_width, col_width])
560
+
561
+ # Style the table
562
+ table.setStyle(TableStyle([
563
+ ('VALIGN', (0, 0), (-1, -1), 'TOP'),
564
+ ('ALIGN', (0, 0), (0, -1), 'LEFT'),
565
+ ('ALIGN', (1, 0), (1, -1), 'LEFT'),
566
+ ('BACKGROUND', (0, 0), (-1, -1), colors.white),
567
+ ('GRID', (0, 0), (-1, -1), 0.5, colors.white),
568
+ ('LINEAFTER', (0, 0), (0, -1), 1, colors.grey),
569
+ ]))
570
 
571
+ story.append(table)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
572
 
573
  doc.build(story)
574
  buffer.seek(0)