euler314 commited on
Commit
e2b402b
·
verified ·
1 Parent(s): e6742d7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +180 -87
app.py CHANGED
@@ -5,7 +5,9 @@ import base64
5
  from pathlib import Path
6
  import os
7
  import shutil
8
- import time
 
 
9
 
10
  # Set page configuration
11
  st.set_page_config(page_title="LaTeX Editor & Compiler", page_icon="📝", layout="wide")
@@ -56,24 +58,32 @@ def get_download_link(pdf_data, filename="document.pdf"):
56
  b64_pdf = base64.b64encode(pdf_data).decode()
57
  return f'<a href="data:application/pdf;base64,{b64_pdf}" download="{filename}" class="download-button">Download PDF</a>'
58
 
59
- # Convert PDF to images for preview (safer than embedding PDF)
60
- def pdf_preview(pdf_data):
61
- # Save PDF to a temporary file
62
  if not pdf_data:
63
- return st.error("No PDF data to preview")
64
 
65
- st.warning("PDF preview is not available due to browser security restrictions.")
66
- st.info("Please download the PDF using the button below to view it.")
67
-
68
- # Provide download button with more prominent styling
69
- st.markdown(
70
- f"""
71
- <div style="text-align: center; margin: 30px 0;">
72
- {get_download_link(pdf_data)}
73
- </div>
74
- """,
75
- unsafe_allow_html=True
76
- )
 
 
 
 
 
 
 
 
 
77
 
78
  # LaTeX package reference
79
  latex_packages = {
@@ -256,15 +266,23 @@ Your conclusion here.
256
  \end{document}
257
  """
258
 
259
- # Add custom CSS
260
  st.markdown("""
261
  <style>
 
262
  .editor-container {
263
  border: 1px solid #ccc;
264
  border-radius: 5px;
265
  padding: 10px;
266
  background-color: #f8f9fa;
267
  }
 
 
 
 
 
 
 
268
  .download-button {
269
  display: inline-block;
270
  padding: 0.7em 1.4em;
@@ -283,56 +301,91 @@ st.markdown("""
283
  background-color: #45a049;
284
  box-shadow: 0 4px 8px rgba(0,0,0,0.3);
285
  }
286
- .stTextArea textarea {
287
- font-family: 'Courier New', Courier, monospace !important;
288
- font-size: 14px !important;
289
- line-height: 1.5 !important;
290
  }
 
 
291
  .latex-command {
292
- background-color: #f1f1f1;
293
- padding: 2px 4px;
294
- border-radius: 3px;
295
- font-family: monospace;
 
296
  cursor: pointer;
 
 
 
297
  }
298
- .reference-tabs .stTabs {
299
- background-color: #f5f5f5;
300
- border-radius: 5px;
301
- padding: 10px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
  }
303
- .stMarkdown h4 {
304
- margin-top: 0.5rem !important;
305
- margin-bottom: 0.5rem !important;
 
 
 
 
306
  }
307
- .command-category {
308
- margin-bottom: 15px !important;
 
 
 
309
  }
310
  </style>
311
  """, unsafe_allow_html=True)
312
 
313
- # JavaScript for copying commands (optional, may not work in all environments)
314
- st.markdown("""
315
- <script>
316
- document.addEventListener('DOMContentLoaded', (event) => {
317
- // Add click handlers to copy LaTeX commands
318
- document.querySelectorAll('.latex-command').forEach(element => {
319
- element.addEventListener('click', function() {
320
- const textToCopy = this.textContent;
321
- const textArea = document.querySelector('.stTextArea textarea');
322
- if (textArea) {
323
- const start = textArea.selectionStart;
324
- const end = textArea.selectionEnd;
325
- const value = textArea.value;
326
- textArea.value = value.substring(0, start) + textToCopy + value.substring(end);
327
- textArea.selectionStart = textArea.selectionEnd = start + textToCopy.length;
328
- textArea.focus();
329
- }
330
- });
331
- });
332
- });
333
- </script>
334
- """, unsafe_allow_html=True)
335
-
336
  # Main application
337
  def main():
338
  st.title("LaTeX Editor & PDF Compiler")
@@ -345,9 +398,13 @@ def main():
345
  # Show packages.txt content suggestion
346
  with st.expander("Required packages.txt content"):
347
  st.code("""texlive
 
348
  texlive-latex-extra
349
  texlive-fonts-recommended
350
- texlive-science""", language="text")
 
 
 
351
 
352
  # Create layout with sidebar
353
  col1, col2 = st.columns([3, 2])
@@ -386,9 +443,9 @@ texlive-science""", language="text")
386
  st.rerun()
387
 
388
  with col2:
389
- st.subheader("PDF Output")
390
 
391
- # PDF compilation and download
392
  if 'compile_clicked' in st.session_state and st.session_state.compile_clicked:
393
  with st.spinner("Compiling LaTeX to PDF..."):
394
  pdf_data, stdout, stderr = latex_to_pdf(latex_code)
@@ -396,7 +453,18 @@ texlive-science""", language="text")
396
  if pdf_data:
397
  st.session_state.pdf_data = pdf_data
398
  st.success("PDF compiled successfully!")
399
- pdf_preview(pdf_data)
 
 
 
 
 
 
 
 
 
 
 
400
  st.session_state.compile_clicked = False
401
  else:
402
  st.error("Compilation Error")
@@ -407,9 +475,17 @@ texlive-science""", language="text")
407
 
408
  # Display previous PDF if available
409
  elif 'pdf_data' in st.session_state and st.session_state.pdf_data:
410
- pdf_preview(st.session_state.pdf_data)
 
 
 
 
 
 
 
 
411
  else:
412
- st.info("Compile your LaTeX document to generate a PDF for download")
413
 
414
  # LaTeX Reference Sidebar
415
  st.sidebar.title("LaTeX Reference")
@@ -429,13 +505,16 @@ texlive-science""", language="text")
429
  if filtered_commands:
