org-chart icon indicating copy to clipboard operation
org-chart copied to clipboard

chart.exportImg() is throwing blurry image

Open sachin717 opened this issue 1 year ago • 17 comments

i am trying to export the full org chart in expanded mode, i have tried with 50 records its working fine, now as things are getting complexed and with 250 records its printing blurry images is there any way to achieve the clear pdf for the records image

sachin717 avatar Nov 20 '23 05:11 sachin717

Image quality varies on the screen PPI , it looks good on retina macbook screens for example, but not so good on non-retina macbook screens.

That's because converting happens directly on the frontend.

There is a way to increase scale (default is 3), but be aware that it's performance heavy operation and it might work for poor screens, but fail on retina screens , because retina needs to do at least 4X times more operations

So, I'd suggest having some kind of conditional scaling set depending on the screen, remember default is 3

image

bumbeishvili avatar Nov 20 '23 11:11 bumbeishvili

is am using the pdf option sent by dufault just need the highest image quality can you guide me

sachin717 avatar Nov 20 '23 11:11 sachin717

Adding scale:5 in place of green line should do the trick image

bumbeishvili avatar Nov 22 '23 11:11 bumbeishvili

Hi, i have the same problem with my export. I have changed the scale to 6 but I still have blur and a big file (150MB)!

Do you have any tip how can I export smaller file?

grzegorzCieslik95 avatar Feb 06 '24 12:02 grzegorzCieslik95

@grzegorzCieslik95 scale controls how bit the file is. scale:1 exports exactly the same size as on the screen, scale 6 - makes it 36x bigger image.

Can you post a 3x scale photo of the exported org chart?

bumbeishvili avatar Feb 07 '24 07:02 bumbeishvili

Chart-scale-6 Chart-scale-3

Sure, i post scale 3 and 6

grzegorzCieslik95 avatar Feb 07 '24 08:02 grzegorzCieslik95

I can see the quality difference.

The issue with blurriness is that you are trying to export a horizontally spread chart on a vertical screen :/

Not sure how to approach this - I think I have to apply some kind of cropping to make it look better. I will keep this issue open, not sure when it will be fixed

bumbeishvili avatar Feb 07 '24 09:02 bumbeishvili

Hi, I have sth like this https://stackblitz.com/edit/js-7qzbix?file=index.html . I'm still having problem with rendering images but it works very well in big organisation.

Of course this is the pdf export, but on pdf zoom works different than on HTML -> this is why I changed some part of export

grzegorzCieslik95 avatar Feb 07 '24 10:02 grzegorzCieslik95

Hello, i paste here my code for export pdf without blur for ~300 nodes

      const { svg } = chart.current.getChartState();
      const graphNode = svg.node();
      if (!graphNode) {
        return null;
      }
      const nodeWithInlineStyles = computeToInlineStyles(graphNode); // replace style to inline style
      await transformImagesToBase64(nodeWithInlineStyles); // replace image URL to base64
      const { y: oY, x: oX } = graphNode.getBoundingClientRect();
      const { height, width, x, y } =
        graphNode.children[0].getBoundingClientRect();
      const opt = {
        margin: [4, 8],
        filename: fileName,
        image: { type: 'jpeg', quality: 0.98 },
        html2canvas: {
          useCORS: true,
          allowTaint: true,
          letterRendering: true,
          scale: getCorrectScale(
            graphNode.getElementsByClassName(nodeClassName).length, // get correct scale for nodes count
          ), 
          height,
          width,
          x: x - oX,
          y: y - oY,
        },
        jsPDF: {
          unit: 'px',
          format: [height + 12, width + 16],
          orientation: height > width ? 'p' : 'l',
        },
      };
      return html2pdf()
        .set(opt)
        .from(nodeWithInlineStyles)
        .save()

computeToInlineStyles -> return new copied node

if you want, I can create pr for this but this is only pdf export

grzegorzCieslik95 avatar Feb 19 '24 10:02 grzegorzCieslik95

Hi,

I tried several variations, for A0 format the scale factor 4.6 is the highest I can choose (or nothing actually happens) now the file is quit large (130MB) but still blurry...Any idea why? Thank you!

  function downloadPdf(chart) {
    chart.exportImg({
      save: false,
      full: true,
      scale: 4.6,
      onLoad: (base64) => {
        var pdf = new jspdf.jsPDF('landscape','mm','a0');
        var img = new Image();
        img.src = base64;
        img.onload = function () {
          var widthInInches = img.width;
          var heightInInches = img.height;

          var aspectRatio = img.height / img.width;

          var pdfWidth = '1189';
          var pdfHeight = pdfWidth * aspectRatio;

          pdf.addImage(
            img,
            'JPEG',
            0,
            0,
            pdfWidth,
            pdfHeight
          );
          pdf.save('chart.pdf');
        };
      },
    });
  }
</script>

dbdaniellozynski avatar Mar 04 '24 09:03 dbdaniellozynski

dbdaniellozynski Did you tried my method to generate pdf? I dont use native export from library.

grzegorzCieslik95 avatar Mar 04 '24 09:03 grzegorzCieslik95

