From 3bc189c59251261fe162300ba3737c80ac433362d7effad7320330ff10ed1040 Mon Sep 17 00:00:00 2001 From: Stefan Hamminga Date: Thu, 20 Mar 2025 14:54:46 +0200 Subject: [PATCH] Initial release --- .gitignore | 1 + README.md | 33 +++++++++++- config/kicad_pcb.ini | 25 +++++++++ config/kicad_sch.ini | 25 +++++++++ config/libreoffice.ini | 23 ++++++++ custom/public/assets/css/renderers.css | 20 +++++++ custom/public/assets/img/.gitkeep | 0 custom/templates/custom/footer.tmpl | 15 ++++++ custom/templates/custom/header.tmpl | 1 + scripts/kicad_pcb.sh | 67 ++++++++++++++++++++++++ scripts/kicad_sch.sh | 62 ++++++++++++++++++++++ scripts/libreoffice.sh | 72 ++++++++++++++++++++++++++ 12 files changed, 342 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 config/kicad_pcb.ini create mode 100644 config/kicad_sch.ini create mode 100644 config/libreoffice.ini create mode 100644 custom/public/assets/css/renderers.css create mode 100644 custom/public/assets/img/.gitkeep create mode 100644 custom/templates/custom/footer.tmpl create mode 100644 custom/templates/custom/header.tmpl create mode 100644 scripts/kicad_pcb.sh create mode 100644 scripts/kicad_sch.sh create mode 100644 scripts/libreoffice.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..99ef59d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.swp diff --git a/README.md b/README.md index 52a8898..6d554dd 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,32 @@ -# gitea_external_renderers +# External rendering scripts for Gitea -External rendering scripts for various file types \ No newline at end of file +These scripts attempt to render several documents so viewing them in Gitea is actually informative. Most are a work in progress. As some files can take a very long time to render, caching is used by leaving the generated images in the Gitea custom assets directory (if present and writable). You'd probably want to clean that out now and then. + +List of renderers: +- LibreOffice +- KiCad + +TODO: +- 3D models (STL, STEP, etc) +- Gerber files + +## Prerequists + +The KiCad renderers assume KiCad (9 or newer) is installed. They also need Inkscape (to properly fit the images). Libreoffice is required for the LO renderers. + +The SVG Pan & Zoom library is loaded from a CDN. + +`mktemp` is used to create a temporary directory. + +## Installation & Configuration + +1. Link the scripts in some place Gitea can execute them +2. Add the content of the `.ini` files to your `app.ini` file +3. Edit the new script path entries in `app.ini` to match your system +4. Copy / merge the contents of the `custom` dir with the custom dir in your Gitea root + +## Repo and License + +Feel free to copy and share this under the terms of the Apache 2.0 license. +Original author: Stefan Hamminga +Original repo: https://git.rbts.co/rbts.co/gitea_external_renderers diff --git a/config/kicad_pcb.ini b/config/kicad_pcb.ini new file mode 100644 index 0000000..2b53b84 --- /dev/null +++ b/config/kicad_pcb.ini @@ -0,0 +1,25 @@ +[markup.kicad_pcb] +ENABLED = true +FILE_EXTENSIONS = .kicad_pcb,.kicad_pcb.1,.kicad_pcb.2,.kicad_pcb.3,.kicad_pcb.4,.kicad_pcb.5,.kicad_pcb.6,.kicad_pcb.7,.kicad_pcb.8,.kicad_pcb.9 +RENDER_COMMAND = /usr/local/bin/gitea_render_kicad_pcb.sh +IS_INPUT_FILE = true +NEED_POSTPROCESS = false +;RENDER_CONTENT_MODE = no-sanitizer + +[markup.sanitizer.kicad_pcb.1] +ALLOW_DATA_URI_IMAGES = true +ELEMENT = div +ALLOW_ATTR = class +REGEXP = + +[markup.sanitizer.kicad_pcb.2] +ALLOW_DATA_URI_IMAGES = true +ELEMENT = img +ALLOW_ATTR = src +REGEXP = + +[markup.sanitizer.kicad_pcb.3] +ALLOW_DATA_URI_IMAGES = true +ELEMENT = img +ALLOW_ATTR = class +REGEXP = \ No newline at end of file diff --git a/config/kicad_sch.ini b/config/kicad_sch.ini new file mode 100644 index 0000000..018f535 --- /dev/null +++ b/config/kicad_sch.ini @@ -0,0 +1,25 @@ +[markup.kicad_sch] +ENABLED = true +FILE_EXTENSIONS = .kicad_sch,.kicad_sch.1,.kicad_sch.2,.kicad_sch.3,.kicad_sch.4,.kicad_sch.5,.kicad_sch.6,.kicad_sch.7,.kicad_sch.8,.kicad_sch.9 +RENDER_COMMAND = /usr/local/bin/gitea_render_kicad_sch.sh +IS_INPUT_FILE = true +NEED_POSTPROCESS = false +;RENDER_CONTENT_MODE = no-sanitizer + +[markup.sanitizer.kicad_sch.1] +ALLOW_DATA_URI_IMAGES = true +ELEMENT = div +ALLOW_ATTR = class +REGEXP = + +[markup.sanitizer.kicad_sch.2] +ALLOW_DATA_URI_IMAGES = true +ELEMENT = img +ALLOW_ATTR = src +REGEXP = + +[markup.sanitizer.kicad_sch.3] +ALLOW_DATA_URI_IMAGES = true +ELEMENT = img +ALLOW_ATTR = class +REGEXP = \ No newline at end of file diff --git a/config/libreoffice.ini b/config/libreoffice.ini new file mode 100644 index 0000000..51f96b4 --- /dev/null +++ b/config/libreoffice.ini @@ -0,0 +1,23 @@ +[markup.libreoffice] +ENABLED = true +FILE_EXTENSIONS = .odt,.ods,.odg,.docx,.ppt +RENDER_COMMAND = /usr/local/bin/gitea_render_libreoffice.sh +IS_INPUT_FILE = true +NEED_POSTPROCESS = false + +[markup.sanitizer.libreoffice.1] +ALLOW_DATA_URI_IMAGES = true +ELEMENT = img +ALLOW_ATTR = class +REGEXP = + +[markup.sanitizer.libreoffice.2] +ALLOW_DATA_URI_IMAGES = true +ELEMENT = src +ALLOW_ATTR = class +REGEXP = + +[markup.sanitizer.libreoffice.3] +ELEMENT = div +ALLOW_ATTR = class +REGEXP = diff --git a/custom/public/assets/css/renderers.css b/custom/public/assets/css/renderers.css new file mode 100644 index 0000000..7782f81 --- /dev/null +++ b/custom/public/assets/css/renderers.css @@ -0,0 +1,20 @@ +.file-view.markup .full_width { + width: 100%; + display: block; +} + +.file-view.markup.kicad_sch, +.file-view.markup.kicad_pcb, +.file-view.markup.libreoffice +{ + background-color: rgba(127, 127, 127, 0.05); +} + +.file-view.markup .page { + background-color: white; + padding: 1em; +} + +.file-view.markup .page + .page { + margin-top: 1em; +} diff --git a/custom/public/assets/img/.gitkeep b/custom/public/assets/img/.gitkeep new file mode 100644 index 0000000..473a0f4 diff --git a/custom/templates/custom/footer.tmpl b/custom/templates/custom/footer.tmpl new file mode 100644 index 0000000..f8ccc5c --- /dev/null +++ b/custom/templates/custom/footer.tmpl @@ -0,0 +1,15 @@ + + + diff --git a/custom/templates/custom/header.tmpl b/custom/templates/custom/header.tmpl new file mode 100644 index 0000000..33f591f --- /dev/null +++ b/custom/templates/custom/header.tmpl @@ -0,0 +1 @@ + diff --git a/scripts/kicad_pcb.sh b/scripts/kicad_pcb.sh new file mode 100644 index 0000000..0acffb4 --- /dev/null +++ b/scripts/kicad_pcb.sh @@ -0,0 +1,67 @@ +#!/bin/bash +#set -x +INPUT="$*" +OUTDIR=$(mktemp -d) +IMGDIR="$GITEA_WORK_DIR/custom/public/assets/img" + +KIOPTS=( + # --subtract-soldermask + --mode-single + --exclude-drawing-sheet + --page-size-mode 2 + --sketch-pads-on-fab-layers +) + +ln -s "$INPUT" "$OUTDIR"/pcb.kicad_pcb + +if [[ -d "$IMGDIR" && -w "$IMGDIR" ]]; then + # We can use caching + HASH=$(sha256sum "$INPUT" | cut -c1-64) + + shopt -s nullglob + IMAGES=("${IMGDIR}"/${HASH}.*.svg) + shopt -u nullglob + + if [ ${#IMAGES[@]} -le 0 ]; then + kicad-cli pcb export svg "${KIOPTS[@]}" --layers Edge.Cuts,F.Cu,F.Mask,F.Silkscreen,F.Adhesive,F.Paste --output "${OUTDIR}/${HASH}.1.svg" "$OUTDIR"/pcb.kicad_pcb >/dev/null + kicad-cli pcb export svg "${KIOPTS[@]}" --mirror --layers Edge.Cuts,B.Cu,B.Mask,B.Silkscreen,B.Adhesive,B.Paste --output "${OUTDIR}/${HASH}.2.svg" "$OUTDIR"/pcb.kicad_pcb >/dev/null + + inkscape --actions="page-fit-to-selection" -o "${IMGDIR}/${HASH}.1.svg" "${OUTDIR}/${HASH}.1.svg" + inkscape --actions="page-fit-to-selection" -o "${IMGDIR}/${HASH}.2.svg" "${OUTDIR}/${HASH}.2.svg" + + shopt -s nullglob + IMAGES=("${IMGDIR}"/${HASH}.*.svg) + shopt -u nullglob + fi + + echo '
' + for IMG in "${IMAGES[@]}"; do + echo '
' + # echo "bla" + echo -n '' + echo '
' + done + echo '
' + +else + echo "