430
  found = True
431
  with st.sidebar.expander(f"{category} ({len(filtered_commands)} results)"):
 
432
  for cmd, desc in filtered_commands.items():
433
- cmd_display = cmd.replace("\\", "\\\\") # Escape backslashes for display
434
- st.markdown(f"<div class='latex-command' title='Click to copy'>{cmd}</div> - {desc}", unsafe_allow_html=True)
435
- if st.button(f"Insert '{cmd}'", key=f"btn_{cmd}"):
436
- # Insert at cursor not supported directly, but we can append
437
- st.session_state.latex_code += f"\n{cmd}"
438
- st.rerun()
 
 
439
 
440
  # Search packages
441
  for category, packages in latex_packages.items():
@@ -445,37 +524,51 @@ texlive-science""", language="text")
445
  if filtered_packages:
446
  found = True
447
  with st.sidebar.expander(f"Packages: {category} ({len(filtered_packages)} results)"):
 
448
  for pkg, desc in filtered_packages.items():
449
- pkg_display = pkg.replace("\\", "\\\\") # Escape backslashes for display
450
- st.markdown(f"<div class='latex-command' title='Click to copy'>{pkg}</div> - {desc}", unsafe_allow_html=True)
451
- if st.button(f"Insert '{pkg}'", key=f"btn_{pkg}"):
452
- st.session_state.latex_code += f"\n{pkg}"
453
- st.rerun()
 
 
 
454
 
455
  if not found:
456
  st.sidebar.info("No matching commands or packages found")
457
 
458
  else:
459
  # Display full reference when not searching
460
- tabs = st.sidebar.tabs(["Commands", "Packages"])
461
 
462
- with tabs[0]:
463
  for category, commands in latex_commands.items():
464
- with st.expander(category):
 
465
  for cmd, desc in commands.items():
466
- st.markdown(f"<div class='latex-command'>{cmd}</div> - {desc}", unsafe_allow_html=True)
467
- if st.button(f"Insert '{cmd}'", key=f"btn_{cmd}"):
468
- st.session_state.latex_code += f"\n{cmd}"
469
- st.rerun()
 
 
 
 
470
 
471
- with tabs[1]:
472
  for category, packages in latex_packages.items():
473
  with st.expander(category):
 
474
  for pkg, desc in packages.items():
475
- st.markdown(f"<div class='latex-command'>{pkg}</div> - {desc}", unsafe_allow_html=True)
476
- if st.button(f"Insert '{pkg}'", key=f"btn_{pkg}"):
477
- st.session_state.latex_code += f"\n{pkg}"
478
- st.rerun()
 
 
 
 
479
 
480
  if __name__ == "__main__":
481
  main()
 
5
  from pathlib import Path
6
  import os
7
  import shutil
8
+ import io
9
+ from PIL import Image
10
+ import fitz # PyMuPDF
11
 
12
  # Set page configuration
13
  st.set_page_config(page_title="LaTeX Editor & Compiler", page_icon="📝", layout="wide")
 
58
  b64_pdf = base64.b64encode(pdf_data).decode()
59
  return f'<a href="data:application/pdf;base64,{b64_pdf}" download="{filename}" class="download-button">Download PDF</a>'
60
 
61
+ # Convert PDF to image for preview
62
+ def render_pdf_preview(pdf_data):
 
63
  if not pdf_data:
64
+ return None
65
 
66
+ try:
67
+ # Create a file-like object from the PDF data
68
+ pdf_stream = io.BytesIO(pdf_data)
69
+
70
+ # Open PDF with PyMuPDF (fitz)
71
+ pdf_document = fitz.open(stream=pdf_stream, filetype="pdf")
72
+
73
+ # Render pages as images
74
+ images = []
75
+ for page_num in range(min(3, len(pdf_document))): # Preview first 3 pages max
76
+ page = pdf_document.load_page(page_num)
77
+ pix = page.get_pixmap(matrix=fitz.Matrix(2, 2)) # Zoom factor 2 for better resolution
78
+ img_data = pix.tobytes("png")
79
+ img = Image.open(io.BytesIO(img_data))
80
+ images.append(img)
81
+
82
+ pdf_document.close()
83
+ return images
84
+ except Exception as e:
85
+ st.error(f"Error rendering PDF preview: {str(e)}")
86
+ return None
87
 
88
  # LaTeX package reference
89
  latex_packages = {
 
266
  \end{document}
267
  """
