github.com/graemephi/kahugo@v0.62.3-0.20211121071557-d78c0423784d/markup/rst/convert.go (about) 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/cli/safeexec" 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 return converter.Bytes(c.getRstContent(ctx.Src, c.ctx)), nil 52 } 53 54 func (c *rstConverter) Supports(feature identity.Identity) bool { 55 return false 56 } 57 58 // getRstContent calls the Python script rst2html as an external helper 59 // to convert reStructuredText content to HTML. 60 func (c *rstConverter) getRstContent(src []byte, ctx converter.DocumentContext) []byte { 61 logger := c.cfg.Logger 62 path := getRstExecPath() 63 64 if path == "" { 65 logger.Println("rst2html / rst2html.py not found in $PATH: Please install.\n", 66 " Leaving reStructuredText content unrendered.") 67 return src 68 } 69 70 logger.Infoln("Rendering", ctx.DocumentName, "with", path, "...") 71 72 var result []byte 73 // certain *nix based OSs wrap executables in scripted launchers 74 // invoking binaries on these OSs via python interpreter causes SyntaxError 75 // invoke directly so that shebangs work as expected 76 // handle Windows manually because it doesn't do shebangs 77 if runtime.GOOS == "windows" { 78 python := internal.GetPythonExecPath() 79 args := []string{path, "--leave-comments", "--initial-header-level=2"} 80 result = internal.ExternallyRenderContent(c.cfg, ctx, src, python, args) 81 } else { 82 args := []string{"--leave-comments", "--initial-header-level=2"} 83 result = internal.ExternallyRenderContent(c.cfg, ctx, src, path, args) 84 } 85 // TODO(bep) check if rst2html has a body only option. 86 bodyStart := bytes.Index(result, []byte("<body>\n")) 87 if bodyStart < 0 { 88 bodyStart = -7 // compensate for length 89 } 90 91 bodyEnd := bytes.Index(result, []byte("\n</body>")) 92 if bodyEnd < 0 || bodyEnd >= len(result) { 93 bodyEnd = len(result) - 1 94 if bodyEnd < 0 { 95 bodyEnd = 0 96 } 97 } 98 99 return result[bodyStart+7 : bodyEnd] 100 } 101 102 func getRstExecPath() string { 103 path, err := safeexec.LookPath("rst2html") 104 if err != nil { 105 path, err = safeexec.LookPath("rst2html.py") 106 if err != nil { 107 return "" 108 } 109 } 110 return path 111 } 112 113 // Supports returns whether rst is installed on this computer. 114 func Supports() bool { 115 if htesting.SupportsAll() { 116 return true 117 } 118 return getRstExecPath() != "" 119 }