1
0

generate_pdfs.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. #ident "@(#)$Format:LocalFoodAI_lanfr144:generate_pdfs.py:%an:%ae:%ad:%cn:%ce:%cd:%H:%D:%N$"
  2. import os
  3. import glob
  4. from markdown_pdf import MarkdownPdf
  5. from markdown_pdf import Section
  6. def main():
  7. docs_dir = os.path.join(os.path.dirname(__file__), '..', 'docs')
  8. md_files = glob.glob(os.path.join(docs_dir, '*.md'))
  9. if not md_files:
  10. print("No markdown files found in docs/")
  11. return
  12. # Resolve dynamic absolute paths for downloaded TrueType fonts
  13. fonts_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'docs', 'fonts')).replace('\\', '/')
  14. regular_font = f"{fonts_dir}/Roboto-Regular.ttf"
  15. bold_font = f"{fonts_dir}/Roboto-Bold.ttf"
  16. mono_font = f"{fonts_dir}/RobotoMono-Regular.ttf"
  17. user_css = f"""
  18. @font-face {{
  19. font-family: 'Roboto';
  20. src: url('{regular_font}');
  21. }}
  22. @font-face {{
  23. font-family: 'Roboto';
  24. font-weight: bold;
  25. src: url('{bold_font}');
  26. }}
  27. @font-face {{
  28. font-family: 'RobotoMono';
  29. src: url('{mono_font}');
  30. }}
  31. * {{
  32. color: #1a1a1a !important;
  33. }}
  34. body {{
  35. font-family: 'Roboto', sans-serif;
  36. color: #1a1a1a !important;
  37. background-color: #ffffff !important;
  38. }}
  39. h1, h2, h3, h4, h5, h6, h1 *, h2 *, h3 *, h4 *, h5 *, h6 * {{
  40. color: #000000 !important;
  41. }}
  42. code, pre, code *, pre * {{
  43. font-family: 'RobotoMono', monospace !important;
  44. color: #b02a37 !important;
  45. background-color: #f8f9fa !important;
  46. }}
  47. a, a * {{
  48. color: #0d6efd !important;
  49. }}
  50. blockquote, blockquote * {{
  51. color: #555555 !important;
  52. border-left: 4px solid #ccc !important;
  53. padding-left: 10px !important;
  54. }}
  55. table, tr, td, th, table * {{
  56. color: #1a1a1a !important;
  57. border-color: #cccccc !important;
  58. }}
  59. th {{
  60. background-color: #f2f2f2 !important;
  61. }}
  62. """
  63. for md_file in md_files:
  64. pdf_file = md_file.replace('.md', '.pdf')
  65. print(f"Converting {os.path.basename(md_file)} to PDF...")
  66. with open(md_file, 'r', encoding='utf-8') as f:
  67. md_content = f.read()
  68. import subprocess
  69. try:
  70. log_info = subprocess.check_output(['git', 'log', '-1', '--format=%H %an %ae %ad %cn %ce %cd %N %s', '--date=format:%Y/%m/%d %H:%M:%S'], encoding='utf-8').strip()
  71. try:
  72. tag_info = subprocess.check_output(['git', 'describe', '--tags', '--always'], stderr=subprocess.DEVNULL, encoding='utf-8').strip()
  73. except Exception:
  74. tag_info = ""
  75. if tag_info:
  76. git_id = f"$Id$"
  77. else:
  78. git_id = f"$Id$"
  79. except Exception:
  80. git_id = "$Id$"
  81. md_content = md_content.replace('$Id$', git_id)
  82. try:
  83. pdf = MarkdownPdf(toc_level=2, optimize=True)
  84. base_name = os.path.basename(md_file)
  85. if base_name == 'project_report.md':
  86. print("Splitting project_report.md into Portrait/Landscape/Portrait sections...")
  87. parts = md_content.split('## 2. Project File Catalog & Documentation')
  88. if len(parts) == 2:
  89. portrait_part1 = parts[0]
  90. remaining = parts[1]
  91. parts2 = remaining.split('## 3. Directory Structure Map')
  92. if len(parts2) == 2:
  93. landscape_part = '## 2. Project File Catalog & Documentation' + parts2[0]
  94. portrait_part2 = '## 3. Directory Structure Map' + parts2[1]
  95. pdf.add_section(Section(portrait_part1, paper_size="A4"), user_css=user_css)
  96. pdf.add_section(Section(landscape_part, paper_size="A4-L"), user_css=user_css)
  97. pdf.add_section(Section(portrait_part2, paper_size="A4"), user_css=user_css)
  98. else:
  99. print("WARNING: Could not find Directory Structure Map heading. Defaulting to full portrait.")
  100. pdf.add_section(Section(md_content, paper_size="A4"), user_css=user_css)
  101. else:
  102. print("WARNING: Could not find Project File Catalog heading. Defaulting to full portrait.")
  103. pdf.add_section(Section(md_content, paper_size="A4"), user_css=user_css)
  104. else:
  105. pdf.add_section(Section(md_content, paper_size="A4"), user_css=user_css)
  106. pdf.save(pdf_file)
  107. print(f"Saved {os.path.basename(pdf_file)}")
  108. # Copy to workspace root if applicable
  109. import shutil
  110. root_dir = os.path.join(os.path.dirname(__file__), '..')
  111. if base_name == 'project_report.md':
  112. dest = os.path.join(root_dir, 'Project.pdf')
  113. try:
  114. if os.path.exists(dest):
  115. os.remove(dest)
  116. shutil.copy2(pdf_file, dest)
  117. print("Successfully updated root Project.pdf")
  118. except Exception as copy_err:
  119. print(f"WARNING: Could not copy to root Project.pdf: {copy_err}")
  120. elif base_name == 'retro_planning.md':
  121. dest = os.path.join(root_dir, 'Retro Planning.pdf')
  122. try:
  123. if os.path.exists(dest):
  124. os.remove(dest)
  125. shutil.copy2(pdf_file, dest)
  126. print("Successfully updated root Retro Planning.pdf")
  127. except Exception as copy_err:
  128. print(f"WARNING: Could not copy to root Retro Planning.pdf: {copy_err}")
  129. except Exception as e:
  130. print(f"WARNING: Could not save {os.path.basename(pdf_file)}. File might be locked or open in a viewer. Error: {e}")
  131. if __name__ == "__main__":
  132. main()