268
 
269
+ # Add custom CSS with improved sidebar styling
270
  st.markdown("""
271
  <style>
272
+ /* Editor styling */
273
  .editor-container {
274
  border: 1px solid #ccc;
275
  border-radius: 5px;
276
  padding: 10px;
277
  background-color: #f8f9fa;
278
  }
279
+ .stTextArea textarea {
280
+ font-family: 'Courier New', Courier, monospace !important;
281
+ font-size: 14px !important;
282
+ line-height: 1.5 !important;
283
+ }
284
+
285
+ /* Download button styling */
286
  .download-button {
287
  display: inline-block;
288
  padding: 0.7em 1.4em;
 
301
  background-color: #45a049;
302
  box-shadow: 0 4px 8px rgba(0,0,0,0.3);
303
  }
304
+
305
+ /* Sidebar styling */
306
+ .sidebar .sidebar-content {
307
+ background-color: #f0f2f6;
308
  }
309
+
310
+ /* LaTeX command styling */
311
  .latex-command {
312
+ background-color: #e9ecef;
313
+ padding: 4px 8px;
314
+ border-radius: 4px;
315
+ font-family: 'Courier New', Courier, monospace;
316
+ color: #1e1e1e;
317
  cursor: pointer;
318
+ display: inline-block;
319
+ margin-bottom: 4px;
320
+ border: 1px solid #ced4da;
321
  }
322
+ .latex-command:hover {
323
+ background-color: #d0d7de;
324
+ border-color: #adb5bd;
325
+ }
326
+
327
+ /* Command description styling */
328
+ .command-description {
329
+ color: #495057;
330
+ padding-left: 8px;
331
+ display: inline-block;
332
+ }
333
+
334
+ /* Category title styling */
335
+ .category-title {
336
+ font-weight: bold;
337
+ color: #212529;
338
+ margin-top: 15px;
339
+ margin-bottom: 8px;
340
+ }
341
+
342
+ /* Expander styling */
343
+ .streamlit-expanderHeader {
344
+ font-weight: bold;
345
+ color: #212529;
346
+ background-color: #e9ecef;
347
+ border-radius: 4px;
348
+ }
349
+
350
+ /* Command list container */
351
+ .command-list {
352
+ background-color: #f8f9fa;
353
+ border-radius: 4px;
354
+ padding: 8px;
355
+ border: 1px solid #dee2e6;
356
+ }
357
+
358
+ /* Insert button styling */
359
+ .insert-button {
360
+ background-color: #007bff;
361
+ color: white;
362
+ border: none;
363
+ border-radius: 4px;
364
+ padding: 2px 8px;
365
+ margin-left: 8px;
366
+ cursor: pointer;
367
+ font-size: 12px;
368
+ }
369
+ .insert-button:hover {
370
+ background-color: #0069d9;
371
  }
372
+
373
+ /* PDF preview container */
374
+ .pdf-preview-container {
375
+ border: 1px solid #dee2e6;
376
+ border-radius: 5px;
377
+ padding: 15px;
378
+ background-color: #f8f9fa;
379
  }
380
+
381
+ /* PDF page image */
382
+ .pdf-page-image {
383
+ border: 1px solid #dee2e6;
384
+ margin-bottom: 15px;
385
  }
386
  </style>
387
  """, unsafe_allow_html=True)
