uniword 1.0.8 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/data/themes/office_format_scheme.xml +137 -0
  3. data/data/themes/office_theme.xml +2 -0
  4. data/lib/uniword/builder/comment_builder.rb +2 -1
  5. data/lib/uniword/builder/footnote_builder.rb +4 -2
  6. data/lib/uniword/builder/run_builder.rb +5 -0
  7. data/lib/uniword/document_input.rb +7 -0
  8. data/lib/uniword/document_writer.rb +1 -5
  9. data/lib/uniword/docx/package.rb +18 -0
  10. data/lib/uniword/docx/package_serialization.rb +38 -0
  11. data/lib/uniword/docx/reconciler.rb +159 -4
  12. data/lib/uniword/format_converter.rb +4 -19
  13. data/lib/uniword/has_run_position.rb +9 -0
  14. data/lib/uniword/lazy_loader.rb +1 -1
  15. data/lib/uniword/math/o_math.rb +3 -0
  16. data/lib/uniword/mhtml/document.rb +18 -0
  17. data/lib/uniword/properties/bold.rb +3 -33
  18. data/lib/uniword/properties/boolean_element_factory.rb +66 -0
  19. data/lib/uniword/properties/boolean_formatting.rb +15 -215
  20. data/lib/uniword/properties/italic.rb +3 -33
  21. data/lib/uniword/properties.rb +8 -3
  22. data/lib/uniword/schema/schema_loader.rb +1 -1
  23. data/lib/uniword/transformation/mhtml_element_renderer.rb +1 -1
  24. data/lib/uniword/version.rb +1 -1
  25. data/lib/uniword/wordprocessingml/attached_template.rb +18 -0
  26. data/lib/uniword/wordprocessingml/body.rb +18 -36
  27. data/lib/uniword/wordprocessingml/brk_bin.rb +17 -0
  28. data/lib/uniword/wordprocessingml/brk_bin_sub.rb +17 -0
  29. data/lib/uniword/wordprocessingml/character_spacing_control.rb +17 -0
  30. data/lib/uniword/wordprocessingml/clr_scheme_mapping.rb +39 -0
  31. data/lib/uniword/wordprocessingml/compat.rb +16 -0
  32. data/lib/uniword/wordprocessingml/decimal_symbol.rb +17 -0
  33. data/lib/uniword/wordprocessingml/def_jc.rb +17 -0
  34. data/lib/uniword/wordprocessingml/default_tab_stop.rb +17 -0
  35. data/lib/uniword/wordprocessingml/disp_def.rb +14 -0
  36. data/lib/uniword/wordprocessingml/do_not_display_page_boundaries.rb +14 -0
  37. data/lib/uniword/wordprocessingml/do_not_include_subdocs_in_stats.rb +17 -0
  38. data/lib/uniword/wordprocessingml/doc_vars.rb +36 -0
  39. data/lib/uniword/wordprocessingml/document_root.rb +17 -5
  40. data/lib/uniword/wordprocessingml/endnote_pr.rb +18 -0
  41. data/lib/uniword/wordprocessingml/endnotes.rb +18 -0
  42. data/lib/uniword/wordprocessingml/even_and_odd_headers.rb +17 -0
  43. data/lib/uniword/wordprocessingml/footnote_pos.rb +17 -0
  44. data/lib/uniword/wordprocessingml/footnote_pr.rb +20 -0
  45. data/lib/uniword/wordprocessingml/footnotes.rb +18 -0
  46. data/lib/uniword/wordprocessingml/hdr_shape_defaults.rb +19 -0
  47. data/lib/uniword/wordprocessingml/hyperlink.rb +3 -0
  48. data/lib/uniword/wordprocessingml/hyphenation_zone.rb +20 -0
  49. data/lib/uniword/wordprocessingml/int_lim.rb +17 -0
  50. data/lib/uniword/wordprocessingml/l_margin.rb +17 -0
  51. data/lib/uniword/wordprocessingml/list_separator.rb +17 -0
  52. data/lib/uniword/wordprocessingml/math_font.rb +17 -0
  53. data/lib/uniword/wordprocessingml/math_pr.rb +49 -0
  54. data/lib/uniword/wordprocessingml/mirror_margins.rb +17 -0
  55. data/lib/uniword/wordprocessingml/nary_lim.rb +17 -0
  56. data/lib/uniword/wordprocessingml/proof_state.rb +19 -0
  57. data/lib/uniword/wordprocessingml/r_margin.rb +17 -0
  58. data/lib/uniword/wordprocessingml/rsid.rb +17 -0
  59. data/lib/uniword/wordprocessingml/rsid_root.rb +17 -0
  60. data/lib/uniword/wordprocessingml/rsids.rb +22 -0
  61. data/lib/uniword/wordprocessingml/settings.rb +45 -511
  62. data/lib/uniword/wordprocessingml/shape_defaults.rb +21 -0
  63. data/lib/uniword/wordprocessingml/small_frac.rb +17 -0
  64. data/lib/uniword/wordprocessingml/style_pane_format_filter.rb +50 -0
  65. data/lib/uniword/wordprocessingml/style_pane_sort_method.rb +20 -0
  66. data/lib/uniword/wordprocessingml/table_cell.rb +6 -0
  67. data/lib/uniword/wordprocessingml/table_row_properties.rb +2 -0
  68. data/lib/uniword/wordprocessingml/theme_font_lang.rb +19 -0
  69. data/lib/uniword/wordprocessingml/w14_doc_id.rb +17 -0
  70. data/lib/uniword/wordprocessingml/w15_chart_tracking_ref_based.rb +14 -0
  71. data/lib/uniword/wordprocessingml/w15_doc_id.rb +17 -0
  72. data/lib/uniword/wordprocessingml/wrap_indent.rb +17 -0
  73. data/lib/uniword/wordprocessingml.rb +42 -56
  74. data/lib/uniword.rb +12 -5
  75. metadata +52 -3
  76. data/lib/uniword/ooxml/types/on_off_type.rb +0 -64
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a62022223f8643943947659aae4d0e27b5d8566cab7486e0c688566cc0428208
4
- data.tar.gz: 61727290e9fac98b2e4cf3b43bb1fd70f09c4b9648206eb861d1bc3766b964d9
3
+ metadata.gz: a7feae85851d80946524005c3cc6419419d492c717e559628093494ee7532cd9
4
+ data.tar.gz: eba8d3d3d433aa158b5a4214659e6afd0e12d74e3fada13997da066021a78f9d
5
5
  SHA512:
