Fix LaTeX PDF generation by adding comprehensive package support
- Add fancyvrb package and Shaded environment for syntax highlighting - Include amsmath and amssymb packages for mathematical symbols - Switch from pdflatex to xelatex with fontspec for Unicode support - Add complete set of syntax highlighting commands for code blocks - Fix directory-relative Mermaid file generation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
		
							parent
							
								
									0bbff27b44
								
							
						
					
					
						commit
						90891d3797
					
				
							
								
								
									
										88
									
								
								app/Main.hs
									
									
									
									
									
								
							
							
						
						
									
										88
									
								
								app/Main.hs
									
									
									
									
									
								
							@ -9,7 +9,7 @@ import Text.Pandoc.PDF (makePDF)
 | 
				
			|||||||
import Text.Pandoc.Walk (walkM)
 | 
					import Text.Pandoc.Walk (walkM)
 | 
				
			||||||
import Text.Pandoc.Extensions (Extension(..), enableExtension, getDefaultExtensions)
 | 
					import Text.Pandoc.Extensions (Extension(..), enableExtension, getDefaultExtensions)
 | 
				
			||||||
import System.Environment (getArgs)
 | 
					import System.Environment (getArgs)
 | 
				
			||||||
import System.FilePath (replaceExtension)
 | 
					import System.FilePath (replaceExtension, takeDirectory, (</>))
 | 
				
			||||||
import System.Process (callProcess)
 | 
					import System.Process (callProcess)
 | 
				
			||||||
import System.Directory (doesFileExist)
 | 
					import System.Directory (doesFileExist)
 | 
				
			||||||
import Data.Text (Text)
 | 
					import Data.Text (Text)
 | 
				
			||||||
@ -20,30 +20,24 @@ import Control.Monad (when, void)
 | 
				
			|||||||
import qualified Data.ByteString.Lazy as BL
 | 
					import qualified Data.ByteString.Lazy as BL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- Transform Mermaid code blocks into image embeds
 | 
					-- Transform Mermaid code blocks into image embeds
 | 
				
			||||||
processMermaid :: Block -> IO Block
 | 
					processMermaidInDir :: FilePath -> Block -> IO Block
 | 
				
			||||||
