go-hep.org/x/hep@v0.38.1/hplot/htex/latex.go (about)

     1  // Copyright ©2020 The go-hep Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package htex // import "go-hep.org/x/hep/hplot/htex"
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"io"
    11  	"os"
    12  	"os/exec"
    13  	"path"
    14  )
    15  
    16  // Handler is the interface that handles the generation of PDFs
    17  // from TeX, usually via pdflatex.
    18  type Handler interface {
    19  	// CompileLatex compiles the provided .tex document.
    20  	CompileLatex(fname string) error
    21  }
    22  
    23  var (
    24  	// DefaultHandler generates PDFs via the pdflatex executable.
    25  	// A LaTeX installation is required, as well as the pdflatex command.
    26  	DefaultHandler = NewHandler("pdflatex")
    27  )
    28  
    29  // NoopLatexHandler is a no-op LaTeX compiler.
    30  type NoopHandler struct{}
    31  
    32  func (NoopHandler) CompileLatex(fname string) error { return nil }
    33  
    34  type pdfLatex struct {
    35  	cmd string
    36  }
    37  
    38  // NewHandler returns a Handler compiling .tex documents
    39  // with the provided cmd executable.
    40  func NewHandler(cmd string) Handler {
    41  	return &pdfLatex{cmd: cmd}
    42  }
    43  
    44  // CompileLatex compiles the provided .tex document.
    45  func (pdf *pdfLatex) CompileLatex(fname string) error {
    46  	tmp, err := os.MkdirTemp("", "hplot-htex-")
    47  	if err != nil {
    48  		return fmt.Errorf("htex: could not create tmp dir: %w", err)
    49  	}
    50  	defer os.RemoveAll(tmp)
    51  
    52  	var (
    53  		stdout = new(bytes.Buffer)
    54  		args   = []string{
    55  			fmt.Sprintf("-output-directory=%s", tmp),
    56  			fname,
    57  		}
    58  	)
    59  
    60  	cmd := exec.Command(pdf.cmd, args...)
    61  	cmd.Stdout = stdout
    62  	cmd.Stderr = stdout
    63  
    64  	err = cmd.Run()
    65  	if err != nil {
    66  		return fmt.Errorf(
    67  			"htex: could not generate PDF from vgtex:\n%s\nerror: %w",
    68  			stdout.Bytes(),
    69  			err,
    70  		)
    71  	}
    72  
    73  	oname := fname[:len(fname)-len(".tex")] + ".pdf"
    74  	o, err := os.Create(oname)
    75  	if err != nil {
    76  		return fmt.Errorf("htex: could not create output PDF file: %w", err)
    77  	}
    78  	defer o.Close()
    79  
    80  	f, err := os.Open(path.Join(tmp, path.Base(oname)))
    81  	if err != nil {
    82  		return fmt.Errorf("htex: could not open generated PDF file: %w", err)
    83  	}
    84  	defer f.Close()
    85  
    86  	_, err = io.Copy(o, f)
    87  	if err != nil {
    88  		return fmt.Errorf("htex: could not copy PDF file: %w", err)
    89  	}
    90  
    91  	err = o.Close()
    92  	if err != nil {
    93  		return fmt.Errorf("htex: could not close PDF file: %w", err)
    94  	}
    95  
    96  	return nil
    97  }
    98  
    99  var (
   100  	_ Handler = (*NoopHandler)(nil)
   101  	_ Handler = (*pdfLatex)(nil)
   102  )