6
- metadata.gz: f99053000864fae5910ee3c34f07504daf4afb061093aced4a5589f0a80c335cca44fa48a72bad5f1725d87a82346dc5abc577c14e6238875a9a175bfd33c788
7
- data.tar.gz: cf9546e5ad9b15a3d4973785b922589d29bbd8bea8a34c75b1012f10a2a9190988abaccae1c5e0f41217589fc9f27151342210373ca9a00c355f86ad2218b15c
6
+ metadata.gz: 940e11bb305505cd3e6acdc6e9cca98e002d151a0f75e7c8872d613353b4c5d85b424062a661572b14474c342b62db3072d2cc081cb9e924f95bb8fdf80e6c39
7
+ data.tar.gz: 3ca3c9bea8133f7a18e64897b687ef8dd10843c85aa2e4422dabceed5152ae5a5f9cce56fdff8999f03e734f8510b919732f9f5b5b0d5ac0e9470c8dd0db11fb
@@ -0,0 +1,137 @@
1
+ <a:fmtScheme name="Office">
2
+ <a:fillStyleLst>
3
+ <a:solidFill>
4
+ <a:schemeClr val="phClr"/>
5
+ </a:solidFill>
6
+ <a:gradFill rotWithShape="1">
7
+ <a:gsLst>
8
+ <a:gs pos="0">
9
+ <a:schemeClr val="phClr">
10
+ <a:lumMod val="110000"/>
11
+ <a:satMod val="105000"/>
12
+ <a:tint val="67000"/>
13
+ </a:schemeClr>
14
+ </a:gs>
15
+ <a:gs pos="50000">
16
+ <a:schemeClr val="phClr">
17
+ <a:lumMod val="105000"/>
18
+ <a:satMod val="103000"/>
19
+ <a:tint val="73000"/>
20
+ </a:schemeClr>
21
+ </a:gs>
22
+ <a:gs pos="100000">
23
+ <a:schemeClr val="phClr">
24
+ <a:lumMod val="105000"/>
25
+ <a:satMod val="109000"/>
26
+ <a:tint val="81000"/>
27
+ </a:schemeClr>
28
+ </a:gs>
29
+ </a:gsLst>
30
+ <a:lin ang="5400000" scaled="0"/>
31
+ </a:gradFill>
32
+ <a:gradFill rotWithShape="1">
33
+ <a:gsLst>
34
+ <a:gs pos="0">
35
+ <a:schemeClr val="phClr">
36
+ <a:satMod val="103000"/>
37
+ <a:lumMod val="102000"/>
38
+ <a:tint val="94000"/>
39
+ </a:schemeClr>
40
+ </a:gs>
41
+ <a:gs pos="50000">
42
+ <a:schemeClr val="phClr">
43
+ <a:satMod val="110000"/>
44
+ <a:lumMod val="100000"/>
45
+ <a:shade val="100000"/>
46
+ </a:schemeClr>
47
+ </a:gs>
48
+ <a:gs pos="100000">
49
+ <a:schemeClr val="phClr">
50
+ <a:lumMod val="99000"/>
51
+ <a:satMod val="120000"/>
52
+ <a:shade val="78000"/>
53
+ </a:schemeClr>
54
+ </a:gs>
55
+ </a:gsLst>
56
+ <a:lin ang="5400000" scaled="0"/>
57
+ </a:gradFill>
58
+ </a:fillStyleLst>
59
+ <a:lnStyleLst>
60
+ <a:ln w="12700" cap="flat" cmpd="sng" algn="ctr">
61
+ <a:solidFill>
62
+ <a:schemeClr val="phClr"/>
63
+ </a:solidFill>
64
+ <a:prstDash val="solid"/>
65
+ <a:miter lim="800000"/>
66
+ </a:ln>
67
+ <a:ln w="19050" cap="flat" cmpd="sng" algn="ctr">
68
+ <a:solidFill>
69
+ <a:schemeClr val="phClr"/>
70
+ </a:solidFill>
71
+ <a:prstDash val="solid"/>
72
+ <a:miter lim="800000"/>
73
+ </a:ln>
74
+ <a:ln w="25400" cap="flat" cmpd="sng" algn="ctr">
75
+ <a:solidFill>
76
+ <a:schemeClr val="phClr"/>
77
+ </a:solidFill>
78
+ <a:prstDash val="solid"/>
79
+ <a:miter lim="800000"/>
80
+ </a:ln>
81
+ </a:lnStyleLst>
82
+ <a:effectStyleLst>
83
+ <a:effectStyle>
84
+ <a:effectLst/>
85
+ </a:effectStyle>
86
+ <a:effectStyle>
87
+ <a:effectLst/>
88
+ </a:effectStyle>
89
+ <a:effectStyle>
90
+ <a:effectLst>
91
+ <a:outerShdw blurRad="57150" dist="19050" dir="5400000" algn="ctr" rotWithShape="0">
92
+ <a:srgbClr val="000000">
93
+ <a:alpha val="63000"/>
94
+ </a:srgbClr>
95
+ </a:outerShdw>
96
+ </a:effectLst>
97
+ </a:effectStyle>
98
+ </a:effectStyleLst>
99
+ <a:bgFillStyleLst>
100
+ <a:solidFill>
101
+ <a:schemeClr val="phClr"/>
102
+ </a:solidFill>
103
+ <a:solidFill>
104
+ <a:schemeClr val="phClr">
105
+ <a:tint val="95000"/>
106
+ <a:satMod val="170000"/>
107
+ </a:schemeClr>
108
+ </a:solidFill>
109
+ <a:gradFill rotWithShape="1">
110
+ <a:gsLst>
111
+ <a:gs pos="0">
112
+ <a:schemeClr val="phClr">
113
+ <a:tint val="93000"/>
114
+ <a:satMod val="150000"/>
115
+ <a:shade val="98000"/>
116
+ <a:lumMod val="102000"/>
117
+ </a:schemeClr>
118
+ </a:gs>
119
+ <a:gs pos="50000">
120
+ <a:schemeClr val="phClr">
121
+ <a:tint val="98000"/>
122
+ <a:satMod val="130000"/>
123
+ <a:shade val="90000"/>
124
+ <a:lumMod val="103000"/>
125
+ </a:schemeClr>
126
+ </a:gs>
127
+ <a:gs pos="100000">
128
+ <a:schemeClr val="phClr">
129
+ <a:shade val="63000"/>
130
+ <a:satMod val="120000"/>
131
+ </a:schemeClr>
132
+ </a:gs>
133
+ </a:gsLst>
134
+ <a:lin ang="5400000" scaled="0"/>
135
+ </a:gradFill>
136
+ </a:bgFillStyleLst>
137
+ </a:fmtScheme>
@@ -0,0 +1,2 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2
+ <a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme"><a:themeElements><a:clrScheme name="Office"><a:dk1><a:sysClr val="windowText" lastClr="000000"/></a:dk1><a:lt1><a:sysClr val="window" lastClr="FFFFFF"/></a:lt1><a:dk2><a:srgbClr val="0E2841"/></a:dk2><a:lt2><a:srgbClr val="E8E8E8"/></a:lt2><a:accent1><a:srgbClr val="156082"/></a:accent1><a:accent2><a:srgbClr val="E97132"/></a:accent2><a:accent3><a:srgbClr val="196B24"/></a:accent3><a:accent4><a:srgbClr val="0F9ED5"/></a:accent4><a:accent5><a:srgbClr val="A02B93"/></a:accent5><a:accent6><a:srgbClr val="4EA72E"/></a:accent6><a:hlink><a:srgbClr val="467886"/></a:hlink><a:folHlink><a:srgbClr val="96607D"/></a:folHlink></a:clrScheme><a:fontScheme name="Office"><a:majorFont><a:latin typeface="Aptos Display" panose="02110004020202020204"/><a:ea typeface=""/><a:cs typeface=""/><a:font script="Jpan" typeface="游ゴシック Light"/><a:font script="Hang" typeface="맑은 고딕"/><a:font script="Hans" typeface="等线 Light"/><a:font script="Hant" typeface="新細明體"/><a:font script="Arab" typeface="Times New Roman"/><a:font script="Hebr" typeface="Times New Roman"/><a:font script="Thai" typeface="Angsana New"/><a:font script="Ethi" typeface="Nyala"/><a:font script="Beng" typeface="Vrinda"/><a:font script="Gujr" typeface="Shruti"/><a:font script="Khmr" typeface="MoolBoran"/><a:font script="Knda" typeface="Tunga"/><a:font script="Guru" typeface="Raavi"/><a:font script="Cans" typeface="Euphemia"/><a:font script="Cher" typeface="Plantagenet Cherokee"/><a:font script="Yiii" typeface="Microsoft Yi Baiti"/><a:font script="Tibt" typeface="Microsoft Himalaya"/><a:font script="Thaa" typeface="MV Boli"/><a:font script="Deva" typeface="Mangal"/><a:font script="Telu" typeface="Gautami"/><a:font script="Taml" typeface="Latha"/><a:font script="Syrc" typeface="Estrangelo Edessa"/><a:font script="Orya" typeface="Kalinga"/><a:font script="Mlym" typeface="Kartika"/><a:font script="Laoo" typeface="DokChampa"/><a:font script="Sinh" typeface="Iskoola Pota"/><a:font script="Mong" typeface="Mongolian Baiti"/><a:font script="Viet" typeface="Times New Roman"/><a:font script="Uigh" typeface="Microsoft Uighur"/><a:font script="Geor" typeface="Sylfaen"/><a:font script="Armn" typeface="Arial"/><a:font script="Bugi" typeface="Leelawadee UI"/><a:font script="Bopo" typeface="Microsoft JhengHei"/><a:font script="Java" typeface="Javanese Text"/><a:font script="Lisu" typeface="Segoe UI"/><a:font script="Mymr" typeface="Myanmar Text"/><a:font script="Nkoo" typeface="Ebrima"/><a:font script="Olck" typeface="Nirmala UI"/><a:font script="Osma" typeface="Ebrima"/><a:font script="Phag" typeface="Phagspa"/><a:font script="Syrn" typeface="Estrangelo Edessa"/><a:font script="Syrj" typeface="Estrangelo Edessa"/><a:font script="Syre" typeface="Estrangelo Edessa"/><a:font script="Sora" typeface="Nirmala UI"/><a:font script="Tale" typeface="Microsoft Tai Le"/><a:font script="Talu" typeface="Microsoft New Tai Lue"/><a:font script="Tfng" typeface="Ebrima"/></a:majorFont><a:minorFont><a:latin typeface="Aptos" panose="02110004020202020204"/><a:ea typeface=""/><a:cs typeface=""/><a:font script="Jpan" typeface="游明朝"/><a:font script="Hang" typeface="맑은 고딕"/><a:font script="Hans" typeface="等线"/><a:font script="Hant" typeface="新細明體"/><a:font script="Arab" typeface="Arial"/><a:font script="Hebr" typeface="Arial"/><a:font script="Thai" typeface="Cordia New"/><a:font script="Ethi" typeface="Nyala"/><a:font script="Beng" typeface="Vrinda"/><a:font script="Gujr" typeface="Shruti"/><a:font script="Khmr" typeface="DaunPenh"/><a:font script="Knda" typeface="Tunga"/><a:font script="Guru" typeface="Raavi"/><a:font script="Cans" typeface="Euphemia"/><a:font script="Cher" typeface="Plantagenet Cherokee"/><a:font script="Yiii" typeface="Microsoft Yi Baiti"/><a:font script="Tibt" typeface="Microsoft Himalaya"/><a:font script="Thaa" typeface="MV Boli"/><a:font script="Deva" typeface="Mangal"/><a:font script="Telu" typeface="Gautami"/><a:font script="Taml" typeface="Latha"/><a:font script="Syrc" typeface="Estrangelo Edessa"/><a:font script="Orya" typeface="Kalinga"/><a:font script="Mlym" typeface="Kartika"/><a:font script="Laoo" typeface="DokChampa"/><a:font script="Sinh" typeface="Iskoola Pota"/><a:font script="Mong" typeface="Mongolian Baiti"/><a:font script="Viet" typeface="Arial"/><a:font script="Uigh" typeface="Microsoft Uighur"/><a:font script="Geor" typeface="Sylfaen"/><a:font script="Armn" typeface="Arial"/><a:font script="Bugi" typeface="Leelawadee UI"/><a:font script="Bopo" typeface="Microsoft JhengHei"/><a:font script="Java" typeface="Javanese Text"/><a:font script="Lisu" typeface="Segoe UI"/><a:font script="Mymr" typeface="Myanmar Text"/><a:font script="Nkoo" typeface="Ebrima"/><a:font script="Olck" typeface="Nirmala UI"/><a:font script="Osma" typeface="Ebrima"/><a:font script="Phag" typeface="Phagspa"/><a:font script="Syrn" typeface="Estrangelo Edessa"/><a:font script="Syrj" typeface="Estrangelo Edessa"/><a:font script="Syre" typeface="Estrangelo Edessa"/><a:font script="Sora" typeface="Nirmala UI"/><a:font script="Tale" typeface="Microsoft Tai Le"/><a:font script="Talu" typeface="Microsoft New Tai Lue"/><a:font script="Tfng" typeface="Ebrima"/></a:minorFont></a:fontScheme><a:fmtScheme name="Office"><a:fillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:lumMod val="110000"/><a:satMod val="105000"/><a:tint val="67000"/></a:schemeClr></a:gs><a:gs pos="50000"><a:schemeClr val="phClr"><a:lumMod val="105000"/><a:satMod val="103000"/><a:tint val="73000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:lumMod val="105000"/><a:satMod val="109000"/><a:tint val="81000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="5400000" scaled="0"/></a:gradFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:satMod val="103000"/><a:lumMod val="102000"/><a:tint val="94000"/></a:schemeClr></a:gs><a:gs pos="50000"><a:schemeClr val="phClr"><a:satMod val="110000"/><a:lumMod val="100000"/><a:shade val="100000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:lumMod val="99000"/><a:satMod val="120000"/><a:shade val="78000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="5400000" scaled="0"/></a:gradFill></a:fillStyleLst><a:lnStyleLst><a:ln w="12700" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/><a:miter lim="800000"/></a:ln><a:ln w="19050" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/><a:miter lim="800000"/></a:ln><a:ln w="25400" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/><a:miter lim="800000"/></a:ln></a:lnStyleLst><a:effectStyleLst><a:effectStyle><a:effectLst/></a:effectStyle><a:effectStyle><a:effectLst/></a:effectStyle><a:effectStyle><a:effectLst><a:outerShdw blurRad="57150" dist="19050" dir="5400000" algn="ctr" rotWithShape="0"><a:srgbClr val="000000"><a:alpha val="63000"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle></a:effectStyleLst><a:bgFillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:solidFill><a:schemeClr val="phClr"><a:tint val="95000"/><a:satMod val="170000"/></a:schemeClr></a:solidFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="93000"/><a:satMod val="150000"/><a:shade val="98000"/><a:lumMod val="102000"/></a:schemeClr></a:gs><a:gs pos="50000"><a:schemeClr val="phClr"><a:tint val="98000"/><a:satMod val="130000"/><a:shade val="90000"/><a:lumMod val="103000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="63000"/><a:satMod val="120000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="5400000" scaled="0"/></a:gradFill></a:bgFillStyleLst></a:fmtScheme></a:themeElements><a:objectDefaults><a:lnDef><a:spPr/><a:bodyPr/><a:lstStyle/><a:style><a:lnRef idx="2"><a:schemeClr val="accent1"/></a:lnRef><a:fillRef idx="0"><a:schemeClr val="accent1"/></a:fillRef><a:effectRef idx="1"><a:schemeClr val="accent1"/></a:effectRef><a:fontRef idx="minor"><a:schemeClr val="tx1"/></a:fontRef></a:style></a:lnDef></a:objectDefaults><a:extraClrSchemeLst/><a:extLst><a:ext uri="{05A4C25C-085E-4340-85A3-A5531E510DB2}"><thm15:themeFamily xmlns:thm15="http://schemas.microsoft.com/office/thememl/2012/main" name="Office Theme" id="{2E142A2C-CD16-42D6-873A-C26D2A0506FA}" vid="{1BDDFF52-6CD6-40A5-AB3C-68EB2F1E4D0A}"/></a:ext></a:extLst></a:theme>
@@ -11,7 +11,8 @@ module Uniword
11
11
  class CommentBuilder
