oliverfindl / vue-svg-inline-plugin

Vue plugin for inline replacement of SVG images with actual content of SVG files.
MIT License
33 stars 3 forks source link

Trying to use vue-svg-inline-plugin in Quasar app #16

Open mickey58github opened 1 year ago

mickey58github commented 1 year ago

Hi, I would like load an SVG file in an image, . Is this possible with this vue-svg-inline-plugin?

My app is based on Vue 3 and Quasar. Can you provide some hints how I would have to register this plugin to be able to use it ?

oliverfindl commented 1 year ago

Hello,

I'm not familiar with Quasar, but I don't see any roadblocks. This is just plugin for Vue, so it should work like any other Vue plugin. Please follow readme and you should be good.

If you have any questions, feels free to ask here.

Thanks.

mickey58github commented 1 year ago

Oliver, thanks. I got it running with Quasar through configuring a Quasar boot file for it.

It successfully loads svgs through <img v-svg-inline src="path/my.svg" /> - that's probably what it is supposed to do.

However, I realize that my code has, within an "umbrella" svg, various embedded "sub" svg files. Those are embedded in my "umbrella" svg through <image href="path/my-sub.svg" />.

I can't get your inliner working on such images. It throws this error: "[vue-svg-inline-plugin] Required property is missing! [node.data-src || node.src]"

Is using vue-svg-inline-plugin from images in svgs a supported use case?

oliverfindl commented 1 year ago

Hello,

that error means, that element tagged with v-svg-inline directive does not have src or data-src property. Anyway, I'm afraid it won't work in your setup. This plugin just downloads raw SVG file and replaces element tagged with v-svg-inline directive with it, so it expects simple SVG image.

Thanks.

mickey58github commented 1 year ago

Thanks. As mentioned, it works in my setup, if used on <img>. But it doesn't work on <image>. <image> is a special <img> that can be used inside <svg> to display "child svgs" (or other pics like pngs etc.).Not sure how common that use case is. It happens to be used prominently in our app. I'll play further with it to see if I can change the code from <image> to <img>, but fear it will create different headaches.

oliverfindl commented 1 year ago

Hello,

this plugin has hard-coded condition for src or data-src attribute only, while image tag is using href attribute. Actually I think, there is missing condition for img tag, because I can't find it anywhere in code.

Adding href attribute in case of image tag should be kinda easy, but it still won't work if your use case has nested SVGs (PNGs, JPGs should be fine thou). It will have to parse SVGs from most deeply nested one, wait to fetch it, somehow replace it and then go 1 level up and so on. While it might be done with recursion, I can not even predict how long it would take me (I did not touch this code for some time), most probably it will require some refactoring and it will result into performance degradation (could be solved by some flag in options) and larger bundle size.

Personally I think, this is kinda specific situation and will most probably benefit only small portion of users (maybe only you). I don't plan to invest much time into this plugin, so feel free to fork it, solve your issue and submit PR. If you are interested, I can give you some hints.

Thanks.

mickey58github commented 1 year ago

Thanks for looking into this.. The nesting level of the embedded SVGs (referenced through image hrefs) is just one level down in my case, but I understand that a general solution would probably have to handle multiple levels.

mickey58github commented 1 year ago

@oliverfindl - sorry for bothering you once more, but could you provide me some hints where this support for <image href="my.svg"> would have to be factored into your code, with only one level of such embedded, <image> based svgs within a surrounding <svg>? I tried to find an alternative solution for it, but have been unsuccessful with that.

oliverfindl commented 1 year ago

Hello,

I can, but I got one more idea. If you don't rely on using img tag with v-svg-inline directive, I also created custom Vue component for fetching SVG files - vue-svg-inline-component. As of current state, it won't work for your use case, but it will require only small change to support such scenario as opposed to refactoring this plugin. Please provide me your parent and child SVGs and I can try implement such change.

Thanks.

mickey58github commented 1 year ago

