From bf6f82abe528581e573d0943b7c1f6706ddae4d4 Mon Sep 17 00:00:00 2001 From: Willem van den Ende Date: Tue, 29 Jul 2025 23:22:40 +0200 Subject: [PATCH] Replace makePDF with direct XeLaTeX processing to eliminate API mismatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- docster.cabal | 3 +- src/Docster/Compiler.hs | 64 +++++++++++++++++++++++++++++++---------- 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/docster.cabal b/docster.cabal index d69c795..eec832b 100644 --- a/docster.cabal +++ b/docster.cabal @@ -42,7 +42,8 @@ library hashable >=1.4 && <1.6, pandoc >=3.0 && <3.2, pandoc-types >=1.23 && <1.25, - bytestring >=0.11 && <0.13 + bytestring >=0.11 && <0.13, + temporary >=1.3 && <1.4 default-language: Haskell2010 executable docster diff --git a/src/Docster/Compiler.hs b/src/Docster/Compiler.hs index 6996332..d9c8bc5 100644 --- a/src/Docster/Compiler.hs +++ b/src/Docster/Compiler.hs @@ -12,13 +12,14 @@ import Docster.Types import Docster.Transform (transformDocument) import Docster.LaTeX (latexTemplate) import Text.Pandoc -import Text.Pandoc.PDF (makePDF) import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.IO as TIO -import qualified Data.ByteString.Lazy as BL -import System.FilePath (takeDirectory, replaceExtension) -import System.Process (callProcess) +import System.FilePath (takeDirectory, replaceExtension, ()) +import System.Process (callProcess, readProcessWithExitCode) +import System.IO.Temp (withSystemTempDirectory) +import System.Directory (copyFile, doesFileExist) +import System.Exit (ExitCode(..)) import Control.Exception (throwIO) import Control.Monad (void) @@ -56,20 +57,53 @@ htmlStrategy = CompilationStrategy , 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 latexOutput outputPath = do 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 - let dummyDoc = Pandoc nullMeta [] - pdfResult <- runIO $ makePDF "xelatex" [] (\_ _ -> return completeLatex) def dummyDoc - case pdfResult of - Left err -> return $ Left $ PDFGenerationError $ T.pack $ show err - Right (Left err) -> return $ Left $ PDFGenerationError $ T.pack $ show err - Right (Right bs) -> do - BL.writeFile outputPath bs - return $ Right () + + -- Use temporary directory for LaTeX compilation + withSystemTempDirectory "docster-latex" $ \tempDir -> do + let texFile = tempDir "document.tex" + pdfFile = tempDir "document.pdf" + logFile = tempDir "document.log" + + -- Write LaTeX content to temporary file + 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 () + 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 processHTMLOutput :: Text -> String -> IO (Either DocsterError ())