12
12
  attr_reader :model
13
13
 
14
- def initialize(model: nil, author: nil, comment_id: nil, date: nil, initials: nil)
14
+ def initialize(model: nil, author: nil, comment_id: nil, date: nil,
15
+ initials: nil)
15
16
  @model = model || Comment.new(
16
17
  author: author,
17
18
  comment_id: comment_id,
@@ -75,7 +75,9 @@ module Uniword
75
75
  end
76
76
 
77
77
  def create_footnote_entry(id, text, &block)
78
- entry = Wordprocessingml::Footnote.new(id: id.to_s, type: "normal")
78
+ # OOXML spec: regular footnotes must NOT have w:type.
79
+ # Only separator/continuationSeparator use w:type.
80
+ entry = Wordprocessingml::Footnote.new(id: id.to_s)
79
81
  para = ParagraphBuilder.new
80
82
  if block_given?
81
83
  block.call(para)
@@ -87,7 +89,7 @@ module Uniword
87
89
  end
88
90
 
89
91
  def create_endnote_entry(id, text, &block)
90
- entry = Wordprocessingml::Endnote.new(id: id.to_s, type: "normal")
92
+ entry = Wordprocessingml::Endnote.new(id: id.to_s)
91
93
  para = ParagraphBuilder.new
92
94
  if block_given?
93
95
  block.call(para)
@@ -22,6 +22,11 @@ module Uniword
22
22
  self
23
23
  end
24
24
 
25
+ def character_style(value)
26
+ ensure_properties.style = Properties::RunStyleReference.new(value: value)
27
+ self
28
+ end
29
+
25
30
  def bold(value = true)
26
31
  ensure_properties.bold = Properties::Bold.new(value: value)
27
32
  self
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uniword
4
+ # Tagging module for document types accepted by DocumentWriter and FormatConverter.
5
+ module DocumentInput
6
+ end
7
+ end
@@ -136,11 +136,7 @@ module Uniword
136
136
  # @raise [ArgumentError] if document is invalid
137
137
  def validate_document(doc)
138
138
  raise ArgumentError, "Document cannot be nil" if doc.nil?
139
-
140
- # Accept OOXML Document
141
- return if doc.is_a?(Uniword::Wordprocessingml::DocumentRoot)
142
- # Accept MHTML Document
143
- return if doc.is_a?(Uniword::Mhtml::Document)
139
+ return if doc.is_a?(Uniword::DocumentInput)
144
140
 
145
141
  raise ArgumentError, "Must be a Document instance"
146
142
  end
@@ -87,6 +87,7 @@ module Uniword
87
87
 
88
88
  # Non-serialized attributes (DOCX packaging helpers)
89
89
  attr_accessor :chart_parts, :bibliography_sources, :profile
90
+ attr_accessor :settings_rels, :embeddings
90
91
 
91
92
  # Load DOCX package from file
92
93
  #
@@ -194,6 +195,13 @@ module Uniword
194
195
  )
