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