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) }