195
196
  end
196
197
 
198
+ if zip_content["word/_rels/settings.xml.rels"]
199
+ package.settings_rels =
200
+ Ooxml::Relationships::PackageRelationships.from_xml(
201
+ zip_content["word/_rels/settings.xml.rels"]
202
+ )
203
+ end
204
+
197
205
  if zip_content["word/fontTable.xml"]
198
206
  package.font_table = Uniword::Wordprocessingml::FontTable.from_xml(
199
207
  zip_content["word/fontTable.xml"]
@@ -272,6 +280,16 @@ module Uniword
272
280
  # Extract image parts from word/media/ directory
273
281
  extract_image_parts(zip_content, package, zip_path)
274
282
 
283
+ # Extract OLE/embedded object binaries from word/embeddings/
284
+ embedding_files = zip_content.keys.grep(%r{^word/embeddings/.+$})
285
+ if embedding_files.any?
286
+ package.embeddings = {}
287
+ embedding_files.each do |emb_path|
288
+ target = emb_path.sub("word/", "")
289
+ package.embeddings[target] = zip_content[emb_path]
290
+ end
291
+ end
292
+
275
293
  package
276
294
  end
277
295
 
@@ -26,6 +26,7 @@ document_rels)
26
26
  inject_notes(content_types, document_rels)
