Replace makePDF with direct XeLaTeX processing to eliminate API mismatch
- Remove Text.Pandoc.PDF dependency and dummy Pandoc document - Add direct xelatex invocation with temporary file handling - Improve error reporting with LaTeX log file parsing - Add temporary package dependency for proper temp file cleanup - Maintain same external API while cleaning internal architecture - Eliminate architectural mismatch between our pipeline and Pandoc's expectations The previous code used makePDF in a way that fought against its intended usage, requiring a dummy Pandoc document. Now we directly call xelatex after our custom LaTeX template processing, creating a cleaner separation of concerns. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
		
							parent
							
								
									303f347243
								
							
						
					
					
						commit
						bf6f82abe5
					
				@ -42,7 +42,8 @@ library
 | 
				
			|||||||
      hashable >=1.4 && <1.6,
 | 
					      hashable >=1.4 && <1.6,
 | 
				
			||||||
      pandoc >=3.0 && <3.2,
 | 
					      pandoc >=3.0 && <3.2,
 | 
				
			||||||
      pandoc-types >=1.23 && <1.25,
 | 
					      pandoc-types >=1.23 && <1.25,
 | 
				
			||||||
      bytestring >=0.11 && <0.13
 | 
					      bytestring >=0.11 && <0.13,
 | 
				
			||||||
 | 
					      temporary >=1.3 && <1.4
 | 
				
			||||||
  default-language:    Haskell2010
 | 
					  default-language:    Haskell2010
 | 
				
			||||||
 | 
					
 | 
				
			||||||
executable docster
 | 
					executable docster
 | 
				
			||||||
 | 
				
			|||||||
@ -12,13 +12,14 @@ import Docster.Types
 | 
				
			|||||||
import Docster.Transform (transformDocument)
 | 
					import Docster.Transform (transformDocument)
 | 
				
			||||||
import Docster.LaTeX (latexTemplate)
 | 
					import Docster.LaTeX (latexTemplate)
 | 
				
			||||||
import Text.Pandoc
 | 
					import Text.Pandoc
 | 
				
			||||||
import Text.Pandoc.PDF (makePDF)
 | 
					 | 
				
			||||||
import Data.Text (Text)
 | 
					import Data.Text (Text)
 | 
				
			||||||
import qualified Data.Text as T
 | 
					import qualified Data.Text as T
 | 
				
			||||||
import qualified Data.Text.IO as TIO
 | 
					import qualified Data.Text.IO as TIO
 | 
				
			||||||
import qualified Data.ByteString.Lazy as BL
 | 
					import System.FilePath (takeDirectory, replaceExtension, (</>))
 | 
				
			||||||
import System.FilePath (takeDirectory, replaceExtension)
 | 
					import System.Process (callProcess, readProcessWithExitCode)
 | 
				
			||||||
import System.Process (callProcess)
 | 
					import System.IO.Temp (withSystemTempDirectory)
 | 
				
			||||||
 | 
					import System.Directory (copyFile, doesFileExist)
 | 
				
			||||||
 | 
					import System.Exit (ExitCode(..))
 | 
				
			||||||
import Control.Exception (throwIO)
 | 
					import Control.Exception (throwIO)
 | 
				
			||||||
import Control.Monad (void)
 | 
					import Control.Monad (void)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -56,20 +57,53 @@ htmlStrategy = CompilationStrategy
 | 
				
			|||||||
  , csSuccessMessage = \path -> successEmoji <> " HTML written to " <> T.pack path
 | 
					  , csSuccessMessage = \path -> successEmoji <> " HTML written to " <> T.pack path
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- | Process PDF output: LaTeX template application and PDF generation
 | 
					-- | Process PDF output: LaTeX template application and direct XeLaTeX compilation
 | 
				
			||||||
processPDFOutput :: Text -> String -> IO (Either DocsterError ())
 | 
					processPDFOutput :: Text -> String -> IO (Either DocsterError ())
 | 
				
			||||||