processMermaid block@(CodeBlock (id', classes, _) contents)
 | 
					processMermaidInDir sourceDir block@(CodeBlock (id', classes, _) contents)
 | 
				
			||||||
  | "mermaid" `elem` classes = do
 | 
					  | "mermaid" `elem` classes = do
 | 
				
			||||||
      putStrLn $ "🎯 Found Mermaid block with classes: " ++ show classes
 | 
					 | 
				
			||||||
      let baseName = if T.null id' then "diagram-" ++ take 6 (show (abs (hash (T.unpack contents)))) else T.unpack id'
 | 
					      let baseName = if T.null id' then "diagram-" ++ take 6 (show (abs (hash (T.unpack contents)))) else T.unpack id'
 | 
				
			||||||
          mmdFile = baseName ++ ".mmd"
 | 
					          mmdFile = sourceDir </> baseName ++ ".mmd"
 | 
				
			||||||
          pngFile = baseName ++ ".png"
 | 
					          pngFile = sourceDir </> baseName ++ ".png"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      putStrLn $ "📝 Writing to " ++ mmdFile ++ " and generating " ++ pngFile
 | 
					 | 
				
			||||||
      writeFile mmdFile (T.unpack contents)
 | 
					      writeFile mmdFile (T.unpack contents)
 | 
				
			||||||
      void $ callProcess "mmdc" ["-i", mmdFile, "-o", pngFile]
 | 
					      void $ callProcess "mmdc" ["-i", mmdFile, "-o", pngFile]
 | 
				
			||||||
      putStrLn $ "✅ Generated " ++ pngFile
 | 
					      putStrLn $ "✅ Generated " ++ pngFile
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return $ Para [Image nullAttr [] (T.pack pngFile, "Mermaid diagram")]
 | 
					      return $ Para [Image nullAttr [] (T.pack pngFile, "Mermaid diagram")]
 | 
				
			||||||
processMermaid x = do
 | 
					processMermaidInDir _ x = return x
 | 
				
			||||||
  -- Debug: show what blocks we're processing
 | 
					
 | 
				
			||||||
  case x of
 | 
					 | 
				
			||||||
    CodeBlock (_, classes, _) _ -> putStrLn $ "📄 Found code block with classes: " ++ show classes
 | 
					 | 
				
			||||||
    _ -> return ()
 | 
					 | 
				
			||||||
  return x
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- Walk the Pandoc AST and process blocks using walkM
 | 
					-- Walk the Pandoc AST and process blocks using walkM
 | 
				
			||||||
transformDoc :: Pandoc -> IO Pandoc
 | 
					transformDoc :: FilePath -> Pandoc -> IO Pandoc
 | 
				
			||||||
transformDoc = walkM processMermaid
 | 
					transformDoc sourceDir = walkM (processMermaidInDir sourceDir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
main :: IO ()
 | 
					main :: IO ()
 | 
				
			||||||
main = do
 | 
					main = do
 | 
				
			||||||
@ -77,15 +71,65 @@ compileToPDF :: FilePath -> IO ()
 | 
				
			|||||||
compileToPDF path = do
 | 
					compileToPDF path = do
 | 
				
			||||||
  content <- TIO.readFile path
 | 
					  content <- TIO.readFile path
 | 
				
			||||||
  let readerOptions = def { readerExtensions = getDefaultExtensions "markdown" }
 | 
					  let readerOptions = def { readerExtensions = getDefaultExtensions "markdown" }
 | 
				
			||||||
 | 
					      sourceDir = takeDirectory path
 | 
				
			||||||
  pandoc <- runIOorExplode $ readMarkdown readerOptions content
 | 
					  pandoc <- runIOorExplode $ readMarkdown readerOptions content
 | 
				
			||||||
  transformed <- transformDoc pandoc
 | 
					  transformed <- transformDoc sourceDir pandoc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let outputPath = replaceExtension path "pdf"
 | 
					  let outputPath = replaceExtension path "pdf"
 | 
				
			||||||
      writerOptions = def
 | 
					      writerOptions = def
 | 
				
			||||||
  -- First generate LaTeX with proper document structure
 | 
					  -- Generate LaTeX and add proper header with tightlist definition
 | 
				
			||||||
  latexOutput <- runIOorExplode $ writeLaTeX writerOptions transformed
 | 
					  latexOutput <- runIOorExplode $ writeLaTeX writerOptions transformed
 | 
				
			||||||
  let latexWithHeader = "\\documentclass{article}\n\\usepackage[utf8]{inputenc}\n\\usepackage{graphicx}\n\\usepackage{geometry}\n\\geometry{margin=1in}\n\\begin{document}\n" <> latexOutput <> "\n\\end{document}"
 | 
					  let latexWithProperHeader = T.unlines [
 | 
				
			||||||
  result <- runIOorExplode $ makePDF "pdflatex" [] (\_ _ -> return latexWithHeader) def transformed
 | 
					        "\\documentclass{article}",
 | 
				
			||||||
 | 
					        "\\usepackage[utf8]{inputenc}",
 | 
				
			||||||
 | 
					        "\\usepackage{fontspec}",
 | 
				
			||||||
 | 
					        "\\usepackage{graphicx}",
 | 
				
			||||||
 | 
					        "\\usepackage{geometry}",
 | 
				
			||||||
 | 
					        "\\geometry{margin=1in}",
 | 
				
			||||||
 | 
					        "\\usepackage{hyperref}",
 | 
				
			||||||
 | 
					        "\\usepackage{enumitem}",
 | 
				
			||||||
 | 
					        "\\usepackage{amsmath}",
 | 
				
			||||||
 | 
					        "\\usepackage{amssymb}",
 | 
				
			||||||
 | 
					        "\\usepackage{fancyvrb}",
 | 
				
			||||||
 | 
					        "\\usepackage{color}",
 | 
				
			||||||
 | 
					        "\\DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\\\\{\\}}",
 | 
				
			||||||
 | 
					        "\\newenvironment{Shaded}{}{}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\AlertTok}[1]{\\textcolor[rgb]{1.00,0.00,0.00}{\\textbf{#1}}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\AnnotationTok}[1]{\\textcolor[rgb]{0.38,0.63,0.69}{\\textbf{\\textit{#1}}}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\AttributeTok}[1]{\\textcolor[rgb]{0.49,0.56,0.16}{#1}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\BaseNTok}[1]{\\textcolor[rgb]{0.25,0.63,0.44}{#1}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\BuiltInTok}[1]{#1}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\CharTok}[1]{\\textcolor[rgb]{0.25,0.44,0.63}{#1}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\CommentTok}[1]{\\textcolor[rgb]{0.38,0.63,0.69}{\\textit{#1}}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\CommentVarTok}[1]{\\textcolor[rgb]{0.38,0.63,0.69}{\\textbf{\\textit{#1}}}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\ConstantTok}[1]{\\textcolor[rgb]{0.53,0.00,0.00}{#1}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\ControlFlowTok}[1]{\\textcolor[rgb]{0.00,0.44,0.13}{\\textbf{#1}}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\DataTypeTok}[1]{\\textcolor[rgb]{0.56,0.13,0.00}{#1}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\DecValTok}[1]{\\textcolor[rgb]{0.25,0.63,0.44}{#1}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\DocumentationTok}[1]{\\textcolor[rgb]{0.73,0.13,0.13}{\\textit{#1}}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\ErrorTok}[1]{\\textcolor[rgb]{1.00,0.00,0.00}{\\textbf{#1}}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\ExtensionTok}[1]{#1}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\FloatTok}[1]{\\textcolor[rgb]{0.25,0.63,0.44}{#1}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\FunctionTok}[1]{\\textcolor[rgb]{0.02,0.16,0.49}{#1}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\ImportTok}[1]{#1}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\InformationTok}[1]{\\textcolor[rgb]{0.38,0.63,0.69}{\\textbf{\\textit{#1}}}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\KeywordTok}[1]{\\textcolor[rgb]{0.00,0.44,0.13}{\\textbf{#1}}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\NormalTok}[1]{#1}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\OperatorTok}[1]{\\textcolor[rgb]{0.40,0.40,0.40}{#1}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\OtherTok}[1]{\\textcolor[rgb]{0.00,0.44,0.13}{#1}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\PreprocessorTok}[1]{\\textcolor[rgb]{0.74,0.48,0.00}{#1}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\RegionMarkerTok}[1]{#1}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\SpecialCharTok}[1]{\\textcolor[rgb]{0.25,0.44,0.63}{#1}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\SpecialStringTok}[1]{\\textcolor[rgb]{0.73,0.40,0.53}{#1}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\StringTok}[1]{\\textcolor[rgb]{0.25,0.44,0.63}{#1}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\VariableTok}[1]{\\textcolor[rgb]{0.10,0.09,0.49}{#1}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\VerbatimStringTok}[1]{\\textcolor[rgb]{0.25,0.44,0.63}{#1}}",
 | 
				
			||||||
 | 
					        "\\newcommand{\\WarningTok}[1]{\\textcolor[rgb]{0.38,0.63,0.69}{\\textbf{\\textit{#1}}}}",
 | 
				
			||||||
 | 
					        "\\providecommand{\\tightlist}{%",
 | 
				
			||||||
 | 
					        "  \\setlength{\\itemsep}{0pt}\\setlength{\\parskip}{0pt}}",
 | 
				
			||||||
 | 
					        "\\begin{document}"
 | 
				
			||||||
 | 
					        ] <> latexOutput <> "\n\\end{document}"
 | 
				
			||||||
 | 
					  result <- runIOorExplode $ makePDF "xelatex" [] (\_ _ -> return latexWithProperHeader) def transformed
 | 
				
			||||||
  case result of
 | 
					  case result of
 | 
				
			||||||
    Left err -> error $ "PDF error: " ++ show err
 | 
					    Left err -> error $ "PDF error: " ++ show err
 | 
				
			||||||
    Right bs -> BL.writeFile outputPath bs >> putStrLn ("✅ PDF written to " ++ outputPath)
 | 
					    Right bs -> BL.writeFile outputPath bs >> putStrLn ("✅ PDF written to " ++ outputPath)
 | 
				
			||||||
@ -93,12 +137,10 @@ compileToPDF path = do
 | 
				
			|||||||
compileToHTML :: FilePath -> IO ()
 | 
					compileToHTML :: FilePath -> IO ()
 | 
				
			||||||
compileToHTML path = do
 | 
					compileToHTML path = do
 | 
				
			||||||
  content <- TIO.readFile path
 | 
					  content <- TIO.readFile path
 | 
				
			||||||
  putStrLn $ "📖 Reading: " ++ path
 | 
					 | 
				
			||||||
  putStrLn $ "📝 Content: " ++ T.unpack content
 | 
					 | 
				
			||||||
  let readerOptions = def { readerExtensions = getDefaultExtensions "markdown" }
 | 
					  let readerOptions = def { readerExtensions = getDefaultExtensions "markdown" }
 | 
				
			||||||
 | 
					      sourceDir = takeDirectory path
 | 
				
			||||||
  pandoc <- runIOorExplode $ readMarkdown readerOptions content
 | 
					  pandoc <- runIOorExplode $ readMarkdown readerOptions content
 | 
				
			||||||
  putStrLn $ "🔍 Parsed AST: " ++ show pandoc
 | 
					  transformed <- transformDoc sourceDir pandoc
 | 
				
			||||||
  transformed <- transformDoc pandoc
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let outputPath = replaceExtension path "html"
 | 
					  let outputPath = replaceExtension path "html"
 | 
				
			||||||
  html <- runIOorExplode $ writeHtml5String def transformed
 | 
					  html <- runIOorExplode $ writeHtml5String def transformed
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user