27
27
  inject_theme(content_types, document_rels)
28
28
  inject_numbering(content_types, document_rels)
29
+ inject_embeddings(content_types, document_rels)
29
30
  end
30
31
 
31
32
  # Serialize all package parts to XML and add to content hash
@@ -88,6 +89,10 @@ document_rels)
88
89
  content["word/settings.xml"] =
89
90
  serialize_part(settings)
90
91
  end
92
+ if settings_rels
93
+ content["word/_rels/settings.xml.rels"] =
94
+ serialize_infrastructure(settings_rels)
95
+ end
91
96
  if font_table
92
97
  content["word/fontTable.xml"] =
93
98
  serialize_part(font_table)
@@ -131,6 +136,9 @@ document_rels)
131
136
  serialize_headers(content)
132
137
  serialize_footers(content)
133
138
  serialize_header_footer_parts(content)
139
+
140
+ # OLE/embedded object binaries
141
+ serialize_embeddings(content)
134
142
  end
135
143
 
136
144
  # Serialize an OOXML document part with standard encoding
@@ -409,6 +417,28 @@ document_rels)
409
417
  )
410
418
  end
411
419
 
420
+ def inject_embeddings(content_types, document_rels)
421
+ return unless embeddings && !embeddings.empty?
422
+
423
+ embeddings.each_with_index do |(target, _data), idx|
424
+ part_name = "/word/#{target}"
425
+ next if content_types.overrides.any? { |o| o.part_name == part_name }
426
+
427
+ content_types.overrides << Uniword::ContentTypes::Override.new(
428
+ part_name: part_name,
429
+ content_type: "application/vnd.openxmlformats-officedocument.oleObject",
430
+ )
431
+
432
+ next if document_rels.relationships.any? { |r| r.target == target }
433
+
434
+ document_rels.relationships << Ooxml::Relationships::Relationship.new(
435
+ id: "rIdEmbedding#{idx + 1}",
436
+ type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject",
437
+ target: target,
438
+ )
439
+ end
440
+ end
441
+
412
442
  def wire_header_reference(type, r_id)
