1

I'm creating family tree using d3 and in the front-end I convert my SVG into string

if(document.querySelector('svg')){
        const svgString = new XMLSerializer().serializeToString(document.querySelector('svg'))
        dispatch(convertToPNG(svgString))
    }

and dispatch it to send it to backend, in the back-end I'm using convert-svg-to-png package to convert my SVG to PNG and this is my controller

import {convert} from 'convert-svg-to-png'
const SvgToPng = async(req, res, next) => {
try {
    if(!req.body.svg){
        res.status(400)
        throw new Error('Please upload the required svg file')
    }
    const png = await convert(req.body.svg,{
        puppeteer:{args: ['--no-sandbox'] }
    });
    res.set('Content-Type', 'image/png');
    res.send({png});
} catch (error) {
    next(error)
}

then back to the front-end I received the buffer and convert it to png

        const source = bufferToPng(pngData.data)
        const hiddenElement = document.createElement('a');
              hiddenElement.href = source;
              hiddenElement.download = 'family-tree.png'
              hiddenElement.click();

function buffertoPng(buffer){
    const arrayBufferView = new Uint8Array(buffer)
    const blob = new Blob([ arrayBufferView ], { type: 'image/png' })
    const urlCreator = window.URL || window.webkitURL
    const imageUrl = urlCreator.createObjectURL(blob)
    return imageUrl
  }

My issue is when I try to convert small SVG with Arabic names the library doing great job but when it comes to large SVG full of Arabic names I start to see weird characters instead of Arabic ones, instead of seeing Arabic name like that (أحمد) I see that (Ù�اطمة)

Note: the package use another package call puppeteer

And this is my d3 code

import {useEffect, useRef} from 'react'
import * as d3 from 'd3'
import { useHistory} from 'react-router'

const Tree = ({familyData,isProfile, isPoint}) => {
    const wrapperRef = useRef(null)
    const history = useHistory()
    const radialPoint = (x, y) => {
        return [(y = +y) * Math.cos(x -= Math.PI / 2), y * Math.sin(x)];
    }
    const genColors = [
            '#000','#7d1e01', '#2c3e50',
            '#80016e','#673ab7',
            '#ff9800','#795548','#142796',
            '#61b301','#19a0b1','#a0342c',
            '#CEE397','#F5A25D','#625261',
            '#87556F','#E5EDB7','#231E23',
            '#6F0000','#FFF0F5','#FFEBD9',
            '#BEEBE9','#B0A160','#E4F9FF',
        ]
    
    const createTree = () => {
        wrapperRef.current.innerHTML = '';
        const margin = {top: 140, right: 0, bottom: 20, left: 0}
        wrapperRef.current.width = wrapperRef.current.getBoundingClientRect().width
        wrapperRef.current.height = wrapperRef.current.getBoundingClientRect().height
        const innerHeight = 2000 - margin.top - margin.bottom;
        const svg = d3.select(wrapperRef.current)
                    .append('svg')
                    .attr('width', 4000)
                    .attr('height', 2200)
                    .attr('encoding', 'UTF-8')
                    
        const width = +svg.attr("width")
        const height = +svg.attr("height")
        const g = svg.append("g")
        .attr("transform", `translate(${(width/2+ 40)},${(height - 30)})`);
        
        const dataStructure = d3.stratify().id(d => d._id).parentId(d => d.parentId)(familyData)
        const treeLayout = d3.tree().size([(1 * Math.PI), innerHeight])
        const root = treeLayout(dataStructure)
        
        
        g.selectAll(".link")
            .data(root.links())
            .enter().append("path")
            .attr('fill', 'none')
            .attr('stroke', '#555')
            .attr('stroke-opacity', '0.4')
            .attr('stroke-width', (d) => {
                if(d.target.depth <= 9){
                    return 8 - d.target.depth
                } else {
                    return 1
                }
            })
            .attr("d", d3.linkRadial()
            .angle(d => d.x-Math.PI/2)
            .radius(d => d.y + 10))
            .attr('id',  d => 'link_' + d.target.data._id)
            .attr('stroke-dasharray', function(){
                const length = this.getTotalLength()
                return `${length} ${length}`
            })
            .attr('stroke-dashoffset', function(){
                const length = this.getTotalLength()
                return length
            })
            .transition()
            .duration(1500)
            .delay(d => d.source.depth * 500)
            .attr('stroke-dashoffset', 0)

        const node = g.selectAll(".node")
            .data(root.descendants())
            .enter().append("g")
            .attr("class", d => d.children ? " node--internal" : " node--leaf")
            .attr('transform', d =>  "translate(" + radialPoint(d.x-Math.PI/2, (d.y+10)) + ")");
            node.append("circle")
            .attr("r", d => !d.children && d.depth === 1 ? 3 : 7 - (d.depth))
            .style("stroke","white")
             .style("fill", d => {   
                if(d.children){
                    return genColors[d.depth]
                } else {
                    return '#04a21a'
                }
            })
            .attr('opacity', 0)
            .transition()
            .duration(1500)
            .delay(d => d.depth * 500)
            .attr('opacity', 1)
        
        node.append("image")
            .attr("xlink:href", d => !d.children ? "/image/leaf.png":"")
            .attr('x', d => d.x > Math.PI/2 ? '-5px': '-10px')
            .attr('y', d => !d.children && d.depth === 1 ? '-55px':"-90px")
            .attr("transform", d => `rotate(${(d.x > Math.PI/2 ? d.x-Math.PI/2  : d.x-Math.PI/2 ) * 180 / Math.PI})`)
            .attr('width', d => d.depth === 1 ? '1rem' : '1.5rem')
            .attr('opacity', 0)
            .transition()
            .duration(1500)
            .delay(d => d.depth * 500)
            .attr('opacity', 1)
            
        node.append("text")
            .attr("dy","0.32em")
            .attr('y', (d) => d.depth === 1 ? -1: 2)
            .attr("x", (d) => {
                if(!d.children){
                    if(d.depth === 1){
                        return 20
                    }
                    if(d.x > Math.PI/2){
                        return 30
                    }else {
                        return -40
                    }
                }else {
                    return -80
                }
            })
            // eslint-disable-next-line
            .attr("text-anchor", d => d.x > Math.PI/2 === !d.children ? "middle" : "end")
            .attr("transform", d => d.depth > 1 ? `rotate(${(d.x > Math.PI/2 ? d.x-Math.PI/2 - Math.PI / 2 : d.x-Math.PI/2 + Math.PI / 2) * 180 / Math.PI})` : '')
            .style("font-size", d => {
                return d.children  ?  2 - (d.depth * 2) /10 + 'em' 
                :d.depth === 1 ?'0.37em' : d.depth === 2 ? '0.6em' :'0.85em'
            })
            .text(d => d.data.firstName)
            .attr('id',  d => 'name_' + d.data._id)
            .style('cursor', 'pointer')
            .style(' z-index', '9999999')
            .attr('fill', (d) =>{
                if(d.children){
                    return genColors[d.depth]
                } else {
                    return '#04a21a'
                }
            })
            .on('mouseover', (e, d) => {
                if(isPoint){
                    d3.selectAll('path').style('stroke', '#2c3e50').style('opacity', 0.2)
                    d3.selectAll('text').style('fill', '#2c3e50').style('opacity', 0.2)
                    d3.selectAll('circle').style('fill', '#2c3e50').style('opacity', 0.2)
                    d3.selectAll('image').style('opacity', '0.1')
                    while(d){
                        if(!d.data.parentId ) {
                            d3.select(`#name_${d.data._id}`).style('fill','#000').style('opacity','1')
                        }
                        if(d.data.parentId !== null){
                            d3.select(`#link_${d.data._id}`).style('stroke','#b70202').style('opacity','1')
                            d3.select(`#name_${d.data._id}`).attr('fill','#000').style('opacity','1')
                            .style('font-size', '3rem')
                            .transition()
                            .duration(500)
                            .style('font-size', '5rem');
                        }
                        d = d.parent
                    }
                }    
            })
            .on('mouseout', () => {
                if(isPoint){
                    d3.selectAll('path').style('stroke', '#555').style('opacity','0.4')
                d3.selectAll('text').style('fill', (d) => {
                    if(d.children){
                        return genColors[d.depth]
                    } else {
                        return '#04a21a'
                    }
                }).style('font-size', d => {
                    return d.children  ?  2 - (d.depth * 2) /10 + 'em' 
                    :d.depth === 1 ?'0.4em' :'0.5em'
                }).style('opacity','1')
                d3.selectAll('image').style('opacity', '1')
                d3.selectAll('circle').style('fill', (d) => {
                    if(d.children){
                        return genColors[d.depth]
                    } else {
                        return '#04a21a'
                    }
                }).style('opacity','1')
                }      
        })
        .on('click', (e, d) => {
            if(!isProfile){
                history.push(`/info/${d.data._id}`)
            } 
        })
        .attr('opacity', 0)
            .transition()
            .duration(1500)
            .delay(d => d.depth * 600)
            .attr('opacity', 1)
        
       
    }
    useEffect(() => {
       createTree()   
    })
   return (
        <div className="tree__wrapper" ref={wrapperRef}></div>
    )
}

export default Tree
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

and this is a portion of svg generated by d3

 <svg xmlns="http://www.w3.org/2000/svg" width="4000" height="2200" encoding="UTF-8"><g transform="translate(2040,2170)"><path fill="none" stroke="#555" stroke-opacity="0.4" stroke-width="7" d="M1.6007775447085935,-9.871044081167742C38.41866107300624,-236.9050579480258,-222.4503319176191,-90.08801157613091,-435.6319000053374,-176.42235600325637" id="link_60f2c8e7131cad0723062518" stroke-dasharray="540.5701293945312 540.5701293945312" stroke-dashoffset="0"/><path fill="none" stroke="#555" stroke-opacity="0.4" stroke-width="7" d="M1.6007775447085935,-9.871044081167742C38.41866107300624,-236.9050579480258,-127.22830174504648,-203.50174258485208,-249.155424250716,-398.52424589533535" id="link_60f2c8e7131cad0723062519" stroke-dasharray="490.6072692871094 490.6072692871094" stroke-dashoffset="0"/><path fill="none" stroke="#555" stroke-opacity="0.4" stroke-width="7" d="M1.6007775447085935,-9.871044081167742C38.41866107300624,-236.9050579480258,127.2283017450465,-203.50174258485208,249.15542425071607,-398.52424589533535" id="link_60f2c8e7131cad072306251a" stroke-dasharray="470.35516357421875 470.35516357421875" stroke-dashoffset="0"/><path fill="none" stroke="#555" stroke-opacity="0.4" stroke-width="7" d="M1.6007775447085935,-9.871044081167742C38.41866107300624,-236.9050579480258,238.08207476904383,-30.280780598713555,466.2440630893775,-59.29986200581405" id="link_60f2c8e7131cad072306251b" stroke-dasharray="535.5938720703125 535.5938720703125" stroke-dashoffset="0"/><path fill="none" stroke="#555" stroke-opacity="0.4" stroke-width="7" d="M1.6007775447085935,-9.871044081167742C38.41866107300624,-236.9050579480258,238.38806663242283,-27.7692219418467,466.84329715516134,-54.381392969449784" id="link_60f2c8e7131cad072306251c" stroke-dasharray="536.4296875 536.4296875" stroke-dashoffset="0"/><path fill="none" stroke="#555" stroke-opacity="0.4" stroke-width="7" d="M1.6007775447085935,-9.871044081167742C38.41866107300624,-236.9050579480258,238.66756448516003,-25.254577064800976,467.39064711677173,-49.45688008523525" id="link_60f2c8e7131cad072306251d" stroke-dasharray="537.2632446289062 537.2632446289062" stroke-dashoffset="0"/><path fill="none" stroke="#555" stroke-opacity="0.4" stroke-width="7" d="M1.6007775447085935,-9.871044081167742C38.41866107300624,-236.9050579480258,238.92053726437896,-22.73712544057634,467.88605214274213,-44.526870654462" id="link_60f2c8e7131cad072306251e" stroke-dasharray="538.0941772460938 538.0941772460938" stroke-dashoffset="0"/><path fill="none" stroke="#555" stroke-opacity="0.4" stroke-width="7" d="M1.6007775447085935,-9.871044081167742C38.41866107300624,-236.9050579480258,239.14695685515082,-20.21714685410944,468.3294571746704,-39.59191258929766" id="link_60f2c8e7131cad072306251f" stroke-dasharray="538.92236328125 538.92236328125" stroke-dashoffset="0"/><path fill="none" stroke="#555" stroke-opacity="0.4" stroke-width="7" d="M1.6007775447085935,-9.871044081167742C38.41866107300624,-236.9050579480258,239.34679809361936,-17.694921371178975,468.7208129333379,-34.652554351892164" id="link_60f2c8e7131cad0723062520" stroke-dasharray="539.747802734375 539.747802734375" stroke-dashoffset="0"/>
halfer
  • 19,824
  • 17
  • 99
  • 186
  • This is great, thanks. Can you share a link to or share the SVG source that's causing the problem? – ggorlen Jul 19 '21 at 20:32
  • the svg generated by d3 is so large, how can i share it unless you want me to share the d3 code – Ahmed Abdelrahman Jul 19 '21 at 21:04
  • Yeah, the d3 code would be great, but just as good would probably be a minimal example that only includes the portion of the SVG that causes problems -- presumably just a small amount of text would be sufficient. – ggorlen Jul 19 '21 at 21:08
  • I include d3 code and portion of svg in my question – Ahmed Abdelrahman Jul 19 '21 at 21:16
  • I will add a comment that was posted by [SIMBIOSIS](https://stackoverflow.com/users/15107450/simbiosis) as an answer (which is likely to be deleted): _¿Does the arabic text in the svgs has been converted to path or they are text tags? If it is the second it would be a problem related to the fonts. Maybe your code can not display arabic text because it can find the appropiate font to do it. If it's the case providing the appropiate fonts will probably solve the issue._ – halfer Jul 27 '21 at 19:31

0 Answers0