BookStackApp / BookStack

A platform to create documentation/wiki content built with PHP & Laravel
https://www.bookstackapp.com/
MIT License
14.8k stars 1.86k forks source link

Feature Request: Allow svg images #1103

Open TBK opened 5 years ago

TBK commented 5 years ago

Describe the feature you'd like Make it possible to upload svg images.

Describe the benefits this feature would bring to BookStack users Scalable Vector Graphics FTW 👍

Additional context This is what happens at the moment: image

wouterloedeman commented 5 years ago

I agree. Would be nice if they could be used as application logo as well!

xblitz commented 5 years ago

Yes! Just tried using SVG logo since the current requirement of 43px Height looks bad on HiDPI Screens (4k, phones)

bharatrajagopalan commented 5 years ago

Hi

EDIT: a slight tweak that lets me get rid of the div element by using the beforeInject hook of SVGInject

Not perfect but managed to get interactive SVG working with the following

I am using plantuml to generate SVG for UML diagrams

I upload svgs as attachments and then am using styles to add a nice grey background. i am using the excellent SVGinject javascript to find img tags with class plantsvg and replace it with the SVG content. Consequently as SVG is basically html text, i am using an enclosing div to center it

Paste this into Settings > Custom HTML Head

image
<style> 
 .plantsvg  {
   height:auto;
   background-color:#FAFAFA;
   padding:20px;
   max-width:100%;
 }

 .plantdiv {
   text-align:center;

 } 
</style>

<script>

