node.js - 使用阿拉伯字符将 SVG 转换为 PNG
问题描述
我正在使用 d3 创建家谱,并在前端将我的 SVG 转换为字符串
if(document.querySelector('svg')){
const svgString = new XMLSerializer().serializeToString(document.querySelector('svg'))
dispatch(convertToPNG(svgString))
}
并将其发送到后端,在后端我使用convert-svg-to-png 包将我的 SVG 转换为 PNG,这是我的控制器
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)
}
然后回到前端我收到缓冲区并将其转换为 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
}
我的问题是,当我尝试使用阿拉伯名称转换小型 SVG 时,图书馆做得很好,但是当涉及到充满阿拉伯名称的大型 SVG 时,我开始看到奇怪的字符而不是阿拉伯字符,而不是看到这样的阿拉伯名称 (أحمد)我看到了(Ù�اطمة)
注意:该包使用另一个包调用puppeteer
这是我的 d3 代码
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>
这是 d3 生成的 svg 的一部分
<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"/>
解决方案
推荐阅读
- python - 使用 BeautifulSoup 从 HTML 表中提取数据
- node.js - Docker 上的 Node.js + Puppeteer,没有可用的沙箱
- wordpress - 无法从联系表发送电子邮件
- javascript - XMLHttpRequest 格式问题
- reactjs - 在特定条件下通过单击表格展开一行 Ant Design table react js
- scala - 如何在 Scala 中运行 docker 命令?
- artifactory - Artifactory Cloud - 如何在不弄乱模块中的 repo 路径的情况下复制工件
- python - 如何在 python 中打开 URL 1 分钟并在 1 分钟后关闭?
- ios - 每个设备的 Swift 布局
- typescript - 如何实现带可变键的接口并同时定义键