feat: add characters
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/base64"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// برخی ابزارها (مثلِ خروجیِ AI) یک PNG را داخلِ یک <svg> با base64 قرار میدهند.
|
||||
// flutter_svg نمیتواند این «SVGِ رستری» را رندر کند، پس آن را به PNG واقعی تبدیل
|
||||
// میکنیم.
|
||||
var rasterInSVG = regexp.MustCompile(`data:image/(png|jpe?g);base64,([A-Za-z0-9+/=\s]+)`)
|
||||
|
||||
// extractEmbeddedRaster اگر محتوای SVG صرفاً یک تصویرِ رستریِ base64 باشد، آن را
|
||||
// استخراج میکند (بایتهای خام + پسوند). برای SVGهای واقعیِ برداری false برمیگرداند.
|
||||
func extractEmbeddedRaster(svg []byte) (data []byte, ext string, ok bool) {
|
||||
m := rasterInSVG.FindSubmatch(svg)
|
||||
if m == nil {
|
||||
return nil, "", false
|
||||
}
|
||||
ext = "png"
|
||||
if strings.HasPrefix(string(m[1]), "jp") {
|
||||
ext = "jpg"
|
||||
}
|
||||
b64 := regexp.MustCompile(`\s`).ReplaceAll(m[2], nil)
|
||||
raw, err := base64.StdEncoding.DecodeString(string(b64))
|
||||
if err != nil || len(raw) == 0 {
|
||||
return nil, "", false
|
||||
}
|
||||
return raw, ext, true
|
||||
}
|
||||
|
||||
// convertRasterAvatars فایلهای آواتارِ svg را که در واقع رستر هستند به png/jpg
|
||||
// تبدیل میکند و img_ext را در DB بهروزرسانی میکند. یکبار در startup اجرا میشود
|
||||
// (idempotent). برای رفعِ آواتارهایی که قبلاً بهصورتِ «svgِ رستری» آپلود شدهاند.
|
||||
func convertRasterAvatars(ctx context.Context, db *sql.DB, dir string) {
|
||||
if dir == "" {
|
||||
return
|
||||
}
|
||||
rows, err := db.QueryContext(ctx, `SELECT id FROM avatars WHERE img_ext = 'svg'`)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var ids []string
|
||||
for rows.Next() {
|
||||
var id string
|
||||
if rows.Scan(&id) == nil {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
}
|
||||
rows.Close()
|
||||
|
||||
for _, id := range ids {
|
||||
svgPath := filepath.Join(dir, id+".svg")
|
||||
content, err := os.ReadFile(svgPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
raw, ext, ok := extractEmbeddedRaster(content)
|
||||
if !ok {
|
||||
continue // SVGِ واقعیِ برداری — دست نزن.
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(dir, id+"."+ext), raw, 0o644); err != nil {
|
||||
continue
|
||||
}
|
||||
_ = os.Remove(svgPath)
|
||||
_, _ = db.ExecContext(ctx, `UPDATE avatars SET img_ext = ? WHERE id = ?`, ext, id)
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertRasterAvatars نسخهی عمومی برای فراخوانی از main در startup.
|
||||
func ConvertRasterAvatars(ctx context.Context, db *sql.DB, dir string) {
|
||||
convertRasterAvatars(ctx, db, dir)
|
||||
}
|
||||
Reference in New Issue
Block a user