388
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
389
  # Main application
390
  def main():
391
  st.title("LaTeX Editor & PDF Compiler")
 
398
  # Show packages.txt content suggestion
399
  with st.expander("Required packages.txt content"):
400
  st.code("""texlive
401
+ texlive-latex-base
402
  texlive-latex-extra
403
  texlive-fonts-recommended
404
+ texlive-science
405
+ python3-dev
406
+ python3-pip
407
+ poppler-utils""", language="text")
408
 
409
  # Create layout with sidebar
410
  col1, col2 = st.columns([3, 2])
 
443
  st.rerun()
444
 
445
  with col2:
446
+ st.subheader("PDF Preview")
447
 
448
+ # PDF compilation and preview
449
  if 'compile_clicked' in st.session_state and st.session_state.compile_clicked:
450
  with st.spinner("Compiling LaTeX to PDF..."):
451
  pdf_data, stdout, stderr = latex_to_pdf(latex_code)
 
453
  if pdf_data:
454
  st.session_state.pdf_data = pdf_data
455
  st.success("PDF compiled successfully!")
456
+
457
+ # Render preview
458
+ preview_images = render_pdf_preview(pdf_data)
459
+ if preview_images:
460
+ st.write("PDF Preview (First Pages):")
461
+ for i, img in enumerate(preview_images):
462
+ st.image(img, caption=f"Page {i+1}", use_column_width=True,
463
+ clamp=True, output_format="PNG")
464
+
465
+ # Download button
466
+ st.markdown(get_download_link(pdf_data), unsafe_allow_html=True)
467
+
468
  st.session_state.compile_clicked = False
469
  else:
470
  st.error("Compilation Error")
 
475
 
476
  # Display previous PDF if available
477
  elif 'pdf_data' in st.session_state and st.session_state.pdf_data:
478
+ preview_images = render_pdf_preview(st.session_state.pdf_data)
479
+ if preview_images:
480
+ st.write("PDF Preview (First Pages):")
481
+ for i, img in enumerate(preview_images):
482
+ st.image(img, caption=f"Page {i+1}", use_column_width=True,
483
+ clamp=True, output_format="PNG")
484
+
485
+ # Download button
486
+ st.markdown(get_download_link(st.session_state.pdf_data), unsafe_allow_html=True)
487
  else:
