diff --git a/docs/content/doc/advanced/external-renderers.en-us.md b/docs/content/doc/advanced/external-renderers.en-us.md index 4e5e72554d9d3..8f60387466661 100644 --- a/docs/content/doc/advanced/external-renderers.en-us.md +++ b/docs/content/doc/advanced/external-renderers.en-us.md @@ -158,6 +158,35 @@ RENDER_COMMAND = "jupyter-nbconvert --stdin --stdout --to html --template basic" ALLOW_DATA_URI_IMAGES = true ``` +### Example: Office PPTX + +Convert Office PPTX files to PDF using +[LibreOffice CLI](https://help.libreoffice.org/latest/en-US/text/shared/guide/start_parameters.html): + +```ini +[markup.pptx] +ENABLED = true +FILE_EXTENSIONS = .pptx +IS_INPUT_FILE = true +RENDER_COMMAND = ./convert-pptx.sh +RENDER_CONTENT_MODE = pdf +``` + +The script `convert-pptx.sh`: + +```sh +#!/usr/bin/env sh +set -eu +file="$1" +dir=`mktemp -d` +libreoffice --convert-to pdf "$file" --outdir "$dir" +cat "$dir/$(basename $file .pptx).pdf" +rm -rf "$dir" +``` + +Using `RENDER_CONTENT_MODE = pdf` makes Gitea to embed files into a PDF viewer. +It is mutually exclusive with post-processing and sanitization. + ## Customizing CSS The external renderer is specified in the .ini in the format `[markup.XXXXX]` and the HTML supplied by your external renderer will be wrapped in a `
` with classes `markup` and `XXXXX`. The `markup` class provides out of the box styling (as does `markdown` if `XXXXX` is `markdown`). Otherwise you can use these classes to specifically target the contents of your rendered HTML. diff --git a/modules/markup/external/external.go b/modules/markup/external/external.go index 23dd45ba0a1f2..9b6373a999ae6 100644 --- a/modules/markup/external/external.go +++ b/modules/markup/external/external.go @@ -61,7 +61,9 @@ func (p *Renderer) SanitizerRules() []setting.MarkupSanitizerRule { // SanitizerDisabled disabled sanitize if return true func (p *Renderer) SanitizerDisabled() bool { - return p.RenderContentMode == setting.RenderContentModeNoSanitizer || p.RenderContentMode == setting.RenderContentModeIframe + return p.RenderContentMode == setting.RenderContentModeNoSanitizer || + p.RenderContentMode == setting.RenderContentModeIframe || + p.RenderContentMode == setting.RenderContentModePDF } // DisplayInIFrame represents whether render the content with an iframe @@ -69,6 +71,10 @@ func (p *Renderer) DisplayInIFrame() bool { return p.RenderContentMode == setting.RenderContentModeIframe } +func (p *Renderer) DisplayAsPDF() bool { + return p.RenderContentMode == setting.RenderContentModePDF +} + func envMark(envName string) string { if runtime.GOOS == "windows" { return "%" + envName + "%" diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go index 5f69dc72354f0..4815e12c5ddf1 100644 --- a/modules/markup/renderer.go +++ b/modules/markup/renderer.go @@ -106,6 +106,9 @@ type ExternalRenderer interface { // DisplayInIFrame represents whether render the content with an iframe DisplayInIFrame() bool + + // DisplayAsPDF represents whether to the renderer output should be viewed as PDF. + DisplayAsPDF() bool } // RendererContentDetector detects if the content can be rendered @@ -177,23 +180,38 @@ type nopCloser struct { func (nopCloser) Close() error { return nil } +func getRenderURL(ctx *RenderContext) string { + return fmt.Sprintf("%s/%s/%s/render/%s/%s", + setting.AppSubURL, + url.PathEscape(ctx.Metas["user"]), + url.PathEscape(ctx.Metas["repo"]), + ctx.Metas["BranchNameSubURL"], + url.PathEscape(ctx.RelativePath), + ) +} + func renderIFrame(ctx *RenderContext, output io.Writer) error { // set height="0" ahead, otherwise the scrollHeight would be max(150, realHeight) // at the moment, only "allow-scripts" is allowed for sandbox mode. // "allow-same-origin" should never be used, it leads to XSS attack, and it makes the JS in iframe can access parent window's config and CSRF token // TODO: when using dark theme, if the rendered content doesn't have proper style, the default text color is black, which is not easy to read _, err := io.WriteString(output, fmt.Sprintf(` -`, - setting.AppSubURL, - url.PathEscape(ctx.Metas["user"]), - url.PathEscape(ctx.Metas["repo"]), - ctx.Metas["BranchNameSubURL"], - url.PathEscape(ctx.RelativePath), + getRenderURL(ctx), + )) + return err +} + +func renderPDFViewer(ctx *RenderContext, output io.Writer) error { + _, err := io.WriteString(output, fmt.Sprintf(` +`, + setting.StaticURLPrefix+"/assets", + getRenderURL(ctx), )) return err } @@ -281,11 +299,13 @@ func (err ErrUnsupportedRenderExtension) Error() string { func renderFile(ctx *RenderContext, input io.Reader, output io.Writer) error { extension := strings.ToLower(filepath.Ext(ctx.RelativePath)) if renderer, ok := extRenderers[extension]; ok { - if r, ok := renderer.(ExternalRenderer); ok && r.DisplayInIFrame() { - if !ctx.InStandalonePage { - // for an external render, it could only output its content in a standalone page - // otherwise, a