413
443
  return unless document&.body
414
444
 
@@ -467,6 +497,14 @@ document_rels)
467
497
  serialize_part(part[:content])
468
498
  end
469
499
  end
500
+
501
+ def serialize_embeddings(content)
502
+ return unless embeddings && !embeddings.empty?
503
+
504
+ embeddings.each do |target, data|
505
+ content["word/#{target}"] = data
506
+ end
507
+ end
470
508
  end
471
509
  end
472
510
  end
@@ -28,6 +28,7 @@ module Uniword
28
28
  reconcile_section_properties
29
29
  reconcile_footnotes
30
30
  reconcile_endnotes
31
+ reconcile_note_references
31
32
  repair_theme
32
33
 
33
34
  # Group 2: Support parts (profile-dependent)
@@ -85,14 +86,13 @@ module Uniword
85
86
  ].compact
86
87
 
87
88
  parts.each do |part|
88
- part.pending_namespace_data = nil
89
89
  part.import_declaration_plan = nil
90
90
  part.pending_plan_root_element = nil
91
- # TODO: @xml_input_namespaces has no public accessor in lutaml-model.
92
- # Replace with public API once lutaml-model exposes one.
93
- part.instance_variable_set(:@xml_input_namespaces, nil)
94
91
  end
92
+
93
+ parts.each(&:clear_xml_parse_state!)
95
94
  end
95
+
96
96
  def reconcile_section_properties
97
97
  return unless package.document&.body
98
98
 
@@ -173,6 +173,114 @@ module Uniword
173
173
  ensure_separators(package.endnotes, :endnote) if package.endnotes
174
174
  end
175
175
 
176
+ # -- Note reference consistency --
177
+
178
+ def reconcile_note_references
179
+ reconcile_footnote_references
180
+ reconcile_endnote_references
181
+ reconcile_note_definition_integrity
182
+ end
183
+
184
+ def reconcile_footnote_references
185
+ return unless package.document&.body
186
+
187
+ referenced = collect_note_ids(:footnote)
188
+ return if referenced.empty?
189
+
190
+ ensure_footnotes_part
191
+ defined = package.footnotes.footnote_entries.each_with_object(Set.new) do |fn, s|
192
+ s << fn.id
193
+ end
194
+
195
+ missing = referenced.to_set - defined
196
+ return if missing.empty?
197
+
198
+ missing.each do |id|
199
+ package.footnotes.footnote_entries << Wordprocessingml::Footnote.new(
200
+ id: id, paragraphs: [Wordprocessingml::Paragraph.new],
201
+ )
202
+ end
203
+ record_fix("R10",
204
+ "Added #{missing.size} missing footnote definition(s) " \
205
+ "for orphaned footnoteReference IDs: #{missing.sort.join(', ')}")
206
+ end
207
+
208
+ def reconcile_endnote_references
209
+ return unless package.document&.body
210
+
211
+ referenced = collect_note_ids(:endnote)
212
+ return if referenced.empty?
213
+
214
+ ensure_endnotes_part
215
+ defined = package.endnotes.endnote_entries.each_with_object(Set.new) do |en, s|
216
+ s << en.id
217
+ end
218
+
219
+ missing = referenced.to_set - defined
220
+ return if missing.empty?
221
+
222
+ missing.each do |id|
223
+ package.endnotes.endnote_entries << Wordprocessingml::Endnote.new(
224
+ id: id, paragraphs: [Wordprocessingml::Paragraph.new],
225
+ )
226
+ end
227
+ record_fix("R10",
228
+ "Added #{missing.size} missing endnote definition(s) " \
229
+ "for orphaned endnoteReference IDs: #{missing.sort.join(', ')}")
230
+ end
231
+
232
+ # Validates structural integrity of footnote/endnote definitions.
233
+ def reconcile_note_definition_integrity
234
+ strip_invalid_note_types(:footnote)
235
+ strip_invalid_note_types(:endnote)
236
+ deduplicate_note_ids(:footnote)
237
+ deduplicate_note_ids(:endnote)
238
+ end
239
+
240
+ # R15: Regular footnotes/endnotes must NOT have w:type.
241
+ # Only separator (id=-1) and continuationSeparator (id=0) use w:type.
242
+ def strip_invalid_note_types(type)
243
+ notes = package.public_send(:"#{type}s")
244
+ return unless notes
245
+
246
+ entries = notes.public_send(:"#{type}_entries")
247
+ invalid = entries.select do |e|
248
+ e.type && e.id != "-1" && e.id != "0"
249
+ end
250
+ return if invalid.empty?
251
+
252
+ invalid.each { |e| e.type = nil }
253
+ record_fix("R15",
254
+ "Removed invalid w:type from #{invalid.size} " \
255
+ "regular #{type} definition(s): " \
256
+ "IDs #{invalid.map(&:id).sort.join(', ')}")
257
+ end
258
+
259
+ # R16: Duplicate IDs in footnotes.xml/endnotes.xml are invalid.
260
+ # Keeps the first occurrence, removes subsequent duplicates.
261
+ def deduplicate_note_ids(type)
262
+ notes = package.public_send(:"#{type}s")
263
+ return unless notes
264
+
265
+ entries = notes.public_send(:"#{type}_entries")
266
+ seen = Set.new
267
+ dupes = []
268
+ entries.reject! do |e|
269
+ if seen.include?(e.id)
270
+ dupes << e
271
+ true
272
+ else
273
+ seen << e.id
274
+ false
275
+ end
276
+ end
277
+ return if dupes.empty?
278
+
279
+ record_fix("R16",
280
+ "Removed #{dupes.size} duplicate #{type} ID(s): " \
281
+ "#{dupes.map(&:id).sort.join(', ')}")
282
+ end
283
+
176
284
  # -- Builders --
177
285
 
178
286
  def minimal_footnotes
@@ -215,6 +323,53 @@ module Uniword
215
323
  entries.unshift(continuation_entry(type)) unless ids.include?("0")
216
324
  end
217
325
 
326
+ def collect_note_ids(type)
327
+ ref_attr = type == :footnote ? :footnote_reference : :endnote_reference
328
+ ids = []
329
+ collect_note_ids_from_element(package.document.body, ref_attr, ids)
330
+ ids
331
+ end
332
+
333
+ def collect_note_ids_from_element(element, ref_attr, ids)
334
+ return unless element
335
+
336
+ collect_note_ids_from_paragraphs(element.paragraphs, ref_attr, ids)
337
+
338
+ tables = element.tables
339
+ return unless tables
340
+ tables.each do |tbl|
341
+ tbl.rows.each do |row|
342
+ row.cells.each do |cell|
343
+ collect_note_ids_from_paragraphs(cell.paragraphs, ref_attr, ids)
344
+ end
345
+ end
346
+ end
347
+ end
348
+
349
+ def collect_note_ids_from_paragraphs(paragraphs, ref_attr, ids)
350
+ return unless paragraphs
351
+ paragraphs.each do |p|
352
+ (p.runs || []).each do |r|
353
+ ref = r.public_send(ref_attr)
354
+ ids << ref.id if ref&.id
355
+ end
356
+ end
357
+ end
358
+
359
+ def ensure_footnotes_part
360
+ return if package.footnotes
361
+
362
+ package.footnotes = minimal_footnotes
363
+ record_fix("R9", "Created footnotes.xml for orphaned references")
364
+ end
365
+
366
+ def ensure_endnotes_part
367
+ return if package.endnotes
368
+
369
+ package.endnotes = minimal_endnotes
370
+ record_fix("R9", "Created endnotes.xml for orphaned references")
371
+ end
372
+
218
373
  # -- Group 2: Support parts (profile-dependent) --
219
374
 
220
375
  def reconcile_theme
@@ -277,30 +277,15 @@ module Uniword
277
277
  # @param document [Object] Document (OOXML or MHTML)
278
278
  # @return [Hash] Statistics hash
279
279
  def document_stats(document)
280
- # Handle OOXML document
281
- if document.is_a?(Uniword::Wordprocessingml::DocumentRoot) || document.is_a?(Uniword::Wordprocessingml::Body)
280
+ if document.is_a?(Uniword::Wordprocessingml::Body)
282
281
  return {
283
282
  paragraphs: document.paragraphs.count,
284
- tables: document.is_a?(Uniword::Wordprocessingml::DocumentRoot) ? document.tables.count : 0,
285
- images: document.is_a?(Uniword::Wordprocessingml::DocumentRoot) ? document.images.count : 0
283
+ tables: 0,
284
+ images: 0
286
285
  }
287
286
  end
288
287
 
289
- # Handle MHTML document - estimate from HTML content
290
- html = if document.is_a?(Mhtml::Document) && document.raw_html
291
- document.raw_html
292
- elsif document.is_a?(Mhtml::Document) && document.html_content
293
- document.html_content
294
- end
295
- if html
296
- {
297
- paragraphs: html.scan(/<p[\s>]/i).count,
298
- tables: html.scan(/<table/i).count,
299
- images: html.scan(/<img/i).count
300
- }
301
- else
302
- { paragraphs: 0, tables: 0, images: 0 }
303
- end
288
+ document.document_stats
304
289
  end
305
290
 
306
291
  # Validate conversion parameters
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uniword
4
+ # Tagging module for elements that track their position among sibling runs.
5
+ # Used by the MHTML renderer to interleave hyperlinks and math objects
6
+ # at the correct position within a paragraph.
7
+ module HasRunPosition
8
+ end
9
+ end
@@ -105,7 +105,7 @@ module Uniword
105
105
 
106
106
  # If not loaded, try to get count without loading
107
107
  # This is a hook for subclasses to override
108
- send(name).size
108
+ public_send(name).size
109
109
  end
110
110
  end
111
111
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "lutaml/model"
4
+ require_relative "../has_run_position"
4
5
 
5
6
  module Uniword
6
7
  module Math
@@ -9,6 +10,8 @@ module Uniword
9
10
  # Generated from OOXML schema: math.yml
10
11
  # Element: <m:oMath>
11
12
  class OMath < Lutaml::Model::Serializable
13
+ include Uniword::HasRunPosition
14
+
12
15
  # Pattern 0: Attributes BEFORE xml mappings
13
16
  attribute :runs, MathRun, collection: true, initialize_empty: true
14
17
  attribute :functions, Function, collection: true, initialize_empty: true
@@ -2,6 +2,8 @@
2
2
 
3
3
  require "lutaml/model"
4
4
 
5
+ require_relative "../document_input"
6
+
5
7
  module Uniword
6
8
  module Mhtml
7
9
  # MHTML Document — top-level model for .mht/.mhtml/.doc files.
@@ -17,6 +19,8 @@ module Uniword
17
19
  # ├── word_document_settings (Metadata::WordDocumentSettings)
18
20
  # └── filelist_xml (String)
19
21
  class Document < Lutaml::Model::Serializable
22
+ include Uniword::DocumentInput
23
+
20
24
  # MIME headers
21
25
  attribute :mime_version, :string, default: -> { "1.0" }
22
26
  attribute :boundary, :string
@@ -161,6 +165,20 @@ module Uniword
161
165
  "#<#{self.class} parts=#{parts.length} images=#{image_parts.length} " \
162
166
  "xml=#{xml_parts.length} theme=#{theme_part ? 'yes' : 'no'}>"
163
167
  end
168
+
169
+ # @return [Hash] Document statistics (paragraphs, tables, images)
170
+ def document_stats
171
+ html = raw_html || html_content
172
+ if html
173
+ {
174
+ paragraphs: html.scan(/<p[\s>]/i).count,
175
+ tables: html.scan(/<table/i).count,
176
+ images: html.scan(/<img/i).count,
177
+ }
178
+ else
179
+ { paragraphs: 0, tables: 0, images: 0 }
180
+ end
181
+ end
164
182
  end
165
183
  end
166
184
  end