488
+ st.info("Compile your LaTeX document to see the PDF preview")
489
 
490
  # LaTeX Reference Sidebar
491
  st.sidebar.title("LaTeX Reference")
 
505
  if filtered_commands:
506
  found = True
507
  with st.sidebar.expander(f"{category} ({len(filtered_commands)} results)"):
508
+ st.markdown('<div class="command-list">', unsafe_allow_html=True)
509
  for cmd, desc in filtered_commands.items():
510
+ col1, col2 = st.columns([3, 1])
511
+ with col1:
512
+ st.markdown(f'<div><span class="latex-command">{cmd}</span> <span class="command-description">{desc}</span></div>', unsafe_allow_html=True)
513
+ with col2:
514
+ if st.button("Insert", key=f"btn_{cmd}", help=f"Insert {cmd} into editor"):
515
+ st.session_state.latex_code += f"\n{cmd}"
516
+ st.rerun()
517
+ st.markdown('</div>', unsafe_allow_html=True)
518
 
519
  # Search packages
520
  for category, packages in latex_packages.items():
 
524
  if filtered_packages:
525
  found = True
526
  with st.sidebar.expander(f"Packages: {category} ({len(filtered_packages)} results)"):
527
+ st.markdown('<div class="command-list">', unsafe_allow_html=True)
528
  for pkg, desc in filtered_packages.items():
529
+ col1, col2 = st.columns([3, 1])
530
+ with col1:
531
+ st.markdown(f'<div><span class="latex-command">{pkg}</span> <span class="command-description">{desc}</span></div>', unsafe_allow_html=True)
532
+ with col2:
533
+ if st.button("Insert", key=f"btn_{pkg}", help=f"Insert {pkg} into editor"):
534
+ st.session_state.latex_code += f"\n{pkg}"
535
+ st.rerun()
536
+ st.markdown('</div>', unsafe_allow_html=True)
537
 
538
  if not found:
539
  st.sidebar.info("No matching commands or packages found")
540
 
541
  else:
542
  # Display full reference when not searching
543
+ tab1, tab2 = st.sidebar.tabs(["Commands", "Packages"])
544
 
545
+ with tab1:
546
  for category, commands in latex_commands.items():
547
+ with st.expander(category, expanded=category=="Math"):
548
+ st.markdown('<div class="command-list">', unsafe_allow_html=True)
549
  for cmd, desc in commands.items():
550
+ col1, col2 = st.columns([3, 1])
551
+ with col1:
552
+ st.markdown(f'<div><span class="latex-command">{cmd}</span> <span class="command-description">{desc}</span></div>', unsafe_allow_html=True)
553
+ with col2:
554
+ if st.button("Insert", key=f"btn_{cmd}", help=f"Insert {cmd} into editor"):
555
+ st.session_state.latex_code += f"\n{cmd}"
556
+ st.rerun()
557
+ st.markdown('</div>', unsafe_allow_html=True)
558
 
559
+ with tab2:
560
  for category, packages in latex_packages.items():
561
  with st.expander(category):
562
+ st.markdown('<div class="command-list">', unsafe_allow_html=True)
563
  for pkg, desc in packages.items():
564
+ col1, col2 = st.columns([3, 1])
565
+ with col1:
566
+ st.markdown(f'<div><span class="latex-command">{pkg}</span> <span class="command-description">{desc}</span></div>', unsafe_allow_html=True)
567
+ with col2:
568
+ if st.button("Insert", key=f"btn_{pkg}", help=f"Insert {pkg} into editor"):
569
+ st.session_state.latex_code += f"\n{pkg}"
570
+ st.rerun()
571
+ st.markdown('</div>', unsafe_allow_html=True)
572
 
573
  if __name__ == "__main__":
574
  main()