ultisuite-backend/internal/api/richtext/paragraph_styles.go
R3D347HR4Y 0466a1c169
Some checks are pending
CI / Go tests (push) Waiting to run
CI / Integration tests (push) Waiting to run
CI / DB migrations (push) Waiting to run
wow
2026-06-11 01:22:52 +02:00

115 lines
5.4 KiB
Go

package richtext
import (
"context"
"encoding/json"
"fmt"
"strings"
)
const userParagraphStylesPath = "/.ultidrive/docs-user-paragraph-styles.json"
type UltiDocParagraphStyleDefinition struct {
ID string `json:"id"`
Name string `json:"name"`
Scope string `json:"scope"`
BasedOn string `json:"basedOn,omitempty"`
BlockType string `json:"blockType"`
Level *int `json:"level,omitempty"`
FontFamily string `json:"fontFamily,omitempty"`
FontSizePx float64 `json:"fontSizePx,omitempty"`
Bold *bool `json:"bold,omitempty"`
Italic *bool `json:"italic,omitempty"`
Underline *bool `json:"underline,omitempty"`
Color string `json:"color,omitempty"`
TextAlign string `json:"textAlign,omitempty"`
LineHeight float64 `json:"lineHeight,omitempty"`
SpaceBeforePt float64 `json:"spaceBeforePt,omitempty"`
SpaceAfterPt float64 `json:"spaceAfterPt,omitempty"`
}
type UltiDocParagraphStyles struct {
Definitions map[string]UltiDocParagraphStyleDefinition `json:"definitions"`
}
type DocsFontDefinition struct {
Name string `json:"name"`
Stack string `json:"stack"`
Source string `json:"source,omitempty"`
URL string `json:"url,omitempty"`
}
var defaultDocsFonts = []DocsFontDefinition{
{Name: "Arial", Stack: "Arial, Helvetica, sans-serif", Source: "system"},
{Name: "Calibri", Stack: "Calibri, Candara, Segoe, sans-serif", Source: "system"},
{Name: "Comic Sans MS", Stack: `"Comic Sans MS", cursive, sans-serif`, Source: "system"},
{Name: "Courier New", Stack: `"Courier New", Courier, monospace`, Source: "system"},
{Name: "Georgia", Stack: "Georgia, serif", Source: "system"},
{Name: "Times New Roman", Stack: `"Times New Roman", Times, serif`, Source: "system"},
{Name: "Trebuchet MS", Stack: `"Trebuchet MS", Helvetica, sans-serif`, Source: "system"},
{Name: "Verdana", Stack: "Verdana, Geneva, sans-serif", Source: "system"},
}
func defaultUltiDocParagraphStyles() UltiDocParagraphStyles {
level := func(n int) *int { v := n; return &v }
bold := func(v bool) *bool { return &v }
italic := func(v bool) *bool { return &v }
return UltiDocParagraphStyles{
Definitions: map[string]UltiDocParagraphStyleDefinition{
"normal": {ID: "normal", Name: "Normal", Scope: "document", BlockType: "paragraph", FontFamily: "Arial, Helvetica, sans-serif", FontSizePx: 11, LineHeight: 1.15},
"title": {ID: "title", Name: "Titre", Scope: "document", BlockType: "paragraph", FontFamily: "Arial, Helvetica, sans-serif", FontSizePx: 26, LineHeight: 1.15},
"subtitle": {ID: "subtitle", Name: "Sous-titre", Scope: "document", BlockType: "paragraph", FontFamily: "Arial, Helvetica, sans-serif", FontSizePx: 15, Color: "#666666", LineHeight: 1.15},
"heading1": {ID: "heading1", Name: "Titre 1", Scope: "document", BlockType: "heading", Level: level(1), FontFamily: "Arial, Helvetica, sans-serif", FontSizePx: 20, LineHeight: 1.15},
"heading2": {ID: "heading2", Name: "Titre 2", Scope: "document", BlockType: "heading", Level: level(2), FontFamily: "Arial, Helvetica, sans-serif", FontSizePx: 16, LineHeight: 1.15},
"heading3": {ID: "heading3", Name: "Titre 3", Scope: "document", BlockType: "heading", Level: level(3), FontFamily: "Arial, Helvetica, sans-serif", FontSizePx: 14, LineHeight: 1.15},
"heading4": {ID: "heading4", Name: "Titre 4", Scope: "document", BlockType: "heading", Level: level(4), FontFamily: "Arial, Helvetica, sans-serif", FontSizePx: 12, Bold: bold(true), LineHeight: 1.15},
"heading5": {ID: "heading5", Name: "Titre 5", Scope: "document", BlockType: "heading", Level: level(5), FontFamily: "Arial, Helvetica, sans-serif", FontSizePx: 11, Bold: bold(true), LineHeight: 1.15},
"heading6": {ID: "heading6", Name: "Titre 6", Scope: "document", BlockType: "heading", Level: level(6), FontFamily: "Arial, Helvetica, sans-serif", FontSizePx: 11, Italic: italic(true), LineHeight: 1.15},
},
}
}
func (s *Service) ListFonts(_ context.Context) []DocsFontDefinition {
out := make([]DocsFontDefinition, len(defaultDocsFonts))
copy(out, defaultDocsFonts)
return out
}
func (s *Service) LoadUserParagraphStyles(ctx context.Context, ncUser string) (UltiDocParagraphStyles, error) {
body, err := s.LoadDocument(ctx, ncUser, userParagraphStylesPath)
if err != nil || len(body) == 0 {
return UltiDocParagraphStyles{Definitions: map[string]UltiDocParagraphStyleDefinition{}}, nil
}
var styles UltiDocParagraphStyles
if err := json.Unmarshal(body, &styles); err != nil {
return UltiDocParagraphStyles{}, fmt.Errorf("parse user paragraph styles: %w", err)
}
if styles.Definitions == nil {
styles.Definitions = map[string]UltiDocParagraphStyleDefinition{}
}
return styles, nil
}
func (s *Service) SaveUserParagraphStyles(ctx context.Context, ncUser string, styles UltiDocParagraphStyles) error {
if styles.Definitions == nil {
styles.Definitions = map[string]UltiDocParagraphStyleDefinition{}
}
payload, err := json.MarshalIndent(styles, "", " ")
if err != nil {
return err
}
return s.nc.Upload(ctx, ncUser, userParagraphStylesPath, strings.NewReader(string(payload)), "application/json")
}
func (s *Service) loadParagraphStylesFromSidecar(ctx context.Context, ncUser, canonical string) (json.RawMessage, error) {
raw, err := s.LoadDocument(ctx, ncUser, canonical)
if err != nil || len(raw) == 0 {
return nil, err
}
doc, err := ParseUltiDoc(raw)
if err != nil || doc.ParagraphStyles == nil {
return nil, err
}
return json.Marshal(doc.ParagraphStyles)
}