syedkhalid076 commited on
Commit
1a69174
Β·
verified Β·
1 Parent(s): b70965d

Added Support of Mermaid rendering.

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +101 -11
src/streamlit_app.py CHANGED
@@ -4,6 +4,9 @@ from weasyprint import HTML, CSS
4
  from datetime import datetime
5
  import base64
6
  import os
 
 
 
7
  from io import BytesIO
8
  from PIL import Image
9
 
@@ -242,6 +245,40 @@ def get_css_template(font_name, spacing="normal", font_size=11):
242
  text-decoration: underline;
243
  }}
244
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  /* Syntax highlighting for code blocks - Complete Pygments style */
246
  .codehilite .hll {{ background-color: #ffffcc }}
247
  .codehilite .c {{ color: #008000; font-style: italic }} /* Comment */
@@ -315,6 +352,9 @@ def get_css_template(font_name, spacing="normal", font_size=11):
315
 
316
  def generate_html(markdown_text, title, font_name, spacing="normal", font_size=11, logo_data=None):
317
  """Convert Markdown to styled HTML"""
 
 
 
318
  # Convert Markdown to HTML with extensions
319
  md = markdown.Markdown(
320
  extensions=[
@@ -334,7 +374,7 @@ def generate_html(markdown_text, title, font_name, spacing="normal", font_size=1
334
  }
335
  }
336
  )
337
- body_html = md.convert(markdown_text)
338
 
339
  # Handle logo
340
  logo_html = ""
@@ -396,6 +436,65 @@ def image_to_base64(image_file):
396
  st.error(f"Error processing image: {str(e)}")
397
  return None
398
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
  # Streamlit UI
400
  st.title("πŸ“„ Markdown to PDF Converter")
401
  st.markdown("Convert your Markdown documents into beautifully formatted PDFs with syntax highlighting")
@@ -471,35 +570,26 @@ with col1:
471
  markdown_text = st.text_area(
472
  "Markdown Content",
473
  value="""# Welcome to Markdown PDF
474
-
475
  This is a **sample** document with **syntax highlighting**.
476
-
477
  ## Features
478
-
479
  - Beautiful typography
480
  - Professional layout
481
  - Code highlighting with Pygments
482
  - Atomic list item page breaks
483
-
484
  ## Code Example
485
-
486
  ```python
487
  def fibonacci(n):
488
  \"\"\"Calculate Fibonacci number\"\"\"
489
  if n <= 1:
490
  return n
491
  return fibonacci(n-1) + fibonacci(n-2)
492
-
493
  print(fibonacci(10))
494
  ```
495
-
496
  ## JavaScript Example
497
-
498
  ```javascript
499
  const greeting = "Hello, World!";
500
  console.log(greeting);
501
  ```
502
-
503
  Enjoy! πŸŽ‰""",
504
  height=400,
505
  help="Supports GitHub-Flavored Markdown"
@@ -590,7 +680,7 @@ with col_btn2:
590
  st.divider()
591
  col_footer1, col_footer2, col_footer3 = st.columns(3)
592
  with col_footer1:
593
- st.caption("πŸš€ Built with Streamlit & WeasyPrint")
594
  with col_footer2:
595
  st.caption("πŸ“š Supports GitHub-Flavored Markdown")
596
  with col_footer3:
 
4
  from datetime import datetime
5
  import base64
6
  import os
7
+ import re
8
+ import zlib
9
+ import requests
10
  from io import BytesIO
11
  from PIL import Image
12
 
 
245
  text-decoration: underline;
246
  }}
247
 
248
+ /* Mermaid diagram container - fits within one page */
249
+ .mermaid-container {{
250
+ max-height: 600px;
251
+ width: 100%;
252
+ page-break-inside: avoid;
253
+ break-inside: avoid;
254
+ margin: {1.5 * margin_multiplier}em 0;
255
+ text-align: center;
256
+ background: #fafafa;
257
+ border: 1px solid #e0e0e0;
258
+ border-radius: 8px;
259
+ padding: 1em;
260
+ box-sizing: border-box;
261
+ }}
262
+
263
+ .mermaid-container img {{
264
+ max-width: 100%;
265
+ max-height: 550px;
266
+ width: auto;
267
+ height: auto;
268
+ object-fit: contain;
269
+ display: block;
270
+ margin: 0 auto;
271
+ }}
272
+
273
+ .mermaid-error {{
274
+ padding: 1em;
275
+ background: #fff3cd;
276
+ border: 1px solid #ffc107;
277
+ border-radius: 4px;
278
+ color: #856404;
279
+ margin: 1em 0;
280
+ }}
281
+
282
  /* Syntax highlighting for code blocks - Complete Pygments style */
283
  .codehilite .hll {{ background-color: #ffffcc }}
284
  .codehilite .c {{ color: #008000; font-style: italic }} /* Comment */
 
352
 
353
  def generate_html(markdown_text, title, font_name, spacing="normal", font_size=11, logo_data=None):
354
  """Convert Markdown to styled HTML"""
355
+ # Process Mermaid blocks first (renders diagrams and keeps code blocks)
356
+ processed_text = process_mermaid_blocks(markdown_text)
357
+
358
  # Convert Markdown to HTML with extensions
359
  md = markdown.Markdown(
360
  extensions=[
 
374
  }
375
  }
376
  )
377
+ body_html = md.convert(processed_text)
378
 
379
  # Handle logo
380
  logo_html = ""
 
436
  st.error(f"Error processing image: {str(e)}")
437
  return None
438
 
439
+ def encode_mermaid_for_ink(mermaid_code):
440
+ """Encode Mermaid code for mermaid.ink API using pako deflate + base64"""
441
+ # Use zlib to compress (pako compatible)
442
+ compressed = zlib.compress(mermaid_code.encode('utf-8'), level=9)
443
+ # Remove zlib header (first 2 bytes) and checksum (last 4 bytes) for raw deflate
444
+ raw_deflate = compressed[2:-4]
445
+ # Base64 encode with URL-safe characters
446
+ encoded = base64.urlsafe_b64encode(raw_deflate).decode('utf-8')
447
+ return encoded
448
+
449
+ def render_mermaid_to_svg(mermaid_code):
450
+ """
451
+ Render Mermaid code to SVG using mermaid.ink API
452
+ Returns: SVG content as string, or None on failure
453
+ """
454
+ try:
455
+ encoded = encode_mermaid_for_ink(mermaid_code)
456
+ url = f"https://mermaid.ink/svg/pako:{encoded}"
457
+
458
+ response = requests.get(url, timeout=30)
459
+ if response.status_code == 200:
460
+ return response.text
461
+ else:
462
+ return None
463
+ except Exception as e:
464
+ print(f"Mermaid rendering error: {e}")
465
+ return None
466
+
467
+ def process_mermaid_blocks(markdown_text):
468
+ """
469
+ Parse ::mermaid...:: blocks and replace with rendered SVG + code block
470
+ Returns: modified markdown/HTML hybrid with embedded diagrams
471
+ """
472
+ # Pattern to match ::mermaid\n...\n::
473
+ pattern = r'::mermaid\n(.*?)\n::'
474
+
475
+ def replace_mermaid(match):
476
+ mermaid_code = match.group(1).strip()
477
+
478
+ # Render to SVG
479
+ svg_content = render_mermaid_to_svg(mermaid_code)
480
+
481
+ if svg_content:
482
+ # Embed SVG as base64 data URL
483
+ svg_base64 = base64.b64encode(svg_content.encode('utf-8')).decode('utf-8')
484
+ img_tag = f'<div class="mermaid-container"><img src="data:image/svg+xml;base64,{svg_base64}" alt="Mermaid Diagram" /></div>'
485
+ else:
486
+ img_tag = '<div class="mermaid-error">⚠️ Failed to render Mermaid diagram</div>'
487
+
488
+ # Create code block for raw mermaid code (will be processed by markdown later)
489
+ code_block = f'\n```mermaid\n{mermaid_code}\n```\n'
490
+
491
+ # Return both: image first, then code block
492
+ return f'\n{img_tag}\n{code_block}'
493
+
494
+ # Process all mermaid blocks
495
+ result = re.sub(pattern, replace_mermaid, markdown_text, flags=re.DOTALL)
496
+ return result
497
+
498
  # Streamlit UI
499
  st.title("πŸ“„ Markdown to PDF Converter")
500
  st.markdown("Convert your Markdown documents into beautifully formatted PDFs with syntax highlighting")
 
570
  markdown_text = st.text_area(
571
  "Markdown Content",
572
  value="""# Welcome to Markdown PDF
 
573
  This is a **sample** document with **syntax highlighting**.
 
574
  ## Features
 
575
  - Beautiful typography
576
  - Professional layout
577
  - Code highlighting with Pygments
578
  - Atomic list item page breaks
 
579
  ## Code Example
 
580
  ```python
581
  def fibonacci(n):
582
  \"\"\"Calculate Fibonacci number\"\"\"
583
  if n <= 1:
584
  return n
585
  return fibonacci(n-1) + fibonacci(n-2)
 
586
  print(fibonacci(10))
587
  ```
 
588
  ## JavaScript Example
 
589
  ```javascript
590
  const greeting = "Hello, World!";
591
  console.log(greeting);
592
  ```
 
593
  Enjoy! πŸŽ‰""",
594
  height=400,
595
  help="Supports GitHub-Flavored Markdown"
 
680
  st.divider()
681
  col_footer1, col_footer2, col_footer3 = st.columns(3)
682
  with col_footer1:
683
+ st.caption("πŸš€ Built with Streamlit")
684
  with col_footer2:
685
  st.caption("πŸ“š Supports GitHub-Flavored Markdown")
686
  with col_footer3: