hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

convert.go (3913B)

    1 // Copyright 2019 The Hugo Authors. All rights reserved.
    2 //
    3 // Licensed under the Apache License, Version 2.0 (the "License");
    4 // you may not use this file except in compliance with the License.
    5 // You may obtain a copy of the License at
    6 // http://www.apache.org/licenses/LICENSE-2.0
    7 //
    8 // Unless required by applicable law or agreed to in writing, software
    9 // distributed under the License is distributed on an "AS IS" BASIS,
   10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   11 // See the License for the specific language governing permissions and
   12 // limitations under the License.
   13 
   14 // Package rst converts content to HTML using the RST external helper.
   15 package rst
   16 
   17 import (
   18 	"bytes"
   19 	"runtime"
   20 
   21 	"github.com/gohugoio/hugo/common/hexec"
   22 	"github.com/gohugoio/hugo/htesting"
   23 
   24 	"github.com/gohugoio/hugo/identity"
   25 	"github.com/gohugoio/hugo/markup/internal"
   26 
   27 	"github.com/gohugoio/hugo/markup/converter"
   28 )
   29 
   30 // Provider is the package entry point.
   31 var Provider converter.ProviderProvider = provider{}
   32 
   33 type provider struct {
   34 }
   35 
   36 func (p provider) New(cfg converter.ProviderConfig) (converter.Provider, error) {
   37 	return converter.NewProvider("rst", func(ctx converter.DocumentContext) (converter.Converter, error) {
   38 		return &rstConverter{
   39 			ctx: ctx,
   40 			cfg: cfg,
   41 		}, nil
   42 	}), nil
   43 }
   44 
   45 type rstConverter struct {
   46 	ctx converter.DocumentContext
   47 	cfg converter.ProviderConfig
   48 }
   49 
   50 func (c *rstConverter) Convert(ctx converter.RenderContext) (converter.Result, error) {
   51 	b, err := c.getRstContent(ctx.Src, c.ctx)
   52 	if err != nil {
   53 		return nil, err
   54 	}
   55 	return converter.Bytes(b), nil
   56 }
   57 
   58 func (c *rstConverter) Supports(feature identity.Identity) bool {
   59 	return false
   60 }
   61 
   62 // getRstContent calls the Python script rst2html as an external helper
   63 // to convert reStructuredText content to HTML.
   64 func (c *rstConverter) getRstContent(src []byte, ctx converter.DocumentContext) ([]byte, error) {
   65 	logger := c.cfg.Logger
   66 	binaryName, binaryPath := getRstBinaryNameAndPath()
   67 
   68 	if binaryName == "" {
   69 		logger.Println("rst2html / rst2html.py not found in $PATH: Please install.\n",
   70 			"                 Leaving reStructuredText content unrendered.")
   71 		return src, nil
   72 	}
   73 
   74 	logger.Infoln("Rendering", ctx.DocumentName, "with", binaryName, "...")
   75 
   76 	var result []byte
   77 	var err error
   78 
   79 	// certain *nix based OSs wrap executables in scripted launchers
   80 	// invoking binaries on these OSs via python interpreter causes SyntaxError
   81 	// invoke directly so that shebangs work as expected
   82 	// handle Windows manually because it doesn't do shebangs
   83 	if runtime.GOOS == "windows" {
   84 		pythonBinary, _ := internal.GetPythonBinaryAndExecPath()
   85 		args := []string{binaryPath, "--leave-comments", "--initial-header-level=2"}
   86 		result, err = internal.ExternallyRenderContent(c.cfg, ctx, src, pythonBinary, args)
   87 	} else {
   88 		args := []string{"--leave-comments", "--initial-header-level=2"}
   89 		result, err = internal.ExternallyRenderContent(c.cfg, ctx, src, binaryName, args)
   90 	}
   91 
   92 	if err != nil {
   93 		return nil, err
   94 	}
   95 
   96 	// TODO(bep) check if rst2html has a body only option.
   97 	bodyStart := bytes.Index(result, []byte("<body>\n"))
   98 	if bodyStart < 0 {
   99 		bodyStart = -7 // compensate for length
  100 	}
  101 
  102 	bodyEnd := bytes.Index(result, []byte("\n</body>"))
  103 	if bodyEnd < 0 || bodyEnd >= len(result) {
  104 		bodyEnd = len(result) - 1
  105 		if bodyEnd < 0 {
  106 			bodyEnd = 0
  107 		}
  108 	}
  109 
  110 	return result[bodyStart+7 : bodyEnd], err
  111 }
  112 
  113 var rst2Binaries = []string{"rst2html", "rst2html.py"}
  114 
  115 func getRstBinaryNameAndPath() (string, string) {
  116 	for _, candidate := range rst2Binaries {
  117 		if pth := hexec.LookPath(candidate); pth != "" {
  118 			return candidate, pth
  119 		}
  120 	}
  121 	return "", ""
  122 }
  123 
  124 // Supports returns whether rst is (or should be) installed on this computer.
  125 func Supports() bool {
  126 	name, _ := getRstBinaryNameAndPath()
  127 	hasBin := name != ""
  128 	if htesting.SupportsAll() {
  129 		if !hasBin {
  130 			panic("rst not installed")
  131 		}
  132 		return true
  133 	}
  134 	return hasBin
  135 }