!function(o,l){var r,a,s="createElement",y="getElementsByTagName",g="length",E="style",d="title",b="undefined",h="setAttribute",k="getAttribute",w=null,x="__svgInject",A="--inject-",C=new RegExp(A+"\\d+","g"),S="LOAD_FAIL",t="SVG_NOT_SUPPORTED",I="SVG_INVALID",v=["src","alt","onload","onerror"],L=l[s]("a"),j=typeof SVGRect!=b,f={useCache:!0,copyAttributes:!0,makeIdsUnique:!0},G={clipPath:["clip-path"],"color-profile":w,cursor:w,filter:w,linearGradient:["fill","stroke"],marker:["marker",
"marker-end","marker-mid","marker-start"],mask:w,pattern:["fill","stroke"],radialGradient:["fill","stroke"]},u=1,c=2,N=1;function O(e){return(r=r||new XMLSerializer).serializeToString(e)}function T(e){var t,r,n,i,o=A+N++,a=e.querySelectorAll("[id]"),f={},u=[],c=!1;for(n=0;n<a[g];n++)(r=(t=a[n]).localName)in G&&(c=!0,f[r]=1,t.id+=o,["xlink:href","href"].forEach(function(e){var r=t[k](e);/^\s*#/.test(r)&&t[h](e,r.trim()+o)}));for(r in f)(G[r]||[r]).forEach(function(e){u.indexOf(e)<0&&u.push(e)})
;if(u[g]){u.push(E);var l,s,d,v,p=/url\("?#([a-zA-Z][\w:.-]*)"?\)/g,m=e[y]("*");for(n=0;n<m[g];n++)if((l=m[n]).localName==E)(v=(d=l.textContent)&&d.replace(p,"url(#$1"+o+")"))!==d&&(l.textContent=v);else if(l.hasAttributes())for(i=0;i<u[g];i++)s=u[i],(v=(d=l[k](s))&&d.replace(p,"url(#$1"+o+")"))!==d&&l[h](s,v)}return c}function P(e,r,t,n){if(r){r[h]("data-inject-url",t);var i=e.parentNode;if(i){n.copyAttributes&&function c(e,r){for(var t,n,i,o=e.attributes,a=0;a<o[g];a++)if(n=(t=o[a]).name,
-1==v.indexOf(n))if(i=t.value,n==d){var f,u=r.firstElementChild;u&&u.localName.toLowerCase()==d?f=u:(f=l[s+"NS"]("http://www.w3.org/2000/svg",d),r.insertBefore(f,u)),f.textContent=i}else r[h](n,i)}(e,r);var o=n.beforeInject,a=o&&o(e,r)||r;i.replaceChild(a,e),e[x]=u,m(e);var f=n.afterInject;f&&f(e,a)}}else _(e,n)}function p(){for(var e={},r=arguments,t=0;t<r[g];t++){var n=r[t];for(var i in n)n.hasOwnProperty(i)&&(e[i]=n[i])}return e}function V(e,r){if(r){var t;try{t=function i(e){return(
a=a||new DOMParser).parseFromString(e,"text/xml")}(e)}catch(o){return w}return t[y]("parsererror")[g]?w:t.documentElement}var n=l.createElement("div");return n.innerHTML=e,n.firstElementChild}function m(e){e.removeAttribute("onload")}function n(e){console.error("SVGInject: "+e)}function i(e,r,t){e[x]=c,t.onFail?t.onFail(e,r):n(r)}function _(e,r){m(e),i(e,I,r)}function D(e,r){m(e),i(e,t,r)}function F(e,r){i(e,S,r)}function M(e){e.onload=w,e.onerror=w}function q(e){n("no img element")}
var e=function R(e,r){var t=p(f,r),h={};function n(a,f){f=p(t,f);var e=function(r){var e=function(){var e=f.onAllFinish;e&&e(),r&&r()};if(a&&typeof a[g]!=b){var t=0,n=a[g];if(0==n)e();else for(var i=function(){++t==n&&e()},o=0;o<n;o++)u(a[o],f,i)}else u(a,f,e)};return typeof Promise==b?e():new Promise(e)}function u(u,c,e){if(u){var r=u[x];if(r)Array.isArray(r)?r.push(e):e();else{if(M(u),!j)return D(u,c),void e();var t=c.beforeLoad,n=t&&t(u)||u[k]("src");if(!n)return""===n&&F(u,c),void e()
;var i=[];u[x]=i;var l=function(){e(),i.forEach(function(e){e()})},s=function f(e){return L.href=e,L.href}(n),d=c.useCache,v=c.makeIdsUnique,p=function(r){d&&(h[s].forEach(function(e){e(r)}),h[s]=r)};if(d){var o,a=function(e){if(e===S)F(u,c);else if(e===I)_(u,c);else{var r,t=e[0],n=e[1],i=e[2];v&&(t===w?(t=T(r=V(n,!1)),e[0]=t,e[2]=t&&O(r)):t&&(n=function o(e){return e.replace(C,A+N++)}(i))),r=r||V(n,!1),P(u,r,s,c)}l()};if(typeof(o=h[s])!=b)return void(o.isCallbackQueue?o.push(a):a(o));(o=[]
).isCallbackQueue=!0,h[s]=o}!function m(e,r,t){if(e){var n=new XMLHttpRequest;n.onreadystatechange=function(){if(4==n.readyState){var e=n.status;200==e?r(n.responseXML,n.responseText.trim()):400<=e?t():0==e&&t()}},n.open("GET",e,!0),n.send()}}(s,function(e,r){var t=e instanceof Document?e.documentElement:V(r,!0),n=c.afterLoad;if(n){var i=n(t,r)||t;if(i){var o="string"==typeof i;r=o?i:O(t),t=o?V(i,!0):i}}if(t instanceof SVGElement){var a=w;if(v&&(a=T(t)),d){var f=a&&O(t);p([a,r,f])}P(u,t,s,c)
}else _(u,c),p(I);l()},function(){F(u,c),p(S),l()})}}else q()}return j&&function i(e){var r=l[y]("head")[0];if(r){var t=l[s](E);t.type="text/css",t.appendChild(l.createTextNode(e)),r.appendChild(t)}}('img[onload^="'+e+'("]{visibility:hidden;}'),n.setOptions=function(e){t=p(t,e)},n.create=R,n.err=function(e,r){e?e[x]!=c&&(M(e),j?(m(e),F(e,t)):D(e,t),r&&(m(e),e.src=r)):q()},o[e]=n}("SVGInject");"object"==typeof module&&"object"==typeof module.exports&&(module.exports=e)}(window,document);

</script>

<script>
    SVGInject.setOptions({

      beforeInject: function(img, svg) {
        // svg text alignment set via div
        //ensure that svg aspect ratio maintained
        svg.style["max-width"]="100%";

        var div = document.createElement('div');
        div.style["text-align"]="center"; 
        div.appendChild(svg);
        return div;
      }, 

    });

</script>

<script>
addEventListener("load", function() {  SVGInject(document.querySelector("img.plantsvg")); })
</script>

You need to use the Markdown editor to get this working - Settings > Page Editor - Select Markdown

image

And then in your editor - paste the following - change the src to the path to your SVG file - in my case it is an attachment

image
  <img src="/attachments/10" class="plantsvg"  />

And the final result looks like this when you save

image

It is interactive - i.e. i can click links & select text

image image
HacKanCuBa commented 5 years ago

I would love this fix too!
Does the fix mentioned above requires ALLOW_CONTENT_SCRIPTS to be true? cc @bharatrajagopalan

WarriorXK commented 3 years ago

Is there any indication that this would be added? I have a lot of diagrams and it would be amazing if we could retain the SVG quality.