fix: support windows certificate fonts
This commit is contained in:
@@ -26,6 +26,7 @@ if err != nil {
|
||||
- `COS_PUBLIC_BASE_URL`:证书公开访问基础地址。
|
||||
- `COS_SECRET_ID`:COS 访问密钥 ID。
|
||||
- `COS_SECRET_KEY`:COS 访问密钥。
|
||||
- `ZXCHAIN_CERTIFICATE_FONT_PATH`:可选。证书绘制使用的中文字体路径;Windows 可用 `C:/Windows/Fonts/msyh.ttc`。
|
||||
|
||||
## 存证和生成证书并一起返回
|
||||
|
||||
@@ -58,6 +59,7 @@ result, err := client.CreateSimpleHashAttestationCertificate(context.Background(
|
||||
CompletedAt: "2026.5.11 15:20:01",
|
||||
WorkPreviewURL: "https://your-cdn/path/work-preview.jpg",
|
||||
EvidenceHash: "作品hash", //可选参数,可以不传
|
||||
FontPath: "C:/Windows/Fonts/msyh.ttc", // 可选;Windows 推荐微软雅黑,或配置 ZXCHAIN_CERTIFICATE_FONT_PATH
|
||||
|
||||
CertificateStorage: &zxchainsdk.COSCertificateStorage{ //可选参数,可以不传
|
||||
BucketURL: os.Getenv("COS_BUCKET_URL"),
|
||||
@@ -88,6 +90,7 @@ fmt.Println("zxChainCertURL:", result.ZXChainCertURL)
|
||||
- `CompletedAt`:作品完成时间。
|
||||
- `WorkPreviewURL`:作品图片 URL。
|
||||
- `EvidenceHash`:可选。作品文件哈希;不传时 SDK 会从 `WorkPreviewURL` 下载内容并自行计算hash值。
|
||||
- `FontPath`:可选。中文字体路径;不传时 SDK 会自动查找 Windows/macOS/Linux 常见中文字体,也可以用 `ZXCHAIN_CERTIFICATE_FONT_PATH` 环境变量指定。Windows 常见可用路径:`C:/Windows/Fonts/msyh.ttc`、`C:/Windows/Fonts/simhei.ttf`、`C:/Windows/Fonts/simsun.ttc`。
|
||||
- `CertificateStorage`:可选。COS 上传配置。
|
||||
|
||||
返回字段:
|
||||
|
||||
@@ -393,16 +393,39 @@ func containImage(src image.Image, boxWidth, boxHeight int) image.Image {
|
||||
|
||||
func resolveCertificateFont(explicitPath string) (string, error) {
|
||||
if explicitPath != "" {
|
||||
if _, err := os.Stat(explicitPath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err := gg.LoadFontFace(explicitPath, 25); err != nil {
|
||||
return "", fmt.Errorf("load certificate font %s: %w", explicitPath, err)
|
||||
}
|
||||
return explicitPath, nil
|
||||
return validateCertificateFont(explicitPath)
|
||||
}
|
||||
|
||||
if envPath := os.Getenv("ZXCHAIN_CERTIFICATE_FONT_PATH"); envPath != "" {
|
||||
return validateCertificateFont(envPath)
|
||||
}
|
||||
|
||||
candidates := certificateFontCandidates()
|
||||
for _, candidate := range candidates {
|
||||
if fontPath, err := validateCertificateFont(candidate); err == nil {
|
||||
return fontPath, nil
|
||||
}
|
||||
}
|
||||
return "", errors.New("missing certificate font, set HashCertificateRequest.FontPath or ZXCHAIN_CERTIFICATE_FONT_PATH to a Chinese TTF/TTC/OTF font")
|
||||
}
|
||||
|
||||
func validateCertificateFont(fontPath string) (string, error) {
|
||||
if _, err := os.Stat(fontPath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err := gg.LoadFontFace(fontPath, 25); err != nil {
|
||||
return "", fmt.Errorf("load certificate font %s: %w", fontPath, err)
|
||||
}
|
||||
return fontPath, nil
|
||||
}
|
||||
|
||||
func certificateFontCandidates() []string {
|
||||
candidates := []string{
|
||||
"C:/Windows/Fonts/msyh.ttc",
|
||||
"C:/Windows/Fonts/msyhbd.ttc",
|
||||
"C:/Windows/Fonts/simhei.ttf",
|
||||
"C:/Windows/Fonts/simsun.ttc",
|
||||
"C:/Windows/Fonts/simkai.ttf",
|
||||
"/System/Library/Fonts/Supplemental/Arial Unicode.ttf",
|
||||
"/System/Library/Fonts/Supplemental/Arial.ttf",
|
||||
"/System/Library/Fonts/Supplemental/Microsoft Sans Serif.ttf",
|
||||
@@ -414,15 +437,14 @@ func resolveCertificateFont(explicitPath string) (string, error) {
|
||||
"/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
|
||||
"/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc",
|
||||
}
|
||||
for _, candidate := range candidates {
|
||||
if _, err := os.Stat(candidate); err != nil {
|
||||
continue
|
||||
}
|
||||
if _, err := gg.LoadFontFace(candidate, 25); err == nil {
|
||||
return candidate, nil
|
||||
|
||||
if windowsDir := firstNonEmpty(os.Getenv("WINDIR"), os.Getenv("SystemRoot")); windowsDir != "" {
|
||||
windowsDir = strings.TrimRight(windowsDir, `\/`)
|
||||
for _, name := range []string{"msyh.ttc", "msyhbd.ttc", "simhei.ttf", "simsun.ttc", "simkai.ttf"} {
|
||||
candidates = append(candidates, windowsDir+"/Fonts/"+name)
|
||||
}
|
||||
}
|
||||
return "", errors.New("missing certificate font, set HashCertificateRequest.FontPath to a Chinese TTF/TTC font")
|
||||
return candidates
|
||||
}
|
||||
|
||||
func certificateImageObjectKey(evidenceID, certNo string) string {
|
||||
|
||||
@@ -62,6 +62,7 @@ type SimpleHashAttestationCertificateRequest struct {
|
||||
CompletedAt string `json:"completed_at"` // CompletedAt 表示作品完成时间
|
||||
WorkPreviewURL string `json:"work_preview_url"` // WorkPreviewURL 表示作品预览图 URL
|
||||
EvidenceHash string `json:"evidence_hash,omitempty"` // EvidenceHash 表示作品文件哈希,可选。如果不传,SDK 将从 WorkPreviewURL 下载并计算哈希
|
||||
FontPath string `json:"font_path,omitempty"` // FontPath 表示用于绘制证书的中文字体路径,可选
|
||||
|
||||
// 可选:如果不传,将使用内置默认值
|
||||
CertificateStorage *COSCertificateStorage `json:"certificate_storage,omitempty"` // CertificateStorage 表示 COS 上传配置
|
||||
@@ -204,6 +205,7 @@ func (c *SDKClient) CreateSimpleHashAttestationCertificate(ctx context.Context,
|
||||
CompletedAt: req.CompletedAt,
|
||||
WorkPreviewURL: req.WorkPreviewURL,
|
||||
EvidenceHash: evidenceHash,
|
||||
FontPath: req.FontPath,
|
||||
CertificateStorage: req.CertificateStorage,
|
||||
}
|
||||
|
||||
@@ -372,7 +374,7 @@ func UploadCertificatePNGToCOS(ctx context.Context, pngBytes []byte, defaultObje
|
||||
_, err = client.Object.Put(ctx, objectKey, bytes.NewReader(pngBytes), &cos.ObjectPutOptions{
|
||||
ObjectPutHeaderOptions: &cos.ObjectPutHeaderOptions{
|
||||
ContentType: "image/png",
|
||||
ContentLength: int64(len(pngBytes)),
|
||||
ContentLength: len(pngBytes),
|
||||
CacheControl: "public, max-age=31536000, immutable",
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user