docx.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. package office
  2. /*
  3. docx.go - shared types for the Word (.docx) converters used by the
  4. Docs webapp (src/web/Office/docs). The JSON model mirrors the Docs
  5. body schema documented in docs.js:
  6. { "html": "<p>...</p>", "page": { "size": "A4", "orientation":
  7. "portrait", "margins": {top,right,bottom,left} }, // mm
  8. "header": "text", "footer": "text", "pageNumbers": false }
  9. Legacy binary .doc (Word 97) is intentionally not supported.
  10. */
  11. import (
  12. "encoding/json"
  13. "errors"
  14. )
  15. // Document is the Docs document body
  16. type Document struct {
  17. HTML string `json:"html"`
  18. Page *PageConf `json:"page,omitempty"`
  19. Header string `json:"header,omitempty"`
  20. Footer string `json:"footer,omitempty"`
  21. PageNumbers bool `json:"pageNumbers,omitempty"`
  22. HFMode string `json:"hfMode,omitempty"` // header/footer repetition
  23. }
  24. // Header/footer repetition modes (body.hfMode); the empty string means
  25. // HFModeAll, which is what documents written before the setting existed get
  26. const (
  27. HFModeAll = "all" // every page carries the same text
  28. HFModeExceptFirst = "except-first" // every page but the first
  29. HFModeNone = "none" // no header/footer text at all
  30. )
  31. // hfOnPage reports whether the header/footer text shows on the given
  32. // 1-based page number under mode
  33. func hfOnPage(mode string, page int) bool {
  34. switch mode {
  35. case HFModeNone:
  36. return false
  37. case HFModeExceptFirst:
  38. return page > 1
  39. }
  40. return true
  41. }
  42. // hfPageNumberOn reports whether the page counter shows on the given page:
  43. // it is its own page-setup option, but a suppressed first page suppresses
  44. // its number too (the same thing Word's titlePg does)
  45. func hfPageNumberOn(doc *Document, page int) bool {
  46. return doc.PageNumbers && (doc.HFMode != HFModeExceptFirst || page > 1)
  47. }
  48. // PageConf holds page geometry (margins in millimetres)
  49. type PageConf struct {
  50. Size string `json:"size,omitempty"` // A4 | Letter | Legal
  51. Orientation string `json:"orientation,omitempty"` // portrait | landscape
  52. Margins *MarginsMM `json:"margins,omitempty"`
  53. Columns int `json:"columns,omitempty"` // text columns (0/1 = single)
  54. ColGap float64 `json:"colGap,omitempty"` // gap between columns, mm
  55. }
  56. // MarginsMM holds page margins in millimetres
  57. type MarginsMM struct {
  58. Top float64 `json:"top"`
  59. Right float64 `json:"right"`
  60. Bottom float64 `json:"bottom"`
  61. Left float64 `json:"left"`
  62. }
  63. // ParseDocumentJSON decodes the Docs body JSON into a Document
  64. func ParseDocumentJSON(jsonStr string) (*Document, error) {
  65. d := Document{}
  66. if err := json.Unmarshal([]byte(jsonStr), &d); err != nil {
  67. return nil, errors.New("invalid document JSON: " + err.Error())
  68. }
  69. return &d, nil
  70. }
  71. // DocumentToJSON serializes a Document back to the Docs body JSON
  72. func DocumentToJSON(d *Document) (string, error) {
  73. if d == nil {
  74. return "", errors.New("nil document")
  75. }
  76. out, err := json.Marshal(d)
  77. if err != nil {
  78. return "", err
  79. }
  80. return string(out), nil
  81. }
  82. /* ---------- unit helpers ---------- */
  83. // twips (twentieths of a point): 1 mm = 56.6929 twips
  84. func mmToTwips(mm float64) int { return int(mm * 1440.0 / 25.4) }
  85. func twipsToMm(tw int) float64 { return float64(tw) * 25.4 / 1440.0 }
  86. // page sizes in twips (portrait)
  87. var pageSizesTwips = map[string][2]int{
  88. "A4": {11906, 16838},
  89. "Letter": {12240, 15840},
  90. "Legal": {12240, 20160},
  91. }
  92. // px (96dpi) -> half-points for w:sz
  93. func pxToHalfPoints(px float64) int { return int(px * 0.75 * 2) }
  94. func halfPointsToPx(hp float64) float64 {
  95. return hp / 2.0 / 0.75
  96. }