@grzegorzCieslik95 Hi, I am not really a JS guy, I simply don't unterstand what to do with your code :-(

dbdaniellozynski avatar Mar 04 '24 09:03 dbdaniellozynski

Oki, so i dont know how to help you with this. I had the same problem with this library native export

grzegorzCieslik95 avatar Mar 04 '24 09:03 grzegorzCieslik95

Ok, I tried to understand your code and came up with this (absolutely no idea if that should work that way, but at least it creates an very blurry PDF ;-)

Is that sort of the way you intended to use the code? Do you know why it's blurry?

`

    d3.csv(
        'd3__team_org_chart_d3.csv'
    ).then((data) => {
        chart = new d3.OrgChart()
            .nodeHeight((d) => 85 + 25)
            .nodeWidth((d) => 290 + 2)
            .childrenMargin((d) => 150)
            .compactMarginBetween((d) => 350)
            .compactMarginPair((d) => 90)
            .neighbourMargin((a, b) => 100)
            .nodeContent(function (d, i, arr, state) {
                const color = '#FFFFFF';
                const imageDiffVert = 25 + 2;
                return `
            <div style='width:${d.width} px;height:${d.data.height} px;padding-top:${imageDiffVert - 2}px;padding-left:1px;padding-right:1px'>
                <div style="font-family: 'Inter', sans-serif;background-color:${color};  margin-left:-1px;width:${d.width - 2}px;height:${d.data.height - imageDiffVert}px;border-radius:10px;border: 1px solid #E4E2E9">
                    <div style="display:flex;justify-content:flex-end;margin-top:5px;margin-right:8px">${d.data.teamMembers} 
                    </div>                      
                    <div style="background-color:${color};margin-top:${-imageDiffVert - 20}px;margin-left:${15}px;border-radius:100px;width:50px;height:50px;" ></div>
                    <div style="margin-top:${-imageDiffVert - 20
                    }px;">   <img src=" ${d.data.image}" style="margin-left:${20}px;border-radius:100px;width:40px;height:40px;" /></div>
                    <div style="font-size:15px;color:#08011E;margin-left:20px;margin-top:10px">  ${d.data.name
                    } </div>
                    <div style="color:#716E7B;margin-left:20px;margin-top:3px;font-size:12px;"> ${d.data.position
                    } </div>

                </div>
            </div>
                        `;
            })
            .container('.chart-container')
            .data(data)
            .render();
    });

    function computeToInlineStyles(node) {
        const computedStyle = window.getComputedStyle(node);
        const inlineStyle = Array.from(computedStyle).reduce((acc, styleName) => {
            acc += `${styleName}: ${computedStyle.getPropertyValue(styleName)};`;
            return acc;
        }, '');
        node.setAttribute('style', inlineStyle);
    }

    function getCorrectScale(nodesCount) {
        // Logic to determine the scale based on the number of nodes
        // Adjust scale based on the number of nodes
        if (nodesCount > 50) {
            return 0.5; // Example: Reduce scale if there are many nodes
        } else {
            return 1; // Default scale
        }
    }

    async function exportChartToPDF(fileName, nodeClassName) {
        if (!chart) {
            console.error("chart not initialized");
            return;
        }

        const { svg } = chart.getChartState();

        if (!svg) {
            console.error("SVG element not found in chart state.");
            return;
        }

        const graphNode = svg.node();

        if (!graphNode) {
            console.error("Graph node not found.");
            return;
        }

        computeToInlineStyles(graphNode); // Replace style to inline style

        const { y: oY, x: oX } = graphNode.getBoundingClientRect();
        const { height, width, x, y } = graphNode.children[0].getBoundingClientRect();

        const opt = {
            margin: [4, 8],
            filename: fileName,
            image: { type: 'jpeg', quality: 1 },
            html2canvas: {
                useCORS: true,
                allowTaint: true,
                letterRendering: true,
                scale: getCorrectScale(document.getElementsByClassName("chart-container").length), // Get correct scale for nodes count
                height,
                width,
                x: x - oX,
                y: y - oY,
            },
            jsPDF: {
                unit: 'px',
                format: [height + 12, width + 16],
                orientation: height > width ? 'p' : 'l',
            },
        };

        return html2pdf()
            .set(opt)
            .from(graphNode)
            .save();
    }

</script>`

dbdaniellozynski avatar Mar 05 '24 07:03 dbdaniellozynski

imho you need to add more scale -> I use values between 5 and 50 in my case

grzegorzCieslik95 avatar Mar 05 '24 07:03 grzegorzCieslik95

hi, @bumbeishvili is there any update on this thread, i have a tree with height 7; total of ~400 nodes the output for my pdf is same as @grzegorzCieslik95 posted in this thread [images and text are blurry ]

i tried generating an svg and using that svg inside the pdf also played with svg2pdf lib -> that didnot worked is there any idea to tackle this issue

@sachin717 how did you resolve this issue

vatraiadarsh avatar Apr 26 '24 05:04 vatraiadarsh

@vatraiadarsh no update on my end

bumbeishvili avatar Apr 26 '24 07:04 bumbeishvili