processPDFOutput latexOutput outputPath = do
 | 
					processPDFOutput latexOutput outputPath = do
 | 
				
			||||||
  let completeLatex = latexTemplate latexOutput
 | 
					  let completeLatex = latexTemplate latexOutput
 | 
				
			||||||
  -- We need a Pandoc document for makePDF, but it's not used in the template function
 | 
					  
 | 
				
			||||||
  -- Create a minimal document for the API
 | 
					  -- Use temporary directory for LaTeX compilation
 | 
				
			||||||
  let dummyDoc = Pandoc nullMeta []
 | 
					  withSystemTempDirectory "docster-latex" $ \tempDir -> do
 | 
				
			||||||
  pdfResult <- runIO $ makePDF "xelatex" [] (\_ _ -> return completeLatex) def dummyDoc
 | 
					    let texFile = tempDir </> "document.tex"
 | 
				
			||||||
  case pdfResult of
 | 
					        pdfFile = tempDir </> "document.pdf"
 | 
				
			||||||
    Left err -> return $ Left $ PDFGenerationError $ T.pack $ show err
 | 
					        logFile = tempDir </> "document.log"
 | 
				
			||||||
    Right (Left err) -> return $ Left $ PDFGenerationError $ T.pack $ show err
 | 
					    
 | 
				
			||||||
    Right (Right bs) -> do
 | 
					    -- Write LaTeX content to temporary file
 | 
				
			||||||
      BL.writeFile outputPath bs
 | 
					    TIO.writeFile texFile completeLatex
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    -- Run XeLaTeX compilation
 | 
				
			||||||
 | 
					    (exitCode, _stdout, stderr) <- readProcessWithExitCode "xelatex" 
 | 
				
			||||||
 | 
					      [ "-output-directory=" <> tempDir
 | 
				
			||||||
 | 
					      , "-interaction=nonstopmode"  -- Don't stop on errors
 | 
				
			||||||
 | 
					      , texFile
 | 
				
			||||||
 | 
					      ] ""
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    case exitCode of
 | 
				
			||||||
 | 
					      ExitSuccess -> do
 | 
				
			||||||
 | 
					        -- Check if PDF was actually generated
 | 
				
			||||||
 | 
					        pdfExists <- doesFileExist pdfFile
 | 
				
			||||||
 | 
					        if pdfExists
 | 
				
			||||||
 | 
					          then do
 | 
				
			||||||
 | 
					            -- Copy the generated PDF to the final location
 | 
				
			||||||
 | 
					            copyFile pdfFile outputPath
 | 
				
			||||||
            return $ Right ()
 | 
					            return $ Right ()
 | 
				
			||||||
 | 
					          else do
 | 
				
			||||||
 | 
					            -- PDF generation failed, read log for details
 | 
				
			||||||
 | 
					            logExists <- doesFileExist logFile
 | 
				
			||||||
 | 
					            logContent <- if logExists 
 | 
				
			||||||
 | 
					              then TIO.readFile logFile 
 | 
				
			||||||
 | 
					              else return "No log file generated"
 | 
				
			||||||
 | 
					            return $ Left $ PDFGenerationError $ 
 | 
				
			||||||
 | 
					              "PDF file not generated. LaTeX log:\n" <> logContent
 | 
				
			||||||
 | 
					      ExitFailure code -> do
 | 
				
			||||||
 | 
					        -- LaTeX compilation failed, read log for details
 | 
				
			||||||
 | 
					        logExists <- doesFileExist logFile
 | 
				
			||||||
 | 
					        logContent <- if logExists 
 | 
				
			||||||
 | 
					          then TIO.readFile logFile 
 | 
				
			||||||
 | 
					          else return (T.pack stderr)
 | 
				
			||||||
 | 
					        return $ Left $ PDFGenerationError $ 
 | 
				
			||||||
 | 
					          "XeLaTeX compilation failed (exit code " <> T.pack (show code) <> "):\n" <> 
 | 
				
			||||||
 | 
					          T.pack stderr <> "\n\nLaTeX log:\n" <> logContent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- | Process HTML output: file writing and browser opening
 | 
					-- | Process HTML output: file writing and browser opening
 | 
				
			||||||
processHTMLOutput :: Text -> String -> IO (Either DocsterError ())
 | 
					processHTMLOutput :: Text -> String -> IO (Either DocsterError ())
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user