Error: Unable to write to $IMGDIR. Please check permissions.

" + # TODO: Implement base64 method + # We'll be generating the images on the fly, including them as base64 + # mapfile -t FILES \ + # < <(kicad-cli sch export svg --exclude-drawing-sheet --no-background-color --output "$OUTDIR" "$OUTDIR/pcb.kicad_pcb" | grep -E 'Plotted to' | sed -r "s/^[^']+'|'[^']+$//g" ) + # for FILE in "${FILES[@]}"; do + # echo -n '
' + # echo -n '' + # echo '
' + # done +fi + +rm -R "$OUTDIR" diff --git a/scripts/kicad_sch.sh b/scripts/kicad_sch.sh new file mode 100644 index 0000000..7667a41 --- /dev/null +++ b/scripts/kicad_sch.sh @@ -0,0 +1,62 @@ +#!/bin/bash +#set -x +INPUT="$*" +OUTDIR=$(mktemp -d) +IMGDIR="$GITEA_WORK_DIR/custom/public/assets/img" + +ln -s "$INPUT" "$OUTDIR"/schematic.kicad_sch + +if [[ -d "$IMGDIR" && -w "$IMGDIR" ]]; then + # We can use caching + HASH=$(sha256sum "$INPUT" | cut -c1-64) + + shopt -s nullglob + IMAGES=("${IMGDIR}"/${HASH}.*.svg) + shopt -u nullglob + + if [ ${#IMAGES[@]} -le 0 ]; then + mapfile -t FILES \ + < <(kicad-cli sch export svg --exclude-drawing-sheet --no-background-color --output "$OUTDIR" "$OUTDIR/schematic.kicad_sch" | grep -E 'Plotted to' | sed -r "s/^[^']+'|'[^']+$//g" ) + + file_num=1 + for FILE in "${FILES[@]}"; do + FN="${IMGDIR}/${HASH}.${file_num}.svg" + inkscape --actions="page-fit-to-selection" -o "$FN" "$FILE" + file_num=$((file_num + 1)) + break # TODO: find some way to handle related (hierarchical) schematic files, as currently only the active file is made available + done + + shopt -s nullglob + IMAGES=("${IMGDIR}"/${HASH}.*.svg) + shopt -u nullglob + fi + + for FILE in "${IMAGES[@]}"; do + echo '
' + # echo "bla" + echo -n '' + echo '
' + done + +else + # We'll be generating the images on the fly, including them as base64 + mapfile -t FILES \ + < <(kicad-cli sch export svg --exclude-drawing-sheet --no-background-color --output "$OUTDIR" "$OUTDIR/schematic.kicad_sch" | grep -E 'Plotted to' | sed -r "s/^[^']+'|'[^']+$//g" ) + for FILE in "${FILES[@]}"; do + echo -n '
' + echo -n '' + echo '
' + + break # TODO: find some way to handle related (hierarchical) schematic files, as currently only the active file is made available + done +fi + +rm -R "$OUTDIR" diff --git a/scripts/libreoffice.sh b/scripts/libreoffice.sh new file mode 100644 index 0000000..7880b22 --- /dev/null +++ b/scripts/libreoffice.sh @@ -0,0 +1,72 @@ +#!/bin/bash +#set -x +INPUT="$*" +OUTDIR=$(mktemp -d) +IMGDIR="$GITEA_WORK_DIR/custom/public/assets/img" + +if [[ -d "$IMGDIR" && -w "$IMGDIR" ]]; then + # We can use caching + HASH=$(sha256sum "$INPUT" | cut -c1-64) + + shopt -s nullglob + IMAGES=("${IMGDIR}"/${HASH}.*.svg) + shopt -u nullglob + + if [ ${#IMAGES[@]} -le 0 ]; then + libreoffice --headless --convert-to pdf --outdir "$OUTDIR" $INPUT >/dev/null + + PDF_FILE="$OUTDIR/$(basename "${INPUT%.*}".pdf)" + + NUM_PAGES=$(pdfinfo "$PDF_FILE" | grep Pages | awk '{print $2}') + + for (( i=1; i<=$NUM_PAGES; i++ )); do + FN="${IMGDIR}/${HASH}.${i}.svg" + pdftocairo -svg -f $i -l $i "$PDF_FILE" "${FN}" + done + + shopt -s nullglob + IMAGES=("${IMGDIR}"/${HASH}.*.svg) + shopt -u nullglob + fi + + echo '
' + for IMG in "${IMAGES[@]}"; do + echo '
' + echo -n '' + echo '
' + done + echo '
' + +else + # We'll be generating the images on the fly, including them as base64 + libreoffice --headless --convert-to pdf --outdir "$OUTDIR" $INPUT >/dev/null + PDF_FILE="$OUTDIR/$(basename "${INPUT%.*}".pdf)" + NUM_PAGES=$(pdfinfo "$PDF_FILE" | grep Pages | awk '{print $2}') + + for (( i=1; i<=$NUM_PAGES; i++ )); do + FN="${IMGDIR}/${HASH}.${i}.svg" + pdftocairo -svg -f $i -l $i "$PDF_FILE" "${FN}" + done + + shopt -s nullglob + IMAGES=("${IMGDIR}"/${HASH}.*.svg) + shopt -u nullglob + + echo '
' + for IMG in "${IMAGES[@]}"; do + echo '
' + echo -n '' + echo '
' + done + echo '
' +fi + +rm -R "$OUTDIR"