The SVGs I would like to inline are embedded in a larger, relatively complex SVG. The slightly simplified snippet that is doing the critical part is this (I can provide the referenced SVG file, if needed, but I don't think that matters much, as the problem seems to be with any SVG file referenced through href):

<svg>
  <!-- ...other pieces of the surrounding "full" SVG omitted here, including v-for with index... -->
  <!-- ...critical part starts here: -->
  <g >
      <!-- ...rectangle, provides border of embedded "sub-SVG" starts here: -->
      <rect
        x="150"
        :y="computeSVGDaten[index].y"
        width="230"
        :height="computeSVGDaten[index].height"
        stroke="black"
        stroke-width="2"
      />
     <!-- ..."sub-SVG" from SVG file, referenced through <image href=".." starts here: -->
      <image
         x="150"
         :y="computeSVGDaten[index].y"
         style="width: 230px"
         :height="computeSVGDaten[index].height"
         href="/statics/images/ss_schn_bt_23f_estrich_20mm_r-resized-with-inkscape.svg"
      />
   </g>
</svg>

This code, in isolation, even WORKS - it successfully renders the full, "surrounding" SVG, including the sub-SVG in the href!

BUT: When I convert the surrounding SVG to a PNG, the part of the SVG that is based on the sub-SVG file referenced in the href is rendered as a black rectangle (= "problem 1"). And when I send the full SVG to my backend (in serialized form), my backend code, which uses PDFKit to create a PDF with the SVG inside, displays the sub-SVG in the href as a black rectangle, too (= "problem 2", even though I made sure that the path and file are also accessible in my backend).

So my broader problem is that other dependencies that I have in my app can't handle the <image href="my.svg"> properly, though part of the SVG spec, which creates a requirement for some sort of "inlining" solution that would replace my code above.

I have written another variant of WORKING code, in addition, meanwhile, which has the (long) code of the sub-SVG manually inlined (pasted in) inside the surrounding SVG, as <svg> <!-- code of sub-svg here --> </svg>, right after the <rect/> above. This is of course an ugly and inflexible workaround - kind of "poor man's inlining". But it solves problem 2 (and most likely problem 1 as well, but I did not test that, since the conversion to PNG is just part of another workaround against problem 2).

So I thought one of your packages could help me against the described problems. But I'm now a bit lost to connect my use case with what your packages can do or can't do.

Looking at my own code above, I realize that I have a reactivity requirement in addition, because the <rect>and <image> :y and :height props can be changed dynamically (which even works in my "poor man's inlining" solution mentioned above). I saw somewhere in your docs that reactive Vue bindings are not transferred to the SVG replacement, which might exclude your solutions?

oliverfindl commented 1 year ago

Hello,

thanks for your example. I can see, you are using Vue bindings in your SVGs and this definitely won't work with vue-svg-inline-plugin (except for first render), because it creates new SVG element and replaces original img tag with v-svg-inline directive directly in DOM without Vue knowing of about it.

And with vue-svg-inline-component, it is kinda similar story - theoretically speaking, it might work (after some changes), but you will have to do all heavy lifting yourself and it will significantly degrade performance.

I'm afraid, there are no shortcuts for your use case. If you want to use Vue bindings effectively inside your SVGs, you will have to manually copy them into your code and also manually resolve its SVG children so Vue will be aware of all bindings.

As for your issue with PDF on backend, I would guess, that problem lies in absolute path for nested SVG (referenced in image tag), because backend does not know about your domain and path starting with / means root on filesystem (if you use Linux/Mac and if you use Windows, its basically C:\). You might try to use relative path or full url (if your PDF lib can handle it).

Thanks.

mickey58github commented 1 year ago

Thanks - @oliverfindl. I'll probably continue to work around the issue, by directly inlining those SVGs in my code, rather than using <image href='my.svg'>. Fortunately, only a few SVGs are affected, not hundreds.

Re. the backend issue in the PDF creation, I successfully tested on my local Windows server <image href='c:/absolute/path/my.png'> with absolute paths, but only png files worked in that way, not svg files (those rendered as black rectangles). This issue is addressed as well by the workaround.