A tool for archiving & converting scans of postcards, and information about them.
at main 114 lines 2.3 kB view raw
1package types 2 3import ( 4 "encoding/json" 5 "encoding/xml" 6 "fmt" 7 "html" 8 "sort" 9 "strings" 10) 11 12type AnnotatedText struct { 13 Text string `json:"text,omitempty" yaml:"text,omitempty"` 14 Annotations []Annotation `json:"annotations,omitempty" yaml:"annotations,omitempty"` 15} 16 17type Annotation struct { 18 Type AnnotationType `json:"type"` 19 Value string `json:"value,omitempty"` 20 // The *byte* count just before this annotation starts 21 Start uint `json:"start"` 22 // The *byte* count just after this annotation ends 23 End uint `json:"end"` 24} 25 26type AnnotationType string 27 28const ( 29 ATLocale AnnotationType = "locale" 30 ATEmphasis AnnotationType = "em" 31 ATStrong AnnotationType = "strong" 32 ATUnderline AnnotationType = "underline" 33) 34 35func (at AnnotatedText) MarshalXML(e *xml.Encoder, start xml.StartElement) error { 36 enc, err := json.Marshal(at) 37 if err != nil { 38 return err 39 } 40 return e.EncodeElement(string(enc), start) 41} 42 43type token struct { 44 isOpen bool 45 pos uint 46 Annotation 47} 48 49type byPos []token 50 51func (a byPos) Len() int { return len(a) } 52func (a byPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 53func (a byPos) Less(i, j int) bool { 54 return a[i].pos < a[j].pos 55} 56 57func (at AnnotatedText) HTML() string { 58 var tokens []token 59 for _, a := range at.Annotations { 60 tokens = append(tokens, token{ 61 isOpen: true, 62 pos: a.Start, 63 Annotation: a, 64 }, token{ 65 isOpen: false, 66 pos: a.End, 67 Annotation: a, 68 }) 69 } 70 71 sort.Sort(byPos(tokens)) 72 73 pos := uint(0) 74 var outHTML strings.Builder 75 for _, tok := range tokens { 76 if tok.pos > pos { 77 outHTML.WriteString(html.EscapeString(at.Text[pos:tok.pos])) 78 pos = tok.pos 79 } 80 outHTML.WriteString(tok.Annotation.HTMLTag(tok.isOpen)) 81 } 82 if pos < uint(len(at.Text)) { 83 outHTML.WriteString(html.EscapeString(at.Text[pos:])) 84 } 85 86 return outHTML.String() 87} 88 89var htmlMap = map[AnnotationType]string{ 90 ATEmphasis: "em", 91 ATStrong: "strong", 92 ATUnderline: "u", 93} 94 95func (a Annotation) HTMLTag(isOpen bool) string { 96 switch a.Type { 97 case ATLocale: 98 if isOpen { 99 return fmt.Sprintf(`<span lang="%s">`, a.Value) 100 } else { 101 return "</span>" 102 } 103 default: 104 tag, ok := htmlMap[a.Type] 105 if !ok { 106 return "" 107 } 108 if isOpen { 109 return fmt.Sprintf("<%s>", tag) 110 } else { 111 return fmt.Sprintf("</%s>", tag) 112 } 113 } 114}