因为我写的是node.js版本,对python不太属性,无法提供python代码
1、有些图片显示前先要对图片进行裁剪图片显示才会正常,easyofd在2025.10.22日测试未对图片裁剪,相关xml代码如下(因为隐私问题,作者可从一级注册建筑师注册证书获取到类似这段xml):
<ofd:ImageObject ID="51" CTM="33.8667 0 0 45.1556 0 0" Boundary="135.5725 134.2143 33.8667 45.1556" ResourceID="50">
<ofd:Clips TransFlag="false">
<ofd:Clip>
<ofd:Area CTM="0.3528 0 0 0.3528 0 0">
<ofd:Path Boundary="-5.75 0 122.3 111.25" Stroke="false" Fill="true">
<ofd:AbbreviatedData>M 0 0 L 122.3 0 L 122.3 111.25 L 0 111.25 C </ofd:AbbreviatedData>
</ofd:Path>
</ofd:Area>
</ofd:Clip>
</ofd:Clips>
</ofd:ImageObject>
js版本的参照代码
var mm1 = 200 / 25.4; //200dpi
var canvas = document.getElementById("canvasid");
canvas.width = mm1 * 210;
canvas.height = mm1 * 297;
var ctx = canvas.getContext("2d");
ctx.save()
ctx.beginPath()
let imgrect = "135.5725 134.2143 33.8667 45.1556".split(" ")
let Boundary = "-5.75 0 122.3 111.25".split(" ")
ctx.translate(Math.ceil(Number(imgrect[0]) * mm1), Math.ceil(Number(imgrect[1]) * mm1))
ctx.transform(0.3528, 0, 0, 0.3528, 0, 0)
let clipsPath = "M 0 0 L 122.3 0 L 122.3 111.25 L 0 111.25 C ".split(" ")
for (let j = 0; j < clipsPath.length;) {
switch (clipsPath[j]) {
case "M":
ctx.moveTo(Math.ceil((Number(Boundary[0]) + Number(clipsPath[j + 1])) * mm1), Math.ceil((Number(Boundary[1]) + Number(clipsPath[j + 2])) * mm1))
j += 3
break;
case "L":
ctx.lineTo(Math.ceil((Number(Boundary[0]) + Number(clipsPath[j + 1])) * mm1), Math.ceil((Number(Boundary[1]) + Number(clipsPath[j + 2])) * mm1))
j += 3
break;
case "Q":
ctx.quadraticCurveTo((Number(Boundary[0]) + Number(clipsPath[j + 1])) * mm1, (Number(Boundary[1]) + Number(clipsPath[j + 2])) * mm1, (Number(Boundary[0]) + Number(clipsPath[j + 3])) * mm1, (Number(Boundary[1]) + Number(clipsPath[j + 4])) * mm1)
j += 5
break;
case "B":
ctx.bezierCurveTo((Number(Boundary[0]) + Number(clipsPath[j + 1])) * mm1, (Number(Boundary[1]) + Number(clipsPath[j + 2])) * mm1, (Number(Boundary[0]) + Number(clipsPath[j + 3])) * mm1, (Number(Boundary[1]) + Number(clipsPath[j + 4])) * mm1, (Number(Boundary[0]) + Number(clipsPath[j + 5])) * mm1, (Number(Boundary[1]) + Number(clipsPath[j + 6])) * mm1)
j += 7
break;
case "S":
j += 3 //还未实现
break;
case "A": //还未实现
j += 8
break;
case "C":
ctx.closePath()
j++
break;
default:
j++
break;
}
}
ctx.clip()//创建裁剪区域
let ctm = "33.8667 0 0 45.1556 0 0".split(" ")
ctx.setTransform(Number(ctm[0]) / Number(imgrect[2]), 0, 0, Number(ctm[3]) / Number(imgrect[3]), Math.ceil(Number(ctm[4]) * mm1),
Math.ceil(Number(ctm[5]) * mm1));
ctx.translate(Math.ceil(Number(imgrect[0]) * mm1), Math.ceil(Number(imgrect[1]) * mm1))
ctx.drawImage(img, 0, 0); //img为ResourceID="50"的图片
ctx.restore()
2.有些文字大小及位置显示不正常,同样参照一级注册建筑师注册证书
<ofd:TextObject ID="10" CTM="0.3528 0 0 0.3528 0 0" Boundary="105.5864 54.1973 71.1101 11.3002" Font="9" Size="28">
<ofd:FillColor Value="153 51 0"/>
<ofd:CGTransform CodePosition="0" CodeCount="7" GlyphCount="7">
<ofd:Glyphs>2 3 4 5 6 7 8</ofd:Glyphs>
</ofd:CGTransform>
<ofd:TextCode X="0" Y="26.208" DeltaX="28 28 28 28 28 28">一级注册建筑师</ofd:TextCode>
</ofd:TextObject>
对于如上带CTM变换矩阵、DeltaX或者DeltaY、还带ofd:CGTransform标签的文字,使用opentype库逐个把文字画出来,就不会出现文字大小不一的情况。下面是我的屎山代码。
if (ctm) {
//带CTM的情况
ctx.save()
ctx.translate(Math.ceil((Number(Boundary[0])) * mm1), Math.ceil((Number(Boundary[1])) * mm1))
ctx.transform(Number(ctm[0]), Number(ctm[1]), Number(ctm[2]), Number(ctm[3]), Math.ceil(Number(ctm[4]) * mm1), Math.ceil(Number(ctm[5]) * mm1));
let addx = Number(TextCode?.[':@']?.X ? TextCode?.[':@']?.X : 0) * mm1
let addy = Number(TextCode?.[':@']?.Y ? TextCode?.[':@']?.Y : 0) * mm1
//有逐字控制位置的情况
if (DeltaX || DeltaY) {
//有CGTransform标签,那么用opentype一个字一个字的画出来
if (CGTransform) {
for (let j = 0; j < textlength; j++) {
addx += Number(DeltaXarray[j]) * mm1
addy += Number(DeltaYarray[j]) * mm1
const font = await opentype.load(`./font/${FontsMap[textmessage?.[':@']?.['Font']]?.FontFile?.__text}`);
const path = font.getPath(String(TextCode?.['ofd:TextCode']?.[0]?.['#text'])[j], addx, addy, fontSize);
path.fill = ctx.fillStyle;
path.draw(ctx);
}
} else {
for (let j = 0; j < textlength; j++) {
addx += Number(DeltaXarray[j]) * mm1
addy += Number(DeltaYarray[j]) * mm1
textmessage?.[':@']?.Stroke ? ctx.strokeText(String(TextCode?.['ofd:TextCode']?.[0]?.['#text'])[j], addx, addy) : null;
ctx.fillText(String(TextCode?.['ofd:TextCode']?.[0]?.['#text'])[j], addx, addy);
}
}
} else {
textmessage?.[':@']?.Stroke ? ctx.strokeText(String(TextCode?.['ofd:TextCode']?.[0]?.['#text']), Number(TextCode?.[':@']?.X ? TextCode?.[':@']?.X : 0) * mm1, Number(TextCode?.[':@']?.Y ? TextCode?.[':@']?.Y : 0) * mm1) : null;
ctx.fillText(String(TextCode?.['ofd:TextCode']?.[0]?.['#text']), Number(TextCode?.[':@']?.X ? TextCode?.[':@']?.X : 0) * mm1, Number(TextCode?.[':@']?.Y ? TextCode?.[':@']?.Y : 0) * mm1);
}
ctx.restore()
} else {
//不带变换就正常的输出文字
let addx = (Number(Boundary[0]) + Number(TextCode?.[':@']?.X ? TextCode?.[':@']?.X : 0)) * mm1
let addy = (Number(Boundary[1]) + Number(TextCode?.[':@']?.Y ? TextCode?.[':@']?.Y : 0)) * mm1
//console.log("YYYYYYYY",addx,addy)
if (DeltaX || DeltaY) {
for (let j = 0; j < textlength; j++) {
ctx.save()
//ctx.scale(0.6, 1);
addx += Number(DeltaXarray[j]) * mm1
addy += Number(DeltaYarray[j]) * mm1
// 移动到当前字的位置
ctx.translate(addx, addy);
// 缩放文字
ctx.scale(textmessage?.[':@']?.HScale ? Number(textmessage?.[':@']?.HScale) : 1, 1);
textmessage?.[':@']?.Stroke ? ctx.strokeText(String(TextCode?.['ofd:TextCode']?.[0]?.['#text'])[j], 0, 0) : null;
ctx.fillText(String(TextCode?.['ofd:TextCode']?.[0]?.['#text'])[j], 0, 0);
ctx.restore()
}
} else {
textmessage?.[':@']?.Stroke ? ctx.strokeText(String(TextCode?.['ofd:TextCode']?.[0]?.['#text']), (Number(Boundary[0]) + Number(TextCode?.[':@']?.X ? TextCode?.[':@']?.X : 0)) * mm1, (Number(Boundary[1]) + Number(TextCode?.[':@']?.Y ? TextCode?.[':@']?.Y : 0)) * mm1) : null;
ctx.fillText(String(TextCode?.['ofd:TextCode']?.[0]?.['#text']), (Number(Boundary[0]) + Number(TextCode?.[':@']?.X ? TextCode?.[':@']?.X : 0)) * mm1, (Number(Boundary[1]) + Number(TextCode?.[':@']?.Y ? TextCode?.[':@']?.Y : 0)) * mm1);
ctx.restore()
}
}
最后提醒一下,PublicRes.xml,里面FontName的字体名字建议不要直接采用,例如这个里面SimSun是用了自带的字体文件font_11_11.ttf,如果向画图系统注册了字体font_11_11.ttf,如果没改名,再画出宋体,可能会直接调用font_11_11.ttf导致出现奇怪造型的字体。
<?xml version="1.0" encoding="UTF-8"?>
<ofd:Res xmlns:ofd="http://www.ofdspec.org/2016" BaseLoc="Res">
<ofd:Fonts>
<ofd:Font ID="9" FontName="SimHei">
<ofd:FontFile>font_9_9.ttf</ofd:FontFile>
</ofd:Font>
<ofd:Font ID="11" FontName="SimSun">
<ofd:FontFile>font_11_11.ttf</ofd:FontFile>
</ofd:Font>
<ofd:Font ID="15" FontName="SimSun">
<ofd:FontFile>font_11_11.ttf</ofd:FontFile>
</ofd:Font>
<ofd:Font ID="58" FontName="宋体" FamilyName="宋体"/>
</ofd:Fonts>
</ofd:Res>
因为我写的是node.js版本,对python不太属性,无法提供python代码
1、有些图片显示前先要对图片进行裁剪图片显示才会正常,easyofd在2025.10.22日测试未对图片裁剪,相关xml代码如下(因为隐私问题,作者可从一级注册建筑师注册证书获取到类似这段xml):
js版本的参照代码
2.有些文字大小及位置显示不正常,同样参照一级注册建筑师注册证书
对于如上带CTM变换矩阵、DeltaX或者DeltaY、还带ofd:CGTransform标签的文字,使用opentype库逐个把文字画出来,就不会出现文字大小不一的情况。下面是我的屎山代码。
最后提醒一下,PublicRes.xml,里面FontName的字体名字建议不要直接采用,例如这个里面SimSun是用了自带的字体文件font_11_11.ttf,如果向画图系统注册了字体font_11_11.ttf,如果没改名,再画出宋体,可能会直接调用font_11_11.ttf导致出现奇怪造型的字体。