@nguyenphp/antigravity-marketing 1.0.16 → 1.0.19

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 (376) hide show
  1. package/README.md +187 -74
  2. package/bin/index.js +4 -4
  3. package/package.json +4 -3
  4. package/templates/.agent/agents/backend-specialist.md +263 -0
  5. package/templates/.agent/agents/database-architect.md +226 -0
  6. package/templates/.agent/agents/debugger.md +225 -0
  7. package/templates/.agent/agents/devops-engineer.md +242 -0
  8. package/templates/.agent/agents/documentation-writer.md +104 -0
  9. package/templates/.agent/agents/explorer-agent.md +73 -0
  10. package/templates/.agent/agents/frontend-specialist.md +527 -0
  11. package/templates/.agent/agents/game-developer.md +162 -0
  12. package/templates/.agent/agents/mobile-developer.md +377 -0
  13. package/templates/.agent/agents/orchestrator.md +400 -0
  14. package/templates/.agent/agents/penetration-tester.md +188 -0
  15. package/templates/.agent/agents/performance-optimizer.md +187 -0
  16. package/templates/.agent/agents/project-planner.md +403 -0
  17. package/templates/.agent/agents/security-auditor.md +170 -0
  18. package/templates/.agent/agents/seo-specialist.md +111 -0
  19. package/templates/.agent/agents/test-engineer.md +158 -0
  20. package/templates/.agent/rules/GEMINI.md +248 -0
  21. package/templates/.agent/skills/analytics-marketing/SKILL.md +172 -324
  22. package/templates/.agent/skills/api-patterns/SKILL.md +81 -0
  23. package/templates/.agent/skills/api-patterns/api-style.md +42 -0
  24. package/templates/.agent/skills/api-patterns/auth.md +24 -0
  25. package/templates/.agent/skills/api-patterns/documentation.md +26 -0
  26. package/templates/.agent/skills/api-patterns/graphql.md +41 -0
  27. package/templates/.agent/skills/api-patterns/rate-limiting.md +31 -0
  28. package/templates/.agent/skills/api-patterns/response.md +37 -0
  29. package/templates/.agent/skills/api-patterns/rest.md +40 -0
  30. package/templates/.agent/skills/api-patterns/scripts/api_validator.py +211 -0
  31. package/templates/.agent/skills/api-patterns/security-testing.md +122 -0
  32. package/templates/.agent/skills/api-patterns/trpc.md +41 -0
  33. package/templates/.agent/skills/api-patterns/versioning.md +22 -0
  34. package/templates/.agent/skills/app-builder/SKILL.md +75 -0
  35. package/templates/.agent/skills/app-builder/agent-coordination.md +71 -0
  36. package/templates/.agent/skills/app-builder/feature-building.md +53 -0
  37. package/templates/.agent/skills/app-builder/project-detection.md +34 -0
  38. package/templates/.agent/skills/app-builder/scaffolding.md +118 -0
  39. package/templates/.agent/skills/app-builder/tech-stack.md +40 -0
  40. package/templates/.agent/skills/app-builder/templates/SKILL.md +39 -0
  41. package/templates/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -0
  42. package/templates/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -0
  43. package/templates/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
  44. package/templates/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -0
  45. package/templates/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +83 -0
  46. package/templates/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -0
  47. package/templates/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -0
  48. package/templates/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +82 -0
  49. package/templates/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +100 -0
  50. package/templates/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +106 -0
  51. package/templates/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +101 -0
  52. package/templates/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -0
  53. package/templates/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +93 -0
  54. package/templates/.agent/skills/architecture/SKILL.md +55 -0
  55. package/templates/.agent/skills/architecture/context-discovery.md +43 -0
  56. package/templates/.agent/skills/architecture/examples.md +94 -0
  57. package/templates/.agent/skills/architecture/pattern-selection.md +68 -0
  58. package/templates/.agent/skills/architecture/patterns-reference.md +50 -0
  59. package/templates/.agent/skills/architecture/trade-off-analysis.md +77 -0
  60. package/templates/.agent/skills/banner-design/SKILL.md +192 -0
  61. package/templates/.agent/skills/banner-design/references/banner-sizes-and-styles.md +118 -0
  62. package/templates/.agent/skills/bash-linux/SKILL.md +199 -0
  63. package/templates/.agent/skills/behavioral-modes/SKILL.md +242 -0
  64. package/templates/.agent/skills/brainstorming/SKILL.md +163 -0
  65. package/templates/.agent/skills/brainstorming/dynamic-questioning.md +350 -0
  66. package/templates/.agent/skills/brand/SKILL.md +97 -0
  67. package/templates/.agent/skills/brand/references/approval-checklist.md +169 -0
  68. package/templates/.agent/skills/brand/references/asset-organization.md +157 -0
  69. package/templates/.agent/skills/brand/references/brand-guideline-template.md +140 -0
  70. package/templates/.agent/skills/brand/references/color-palette-management.md +186 -0
  71. package/templates/.agent/skills/brand/references/consistency-checklist.md +94 -0
  72. package/templates/.agent/skills/brand/references/logo-usage-rules.md +185 -0
  73. package/templates/.agent/skills/brand/references/messaging-framework.md +85 -0
  74. package/templates/.agent/skills/brand/references/typography-specifications.md +214 -0
  75. package/templates/.agent/skills/brand/references/update.md +118 -0
  76. package/templates/.agent/skills/brand/references/visual-identity.md +96 -0
  77. package/templates/.agent/skills/brand/references/voice-framework.md +88 -0
  78. package/templates/.agent/skills/brand/scripts/extract-colors.cjs +341 -0
  79. package/templates/.agent/skills/brand/scripts/inject-brand-context.cjs +349 -0
  80. package/templates/.agent/skills/brand/scripts/sync-brand-to-tokens.cjs +266 -0
  81. package/templates/.agent/skills/brand/scripts/validate-asset.cjs +387 -0
  82. package/templates/.agent/skills/brand/templates/brand-guidelines-starter.md +275 -0
  83. package/templates/.agent/skills/clean-code/SKILL.md +201 -0
  84. package/templates/.agent/skills/code-review-checklist/SKILL.md +109 -0
  85. package/templates/.agent/skills/copywriting/SKILL.md +250 -0
  86. package/templates/.agent/skills/database-design/SKILL.md +52 -0
  87. package/templates/.agent/skills/database-design/database-selection.md +43 -0
  88. package/templates/.agent/skills/database-design/indexing.md +39 -0
  89. package/templates/.agent/skills/database-design/migrations.md +48 -0
  90. package/templates/.agent/skills/database-design/optimization.md +36 -0
  91. package/templates/.agent/skills/database-design/orm-selection.md +30 -0
  92. package/templates/.agent/skills/database-design/schema-design.md +56 -0
  93. package/templates/.agent/skills/database-design/scripts/schema_validator.py +172 -0
  94. package/templates/.agent/skills/deployment-procedures/SKILL.md +241 -0
  95. package/templates/.agent/skills/docker-expert/SKILL.md +409 -0
  96. package/templates/.agent/skills/frontend-design/animation-guide.md +331 -0
  97. package/templates/.agent/skills/frontend-design/color-system.md +311 -0
  98. package/templates/.agent/skills/frontend-design/decision-trees.md +418 -0
  99. package/templates/.agent/skills/frontend-design/motion-graphics.md +306 -0
  100. package/templates/.agent/skills/frontend-design/scripts/accessibility_checker.py +183 -0
  101. package/templates/.agent/skills/frontend-design/scripts/ux_audit.py +722 -0
  102. package/templates/.agent/skills/frontend-design/typography-system.md +345 -0
  103. package/templates/.agent/skills/frontend-design/ux-psychology.md +541 -0
  104. package/templates/.agent/skills/frontend-design/visual-effects.md +383 -0
  105. package/templates/.agent/skills/frontend-slides/SKILL.md +92 -0
  106. package/templates/.agent/skills/frontend-slides/STYLE_PRESETS.md +347 -0
  107. package/templates/.agent/skills/frontend-slides/animation-patterns.md +110 -0
  108. package/templates/.agent/skills/frontend-slides/examples/n8n-jupviec-automation.html +789 -0
  109. package/templates/.agent/skills/frontend-slides/examples/n8n-jupviec-automation.pptx +0 -0
  110. package/templates/.agent/skills/frontend-slides/html-template.md +347 -0
  111. package/templates/.agent/skills/frontend-slides/scripts/export-pptx.py +58 -0
  112. package/templates/.agent/skills/frontend-slides/scripts/extract-pptx.py +96 -0
  113. package/templates/.agent/skills/frontend-slides/viewport-base.css +153 -0
  114. package/templates/.agent/skills/game-development/2d-games/SKILL.md +119 -0
  115. package/templates/.agent/skills/game-development/3d-games/SKILL.md +135 -0
  116. package/templates/.agent/skills/game-development/SKILL.md +167 -0
  117. package/templates/.agent/skills/game-development/game-art/SKILL.md +185 -0
  118. package/templates/.agent/skills/game-development/game-audio/SKILL.md +190 -0
  119. package/templates/.agent/skills/game-development/game-design/SKILL.md +129 -0
  120. package/templates/.agent/skills/game-development/mobile-games/SKILL.md +108 -0
  121. package/templates/.agent/skills/game-development/multiplayer/SKILL.md +132 -0
  122. package/templates/.agent/skills/game-development/pc-games/SKILL.md +144 -0
  123. package/templates/.agent/skills/game-development/vr-ar/SKILL.md +123 -0
  124. package/templates/.agent/skills/game-development/web-games/SKILL.md +150 -0
  125. package/templates/.agent/skills/geo-fundamentals/SKILL.md +156 -0
  126. package/templates/.agent/skills/geo-fundamentals/scripts/geo_checker.py +289 -0
  127. package/templates/.agent/skills/growth-engine/SKILL.md +244 -0
  128. package/templates/.agent/skills/i18n-localization/SKILL.md +154 -0
  129. package/templates/.agent/skills/i18n-localization/scripts/i18n_checker.py +241 -0
  130. package/templates/.agent/skills/lint-and-validate/SKILL.md +45 -0
  131. package/templates/.agent/skills/lint-and-validate/scripts/lint_runner.py +172 -0
  132. package/templates/.agent/skills/lint-and-validate/scripts/type_coverage.py +173 -0
  133. package/templates/.agent/skills/marketing-report-expert/SKILL.md +70 -0
  134. package/templates/.agent/skills/mcp-builder/SKILL.md +176 -0
  135. package/templates/.agent/skills/minimax-docx/LICENSE +21 -0
  136. package/templates/.agent/skills/minimax-docx/SKILL.md +274 -0
  137. package/templates/.agent/skills/minimax-docx/assets/styles/academic_styles.xml +250 -0
  138. package/templates/.agent/skills/minimax-docx/assets/styles/corporate_styles.xml +284 -0
  139. package/templates/.agent/skills/minimax-docx/assets/styles/default_styles.xml +449 -0
  140. package/templates/.agent/skills/minimax-docx/assets/xsd/aesthetic-rules.xsd +470 -0
  141. package/templates/.agent/skills/minimax-docx/assets/xsd/business-rules.xsd +130 -0
  142. package/templates/.agent/skills/minimax-docx/assets/xsd/common-types.xsd +159 -0
  143. package/templates/.agent/skills/minimax-docx/assets/xsd/wml-subset.xsd +589 -0
  144. package/templates/.agent/skills/minimax-docx/references/cjk_typography.md +357 -0
  145. package/templates/.agent/skills/minimax-docx/references/cjk_university_template_guide.md +184 -0
  146. package/templates/.agent/skills/minimax-docx/references/comments_guide.md +191 -0
  147. package/templates/.agent/skills/minimax-docx/references/design_good_bad_examples.md +829 -0
  148. package/templates/.agent/skills/minimax-docx/references/design_principles.md +819 -0
  149. package/templates/.agent/skills/minimax-docx/references/openxml_element_order.md +308 -0
  150. package/templates/.agent/skills/minimax-docx/references/openxml_encyclopedia_part1.md +4061 -0
  151. package/templates/.agent/skills/minimax-docx/references/openxml_encyclopedia_part2.md +2820 -0
  152. package/templates/.agent/skills/minimax-docx/references/openxml_encyclopedia_part3.md +3381 -0
  153. package/templates/.agent/skills/minimax-docx/references/openxml_namespaces.md +82 -0
  154. package/templates/.agent/skills/minimax-docx/references/openxml_units.md +72 -0
  155. package/templates/.agent/skills/minimax-docx/references/scenario_a_create.md +284 -0
  156. package/templates/.agent/skills/minimax-docx/references/scenario_b_edit_content.md +295 -0
  157. package/templates/.agent/skills/minimax-docx/references/scenario_c_apply_template.md +456 -0
  158. package/templates/.agent/skills/minimax-docx/references/track_changes_guide.md +200 -0
  159. package/templates/.agent/skills/minimax-docx/references/troubleshooting.md +506 -0
  160. package/templates/.agent/skills/minimax-docx/references/typography_guide.md +294 -0
  161. package/templates/.agent/skills/minimax-docx/references/xsd_validation_guide.md +158 -0
  162. package/templates/.agent/skills/minimax-docx/scripts/doc_to_docx.sh +40 -0
  163. package/templates/.agent/skills/minimax-docx/scripts/docx_preview.sh +37 -0
  164. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Cli/MiniMaxAIDocx.Cli.csproj +19 -0
  165. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Cli/Program.cs +18 -0
  166. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/AnalyzeCommand.cs +147 -0
  167. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/ApplyTemplateCommand.cs +322 -0
  168. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/CreateCommand.cs +324 -0
  169. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/DiffCommand.cs +155 -0
  170. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/EditContentCommand.cs +487 -0
  171. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/FixOrderCommand.cs +108 -0
  172. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/MergeRunsCommand.cs +122 -0
  173. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/ValidateCommand.cs +107 -0
  174. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/MiniMaxAIDocx.Core.csproj +15 -0
  175. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/CommentSynchronizer.cs +169 -0
  176. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/ElementOrder.cs +80 -0
  177. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/NamespaceConstants.cs +42 -0
  178. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/RunMerger.cs +81 -0
  179. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/StyleAnalyzer.cs +81 -0
  180. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/TrackChangesHelper.cs +99 -0
  181. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/UnitConverter.cs +23 -0
  182. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples.cs +1832 -0
  183. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch1.cs +910 -0
  184. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch2.cs +999 -0
  185. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch3.cs +1048 -0
  186. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch4.cs +1038 -0
  187. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/CharacterFormattingSamples.cs +1020 -0
  188. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/DocumentCreationSamples.cs +1121 -0
  189. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/FieldAndTocSamples.cs +624 -0
  190. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/FootnoteAndCommentSamples.cs +675 -0
  191. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/HeaderFooterSamples.cs +838 -0
  192. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ImageSamples.cs +917 -0
  193. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ListAndNumberingSamples.cs +826 -0
  194. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ParagraphFormattingSamples.cs +1199 -0
  195. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/StyleSystemSamples.cs +1487 -0
  196. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/TableSamples.cs +1163 -0
  197. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/TrackChangesSamples.cs +595 -0
  198. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/CjkHelper.cs +39 -0
  199. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/FontDefaults.cs +24 -0
  200. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/PageSizes.cs +20 -0
  201. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/BusinessRuleValidator.cs +224 -0
  202. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/GateCheckValidator.cs +148 -0
  203. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/ValidationResult.cs +23 -0
  204. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/XsdValidator.cs +69 -0
  205. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.slnx +4 -0
  206. package/templates/.agent/skills/minimax-docx/scripts/env_check.sh +196 -0
  207. package/templates/.agent/skills/minimax-docx/scripts/setup.ps1 +274 -0
  208. package/templates/.agent/skills/minimax-docx/scripts/setup.sh +504 -0
  209. package/templates/.agent/skills/minimax-multimodal-toolkit/SKILL.md +359 -0
  210. package/templates/.agent/skills/minimax-pdf/README.md +222 -0
  211. package/templates/.agent/skills/minimax-pdf/SKILL.md +201 -0
  212. package/templates/.agent/skills/minimax-pdf/design/design.md +381 -0
  213. package/templates/.agent/skills/minimax-pdf/scripts/cover.py +1579 -0
  214. package/templates/.agent/skills/minimax-pdf/scripts/fill_inspect.py +200 -0
  215. package/templates/.agent/skills/minimax-pdf/scripts/fill_write.py +242 -0
  216. package/templates/.agent/skills/minimax-pdf/scripts/make.sh +491 -0
  217. package/templates/.agent/skills/minimax-pdf/scripts/merge.py +112 -0
  218. package/templates/.agent/skills/minimax-pdf/scripts/palette.py +559 -0
  219. package/templates/.agent/skills/minimax-pdf/scripts/reformat_parse.py +374 -0
  220. package/templates/.agent/skills/minimax-pdf/scripts/render_body.py +1055 -0
  221. package/templates/.agent/skills/minimax-pdf/scripts/render_cover.cjs +111 -0
  222. package/templates/.agent/skills/minimax-xlsx/SKILL.md +138 -0
  223. package/templates/.agent/skills/minimax-xlsx/references/create.md +691 -0
  224. package/templates/.agent/skills/minimax-xlsx/references/edit.md +684 -0
  225. package/templates/.agent/skills/minimax-xlsx/references/fix.md +37 -0
  226. package/templates/.agent/skills/minimax-xlsx/references/format.md +768 -0
  227. package/templates/.agent/skills/minimax-xlsx/references/ooxml-cheatsheet.md +231 -0
  228. package/templates/.agent/skills/minimax-xlsx/references/read-analyze.md +97 -0
  229. package/templates/.agent/skills/minimax-xlsx/references/validate.md +772 -0
  230. package/templates/.agent/skills/minimax-xlsx/scripts/formula_check.py +422 -0
  231. package/templates/.agent/skills/minimax-xlsx/scripts/libreoffice_recalc.py +248 -0
  232. package/templates/.agent/skills/minimax-xlsx/scripts/shared_strings_builder.py +163 -0
  233. package/templates/.agent/skills/minimax-xlsx/scripts/style_audit.py +575 -0
  234. package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_add_column.py +395 -0
  235. package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_insert_row.py +274 -0
  236. package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_pack.py +87 -0
  237. package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_reader.py +362 -0
  238. package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_shift_rows.py +396 -0
  239. package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_unpack.py +130 -0
  240. package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/[Content_Types].xml +9 -0
  241. package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/_rels/.rels +6 -0
  242. package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/_rels/workbook.xml.rels +19 -0
  243. package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/sharedStrings.xml +33 -0
  244. package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/styles.xml +160 -0
  245. package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/workbook.xml +30 -0
  246. package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/worksheets/sheet1.xml +70 -0
  247. package/templates/.agent/skills/mobile-design/SKILL.md +394 -0
  248. package/templates/.agent/skills/mobile-design/decision-trees.md +516 -0
  249. package/templates/.agent/skills/mobile-design/mobile-backend.md +491 -0
  250. package/templates/.agent/skills/mobile-design/mobile-color-system.md +420 -0
  251. package/templates/.agent/skills/mobile-design/mobile-debugging.md +122 -0
  252. package/templates/.agent/skills/mobile-design/mobile-design-thinking.md +357 -0
  253. package/templates/.agent/skills/mobile-design/mobile-navigation.md +458 -0
  254. package/templates/.agent/skills/mobile-design/mobile-performance.md +767 -0
  255. package/templates/.agent/skills/mobile-design/mobile-testing.md +356 -0
  256. package/templates/.agent/skills/mobile-design/mobile-typography.md +433 -0
  257. package/templates/.agent/skills/mobile-design/platform-android.md +666 -0
  258. package/templates/.agent/skills/mobile-design/platform-ios.md +561 -0
  259. package/templates/.agent/skills/mobile-design/scripts/mobile_audit.py +670 -0
  260. package/templates/.agent/skills/mobile-design/touch-psychology.md +537 -0
  261. package/templates/.agent/skills/nestjs-expert/SKILL.md +552 -0
  262. package/templates/.agent/skills/nextjs-best-practices/SKILL.md +203 -0
  263. package/templates/.agent/skills/nodejs-best-practices/SKILL.md +333 -0
  264. package/templates/.agent/skills/parallel-agents/SKILL.md +175 -0
  265. package/templates/.agent/skills/performance-profiling/SKILL.md +143 -0
  266. package/templates/.agent/skills/performance-profiling/scripts/lighthouse_audit.py +76 -0
  267. package/templates/.agent/skills/plan-writing/SKILL.md +152 -0
  268. package/templates/.agent/skills/powershell-windows/SKILL.md +167 -0
  269. package/templates/.agent/skills/ppc-advertising/SKILL.md +183 -475
  270. package/templates/.agent/skills/pptx-generator/SKILL.md +249 -0
  271. package/templates/.agent/skills/pptx-generator/references/design-system.md +392 -0
  272. package/templates/.agent/skills/pptx-generator/references/editing.md +162 -0
  273. package/templates/.agent/skills/pptx-generator/references/pitfalls.md +112 -0
  274. package/templates/.agent/skills/pptx-generator/references/pptxgenjs.md +420 -0
  275. package/templates/.agent/skills/pptx-generator/references/slide-types.md +413 -0
  276. package/templates/.agent/skills/prisma-expert/SKILL.md +355 -0
  277. package/templates/.agent/skills/python-patterns/SKILL.md +441 -0
  278. package/templates/.agent/skills/react-patterns/SKILL.md +198 -0
  279. package/templates/.agent/skills/red-team-tactics/SKILL.md +199 -0
  280. package/templates/.agent/skills/remotion-best-practices/SKILL.md +45 -111
  281. package/templates/.agent/skills/remotion-best-practices/rules/3d.md +4 -4
  282. package/templates/.agent/skills/remotion-best-practices/rules/animations.md +5 -7
  283. package/templates/.agent/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +173 -0
  284. package/templates/.agent/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +100 -0
  285. package/templates/.agent/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +103 -0
  286. package/templates/.agent/skills/remotion-best-practices/rules/assets.md +78 -0
  287. package/templates/.agent/skills/remotion-best-practices/rules/audio-visualization.md +198 -0
  288. package/templates/.agent/skills/remotion-best-practices/rules/audio.md +1 -4
  289. package/templates/.agent/skills/remotion-best-practices/rules/calculate-metadata.md +47 -17
  290. package/templates/.agent/skills/remotion-best-practices/rules/can-decode.md +75 -0
  291. package/templates/.agent/skills/remotion-best-practices/rules/charts.md +80 -48
  292. package/templates/.agent/skills/remotion-best-practices/rules/compositions.md +22 -14
  293. package/templates/.agent/skills/remotion-best-practices/rules/display-captions.md +79 -21
  294. package/templates/.agent/skills/remotion-best-practices/rules/extract-frames.md +229 -0
  295. package/templates/.agent/skills/remotion-best-practices/rules/ffmpeg.md +38 -0
  296. package/templates/.agent/skills/remotion-best-practices/rules/fonts.md +96 -54
  297. package/templates/.agent/skills/remotion-best-practices/rules/get-audio-duration.md +58 -0
  298. package/templates/.agent/skills/remotion-best-practices/rules/get-video-dimensions.md +68 -0
  299. package/templates/.agent/skills/remotion-best-practices/rules/get-video-duration.md +60 -0
  300. package/templates/.agent/skills/remotion-best-practices/rules/gifs.md +21 -18
  301. package/templates/.agent/skills/remotion-best-practices/rules/images.md +6 -2
  302. package/templates/.agent/skills/remotion-best-practices/rules/import-srt-captions.md +69 -0
  303. package/templates/.agent/skills/remotion-best-practices/rules/light-leaks.md +73 -0
  304. package/templates/.agent/skills/remotion-best-practices/rules/lottie.md +10 -7
  305. package/templates/.agent/skills/remotion-best-practices/rules/maps.md +412 -0
  306. package/templates/.agent/skills/remotion-best-practices/rules/measuring-dom-nodes.md +34 -0
  307. package/templates/.agent/skills/remotion-best-practices/rules/measuring-text.md +140 -0
  308. package/templates/.agent/skills/remotion-best-practices/rules/parameters.md +109 -0
  309. package/templates/.agent/skills/remotion-best-practices/rules/sequencing.md +13 -1
  310. package/templates/.agent/skills/remotion-best-practices/rules/sfx.md +26 -0
  311. package/templates/.agent/skills/remotion-best-practices/rules/subtitles.md +36 -0
  312. package/templates/.agent/skills/remotion-best-practices/rules/tailwind.md +11 -0
  313. package/templates/.agent/skills/remotion-best-practices/rules/text-animations.md +4 -115
  314. package/templates/.agent/skills/remotion-best-practices/rules/timing.md +19 -19
  315. package/templates/.agent/skills/remotion-best-practices/rules/transcribe-captions.md +70 -0
  316. package/templates/.agent/skills/remotion-best-practices/rules/transitions.md +117 -42
  317. package/templates/.agent/skills/remotion-best-practices/rules/transparent-videos.md +106 -0
  318. package/templates/.agent/skills/remotion-best-practices/rules/trimming.md +51 -0
  319. package/templates/.agent/skills/remotion-best-practices/rules/voiceover.md +99 -0
  320. package/templates/.agent/skills/seo-fundamentals/SKILL.md +83 -441
  321. package/templates/.agent/skills/seo-fundamentals/scripts/seo_checker.py +219 -0
  322. package/templates/.agent/skills/server-management/SKILL.md +161 -0
  323. package/templates/.agent/skills/systematic-debugging/SKILL.md +109 -0
  324. package/templates/.agent/skills/tdd-workflow/SKILL.md +149 -0
  325. package/templates/.agent/skills/testing-patterns/SKILL.md +178 -0
  326. package/templates/.agent/skills/testing-patterns/scripts/test_runner.py +219 -0
  327. package/templates/.agent/skills/tutorial-video-expert/SKILL.md +88 -0
  328. package/templates/.agent/skills/typescript-expert/SKILL.md +429 -0
  329. package/templates/.agent/skills/ui-ux-pro-max/SKILL.md +1 -1
  330. package/templates/.agent/skills/ui-ux-pro-max/data/charts.csv +26 -0
  331. package/templates/.agent/skills/ui-ux-pro-max/data/colors.csv +97 -0
  332. package/templates/.agent/skills/ui-ux-pro-max/data/icons.csv +101 -0
  333. package/templates/.agent/skills/ui-ux-pro-max/data/landing.csv +31 -0
  334. package/templates/.agent/skills/ui-ux-pro-max/data/products.csv +97 -0
  335. package/templates/.agent/skills/ui-ux-pro-max/data/prompts.csv +24 -0
  336. package/templates/.agent/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
  337. package/templates/.agent/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  338. package/templates/.agent/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  339. package/templates/.agent/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  340. package/templates/.agent/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  341. package/templates/.agent/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  342. package/templates/.agent/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  343. package/templates/.agent/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
  344. package/templates/.agent/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  345. package/templates/.agent/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  346. package/templates/.agent/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  347. package/templates/.agent/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  348. package/templates/.agent/skills/ui-ux-pro-max/data/styles.csv +59 -0
  349. package/templates/.agent/skills/ui-ux-pro-max/data/typography.csv +58 -0
  350. package/templates/.agent/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  351. package/templates/.agent/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  352. package/templates/.agent/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
  353. package/templates/.agent/skills/ui-ux-pro-max/scripts/core.py +257 -0
  354. package/templates/.agent/skills/ui-ux-pro-max/scripts/design_system.py +487 -0
  355. package/templates/.agent/skills/ui-ux-pro-max/scripts/search.py +76 -0
  356. package/templates/.agent/skills/vision-analysis/SKILL.md +174 -0
  357. package/templates/.agent/skills/vue-expert/SKILL.md +374 -0
  358. package/templates/.agent/skills/vulnerability-scanner/SKILL.md +276 -0
  359. package/templates/.agent/skills/vulnerability-scanner/checklists.md +121 -0
  360. package/templates/.agent/skills/vulnerability-scanner/scripts/security_scan.py +458 -0
  361. package/templates/.agent/skills/webapp-testing/SKILL.md +187 -0
  362. package/templates/.agent/skills/webapp-testing/scripts/playwright_runner.py +173 -0
  363. package/templates/.agent/workflows/analyze.md +3 -0
  364. package/templates/.agent/workflows/brainstorm.md +113 -0
  365. package/templates/.agent/workflows/brand-report.md +44 -0
  366. package/templates/.agent/workflows/create.md +59 -0
  367. package/templates/.agent/workflows/debug.md +103 -0
  368. package/templates/.agent/workflows/deploy.md +176 -0
  369. package/templates/.agent/workflows/enhance.md +63 -0
  370. package/templates/.agent/workflows/orchestrate.md +237 -0
  371. package/templates/.agent/workflows/plan.md +89 -0
  372. package/templates/.agent/workflows/preview.md +80 -0
  373. package/templates/.agent/workflows/report.md +49 -0
  374. package/templates/.agent/workflows/status.md +86 -0
  375. package/templates/.agent/workflows/test.md +144 -0
  376. package/templates/.agent/workflows/ui-ux-pro-max.md +231 -0
@@ -0,0 +1,3381 @@
1
+ # OpenXML SDK C# Code Encyclopedia
2
+ Complete, heavily commented C# code patterns for DocumentFormat.OpenXml 3.x / .NET 8+ / C# 12.
3
+
4
+ **Namespace aliases used throughout:**
5
+ ```csharp
6
+ using DocumentFormat.OpenXml;
7
+ using DocumentFormat.OpenXml.Packaging;
8
+ using DocumentFormat.OpenXml.Wordprocessing;
9
+ using A = DocumentFormat.OpenXml.Drawing;
10
+ using DW = DocumentFormat.OpenXml.Drawing.Wordprocessing;
11
+ using M = DocumentFormat.OpenXml.Math;
12
+ using PIC = DocumentFormat.OpenXml.Drawing.Pictures;
13
+ ```
14
+
15
+ **EMU conversion reference** (used throughout image/shape code):
16
+ ```
17
+ 1 inch = 914400 EMU
18
+ 1 cm = 360000 EMU
19
+ 1 pixel @ 96dpi = 9525 EMU
20
+ 1 pt = 12700 EMU
21
+ ```
22
+
23
+ ---
24
+
25
+ ## Table of Contents
26
+
27
+ 1. [Table of Contents (TOC)](#1-table-of-contents-toc)
28
+ 2. [Footnotes and Endnotes](#2-footnotes-and-endnotes)
29
+ 3. [Field Codes — Comprehensive](#3-field-codes--comprehensive)
30
+ 4. [Track Changes / Revisions](#4-track-changes--revisions)
31
+ 5. [Comments (4-File System)](#5-comments-4-file-system)
32
+ 6. [Images — Deep Dive](#6-images--deep-dive)
33
+ 7. [Drawing Shapes (Non-Image)](#7-drawing-shapes-non-image)
34
+ 8. [Math / Equations (OMML)](#8-math--equations-omml)
35
+ 9. [Numbering System — Deep Dive](#9-numbering-system--deep-dive)
36
+ 10. [Document Protection & Encryption](#10-document-protection--encryption)
37
+
38
+ ---
39
+
40
+ ## 1. Table of Contents (TOC)
41
+
42
+ ### 1.1 Basic TOC Field (SimpleField Pattern)
43
+
44
+ The simplest way to insert a TOC. Uses `SimpleField` which wraps the entire field in one element.
45
+
46
+ ```csharp
47
+ // Creates:
48
+ // <w:p>
49
+ // <w:r>
50
+ // <w:fldChar w:fldCharType="begin"/>
51
+ // </w:r>
52
+ // <w:r>
53
+ // <w:instrText xml:space="preserve"> TOC \o "1-3" \h \z \u </w:instrText>
54
+ // </w:r>
55
+ // <w:r>
56
+ // <w:fldChar w:fldCharType="separate"/>
57
+ // </w:r>
58
+ // <w:r>
59
+ // <w:t>Update this field to generate table of contents.</w:t>
60
+ // </w:r>
61
+ // <w:r>
62
+ // <w:fldChar w:fldCharType="end"/>
63
+ // </w:r>
64
+ // </w:p>
65
+
66
+ var tocParagraph = new Paragraph(
67
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
68
+ new Run(new FieldCode(" TOC \\o \"1-3\" \\h \\z \\u ") { Space = SpaceProcessingModeValues.Preserve }),
69
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
70
+ // Placeholder text shown before update
71
+ new Run(new Text("Update this field to generate table of contents.") { Space = SpaceProcessingModeValues.Preserve }),
72
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End })
73
+ );
74
+ body.Append(tocParagraph);
75
+ ```
76
+
77
+ **TOC switch reference:**
78
+ | Switch | Meaning |
79
+ |--------|---------|
80
+ | `\o "1-3"` | Include outline levels 1–3 (customize as needed) |
81
+ | `\h` | Make entries hyperlinks (clickable) |
82
+ | `\z` | Hide tab leader and page numbers in Web Layout view |
83
+ | `\u` | Use applied paragraph outline level |
84
+ | `\f` | TOC entry from bookmark |
85
+ | `\t "style1,style2"` | Use custom styles instead of outline levels |
86
+ | `\n "1-2"` | Omit page numbers for levels 1–2 |
87
+
88
+ ### 1.2 TOC Field with SdtBlock Wrapper
89
+
90
+ Wrapping a TOC in a Structured Document Tag (SdtBlock) enables rich content control features.
91
+
92
+ ```csharp
93
+ // SdtBlock wrapper provides:
94
+ // - Ability to repeat/remove the entire TOC
95
+ // - Richer programmatic control
96
+ // - "Content Control" appearance in Word UI
97
+
98
+ var sdtBlock = new SdtBlock(
99
+ // SdtProperties defines the control's identity and behavior
100
+ new SdtProperties(
101
+ new SdtAlias { Val = "Table of Contents" },
102
+ new Tag { Val = "toc" },
103
+ new SdtContentText() // Plain text content
104
+ ),
105
+ // SdtContentBlock contains the actual TOC field
106
+ new SdtContentBlock(
107
+ new Paragraph(
108
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
109
+ new Run(new FieldCode(" TOC \\o \"1-2\" \\h \\z ") { Space = SpaceProcessingModeValues.Preserve }),
110
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
111
+ new Run(new Text("Press F9 or right-click and select 'Update Field'") { Space = SpaceProcessingModeValues.Preserve }),
112
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End })
113
+ )
114
+ )
115
+ );
116
+ body.Append(sdtBlock);
117
+ ```
118
+
119
+ ### 1.3 TOC with Custom Heading Levels
120
+
121
+ Use the `\t` switch to build a TOC from arbitrary styles (not just Heading 1–9).
122
+
123
+ ```csharp
124
+ // TOC using custom style names instead of outline levels:
125
+ // \t switch format: "style1,level1,style2,level2,..."
126
+ // This uses CustomHeading1 and CustomHeading2 styles mapped to TOC levels
127
+
128
+ var customTocPara = new Paragraph(
129
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
130
+ new Run(new FieldCode(" TOC \\t \"CustomHeading1,1,CustomHeading2,2,CustomHeading3,3\" \\h \\z ") { Space = SpaceProcessingModeValues.Preserve }),
131
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
132
+ new Run(new Text("Update to see entries from CustomHeading1/2/3 styles.") { Space = SpaceProcessingModeValues.Preserve }),
133
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End })
134
+ );
135
+ ```
136
+
137
+ ### 1.4 TOC with Hyperlinks (\h switch)
138
+
139
+ The `\h` switch makes TOC entries clickable hyperlinks. This requires the entries to have a hyperlink anchor.
140
+
141
+ ```csharp
142
+ // When \h is used, Word generates internal hyperlinks to each heading.
143
+ // The target is the bookmark automatically created by Word for headings.
144
+ // This is the standard pattern — no additional work needed in the field code itself.
145
+
146
+ var tocWithHyperlinks = new Paragraph(
147
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
148
+ new Run(new FieldCode(" TOC \\o \"1-3\" \\h \\z \\u ") { Space = SpaceProcessingModeValues.Preserve }),
149
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
150
+ // In Web Layout/Print Layout, Word will populate this with real entries
151
+ // Each entry will be a hyperlink pointing to the heading's internal bookmark
152
+ new Run(new Text("Click to update...") { Space = SpaceProcessingModeValues.Preserve }),
153
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End })
154
+ );
155
+ ```
156
+
157
+ ### 1.5 Auto-Update TOC on Document Open
158
+
159
+ You cannot programmatically update a TOC field's content (Word does this on open). Instead, tell Word to update fields automatically.
160
+
161
+ ```csharp
162
+ // Method 1: Via DocumentSettingsPart — UpdateFieldsOnOpen
163
+ var settingsPart = mainDocumentPart.AddNewPart<DocumentSettingsPart>();
164
+ settingsPart.Settings = new Settings(
165
+ new UpdateFieldsOnOpen { Val = true } // Triggers field update on open
166
+ );
167
+ settingsPart.Settings.Save();
168
+
169
+ // Method 2: Field code includes \w to preserve formatting changes
170
+ // (Field code approach is limited — you still need Word to evaluate it)
171
+
172
+ // GOTCHA: OpenXML SDK cannot evaluate field codes.
173
+ // Word evaluates fields on open. Other readers (e.g., LibreOffice) may not.
174
+ // The document opens without content until the user explicitly updates.
175
+ ```
176
+
177
+ ### 1.6 TOC Styles — Custom TOC1, TOC2, TOC3 Styles with Leaders
178
+
179
+ Define custom TOC styles with indentation, tab leaders, and proper formatting.
180
+
181
+ ```csharp
182
+ // First, add TOC1/TOC2/TOC3 styles to StyleDefinitionsPart
183
+ var stylesPart = mainDocumentPart.AddNewPart<StyleDefinitionsPart>();
184
+ var styles = new Styles();
185
+
186
+ // TOC1 — Top-level entry (e.g., "1. Heading")
187
+ var toc1Style = new Style(
188
+ new StyleName { Val = "toc 1" },
189
+ new BasedOn { Val = "Normal" },
190
+ new PrimaryStyle(),
191
+ new StyleParagraphProperties(
192
+ new SpacingBetweenLines { Before = "120", After = "60" },
193
+ new Tabs(new TabStop { Val = TabStopValues.Right, Leader = TabStopLeaderCharValues.Dot, Position = 9072 }) // 5 inches right-aligned with dot leader
194
+ ),
195
+ new StyleRunProperties(
196
+ new Bold(),
197
+ new FontSize { Val = "24" }, // 12pt
198
+ new FontSizeComplexScript { Val = "24" }
199
+ )
200
+ ) { Type = StyleValues.Paragraph, StyleId = "TOC1" };
201
+ styles.Append(toc1Style);
202
+
203
+ // TOC2 — Second-level entry (e.g., "1.1 Subheading")
204
+ var toc2Style = new Style(
205
+ new StyleName { Val = "toc 2" },
206
+ new BasedOn { Val = "Normal" },
207
+ new PrimaryStyle(),
208
+ new StyleParagraphProperties(
209
+ new Indentation { Left = "220", Hanging = "220" }, // 0.15" indent, hang to align after number
210
+ new SpacingBetweenLines { Before = "60", After = "40" },
211
+ new Tabs(new TabStop { Val = TabStopValues.Right, Leader = TabStopLeaderCharValues.Dot, Position = 9072 })
212
+ ),
213
+ new StyleRunProperties(
214
+ new FontSize { Val = "20" }, // 10pt
215
+ new FontSizeComplexScript { Val = "20" }
216
+ )
217
+ ) { Type = StyleValues.Paragraph, StyleId = "TOC2" };
218
+ styles.Append(toc2Style);
219
+
220
+ // TOC3 — Third-level entry
221
+ var toc3Style = new Style(
222
+ new StyleName { Val = "toc 3" },
223
+ new BasedOn { Val = "Normal" },
224
+ new PrimaryStyle(),
225
+ new StyleParagraphProperties(
226
+ new Indentation { Left = "440", Hanging = "440" }, // 0.3" indent
227
+ new SpacingBetweenLines { Before = "40", After = "20" },
228
+ new Tabs(new TabStop { Val = TabStopValues.Right, Leader = TabStopLeaderCharValues.Dot, Position = 9072 })
229
+ ),
230
+ new StyleRunProperties(
231
+ new Italic(),
232
+ new FontSize { Val = "20" },
233
+ new FontSizeComplexScript { Val = "20" }
234
+ )
235
+ ) { Type = StyleValues.Paragraph, StyleId = "TOC3" };
236
+ styles.Append(toc3Style);
237
+
238
+ stylesPart.Styles = styles;
239
+ stylesPart.Styles.Save();
240
+
241
+ // Now use \t switch to reference these styles in the TOC field:
242
+ // TOC \t "TOC1,1,TOC2,2,TOC3,3" \h \z
243
+ ```
244
+
245
+ **Tab leader options:** `TabStopLeaderCharValues.Dot` (........), `TabStopLeaderCharValues.Dash` (--------), `TabStopLeaderCharValues.Underscore` (________), `TabStopLeaderCharValues.MiddleDot` (·······).
246
+
247
+ ### 1.7 Mini TOC for a Section
248
+
249
+ A mini TOC covers only a portion of the document using a bookmark-scoped `\f` switch.
250
+
251
+ ```csharp
252
+ // Step 1: Define a bookmark around the section to be covered
253
+ var sectionStart = new BookmarkStart { Id = "10", Name = "_Section1TOC" };
254
+ var sectionEnd = new BookmarkEnd { Id = "10" };
255
+
256
+ // Step 2: Put heading paragraphs inside the bookmark range
257
+ var headingPara = new Paragraph(
258
+ new ParagraphProperties(new ParagraphStyleId { Val = "Heading1" }),
259
+ new Run(new Text("Section A: Introduction"))
260
+ );
261
+
262
+ // Full section wrapped in bookmark
263
+ body.Append(sectionStart);
264
+ body.Append(headingPara);
265
+ body.Append(sectionEnd);
266
+
267
+ // Step 3: Mini TOC field references that bookmark with \f switch
268
+ var miniTocPara = new Paragraph(
269
+ new Run(new Text("In this section: ") { Space = SpaceProcessingModeValues.Preserve }),
270
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
271
+ new Run(new FieldCode(" TOC \\f _Section1TOC ") { Space = SpaceProcessingModeValues.Preserve }),
272
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
273
+ new Run(new Text("Mini TOC placeholder") { Space = SpaceProcessingModeValues.Preserve }),
274
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End })
275
+ );
276
+ ```
277
+
278
+ ---
279
+
280
+ ## 2. Footnotes and Endnotes
281
+
282
+ ### 2.1 FootnotesPart — Initialization
283
+
284
+ Footnotes in Word require a dedicated `FootnotesPart`. The first three footnotes are special: separator, continuation separator, and continuation notice.
285
+
286
+ ```csharp
287
+ // Initialize the FootnotesPart
288
+ var footnotesPart = mainDocumentPart.AddNewPart<FootnotesPart>();
289
+ var footnotes = new Footnotes();
290
+
291
+ // CRITICAL: Footnotes must start with these 3 special footnotes:
292
+ // 1. Separator (id="0") — thin line between main text and footnotes
293
+ // 2. ContinuationSeparator (id="1") — thick line when footnotes continue to next column/page
294
+ // 3. ContinuationNotice (id="2") — "..." text when footnotes overflow
295
+
296
+ // Footnote ID=0: Separator (appears between main text and first footnote)
297
+ var separatorFootnote = new Footnote(
298
+ new Paragraph(
299
+ new ParagraphProperties(
300
+ new SpacingBetweenLines { After = "0", Line = "240", LineRule = LineSpacingRuleValues.Auto }
301
+ ),
302
+ // The separator is just a paragraph with a border at the bottom
303
+ new Paragraph(
304
+ new Run(new Separator())
305
+ )
306
+ )
307
+ ) { Type = FootnoteEndnoteValues.Separator, Id = 0 };
308
+ footnotes.Append(separatorFootnote);
309
+
310
+ // Footnote ID=1: Continuation Separator
311
+ var continuationSepFootnote = new Footnote(
312
+ new Paragraph(
313
+ new ParagraphProperties(
314
+ new SpacingBetweenLines { After = "0", Line = "240", LineRule = LineSpacingRuleValues.Auto }
315
+ ),
316
+ new Run(new ContinuationSeparator())
317
+ )
318
+ ) { Type = FootnoteEndnoteValues.ContinuationSeparator, Id = 1 };
319
+ footnotes.Append(continuationSepFootnote);
320
+
321
+ // Footnote ID=2: Continuation Notice (optional, appears when footnotes overflow)
322
+ var continuationNoticeFootnote = new Footnote(
323
+ new Paragraph(
324
+ new ParagraphProperties(
325
+ new SpacingBetweenLines { After = "0", Line = "240", LineRule = LineSpacingRuleValues.Auto }
326
+ ),
327
+ new Run(new Text("...") { Space = SpaceProcessingModeValues.Preserve })
328
+ )
329
+ ) { Type = FootnoteEndnoteValues.ContinuationNotice, Id = 2 };
330
+ footnotes.Append(continuationNoticeFootnote);
331
+
332
+ footnotesPart.Footnotes = footnotes;
333
+ footnotesPart.Footnotes.Save();
334
+ ```
335
+
336
+ ### 2.2 Adding a Normal Footnote
337
+
338
+ Place a `FootnoteReference` in the document body and corresponding `Footnote` content in `FootnotesPart`.
339
+
340
+ ```csharp
341
+ // In the document body, at the insertion point:
342
+ var footnoteRefRun = new Run(
343
+ new RunProperties(
344
+ new VerticalTextAlignment { Val = VerticalPositionValues.Superscript }
345
+ ),
346
+ new FootnoteReference { Id = 3 } // ID must match the Footnote's Id
347
+ );
348
+ body.Append(new Paragraph(
349
+ new Run(new Text("Some text with a footnote marker.") { Space = SpaceProcessingModeValues.Preserve }),
350
+ footnoteRefRun
351
+ ));
352
+
353
+ // In FootnotesPart, add the corresponding footnote content:
354
+ // <w:footnote w:id="3">
355
+ // <w:p>
356
+ // <w:pPr><w:pStyle w:val="FootnoteText"/></w:pPr>
357
+ // <w:r><w:footnoteRef/></w:r>
358
+ // <w:r><w:t xml:space="preserve"> This is the footnote text.</w:t></w:r>
359
+ // </w:p>
360
+ // </w:footnote>
361
+
362
+ var newFootnote = new Footnote { Id = 3 };
363
+ newFootnote.Append(new Paragraph(
364
+ new ParagraphProperties(new ParagraphStyleId { Val = "FootnoteText" }),
365
+ new Run(new FootnoteReferenceMark()), // Small superscript mark
366
+ new Run(new Text(" This is the footnote text.") { Space = SpaceProcessingModeValues.Preserve })
367
+ ));
368
+ footnotesPart.Footnotes!.Append(newFootnote);
369
+ footnotesPart.Footnotes.Save();
370
+ ```
371
+
372
+ ### 2.3 Footnote with Custom Mark (Asterisk, Symbol)
373
+
374
+ Override the default auto-numbering with a custom symbol.
375
+
376
+ ```csharp
377
+ // Use FootnoteReferenceMark (the automatic symbol) OR provide a custom character.
378
+ // For custom marks, use a regular Run with the symbol character instead of FootnoteReferenceMark.
379
+
380
+ // Footnote ID=4 with custom asterisk mark
381
+ var customFootnote = new Footnote { Id = 4 };
382
+ customFootnote.Append(new Paragraph(
383
+ new ParagraphProperties(new ParagraphStyleId { Val = "FootnoteText" }),
384
+ // Custom mark: use a bold asterisk from Symbol font
385
+ new Run(
386
+ new RunProperties(
387
+ new VerticalTextAlignment { Val = VerticalPositionValues.Superscript }
388
+ ),
389
+ new RunFonts { Ascii = "Symbol", HighAnsi = "Symbol" },
390
+ new Text("*")
391
+ ),
392
+ new Run(new Text(" Custom footnote with symbol mark.") { Space = SpaceProcessingModeValues.Preserve })
393
+ ));
394
+ footnotesPart.Footnotes!.Append(customFootnote);
395
+
396
+ // In document body, at the insertion point:
397
+ var customFootnoteRef = new Run(
398
+ new RunProperties(
399
+ new VerticalTextAlignment { Val = VerticalPositionValues.Superscript }
400
+ ),
401
+ new FootnoteReference { Id = 4 }
402
+ );
403
+ ```
404
+
405
+ ### 2.4 FootnotePosition — Placement via SectionProperties
406
+
407
+ Control whether footnotes appear at the bottom of each page or beneath the text.
408
+
409
+ ```csharp
410
+ // Add FootnoteProperties to SectionProperties
411
+ var sectPr = body.Elements<SectionProperties>().First()
412
+ ?? body.AppendChild(new SectionProperties());
413
+
414
+ // Footnote placement:
415
+ // - BottomOfPage (default) — footnotes appear at bottom of page
416
+ // - BeneathText — footnotes appear immediately below the last text on the page
417
+ sectPr.Append(new FootnoteProperties(
418
+ new FootnotePosition { Val = FootnotePositionValues.BeneathText }
419
+ ));
420
+
421
+ // Footnote numbering restart options:
422
+ // - RestartAtSection — restart numbering at each section
423
+ // - RestartAtPage — restart at each page (Word default for footnotes)
424
+ // - Continuous — don't restart (number sequentially through document)
425
+ sectPr.Append(new FootnoteProperties(
426
+ new FootnotePosition { Val = FootnotePositionValues.BottomOfPage },
427
+ new FootnoteNumberingFormat { Val = NumberFormatValues.Decimal }, // 1, 2, 3...
428
+ new FootnoteNumberingStart { Val = 1 }, // Start at 1
429
+ new FootnoteNumberingRestart { Val = FootnoteRestartValues.RestartAtPage }
430
+ ));
431
+ ```
432
+
433
+ ### 2.5 EndnotesPart — Same Pattern
434
+
435
+ Endnotes follow the exact same structure as footnotes but use `EndnotesPart`.
436
+
437
+ ```csharp
438
+ var endnotesPart = mainDocumentPart.AddNewPart<EndnotesPart>();
439
+ var endnotes = new Endnotes();
440
+
441
+ // Endnote ID=0: Separator (same pattern as footnotes)
442
+ var endnoteSeparator = new Endnote(
443
+ new Paragraph(new Run(new Separator()))
444
+ ) { Type = FootnoteEndnoteValues.Separator, Id = 0 };
445
+ endnotes.Append(endnoteSeparator);
446
+
447
+ // Endnote ID=1: ContinuationSeparator
448
+ var endnoteContSep = new Endnote(
449
+ new Paragraph(new Run(new ContinuationSeparator()))
450
+ ) { Type = FootnoteEndnoteValues.ContinuationSeparator, Id = 1 };
451
+ endnotes.Append(endnoteContSep);
452
+
453
+ endnotesPart.Endnotes = endnotes;
454
+ endnotesPart.Endnotes.Save();
455
+
456
+ // In document body, use EndnoteReference instead of FootnoteReference
457
+ var endnoteRefRun = new Run(
458
+ new RunProperties(
459
+ new VerticalTextAlignment { Val = VerticalPositionValues.Superscript }
460
+ ),
461
+ new EndnoteReference { Id = 3 }
462
+ );
463
+ body.Append(new Paragraph(
464
+ new Run(new Text("An endnote marker.") { Space = SpaceProcessingModeValues.Preserve }),
465
+ endnoteRefRun
466
+ ));
467
+
468
+ // Corresponding Endnote content in EndnotesPart
469
+ var newEndnote = new Endnote { Id = 3 };
470
+ newEndnote.Append(new Paragraph(
471
+ new ParagraphProperties(new ParagraphStyleId { Val = "EndnoteText" }),
472
+ new Run(new EndnoteReferenceMark()),
473
+ new Run(new Text(" This is the endnote content.") { Space = SpaceProcessingModeValues.Preserve })
474
+ ));
475
+ endnotesPart.Endnotes!.Append(newEndnote);
476
+ ```
477
+
478
+ ### 2.6 Endnote Placement via SectionProperties
479
+
480
+ ```csharp
481
+ // EndnoteProperties on SectionProperties controls endnote placement
482
+ sectPr.Append(new EndnoteProperties(
483
+ new EndnotePosition { Val = EndnotePositionValues.EndOfDocument } // Default
484
+ // Other options: EndOfSection, BeneathText (rarely used for endnotes)
485
+ ));
486
+ ```
487
+
488
+ ---
489
+
490
+ ## 3. Field Codes — Comprehensive
491
+
492
+ ### 3.1 SimpleField vs Complex Field Architecture
493
+
494
+ **SimpleField** — single element, easier to write but less control:
495
+ ```csharp
496
+ // <w:fldSimple w:instr=" PAGE "><w:r><w:t>1</w:t></w:r></w:fldSimple>
497
+ new SimpleField(new Run(new Text("1"))) { Instruction = " PAGE " }
498
+ ```
499
+
500
+ **Complex Field (Begin/Separate/End)** — full control over each field component:
501
+ ```csharp
502
+ // <w:r><w:fldChar w:fldCharType="begin"/></w:r>
503
+ // <w:r><w:instrText> PAGE </w:instrText></w:r>
504
+ // <w:r><w:fldChar w:fldCharType="separate"/></w:r>
505
+ // <w:r><w:t>1</w:t></w:r>
506
+ // <w:r><w:fldChar w:fldCharType="end"/></w:r>
507
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
508
+ new Run(new FieldCode(" PAGE ") { Space = SpaceProcessingModeValues.Preserve }),
509
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
510
+ new Run(new Text("1")), // Cached result shown until update
511
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End }),
512
+ ```
513
+
514
+ **Key differences:**
515
+ - `SimpleField` is one `w:fldSimple` element containing one `w:r`
516
+ - Complex field uses `FieldChar` with `FieldCharValues.Begin/Separate/End` to delimit regions
517
+ - `FieldCode` is `w:instrText` — contains the field instruction string
518
+ - The text between `Separate` and `End` is the "cached result" shown before update
519
+ - After `Separate`, `FieldCode` contains the switches that define field behavior
520
+
521
+ ### 3.2 PAGE, NUMPAGES, DATE, TIME
522
+
523
+ **PAGE — current page number:**
524
+ ```csharp
525
+ // SimpleField version
526
+ new SimpleField(new Run(new Text("1"))) { Instruction = " PAGE " }
527
+
528
+ // Complex field version
529
+ new Paragraph(
530
+ new Run(new Text("Page ") { Space = SpaceProcessingModeValues.Preserve }),
531
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
532
+ new Run(new FieldCode(" PAGE ") { Space = SpaceProcessingModeValues.Preserve }),
533
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
534
+ new Run(new Text("1")), // Cached value
535
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End })
536
+ );
537
+ ```
538
+
539
+ **NUMPAGES — total page count:**
540
+ ```csharp
541
+ new Paragraph(
542
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
543
+ new Run(new FieldCode(" NUMPAGES ") { Space = SpaceProcessingModeValues.Preserve }),
544
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
545
+ new Run(new Text("10")),
546
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End }),
547
+ new Run(new Text(" pages") { Space = SpaceProcessingModeValues.Preserve })
548
+ );
549
+ ```
550
+
551
+ **DATE — current date with format switch:**
552
+ ```csharp
553
+ // DATE with custom format: \@ "yyyy-MM-dd"
554
+ // The \@ switch specifies the date picture
555
+ new Paragraph(
556
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
557
+ new Run(new FieldCode(" DATE \\@ \"yyyy-MM-dd\" ") { Space = SpaceProcessingModeValues.Preserve }),
558
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
559
+ new Run(new Text("2026-03-22")),
560
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End })
561
+ );
562
+
563
+ // DATE with time: \@ "MMMM d, yyyy h:mm AM/PM"
564
+ new Run(new FieldCode(" DATE \\@ \"MMMM d, yyyy h:mm AM/PM\" ") { Space = SpaceProcessingModeValues.Preserve }),
565
+
566
+ // DATE with locale: \* MERGEFORMAT preserves formatting on update
567
+ new Run(new FieldCode(" DATE \\@ \"d/M/yyyy\" \\* MERGEFORMAT ") { Space = SpaceProcessingModeValues.Preserve }),
568
+ ```
569
+
570
+ **TIME — current time:**
571
+ ```csharp
572
+ new Paragraph(
573
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
574
+ new Run(new FieldCode(" TIME \\@ \"HH:mm:ss\" ") { Space = SpaceProcessingModeValues.Preserve }),
575
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
576
+ new Run(new Text("14:30:00")),
577
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End })
578
+ );
579
+ ```
580
+
581
+ ### 3.3 FILENAME, AUTHOR, TITLE (Document Properties)
582
+
583
+ These fields pull from the document's core properties.
584
+
585
+ ```csharp
586
+ // FILENAME — document filename
587
+ new Run(new FieldCode(" FILENAME ") { Space = SpaceProcessingModeValues.Preserve }),
588
+
589
+ // FILENAME with path: \* MERGEFORMAT
590
+ new Run(new FieldCode(" FILENAME \\* MERGEFORMAT ") { Space = SpaceProcessingModeValues.Preserve }),
591
+
592
+ // AUTHOR — from document core properties
593
+ new Run(new FieldCode(" AUTHOR ") { Space = SpaceProcessingModeValues.Preserve }),
594
+
595
+ // TITLE — from document core properties
596
+ new Run(new FieldCode(" TITLE ") { Space = SpaceProcessingModeValues.Preserve }),
597
+
598
+ // SUBJECT — from document core properties
599
+ new Run(new FieldCode(" SUBJECT ") { Space = SpaceProcessingModeValues.Preserve }),
600
+
601
+ // Keywords — from document core properties
602
+ new Run(new FieldCode(" KEYWORDS ") { Space = SpaceProcessingModeValues.Preserve }),
603
+
604
+ // All document property fields
605
+ new Paragraph(
606
+ new Run(new Text("Title: ") { Space = SpaceProcessingModeValues.Preserve }),
607
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
608
+ new Run(new FieldCode(" TITLE ") { Space = SpaceProcessingModeValues.Preserve }),
609
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
610
+ new Run(new Text("My Document")),
611
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End })
612
+ );
613
+ ```
614
+
615
+ **Set document properties programmatically:**
616
+ ```csharp
617
+ // Set core properties via PackageProperties (OfficePackage)
618
+ var package = doc.ExtendedFilePropertiesPart?.Properties;
619
+ if (package != null)
620
+ {
621
+ package.Creator = "Author Name";
622
+ package.Title = "Document Title";
623
+ package.Subject = "Subject";
624
+ package.Description = "Description";
625
+ package.Keywords = "keyword1, keyword2";
626
+ package.Save();
627
+ }
628
+ ```
629
+
630
+ ### 3.4 REF — Cross-Reference to Bookmark
631
+
632
+ `REF` retrieves the text of a bookmarked paragraph or the value of a REF field.
633
+
634
+ ```csharp
635
+ // First, create a bookmark around some content
636
+ var bookmarkStart = new BookmarkStart { Id = "100", Name = "Figure1Caption" };
637
+ var bookmarkEnd = new BookmarkEnd { Id = "100" };
638
+ var captionPara = new Paragraph(
639
+ new Run(new Text("Figure 1: Architecture diagram") { Space = SpaceProcessingModeValues.Preserve })
640
+ );
641
+ body.Append(new Paragraph(
642
+ new ParagraphProperties(new ParagraphStyleId { Val = "Caption" }),
643
+ bookmarkStart,
644
+ new Run(new Text("Figure 1: Architecture diagram")),
645
+ bookmarkEnd
646
+ ));
647
+
648
+ // Now reference it with REF field
649
+ var refField = new Paragraph(
650
+ new Run(new Text("As shown in ") { Space = SpaceProcessingModeValues.Preserve }),
651
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
652
+ new Run(new FieldCode(" REF Figure1Caption \\* MERGEFORMAT ") { Space = SpaceProcessingModeValues.Preserve }),
653
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
654
+ new Run(new Text("Figure 1: Architecture diagram")), // Cached result
655
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End }),
656
+ new Run(new Text(", the system consists of...") { Space = SpaceProcessingModeValues.Preserve })
657
+ );
658
+ ```
659
+
660
+ **REF switches:**
661
+ | Switch | Effect |
662
+ |--------|--------|
663
+ | `\r` | Insert bookmarked text but as hyperlink |
664
+ | `\h` | Make REF a hyperlink to the bookmark |
665
+ | `\n` | Suppress paragraph number |
666
+ | `\p` | Show relative position (above/below) |
667
+ | `\t` | Suppress trailing spaces |
668
+ | `\* MERGEFORMAT` | Preserve formatting |
669
+
670
+ ### 3.5 SEQ — Sequence Numbering for Figures/Tables
671
+
672
+ `SEQ` generates auto-incrementing numbers for elements like figures, tables, and listings.
673
+
674
+ ```csharp
675
+ // First figure caption
676
+ var fig1Caption = new Paragraph(
677
+ new ParagraphProperties(new ParagraphStyleId { Val = "Caption" }),
678
+ new Run(new Text("Figure ") { Space = SpaceProcessingModeValues.Preserve }),
679
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
680
+ new Run(new FieldCode(" SEQ Figure \\* ARABIC ") { Space = SpaceProcessingModeValues.Preserve }),
681
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
682
+ new Run(new Text("1")),
683
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End }),
684
+ new Run(new Text(": System Architecture") { Space = SpaceProcessingModeValues.Preserve })
685
+ );
686
+
687
+ // Second figure (Word auto-increments)
688
+ var fig2Caption = new Paragraph(
689
+ new ParagraphProperties(new ParagraphStyleId { Val = "Caption" }),
690
+ new Run(new Text("Figure ") { Space = SpaceProcessingModeValues.Preserve }),
691
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
692
+ new Run(new FieldCode(" SEQ Figure \\* ARABIC ") { Space = SpaceProcessingModeValues.Preserve }),
693
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
694
+ new Run(new Text("2")),
695
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End }),
696
+ new Run(new Text(": Data Flow") { Space = SpaceProcessingModeValues.Preserve })
697
+ );
698
+
699
+ // Reference a figure number
700
+ var figRef = new Paragraph(
701
+ new Run(new Text("See Figure ") { Space = SpaceProcessingModeValues.Preserve }),
702
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
703
+ new Run(new FieldCode(" SEQ Figure \\* ARABIC ") { Space = SpaceProcessingModeValues.Preserve }),
704
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
705
+ new Run(new Text("1")),
706
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End }),
707
+ new Run(new Text(" above.") { Space = SpaceProcessingModeValues.Preserve })
708
+ );
709
+
710
+ // SEQ sequence identifier: "Figure" can be any name
711
+ // Multiple sequences: SEQ Figure, SEQ Table, SEQ Listing are independent
712
+ ```
713
+
714
+ ### 3.6 HYPERLINK — Internal and External Links
715
+
716
+ ```csharp
717
+ // External hyperlink (to URL)
718
+ var extHyperlinkRel = mainDocumentPart.AddHyperlinkRelationship(
719
+ new Uri("https://example.com"), true);
720
+ var extHyperlink = new Hyperlink(
721
+ new Run(
722
+ new RunProperties(new Color { Val = "0563C1" }, new Underline { Val = UnderlineValues.Single }),
723
+ new Text("Visit Example.com")
724
+ )
725
+ ) { Id = extHyperlinkRel.Id }; // Id references the relationship
726
+
727
+ // Internal hyperlink (to bookmark)
728
+ var intHyperlink = new Hyperlink(
729
+ new Run(
730
+ new RunProperties(new Color { Val = "0563C1" }, new Underline { Val = UnderlineValues.Single }),
731
+ new Text("Go to Chapter 1")
732
+ )
733
+ ) { Anchor = "Chapter1Bookmark" }; // Anchor = bookmark name
734
+
735
+ // HYPERLINK field for advanced cases (with screen tip)
736
+ var hyperlinkedField = new Run(
737
+ new FieldChar { FieldCharType = FieldCharValues.Begin }),
738
+ new Run(new FieldCode(" HYPERLINK \\l \"Chapter1Bookmark\" \\t \"_top\" ") { Space = SpaceProcessingModeValues.Preserve }),
739
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
740
+ new Run(new Text("Go to Chapter 1")),
741
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End })
742
+ );
743
+
744
+ // \l = target (anchor for internal, URL for external)
745
+ // \t = target frame (optional, e.g., "_top" to open in same window)
746
+ ```
747
+
748
+ ### 3.7 MERGEFIELD — Mail Merge
749
+
750
+ ```csharp
751
+ // MERGEFIELD uses a special syntax: MERGEFIELD FieldName
752
+ // The field name must match a mail merge data source column name
753
+
754
+ // Simple MERGEFIELD
755
+ var mergeFieldPara = new Paragraph(
756
+ new Run(new Text("Dear ") { Space = SpaceProcessingModeValues.Preserve }),
757
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
758
+ new Run(new FieldCode(" MERGEFIELD FirstName ") { Space = SpaceProcessingModeValues.Preserve }),
759
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
760
+ new Run(new Text("«FirstName»")), // «» are Word's placeholder markers
761
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End }),
762
+ new Run(new Text(",") { Space = SpaceProcessingModeValues.Preserve })
763
+ );
764
+
765
+ // Full name with formatting
766
+ var mergeFieldWithFormat = new Paragraph(
767
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
768
+ new Run(new FieldCode(" MERGEFIELD FullName \\* UPPERCASE ") { Space = SpaceProcessingModeValues.Preserve }),
769
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
770
+ new Run(new Text("«FullName»")),
771
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End })
772
+ );
773
+
774
+ // To actually perform mail merge, use Word's MailMerge settings
775
+ var settingsPart = mainDocumentPart.AddNewPart<DocumentSettingsPart>();
776
+ settingsPart.Settings = new Settings(
777
+ new MailMerge(
778
+ new DataType { Val = MailMergeDataValues.TextFile },
779
+ new DataSourceReference { Id = "rIdForDataSource" }
780
+ )
781
+ );
782
+ ```
783
+
784
+ ### 3.8 IF Field — Conditional Text
785
+
786
+ The `IF` field evaluates a condition and displays one of two text values. Commonly used with `MERGEFIELD`.
787
+
788
+ ```csharp
789
+ // IF Field syntax: IF [expression] [operator] [value] "true_text" "false_text"
790
+ // Often combined with MERGEFIELD:
791
+
792
+ // If the recipient's region equals "USA", show "Dear Customer", otherwise "Dear Valued Customer"
793
+ var ifFieldPara = new Paragraph(
794
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
795
+ // Complex nested: { IF { MERGEFIELD Region } = "USA" "Dear American Customer" "Dear Customer" }
796
+ new Run(new FieldCode(" IF ") { Space = SpaceProcessingModeValues.Preserve }),
797
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }), // Nested MERGEFIELD begin
798
+ new Run(new FieldCode(" MERGEFIELD Region ") { Space = SpaceProcessingModeValues.Preserve }),
799
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
800
+ new Run(new Text("«Region»")),
801
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End }), // Nested MERGEFIELD end
802
+ new Run(new FieldCode(" = \"USA\" \"Dear American Customer\" \"Dear Customer\" ") { Space = SpaceProcessingModeValues.Preserve }),
803
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
804
+ new Run(new Text("Dear Customer")),
805
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End })
806
+ );
807
+
808
+ // Note: Nested fields within IF are tricky with complex field syntax.
809
+ // A simpler approach: use two separate IF fields checking a bookmark value.
810
+ ```
811
+
812
+ ### 3.9 STYLEREF — Reference Heading Text
813
+
814
+ `STYLEREF` displays the text of the nearest paragraph with a specified style — useful for running headers.
815
+
816
+ ```csharp
817
+ // STYLEREF Heading1 — inserts the text of the most recent Heading1 paragraph
818
+ // Great for running headers that show the current chapter
819
+
820
+ // Running header in footer
821
+ var footerPart = mainDocumentPart.AddNewPart<FooterPart>();
822
+ footerPart.Footer = new Footer(
823
+ new Paragraph(
824
+ new ParagraphProperties(
825
+ new Justification { Val = JustificationValues.Right }
826
+ ),
827
+ // Left-aligned: chapter heading
828
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
829
+ new Run(new FieldCode(" STYLEREF \"Heading 1\" ") { Space = SpaceProcessingModeValues.Preserve }),
830
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
831
+ new Run(new Text("Chapter Title")),
832
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End }),
833
+ new Run(new Text("\t") { Space = SpaceProcessingModeValues.Preserve }), // Tab
834
+ // Right-aligned: page number
835
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
836
+ new Run(new FieldCode(" PAGE ") { Space = SpaceProcessingModeValues.Preserve }),
837
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
838
+ new Run(new Text("1")),
839
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End })
840
+ )
841
+ );
842
+
843
+ // STYLEREF with \n switch to suppress paragraph numbering
844
+ new Run(new FieldCode(" STYLEREF \"Heading 1\" \\n ") { Space = SpaceProcessingModeValues.Preserve }),
845
+
846
+ // STYLEREF with \p switch to show relative position
847
+ new Run(new FieldCode(" STYLEREF \"Heading 2\" \\p ") { Space = SpaceProcessingModeValues.Preserve }),
848
+ ```
849
+
850
+ ### 3.10 SET and ASK Fields
851
+
852
+ `SET` stores a value in a variable. `ASK` prompts the user and stores their response.
853
+
854
+ ```csharp
855
+ // SET — define a document variable (accessed via DOCPROPERTY or REF)
856
+ var setField = new Paragraph(
857
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
858
+ new Run(new FieldCode(" SET MyVariable \"some value\" ") { Space = SpaceProcessingModeValues.Preserve }),
859
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
860
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End })
861
+ );
862
+
863
+ // REF to read the variable
864
+ var refMyVar = new Paragraph(
865
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
866
+ new Run(new FieldCode(" REF MyVariable ") { Space = SpaceProcessingModeValues.Preserve }),
867
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
868
+ new Run(new Text("some value")),
869
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End })
870
+ );
871
+
872
+ // ASK — prompt user for input when field is updated
873
+ // Note: ASK displays a dialog box when updated
874
+ var askField = new Paragraph(
875
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
876
+ new Run(new FieldCode(" ASK AuthorName \"Enter author name:\" ") { Space = SpaceProcessingModeValues.Preserve }),
877
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
878
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End }),
879
+ // REF to display the stored value
880
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
881
+ new Run(new FieldCode(" REF AuthorName ") { Space = SpaceProcessingModeValues.Preserve }),
882
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
883
+ new Run(new Text("Author Name")),
884
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End })
885
+ );
886
+ ```
887
+
888
+ ### 3.11 Calculated Fields (= Expressions)
889
+
890
+ The `=` field evaluates arithmetic expressions.
891
+
892
+ ```csharp
893
+ // = field with arithmetic
894
+ var calcPara = new Paragraph(
895
+ new Run(new Text("Total: $") { Space = SpaceProcessingModeValues.Preserve }),
896
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
897
+ new Run(new FieldCode(" = 100 + 250 - 30 ") { Space = SpaceProcessingModeValues.Preserve }),
898
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
899
+ new Run(new Text("320")),
900
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End })
901
+ );
902
+
903
+ // = with multiplication using SEQ references
904
+ var calcWithSeq = new Paragraph(
905
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Begin }),
906
+ new Run(new FieldCode(" = 3 * 5 ") { Space = SpaceProcessingModeValues.Preserve }),
907
+ new Run(new FieldChar { FieldCharType = FieldCharValues.Separate }),
908
+ new Run(new Text("15")),
909
+ new Run(new FieldChar { FieldCharType = FieldCharValues.End })
910
+ );
911
+
912
+ // Combine with formatting
913
+ new Run(new FieldCode(" = 1000 * 1.08 \\# \"#,##0.00\" ") { Space = SpaceProcessingModeValues.Preserve }),
914
+ // \# switch applies number format to result
915
+ ```
916
+
917
+ ### 3.12 UpdateFieldsOnOpen — Automatic Field Updates
918
+
919
+ ```csharp
920
+ // Settings that trigger field updates when document opens
921
+ var settingsPart = mainDocumentPart.AddNewPart<DocumentSettingsPart>();
922
+ settingsPart.Settings = new Settings(
923
+ // Update all fields (TOC, REF, PAGE, etc.) on open
924
+ new UpdateFieldsOnOpen { Val = true }
925
+ );
926
+ settingsPart.Settings.Save();
927
+
928
+ // Additional field-related settings:
929
+ var additionalSettings = new Settings(
930
+ // Auto-format fractions: 1/2 → ½
931
+ new AutomaticAdjustmentOfFontSizesToFitDocument(),
932
+
933
+ // True: use field codes instead of cached values on update
934
+ new UseXSLTWhenSaving(),
935
+
936
+ // Mail merge settings
937
+ new MailMerge(
938
+ new MainDocumentType { Val = MailMergeDocumentValues.FormLetters }
939
+ )
940
+ );
941
+ ```
942
+
943
+ ---
944
+
945
+ ## 4. Track Changes / Revisions
946
+
947
+ ### 4.1 Enabling Track Changes
948
+
949
+ Track changes must be explicitly enabled via DocumentSettingsPart.
950
+
951
+ ```csharp
952
+ var settingsPart = mainDocumentPart.AddNewPart<DocumentSettingsPart>();
953
+ settingsPart.Settings = new Settings(
954
+ // Enable track changes — any edit will be tracked
955
+ new TrackRevisions()
956
+ );
957
+
958
+ // Also recommended: prevent fields from being updated during tracking
959
+ settingsPart.Settings.Append(new DonNotTrackFormatting());
960
+ ```
961
+
962
+ ### 4.2 InsertedRun (w:ins) — Tracked Insertion
963
+
964
+ ```csharp
965
+ // <w:ins w:id="5" w:author="Alice" w:date="2026-03-22T10:00:00Z">
966
+ // <w:r>
967
+ // <w:t>Inserted text.</w:t>
968
+ // </w:r>
969
+ // </w:ins>
970
+
971
+ var insertedText = new InsertedRun(
972
+ new Run(
973
+ new Text("Inserted text.") { Space = SpaceProcessingModeValues.Preserve }
974
+ )
975
+ )
976
+ {
977
+ Author = "Alice",
978
+ Date = new DateTime(2026, 3, 22, 10, 0, 0, DateTimeKind.Utc),
979
+ Id = "5"
980
+ };
981
+
982
+ var para = new Paragraph(
983
+ new Run(new Text("Existing text. ") { Space = SpaceProcessingModeValues.Preserve }),
984
+ insertedText,
985
+ new Run(new Text(" More existing text.") { Space = SpaceProcessingModeValues.Preserve })
986
+ );
987
+ ```
988
+
989
+ ### 4.3 DeletedRun (w:del) — Tracked Deletion
990
+
991
+ **CRITICAL: Inside `w:del`, text MUST be `DeletedText` (`w:delText`), NOT `Text` (`w:t`)!**
992
+
993
+ ```csharp
994
+ // <w:del w:id="6" w:author="Alice" w:date="2026-03-22T10:05:00Z">
995
+ // <w:r>
996
+ // <w:rPr><w:b/></w:rPr>
997
+ // <w:delText>Deleted text.</w:delText>
998
+ // </w:r>
999
+ // </w:del>
1000
+
1001
+ var deletedRun = new DeletedRun(
1002
+ new Run(
1003
+ new RunProperties(new Bold()),
1004
+ new DeletedText("Deleted text.") { Space = SpaceProcessingModeValues.Preserve }
1005
+ )
1006
+ )
1007
+ {
1008
+ Author = "Alice",
1009
+ Date = new DateTime(2026, 3, 22, 10, 5, 0, DateTimeKind.Utc),
1010
+ Id = "6"
1011
+ };
1012
+
1013
+ var para = new Paragraph(
1014
+ new Run(new Text("Keep this. ") { Space = SpaceProcessingModeValues.Preserve }),
1015
+ deletedRun,
1016
+ new Run(new Text(" Keep this too.") { Space = SpaceProcessingModeValues.Preserve })
1017
+ );
1018
+
1019
+ // GOTCHA: Never use <w:t> inside <w:del> — use <w:delText> only.
1020
+ // Using w:t inside w:del causes corruption or silent repair by Word.
1021
+ ```
1022
+
1023
+ ### 4.4 RunPropertiesChange — Formatting Change Tracking
1024
+
1025
+ Records that a run's formatting was changed. The `w:rPrChange` goes inside `w:rPr`.
1026
+
1027
+ ```csharp
1028
+ // <w:r>
1029
+ // <w:rPr>
1030
+ // <w:b/> <!-- New: bold -->
1031
+ // <w:rPrChange w:id="7" w:author="Bob" w:date="2026-03-22T11:00:00Z">
1032
+ // <w:rPr/> <!-- Old: no formatting -->
1033
+ // </w:rPrChange>
1034
+ // </w:rPr>
1035
+ // <w:t>Formatted text.</w:t>
1036
+ // </w:r>
1037
+
1038
+ // The current (new) formatting is in the outer w:rPr
1039
+ // The old (previous) formatting is in the w:rPrChange child
1040
+ var formattedTextRun = new Run(
1041
+ new RunProperties(
1042
+ new Bold(), // New formatting: now bold
1043
+ new RunPropertiesChange( // Records the old formatting (empty = not bold)
1044
+ new RunProperties() // Empty = previously had no formatting
1045
+ )
1046
+ {
1047
+ Author = "Bob",
1048
+ Date = new DateTime(2026, 3, 22, 11, 0, 0, DateTimeKind.Utc),
1049
+ Id = "7"
1050
+ }
1051
+ ),
1052
+ new Text("Formatted text.") { Space = SpaceProcessingModeValues.Preserve }
1053
+ );
1054
+ ```
1055
+
1056
+ ### 4.5 ParagraphPropertiesChange
1057
+
1058
+ Records that paragraph-level properties were changed.
1059
+
1060
+ ```csharp
1061
+ // <w:pPr>
1062
+ // <w:jc w:val="center"/> <!-- New: centered -->
1063
+ // <w:pPrChange w:id="8" w:author="Bob" w:date="2026-03-22T11:05:00Z">
1064
+ // <w:pPr>
1065
+ // <w:jc w:val="left"/> <!-- Old: left-aligned -->
1066
+ // </w:pPr>
1067
+ // </w:pPrChange>
1068
+ // </w:pPr>
1069
+
1070
+ var changedPara = new Paragraph(
1071
+ new ParagraphProperties(
1072
+ new Justification { Val = JustificationValues.Center }, // New
1073
+ new ParagraphPropertiesChange(
1074
+ new ParagraphProperties(
1075
+ new Justification { Val = JustificationValues.Left } // Old
1076
+ )
1077
+ )
1078
+ {
1079
+ Author = "Bob",
1080
+ Date = new DateTime(2026, 3, 22, 11, 5, 0, DateTimeKind.Utc),
1081
+ Id = "8"
1082
+ }
1083
+ ),
1084
+ new Run(new Text("Centered paragraph."))
1085
+ );
1086
+ ```
1087
+
1088
+ ### 4.6 ParagraphMarkRunPropertiesChange
1089
+
1090
+ Records that the paragraph mark's formatting (trailing formatting) was changed.
1091
+
1092
+ ```csharp
1093
+ // <w:p>
1094
+ // <w:pPr>
1095
+ // <w:pPrChange .../>
1096
+ // </w:pPr>
1097
+ // <w:r>
1098
+ // <w:rPr>
1099
+ // <w:b/> <!-- New paragraph mark: bold -->
1100
+ // <w:rPrChange w:id="9" ...>
1101
+ // <w:rPr/> <!-- Old: no formatting on paragraph mark -->
1102
+ // </w:rPrChange>
1103
+ // </w:rPr>
1104
+ // </w:r>
1105
+ // </w:r>
1106
+ ```
1107
+
1108
+ ### 4.7 Table Revision Marks
1109
+
1110
+ ```csharp
1111
+ // TableRowInsertionRevision — a row was inserted
1112
+ // <w:trPr>
1113
+ // <w:ins w:id="10" w:author="Alice" w:date="..."/>
1114
+ // </w:trPr>
1115
+
1116
+ var insertedRow = new TableRow(
1117
+ new TableRowProperties(
1118
+ new TableRowInsertionRevision
1119
+ {
1120
+ Author = "Alice",
1121
+ Date = new DateTime(2026, 3, 22, 12, 0, 0, DateTimeKind.Utc),
1122
+ Id = "10"
1123
+ }
1124
+ ),
1125
+ new TableCell(new Paragraph(new Run(new Text("New row cell"))))
1126
+ );
1127
+
1128
+ // TableCellInsertionRevision — a cell was inserted
1129
+ var insertedCell = new TableCell(
1130
+ new TableCellProperties(
1131
+ new TableCellInsertionRevision
1132
+ {
1133
+ Author = "Alice",
1134
+ Date = new DateTime(2026, 3, 22, 12, 1, 0, DateTimeKind.Utc),
1135
+ Id = "11"
1136
+ }
1137
+ ),
1138
+ new Paragraph(new Run(new Text("New cell")))
1139
+ );
1140
+ ```
1141
+
1142
+ ### 4.8 SectionPropertiesChange
1143
+
1144
+ ```csharp
1145
+ // <w:sectPr>
1146
+ // <w:sectPrChange w:id="12" w:author="Bob" w:date="...">
1147
+ // <w:sectPr>
1148
+ // <w:pgSz w:w="12240" w:h="15840"/> <!-- Old: Letter -->
1149
+ // </w:sectPr>
1150
+ // </w:sectPrChange>
1151
+ // <w:pgSz w:w="16838" w:h="11906"/> <!-- New: A4 -->
1152
+ // </w:sectPr>
1153
+
1154
+ var changedSection = new SectionProperties(
1155
+ new PageSize { Width = 16838U, Height = 11906U }, // New: A4
1156
+ new SectionPropertiesChange(
1157
+ new SectionProperties(
1158
+ new PageSize { Width = 12240U, Height = 15840U } // Old: Letter
1159
+ )
1160
+ )
1161
+ {
1162
+ Author = "Bob",
1163
+ Date = new DateTime(2026, 3, 22, 12, 30, 0, DateTimeKind.Utc),
1164
+ Id = "12"
1165
+ }
1166
+ );
1167
+ ```
1168
+
1169
+ ### 4.9 NumberingChange
1170
+
1171
+ ```csharp
1172
+ // <w:numPr>
1173
+ // <w:ilvl w:val="0"/>
1174
+ // <w:numId w:val="3"/>
1175
+ // <w:numPrChange w:id="13" w:author="Alice" w:date="...">
1176
+ // <w:numPr>
1177
+ // <w:ilvl w:val="0"/>
1178
+ // <w:numId w:val="1"/> <!-- Old: was numId 1 -->
1179
+ // </w:numPr>
1180
+ // </w:numPrChange>
1181
+ // </w:numPr>
1182
+
1183
+ var changedNumbering = new NumberingProperties(
1184
+ new NumberingLevelReference { Val = 0 },
1185
+ new NumberingId { Val = 3 }, // New: numId 3
1186
+ new NumberingChange(
1187
+ new NumberingProperties(
1188
+ new NumberingLevelReference { Val = 0 },
1189
+ new NumberingId { Val = 1 } // Old: numId 1
1190
+ )
1191
+ )
1192
+ {
1193
+ Author = "Alice",
1194
+ Date = new DateTime(2026, 3, 22, 13, 0, 0, DateTimeKind.Utc),
1195
+ Id = "13"
1196
+ }
1197
+ );
1198
+ ```
1199
+
1200
+ ### 4.10 Accepting All Revisions Programmatically
1201
+
1202
+ ```csharp
1203
+ // Accept all revisions: unwrap w:ins (keep content), remove w:del entirely
1204
+ public static void AcceptAllRevisions(WordprocessingDocument doc)
1205
+ {
1206
+ var body = doc.MainDocumentPart?.Document?.Body;
1207
+ if (body == null) return;
1208
+
1209
+ // Accept insertions: remove w:ins wrapper, keep inner runs
1210
+ var insertions = body.Descendants<InsertedRun>().ToList();
1211
+ foreach (var ins in insertions)
1212
+ {
1213
+ var parent = ins.Parent;
1214
+ if (parent == null) continue;
1215
+ var children = ins.ChildElements.ToList();
1216
+ foreach (var child in children)
1217
+ {
1218
+ child.Remove();
1219
+ parent.InsertBefore(child, ins);
1220
+ }
1221
+ ins.Remove();
1222
+ }
1223
+
1224
+ // Accept deletions: remove entire w:del element
1225
+ var deletions = body.Descendants<DeletedRun>().ToList();
1226
+ foreach (var del in deletions)
1227
+ del.Remove();
1228
+ }
1229
+
1230
+ // Also accept formatting changes:
1231
+ // For w:rPrChange: replace the entire RunProperties with the "old" properties inside the change
1232
+ // For w:pPrChange: replace with the old properties
1233
+ ```
1234
+
1235
+ ### 4.11 Rejecting All Revisions Programmatically
1236
+
1237
+ ```csharp
1238
+ // Reject all revisions: unwrap w:del (restore text), remove w:ins entirely
1239
+ public static void RejectAllRevisions(WordprocessingDocument doc)
1240
+ {
1241
+ var body = doc.MainDocumentPart?.Document?.Body;
1242
+ if (body == null) return;
1243
+
1244
+ // Reject insertions: remove entire w:ins element and its content
1245
+ var insertions = body.Descendants<InsertedRun>().ToList();
1246
+ foreach (var ins in insertions)
1247
+ ins.Remove();
1248
+
1249
+ // Reject deletions: unwrap w:del, convert w:delText back to w:t
1250
+ var deletions = body.Descendants<DeletedRun>().ToList();
1251
+ foreach (var del in deletions)
1252
+ {
1253
+ var parent = del.Parent;
1254
+ if (parent == null) continue;
1255
+ foreach (var run in del.Elements<Run>().ToList())
1256
+ {
1257
+ foreach (var delText in run.Elements<DeletedText>().ToList())
1258
+ {
1259
+ var text = new Text(delText.Text) { Space = delText.Space };
1260
+ delText.InsertAfterSelf(text);
1261
+ delText.Remove();
1262
+ }
1263
+ run.Remove();
1264
+ parent.InsertBefore(run, del);
1265
+ }
1266
+ del.Remove();
1267
+ }
1268
+ }
1269
+ ```
1270
+
1271
+ ### 4.12 MoveFrom / MoveTo — Tracked Text Moving
1272
+
1273
+ ```csharp
1274
+ // MoveFrom (w:moveFrom) marks the origin of moved text
1275
+ // MoveTo (w:moveTo) marks the destination
1276
+ // Both must have the same w:id
1277
+
1278
+ // <w:moveFrom w:id="14" w:author="Alice" w:date="...">
1279
+ // <w:r><w:t>Text that was moved.</w:t></w:r>
1280
+ // </w:moveFrom>
1281
+
1282
+ // At destination:
1283
+ // <w:moveTo w:id="14" w:author="Alice" w:date="...">
1284
+ // <w:r><w:t>Text that was moved.</w:t></w:r>
1285
+ // </w:moveTo>
1286
+
1287
+ var movedFrom = new MoveFromRun(
1288
+ new Run(new Text("Text that was moved.") { Space = SpaceProcessingModeValues.Preserve })
1289
+ )
1290
+ {
1291
+ Author = "Alice",
1292
+ Date = new DateTime(2026, 3, 22, 14, 0, 0, DateTimeKind.Utc),
1293
+ Id = "14"
1294
+ };
1295
+
1296
+ var movedTo = new MoveToRun(
1297
+ new Run(new Text("Text that was moved.") { Space = SpaceProcessingModeValues.Preserve })
1298
+ )
1299
+ {
1300
+ Author = "Alice",
1301
+ Date = new DateTime(2026, 3, 22, 14, 0, 0, DateTimeKind.Utc),
1302
+ Id = "14"
1303
+ };
1304
+ ```
1305
+
1306
+ ### 4.13 RevisionId Generation
1307
+
1308
+ All revision elements need unique, monotonically increasing integer IDs.
1309
+
1310
+ ```csharp
1311
+ public static int GetNextRevisionId(Body body)
1312
+ {
1313
+ int maxId = 0;
1314
+ foreach (var elem in body.Descendants<OpenXmlElement>())
1315
+ {
1316
+ // Check common revision element types for Id attribute
1317
+ var idAttr = elem.GetAttributes()
1318
+ .FirstOrDefault(a => a.LocalName == "id" &&
1319
+ (elem is InsertedRun or DeletedRun or DeletedText or
1320
+ MoveFromRun or MoveToRun or RunPropertiesChange or
1321
+ ParagraphPropertiesChange or SectionPropertiesChange or
1322
+ TableRowInsertionRevision or TableCellInsertionRevision));
1323
+ if (idAttr.Value != null && int.TryParse(idAttr.Value, out int id) && id > maxId)
1324
+ maxId = id;
1325
+ }
1326
+ return maxId + 1;
1327
+ }
1328
+
1329
+ // Simpler approach: scan all elements with "id" attribute in the document
1330
+ public static int GetNextRevisionIdSimple(Body body)
1331
+ {
1332
+ int maxId = 0;
1333
+ foreach (var elem in body.Descendants<OpenXmlElement>())
1334
+ {
1335
+ foreach (var attr in elem.GetAttributes())
1336
+ {
1337
+ if (attr.LocalName == "id" && int.TryParse(attr.Value, out int id) && id > maxId)
1338
+ maxId = id;
1339
+ }
1340
+ }
1341
+ return maxId + 1;
1342
+ }
1343
+ ```
1344
+
1345
+ ---
1346
+
1347
+ ## 5. Comments (4-File System)
1348
+
1349
+ ### 5.1 Full 4-File Comment System Setup
1350
+
1351
+ Comments require four XML files plus markers in `document.xml`.
1352
+
1353
+ ```csharp
1354
+ // This method creates a complete comment with all 4 files properly initialized
1355
+ public static int AddFullComment(
1356
+ WordprocessingDocument doc,
1357
+ string text,
1358
+ string author,
1359
+ string initials,
1360
+ string rangeText,
1361
+ int? existingCommentId = null)
1362
+ {
1363
+ var mainPart = doc.MainDocumentPart
1364
+ ?? throw new InvalidOperationException("Document has no MainDocumentPart.");
1365
+
1366
+ int commentId = existingCommentId ?? GetNextCommentId(doc);
1367
+
1368
+ // Generate paraId (8-char hex) and durableId (8-digit hex)
1369
+ string paraId = Guid.NewGuid().ToString("N")[..8].ToUpperInvariant();
1370
+ string durableId = new Random().Next(0x10000000, 0xFFFFFFFF).ToString("X8");
1371
+
1372
+ var body = mainPart.Document!.Body!;
1373
+
1374
+ // ─────────────────────────────────────────────────────────────
1375
+ // FILE 1: word/comments.xml — Main comment content
1376
+ // ─────────────────────────────────────────────────────────────
1377
+ var commentsPart = mainPart.WordprocessingCommentsPart
1378
+ ?? mainPart.AddNewPart<WordprocessingCommentsPart>();
1379
+
1380
+ if (commentsPart.Comments == null)
1381
+ commentsPart.Comments = new Comments();
1382
+
1383
+ // Create a paragraph for the comment with a unique paraId (via w14:paraId)
1384
+ var commentPara = new Paragraph(
1385
+ new ParagraphProperties(
1386
+ new ParagraphStyleId { Val = "CommentText" },
1387
+ // w14:paraId for modern comment threading
1388
+ new乳啜攠嘶嘐呓顾纨asiId { Val = paraId }
1389
+ ),
1390
+ new Run(
1391
+ new RunProperties(new RunStyle { Val = "CommentReference" }),
1392
+ new AnnotationReferenceMark()
1393
+ ),
1394
+ new Run(new Text(text))
1395
+ );
1396
+
1397
+ var comment = new Comment
1398
+ {
1399
+ Id = commentId.ToString(),
1400
+ Author = author,
1401
+ Date = DateTime.UtcNow,
1402
+ Initials = initials
1403
+ };
1404
+ comment.Append(commentPara);
1405
+ commentsPart.Comments.Append(comment);
1406
+ commentsPart.Comments.Save();
1407
+
1408
+ // ─────────────────────────────────────────────────────────────
1409
+ // FILE 2: word/commentsExtended.xml — W15 extensions (paraId, done status)
1410
+ // ─────────────────────────────────────────────────────────────
1411
+ var commentsExPart = mainPart.WordprocessingCommentsExPart
1412
+ ?? mainPart.AddNewPart<WordprocessingCommentsExPart>();
1413
+
1414
+ if (commentsExPart.CommentsEx == null)
1415
+ commentsExPart.CommentsEx = new CommentExCollection();
1416
+
1417
+ // w15:commentEx links the comment to its paragraph and tracks done/resolved
1418
+ var commentEx = new CommentEx
1419
+ {
1420
+ ParaId = new HexBinaryValue(paraId),
1421
+ Done = new OnOffValue(false) // done="0" = not resolved
1422
+ };
1423
+ commentsExPart.CommentsEx.Append(commentEx);
1424
+ commentsExPart.CommentsEx.Save();
1425
+
1426
+ // ─────────────────────────────────────────────────────────────
1427
+ // FILE 3: word/commentsIds.xml — Persistent ID mapping
1428
+ // ─────────────────────────────────────────────────────────────
1429
+ var commentsIdsPart = mainPart.WordprocessingCommentsIdsPart
1430
+ ?? mainPart.AddNewPart<WordprocessingCommentsIdsPart>();
1431
+
1432
+ if (commentsIdsPart.CommentsIds == null)
1433
+ commentsIdsPart.CommentsIds = new CommentIds();
1434
+
1435
+ // w16cid:commentId maps paraId to a durable (globally unique) ID
1436
+ var commentIdEntry = new CommentId
1437
+ {
1438
+ ParaId = new HexBinaryValue(paraId),
1439
+ DurableId = durableId
1440
+ };
1441
+ commentsIdsPart.CommentsIds.Append(commentIdEntry);
1442
+ commentsIdsPart.CommentsIds.Save();
1443
+
1444
+ // ─────────────────────────────────────────────────────────────
1445
+ // FILE 4: word/commentsExtensible.xml — W16 extensible
1446
+ // ─────────────────────────────────────────────────────────────
1447
+ var commentsExtPart = mainPart.WordprocessingCommentsExtensiblePart
1448
+ ?? mainPart.AddNewPart<WordprocessingCommentsExtensiblePart>();
1449
+
1450
+ if (commentsExtPart.CommentsExtensible == null)
1451
+ commentsExtPart.CommentsExtensible = new CommentExtensibleCollection();
1452
+
1453
+ // w16cex:commentExtensible provides the durable ID with UTC timestamp
1454
+ var extensibleEntry = new CommentExtensible
1455
+ {
1456
+ DurableId = durableId,
1457
+ DateUtc = DateTime.UtcNow
1458
+ };
1459
+ commentsExtPart.CommentsExtensible.Append(extensibleEntry);
1460
+ commentsExtPart.CommentsExtensible.Save();
1461
+
1462
+ // ─────────────────────────────────────────────────────────────
1463
+ // document.xml — Insert range markers around the target text
1464
+ // ─────────────────────────────────────────────────────────────
1465
+ // commentRangeStart and commentRangeEnd bracket the commented text
1466
+ // commentReference is a run containing the visible superscript number
1467
+ var rangeStart = new CommentRangeStart { Id = commentId.ToString() };
1468
+ var rangeEnd = new CommentRangeEnd { Id = commentId.ToString() };
1469
+ var refRun = new Run(
1470
+ new RunProperties(new RunStyle { Val = "CommentReference" }),
1471
+ new CommentReference { Id = commentId.ToString() }
1472
+ );
1473
+
1474
+ // Find the paragraph containing rangeText and insert markers
1475
+ // Simple approach: append at end of body
1476
+ body.Append(rangeStart);
1477
+ body.Append(new Paragraph(new Run(new Text(rangeText))));
1478
+ body.Append(rangeEnd);
1479
+ body.Append(new Paragraph(refRun)); // The comment ref must be in its own paragraph
1480
+
1481
+ return commentId;
1482
+ }
1483
+
1484
+ // Helper: get next comment ID
1485
+ private static int GetNextCommentId(WordprocessingDocument doc)
1486
+ {
1487
+ var commentsPart = doc.MainDocumentPart?.WordprocessingCommentsPart;
1488
+ if (commentsPart?.Comments == null) return 1;
1489
+ int max = 0;
1490
+ foreach (var c in commentsPart.Comments.Elements<Comment>())
1491
+ if (c.Id?.Value != null && int.TryParse(c.Id.Value, out int id) && id > max)
1492
+ max = id;
1493
+ return max + 1;
1494
+ }
1495
+ ```
1496
+
1497
+ **The 4 files at a glance:**
1498
+
1499
+ | File | Part Class | Content | Key Attributes |
1500
+ |------|-----------|---------|----------------|
1501
+ | `comments.xml` | `WordprocessingCommentsPart` | Comment text | `w:id`, `w:author`, `w:date`, `w:initials` |
1502
+ | `commentsExtended.xml` | `WordprocessingCommentsExPart` | W15 extensions | `w15:paraId`, `w15:done` |
1503
+ | `commentsIds.xml` | `WordprocessingCommentsIdsPart` | Persistent IDs | `w16cid:paraId`, `w16cid:durableId` |
1504
+ | `commentsExtensible.xml` | `WordprocessingCommentsExtensiblePart` | W16 extensible | `w16cex:durableId`, `w16cex:dateUtc` |
1505
+
1506
+ ### 5.2 Comment Reply (Threaded Comments)
1507
+
1508
+ ```csharp
1509
+ // To add a reply, create a new comment and link it to the parent via commentsExtended.xml
1510
+
1511
+ public static int AddCommentReply(
1512
+ WordprocessingDocument doc,
1513
+ int parentCommentId,
1514
+ string replyText,
1515
+ string author,
1516
+ string initials)
1517
+ {
1518
+ var mainPart = doc.MainDocumentPart!;
1519
+
1520
+ // Get parent's paraId from commentsExtended.xml
1521
+ var commentsExPart = mainPart.WordprocessingCommentsExPart;
1522
+ var parentParaId = "";
1523
+ if (commentsExPart?.CommentsEx != null)
1524
+ {
1525
+ var parentCommentEx = commentsExPart.CommentsEx
1526
+ .Elements<CommentEx>()
1527
+ .FirstOrDefault(ce =>
1528
+ ce.Parent is Comment c &&
1529
+ c.Id?.Value == parentCommentId.ToString());
1530
+ // Actually need to cross-reference through paraId...
1531
+ // Simpler: look up via comments.xml paraId
1532
+ }
1533
+
1534
+ // Generate new IDs for the reply
1535
+ int replyId = GetNextCommentId(doc);
1536
+ string replyParaId = Guid.NewGuid().ToString("N")[..8].ToUpperInvariant();
1537
+ string durableId = new Random().Next(0x10000000, 0xFFFFFFFF).ToString("X8");
1538
+
1539
+ // Add to comments.xml (new comment with same structure)
1540
+ var commentsPart = mainPart.WordprocessingCommentsPart!;
1541
+ var replyComment = new Comment
1542
+ {
1543
+ Id = replyId.ToString(),
1544
+ Author = author,
1545
+ Date = DateTime.UtcNow,
1546
+ Initials = initials
1547
+ };
1548
+ replyComment.Append(new Paragraph(
1549
+ new ParagraphProperties(new ParagraphStyleId { Val = "CommentText" }),
1550
+ new Run(new RunProperties(new RunStyle { Val = "CommentReference" }), new AnnotationReferenceMark()),
1551
+ new Run(new Text(replyText))
1552
+ ));
1553
+ commentsPart.Comments!.Append(replyComment);
1554
+ commentsPart.Comments.Save();
1555
+
1556
+ // KEY: In commentsExtended.xml, use paraIdParent to link to parent
1557
+ var commentsEx = mainPart.WordprocessingCommentsExPart!;
1558
+ var replyEx = new CommentEx
1559
+ {
1560
+ ParaId = new HexBinaryValue(replyParaId),
1561
+ ParaIdParent = new HexBinaryValue(parentParaId), // Link to parent
1562
+ Done = new OnOffValue(false)
1563
+ };
1564
+ commentsEx.CommentsEx!.Append(replyEx);
1565
+ commentsEx.CommentsEx.Save();
1566
+
1567
+ // Add to commentsIds.xml and commentsExtensible.xml (same pattern as parent)
1568
+ // ... (same as AddFullComment for these two files)
1569
+
1570
+ // Note: Replies do NOT need range markers in document.xml
1571
+ // They appear threaded under the parent in Word's UI
1572
+
1573
+ return replyId;
1574
+ }
1575
+ ```
1576
+
1577
+ ### 5.3 Resolving a Comment
1578
+
1579
+ ```csharp
1580
+ // To resolve (mark done), set w15:done="1" in commentsExtended.xml
1581
+ public static void ResolveComment(WordprocessingDocument doc, int commentId)
1582
+ {
1583
+ var mainPart = doc.MainDocumentPart!;
1584
+
1585
+ // Need to find the paraId for this commentId, then update commentsExtended.xml
1586
+ // Step 1: Get paraId from comments.xml
1587
+ var commentsPart = mainPart.WordprocessingCommentsPart!;
1588
+ var comment = commentsPart.Comments!
1589
+ .Elements<Comment>()
1590
+ .FirstOrDefault(c => c.Id?.Value == commentId.ToString());
1591
+
1592
+ // Find the paragraph and get its paraId
1593
+ string? paraId = null;
1594
+ if (comment != null)
1595
+ {
1596
+ var para = comment.Elements<Paragraph>().FirstOrDefault();
1597
+ var paraIdElem = para?.ParagraphProperties?
1598
+ .Elements<乳啜攠嘶嘐呓顾纨asiId>().FirstOrDefault();
1599
+ paraId = paraIdElem?.Val?.Value;
1600
+ }
1601
+
1602
+ if (paraId == null) return;
1603
+
1604
+ // Step 2: Update commentsExtended.xml
1605
+ var commentsExPart = mainPart.WordprocessingCommentsExPart!;
1606
+ var commentEx = commentsExPart.CommentsEx!
1607
+ .Elements<CommentEx>()
1608
+ .FirstOrDefault(ce => ce.ParaId?.Value == paraId);
1609
+
1610
+ if (commentEx != null)
1611
+ commentEx.Done = new OnOffValue(true); // Sets done="1"
1612
+
1613
+ commentsExPart.CommentsEx!.Save();
1614
+ }
1615
+ ```
1616
+
1617
+ ### 5.4 Deleting a Comment (All 4 Files)
1618
+
1619
+ ```csharp
1620
+ // Must remove from all 4 files AND from document.xml
1621
+ public static void DeleteComment(WordprocessingDocument doc, int commentId)
1622
+ {
1623
+ var mainPart = doc.MainDocumentPart!;
1624
+ string commentIdStr = commentId.ToString();
1625
+
1626
+ // ── Remove from comments.xml ──
1627
+ var commentsPart = mainPart.WordprocessingCommentsPart;
1628
+ if (commentsPart?.Comments != null)
1629
+ {
1630
+ var comment = commentsPart.Comments
1631
+ .Elements<Comment>()
1632
+ .FirstOrDefault(c => c.Id?.Value == commentIdStr);
1633
+ if (comment != null)
1634
+ {
1635
+ // Get paraId before deletion for other files
1636
+ string? paraId = null;
1637
+ var para = comment.Elements<Paragraph>().FirstOrDefault();
1638
+ var paraIdElem = para?.ParagraphProperties?
1639
+ .Elements<乳啜攠嘶嘐呓顾纨asiId>().FirstOrDefault();
1640
+ paraId = paraIdElem?.Val?.Value;
1641
+
1642
+ comment.Remove();
1643
+
1644
+ // ── Remove from commentsExtended.xml ──
1645
+ var commentsExPart = mainPart.WordprocessingCommentsExPart;
1646
+ if (commentsExPart?.CommentsEx != null && paraId != null)
1647
+ {
1648
+ var commentEx = commentsExPart.CommentsEx
1649
+ .Elements<CommentEx>()
1650
+ .FirstOrDefault(ce => ce.ParaId?.Value == paraId);
1651
+ commentEx?.Remove();
1652
+ commentsExPart.CommentsEx.Save();
1653
+ }
1654
+
1655
+ // ── Remove from commentsIds.xml ──
1656
+ var commentsIdsPart = mainPart.WordprocessingCommentsIdsPart;
1657
+ if (commentsIdsPart?.CommentsIds != null && paraId != null)
1658
+ {
1659
+ var cidEntry = commentsIdsPart.CommentsIds
1660
+ .Elements<CommentId>()
1661
+ .FirstOrDefault(ci => ci.ParaId?.Value == paraId);
1662
+ cidEntry?.Remove();
1663
+ commentsIdsPart.CommentsIds.Save();
1664
+ }
1665
+
1666
+ // ── Remove from commentsExtensible.xml ──
1667
+ // Need to look up by durableId...
1668
+ var commentsExtPart = mainPart.WordprocessingCommentsExtensiblePart;
1669
+ if (commentsExtPart?.CommentsExtensible != null)
1670
+ {
1671
+ // Find by matching durableId (must track separately)
1672
+ var extEntry = commentsExtPart.CommentsExtensible
1673
+ .Elements<CommentExtensible>()
1674
+ .FirstOrDefault(); // Match by durableId lookup
1675
+ extEntry?.Remove();
1676
+ commentsExtPart.CommentsExtensible.Save();
1677
+ }
1678
+ }
1679
+ }
1680
+
1681
+ // ── Remove from document.xml ──
1682
+ var body = mainPart.Document!.Body!;
1683
+
1684
+ // Remove CommentRangeStart
1685
+ var rangeStart = body.Descendants<CommentRangeStart>()
1686
+ .FirstOrDefault(crs => crs.Id?.Value == commentIdStr);
1687
+ rangeStart?.Remove();
1688
+
1689
+ // Remove CommentRangeEnd
1690
+ var rangeEnd = body.Descendants<CommentRangeEnd>()
1691
+ .FirstOrDefault(cre => cre.Id?.Value == commentIdStr);
1692
+ rangeEnd?.Remove();
1693
+
1694
+ // Remove CommentReference run (the superscript marker)
1695
+ var commentRefs = body.Descendants<CommentReference>()
1696
+ .Where(cr => cr.Id?.Value == commentIdStr)
1697
+ .ToList();
1698
+ foreach (var cr in commentRefs)
1699
+ {
1700
+ var run = cr.Parent as Run;
1701
+ cr.Remove();
1702
+ run?.Remove();
1703
+ }
1704
+
1705
+ commentsPart?.Comments?.Save();
1706
+ }
1707
+ ```
1708
+
1709
+ ---
1710
+
1711
+ ## 6. Images — Deep Dive
1712
+
1713
+ ### 6.1 Adding an ImagePart (All Image Types)
1714
+
1715
+ ```csharp
1716
+ // All image types supported by AddImagePart:
1717
+ void AddImageExamples(MainDocumentPart mainPart, string pngPath, string jpegPath,
1718
+ string gifPath, string svgPath, string bmpPath, string tiffPath)
1719
+ {
1720
+ // PNG
1721
+ var pngPart = mainPart.AddImagePart(ImagePartType.Png);
1722
+ using (var s = File.OpenRead(pngPath)) pngPart.FeedData(s);
1723
+ string pngRelId = mainPart.GetIdOfPart(pngPart);
1724
+
1725
+ // JPEG
1726
+ var jpegPart = mainPart.AddImagePart(ImagePartType.Jpeg);
1727
+ using (var s = File.OpenRead(jpegPath)) jpegPart.FeedData(s);
1728
+ string jpegRelId = mainPart.GetIdOfPart(jpegPart);
1729
+
1730
+ // GIF
1731
+ var gifPart = mainPart.AddImagePart(ImagePartType.Gif);
1732
+ using (var s = File.OpenRead(gifPath)) gifPart.FeedData(s);
1733
+ string gifRelId = mainPart.GetIdOfPart(gifPart);
1734
+
1735
+ // SVG (may require additional handling for fallback)
1736
+ var svgPart = mainPart.AddImagePart(ImagePartType.Svg);
1737
+ using (var s = File.OpenRead(svgPath)) svgPart.FeedData(s);
1738
+ string svgRelId = mainPart.GetIdOfPart(svgPart);
1739
+
1740
+ // BMP (stored internally as PNG in OOXML)
1741
+ var bmpPart = mainPart.AddImagePart(ImagePartType.Bmp);
1742
+ using (var s = File.OpenRead(bmpPath)) bmpPart.FeedData(s);
1743
+ string bmpRelId = mainPart.GetIdOfPart(bmpPart);
1744
+
1745
+ // TIFF (similarly converted)
1746
+ var tiffPart = mainPart.AddImagePart(ImagePartType.Tiff);
1747
+ using (var s = File.OpenRead(tiffPath)) tiffPart.FeedData(s);
1748
+ string tiffRelId = mainPart.GetIdOfPart(tiffPart);
1749
+
1750
+ // Also available: ImagePartType.Icon, ImagePartType.Emf, ImagePartType.Wmf
1751
+ }
1752
+ ```
1753
+
1754
+ ### 6.2 Inline Image (DW.Inline)
1755
+
1756
+ Inline images are anchored to a specific character position, not floating.
1757
+
1758
+ ```csharp
1759
+ // Dimensions: widthPx * 9525 EMU = EMU width, heightPx * 9525 EMU = EMU height
1760
+ // Assuming 600x400 pixel image at 96dpi:
1761
+ // cx = 600 * 9525 = 5715000 EMU
1762
+ // cy = 400 * 9525 = 3810000 EMU
1763
+
1764
+ long cx = (long)(widthInches * 914400); // From inches to EMU
1765
+ long cy = (long)(heightInches * 914400); // From inches to EMU
1766
+
1767
+ // Or from pixels at 96dpi:
1768
+ long cxPx = 600, cyPx = 400;
1769
+ long cx = cxPx * 9525L; // 5715000 EMU
1770
+ long cy = cyPx * 9525L; // 3810000 EMU
1771
+
1772
+ // Drawing → DW.Inline → A.Graphic → A.GraphicData → PIC.Picture
1773
+ var drawing = new Drawing(
1774
+ new DW.Inline(
1775
+ // Extent: defines the image's display size in EMU
1776
+ new DW.Extent { Cx = cx, Cy = cy },
1777
+ // EffectExtent: needed for some effects (set to 0 for basic images)
1778
+ new DW.EffectExtent { EffectExtentL = 0, EffectExtentT = 0, EffectExtentR = 0, EffectExtentB = 0 },
1779
+ // DocProperties: metadata for the image (Id must be unique in document)
1780
+ new DW.DocProperties { Id = 1U, Name = "Image_1", Description = "A sample image" },
1781
+ // NonVisualGraphicFrameDrawingProperties: locks and frame settings
1782
+ new DW.NonVisualGraphicFrameDrawingProperties(
1783
+ new A.GraphicFrameLocks { NoChangeAspect = true }
1784
+ ),
1785
+ // The actual image
1786
+ new A.Graphic(
1787
+ new A.GraphicData(
1788
+ new PIC.Picture(
1789
+ // Non-visual properties
1790
+ new PIC.NonVisualPictureProperties(
1791
+ new PIC.NonVisualDrawingProperties { Id = 0U, Name = "image1" },
1792
+ new PIC.NonVisualPictureDrawingProperties()
1793
+ ),
1794
+ // Fill: how the image is stretched to fill its frame
1795
+ new PIC.BlipFill(
1796
+ // Blip: the actual image data reference
1797
+ new A.Blip { Embed = relId, CompressionState = A.BlipCompressionValues.Print },
1798
+ // Stretch: how to fill if aspect ratio doesn't match
1799
+ new A.Stretch(new A.FillRectangle())
1800
+ ),
1801
+ // ShapeProperties: transform and geometry
1802
+ new PIC.ShapeProperties(
1803
+ new A.Transform2D(
1804
+ new A.Offset { X = 0L, Y = 0L },
1805
+ new A.Extents { Cx = cx, Cy = cy }
1806
+ ),
1807
+ new A.PresetGeometry(new A.AdjustValueList())
1808
+ { Preset = A.ShapeTypeValues.Rectangle }
1809
+ )
1810
+ )
1811
+ ) { Uri = "http://schemas.openxmlformats.org/drawingml/2006/picture" }
1812
+ )
1813
+ )
1814
+ {
1815
+ DistanceFromTop = 0U,
1816
+ DistanceFromBottom = 0U,
1817
+ DistanceFromLeft = 0U,
1818
+ DistanceFromRight = 0U
1819
+ }
1820
+ );
1821
+
1822
+ // Append to a paragraph
1823
+ var para = new Paragraph(new Run(drawing));
1824
+ body.Append(para);
1825
+ ```
1826
+
1827
+ ### 6.3 Floating / Anchored Image (DW.Anchor)
1828
+
1829
+ Floating images have text wrapping and can be positioned relative to page, margin, column, or paragraph.
1830
+
1831
+ ```csharp
1832
+ // DW.Anchor — positioned floating image with text wrapping
1833
+ // Key differences from Inline:
1834
+ // - DW.Anchor instead of DW.Inline
1835
+ // - DW.PositionH / DW.PositionV for positioning
1836
+ // - wrapping element (WrapSquare, WrapTight, etc.)
1837
+ // - can have extent on the anchor (effect extent)
1838
+
1839
+ var floatingDrawing = new Drawing(
1840
+ new DW.Anchor(
1841
+ // Horizontal positioning
1842
+ new DW.SimplePosition { X = 0L, Y = 0L }, // Offset from anchor point
1843
+ new DW.HorizontalPosition(
1844
+ new DW.PositionOffset((914400L * 2).ToString()) // 2 inches from left
1845
+ )
1846
+ { RelativeFrom = DW.HorizontalRelativePositionValues.Page },
1847
+ // Vertical positioning
1848
+ new DW.VerticalPosition(
1849
+ new DW.PositionOffset((914400L * 3).ToString()) // 3 inches from top
1850
+ )
1851
+ { RelativeFrom = DW.VerticalRelativePositionValues.Page },
1852
+
1853
+ // Image extent (size)
1854
+ new DW.Extent { Cx = cx, Cy = cy },
1855
+ new DW.EffectExtent { EffectExtentL = 0, EffectExtentT = 0, EffectExtentR = 0, EffectExtentB = 0 },
1856
+
1857
+ new DW.DocProperties { Id = 2U, Name = "Floating_Image" },
1858
+ new DW.NonVisualGraphicFrameDrawingProperties(
1859
+ new A.GraphicFrameLocks { NoChangeAspect = true }
1860
+ ),
1861
+
1862
+ // Text wrapping — several options:
1863
+ // WrapSquare: text wraps on all sides (default)
1864
+ // WrapTight: text wraps close to image shape
1865
+ // WrapThrough: text wraps through the image
1866
+ // WrapTopAndBottom: image on its own line, text above and below
1867
+ // WrapNone: image behind/in front of text
1868
+ new DW.WrapSquare { WrapText = DW.WrapTextValues.RightMargin },
1869
+
1870
+ // Layout in table cell (if applicable)
1871
+ new DW.DocPart Gallery { Val = DW.DocPartGalleryValues.Default },
1872
+
1873
+ // Change paragraph that the image is anchored to
1874
+ // Allow the image to move with the paragraph
1875
+ new DW.EditingIndependentFromParagraph { Val = false },
1876
+
1877
+ // Horizontal anchor: anchor to character/column/margin/page
1878
+ new DW.HorizontalAnchor { Val = DW.HorizontalAnchorValues.Page },
1879
+ // Vertical anchor: anchor to character/line/margin/page/paragraph
1880
+ new DW.VerticalAnchor { Val = DW.VerticalAnchorValues.Page },
1881
+
1882
+ // Alignment (if using alignment-based positioning)
1883
+ new DW.Aligned { Horizontal = DW.HorizontalAlignmentValues.Left,
1884
+ Vertical = DW.VerticalAlignmentValues.Top },
1885
+
1886
+ new A.Graphic(...)
1887
+ )
1888
+ {
1889
+ // Anchor lock: prevents moving in Word UI
1890
+ EditId = "1A2B3C",
1891
+ BehindDoc = false, // true = behind text, false = in front
1892
+ Locked = false,
1893
+ LayoutInCell = true, // Allow layout inside table cells
1894
+ AllowOverlap = true // Allow overlap with other floating elements
1895
+ }
1896
+ );
1897
+ ```
1898
+
1899
+ ### 6.4 Text Wrapping Options
1900
+
1901
+ ```csharp
1902
+ // WrapSquare — text surrounds on all sides
1903
+ new DW.WrapSquare { WrapText = DW.WrapTextValues.RightMargin }
1904
+
1905
+ // WrapTight — text follows contour of image (if shape has custom geometry)
1906
+ new DW.WrapTight { WrapText = DW.WrapTextValues.LeftMargin }
1907
+
1908
+ // WrapThrough — text intermingles with image
1909
+ new DW.WrapThrough(
1910
+ new DW.WrapTextValues.LeftMargin, // Text on left
1911
+ new DW.WrapTextValues.RightMargin // Text on right
1912
+ )
1913
+
1914
+ // WrapTopAndBottom — image on own line
1915
+ new DW.WrapTopAndBottom()
1916
+
1917
+ // WrapNone — image is at anchor position, text overlays (or vice versa)
1918
+ new DW.WrapNone()
1919
+
1920
+ // Behind document text:
1921
+ var anchor = new DW.Anchor(...){ BehindDoc = true, Locked = false };
1922
+ ```
1923
+
1924
+ ### 6.5 Image Sizing — EMU Calculations
1925
+
1926
+ ```csharp
1927
+ // EMU reference:
1928
+ // 1 inch = 914400 EMU
1929
+ // 1 cm = 360000 EMU
1930
+ // 1 pixel at 96dpi = 9525 EMU
1931
+ // 1 pixel at 72dpi = 635 EMU (point, not EMU)
1932
+
1933
+ public static class ImageSizing
1934
+ {
1935
+ // From pixel dimensions at given DPI
1936
+ public static (long cx, long cy) FromPixels(int widthPx, int heightPx, int dpi = 96)
1937
+ {
1938
+ long emuPerPixel = 914400L / dpi; // ~9525 at 96dpi
1939
+ return (widthPx * emuPerPixel, heightPx * emuPerPixel);
1940
+ }
1941
+
1942
+ // From inches
1943
+ public static (long cx, long cy) FromInches(double widthIn, double heightIn)
1944
+ {
1945
+ return ((long)(widthIn * 914400), (long)(heightIn * 914400));
1946
+ }
1947
+
1948
+ // From centimeters
1949
+ public static (long cx, long cy) FromCentimeters(double widthCm, double heightCm)
1950
+ {
1951
+ return ((long)(widthCm * 360000), (long)(heightCm * 360000));
1952
+ }
1953
+
1954
+ // Maintain aspect ratio given a target width
1955
+ public static (long cx, long cy) ScaleToWidth(long originalCx, long originalCy, long targetCx)
1956
+ {
1957
+ double ratio = (double)originalCy / originalCx;
1958
+ return (targetCx, (long)(targetCx * ratio));
1959
+ }
1960
+
1961
+ // Common photo sizes in inches: 4x6, 5x7, 8x10
1962
+ public static (long cx, long cy) PhotoSize4x6()
1963
+ => FromInches(4, 6);
1964
+
1965
+ public static (long cx, long cy) PhotoSize5x7()
1966
+ => FromInches(5, 7);
1967
+
1968
+ public static (long cx, long cy) PhotoSize8x10()
1969
+ => FromInches(8, 10);
1970
+ }
1971
+ ```
1972
+
1973
+ ### 6.6 Image with Border
1974
+
1975
+ ```csharp
1976
+ // Add border to image via PIC.ShapeProperties → A.Outline
1977
+ new PIC.ShapeProperties(
1978
+ new A.Transform2D(
1979
+ new A.Offset { X = 0L, Y = 0L },
1980
+ new A.Extents { Cx = cx, Cy = cy }
1981
+ ),
1982
+ new A.PresetGeometry(new A.AdjustValueList())
1983
+ { Preset = A.ShapeTypeValues.Rectangle },
1984
+ // The border/outline
1985
+ new A.Outline(
1986
+ new A.SolidFill(new A.RgbColorModelHex { Val = "000000" }),
1987
+ new A.PresetDash { Val = A.PresetLineDashValues.Solid }
1988
+ )
1989
+ { Width = 12700 } // 12700 EMU = 1pt, so 25400 = 2pt
1990
+ );
1991
+ ```
1992
+
1993
+ ### 6.7 Image with Alt Text (DocProperties.Description)
1994
+
1995
+ ```csharp
1996
+ // Alt text is set via DocProperties.Description
1997
+ // Also accessible via Picture's alternative text in Word UI
1998
+
1999
+ new DW.DocProperties
2000
+ {
2001
+ Id = 1U,
2002
+ Name = "Chart showing growth",
2003
+ Description = "Bar chart showing quarterly revenue growth from Q1 to Q4 2025"
2004
+ // Title is also available but Description is what Word shows as alt text
2005
+ };
2006
+
2007
+ // Also set via A.Descriptive (for some image types)
2008
+ ```
2009
+
2010
+ ### 6.8 Image in Header / Footer
2011
+
2012
+ ```csharp
2013
+ // Images in headers/footers work the same as in body, just on the respective part
2014
+ var headerPart = mainPart.AddNewPart<HeaderPart>();
2015
+
2016
+ // Logo in header
2017
+ var logoDrawing = new Drawing(
2018
+ new DW.Inline(
2019
+ new DW.Extent { Cx = 914400L, Cy = 457200L }, // 1" x 0.5" logo
2020
+ new DW.EffectExtent(),
2021
+ new DW.DocProperties { Id = 1U, Name = "HeaderLogo" },
2022
+ new DW.NonVisualGraphicFrameDrawingProperties(
2023
+ new A.GraphicFrameLocks { NoChangeAspect = true }),
2024
+ new A.Graphic(
2025
+ new A.GraphicData(
2026
+ new PIC.Picture(
2027
+ new PIC.NonVisualPictureProperties(
2028
+ new PIC.NonVisualDrawingProperties { Id = 0U, Name = "logo" },
2029
+ new PIC.NonVisualPictureDrawingProperties()),
2030
+ new PIC.BlipFill(
2031
+ new A.Blip { Embed = headerLogoRelId },
2032
+ new A.Stretch(new A.FillRectangle())),
2033
+ new PIC.ShapeProperties(
2034
+ new A.Transform2D(
2035
+ new A.Offset { X = 0L, Y = 0L },
2036
+ new A.Extents { Cx = 914400L, Cy = 457200L }),
2037
+ new A.PresetGeometry(new A.AdjustValueList())
2038
+ { Preset = A.ShapeTypeValues.Rectangle }))
2039
+ )
2040
+ ) { Uri = "http://schemas.openxmlformats.org/drawingml/2006/picture" }
2041
+ )
2042
+ )
2043
+ { DistanceFromTop = 0U, DistanceFromBottom = 0U,
2044
+ DistanceFromLeft = 0U, DistanceFromRight = 0U }
2045
+ );
2046
+
2047
+ headerPart.Header = new Header(
2048
+ new Paragraph(
2049
+ new ParagraphProperties(
2050
+ new Justification { Val = JustificationValues.Right }),
2051
+ new Run(logoDrawing)
2052
+ )
2053
+ );
2054
+ ```
2055
+
2056
+ ### 6.9 Image in Table Cell
2057
+
2058
+ ```csharp
2059
+ // Images in table cells use the same patterns
2060
+ // With inline: works fine within cell
2061
+ // With floating/anchor: set LayoutInCell = true
2062
+
2063
+ var cellWithImage = new TableCell(
2064
+ new TableCellProperties(
2065
+ new TableCellWidth { Width = 2000, Type = TableWidthUnitValues.Dxa }
2066
+ ),
2067
+ new Paragraph(
2068
+ new Run(
2069
+ new Drawing(
2070
+ new DW.Inline(
2071
+ new DW.Extent { Cx = 914400L, Cy = 914400L }, // 1"x1"
2072
+ new DW.EffectExtent(),
2073
+ new DW.DocProperties { Id = 5U, Name = "CellImage" },
2074
+ new DW.NonVisualGraphicFrameDrawingProperties(
2075
+ new A.GraphicFrameLocks { NoChangeAspect = true }),
2076
+ new A.Graphic(
2077
+ new A.GraphicData(
2078
+ new PIC.Picture(...)
2079
+ ) { Uri = "..." }
2080
+ )
2081
+ )
2082
+ { DistanceFromTop = 0U, DistanceFromBottom = 0U,
2083
+ DistanceFromLeft = 0U, DistanceFromRight = 0U }
2084
+ )
2085
+ )
2086
+ )
2087
+ );
2088
+ ```
2089
+
2090
+ ### 6.10 Replacing an Image (Update Blip.Embed)
2091
+
2092
+ ```csharp
2093
+ // To replace an existing image, update the Blip's Embed relationship ID
2094
+ // 1. Get the existing image's relationship ID from Blip.Embed
2095
+ // 2. Replace the image data in that ImagePart with new data
2096
+ // 3. Keep the same relationship ID (so all references remain valid)
2097
+
2098
+ public static void ReplaceImage(WordprocessingDocument doc, string newImagePath)
2099
+ {
2100
+ var mainPart = doc.MainDocumentPart!;
2101
+ var body = mainPart.Document!.Body!;
2102
+
2103
+ foreach (var drawing in body.Descendants<Drawing>())
2104
+ {
2105
+ // Look for inline or anchor images
2106
+ var inline = drawing.Descendants<DW.Inline>().FirstOrDefault();
2107
+ var anchor = drawing.Descendants<DW.Anchor>().FirstOrDefault();
2108
+
2109
+ var blipFill = (inline ?? anchor as OpenXmlElement)?
2110
+ .Descendants<PIC.BlipFill>().FirstOrDefault();
2111
+
2112
+ if (blipFill == null) continue;
2113
+
2114
+ var blip = blipFill.Blip;
2115
+ if (blip?.Embed == null) continue;
2116
+
2117
+ string relId = blip.Embed.Value!;
2118
+
2119
+ // Get the existing ImagePart
2120
+ if (mainPart.GetPartById(relId) is ImagePart existingImagePart)
2121
+ {
2122
+ // Replace the data
2123
+ using (var newData = File.OpenRead(newImagePath))
2124
+ existingImagePart.FeedData(newData);
2125
+ return; // Replace first found, or loop for all
2126
+ }
2127
+ }
2128
+ }
2129
+ ```
2130
+
2131
+ ### 6.11 SVG with PNG Fallback (SvgBlip)
2132
+
2133
+ ```csharp
2134
+ // SVG images use SvgBlip for modern Word apps, with PNG fallback for older versions
2135
+ // This is handled through the package structure — Word picks the best supported format
2136
+
2137
+ // SVG stored as ImagePartType.Svg, but rendered via BlipFill with extension:
2138
+ // <a:blip xmlns:a="..." r:embed="rId...">
2139
+ // <a:extLst>
2140
+ // <a:ext uri="http://schemas.openxmlformats.org/drawingml/2006/svg">
2141
+ // <asvg:svg xmlns:asvg="..."/> <!-- SVG-specific data -->
2142
+ // </a:ext>
2143
+ // </a:extLst>
2144
+ // </a:blip>
2145
+
2146
+ var svgImagePart = mainPart.AddImagePart(ImagePartType.Svg);
2147
+ using (var s = File.OpenRead("chart.svg")) svgImagePart.FeedData(s);
2148
+ string svgRelId = mainPart.GetIdOfPart(svgImagePart);
2149
+
2150
+ // Word automatically handles SVG→PNG fallback in older versions
2151
+ // No explicit fallback needed in code — the document format handles it
2152
+
2153
+ // Note: SvgBlip class in SDK 3.x provides direct support
2154
+ new A.SvgBlip { Embed = svgRelId };
2155
+ ```
2156
+
2157
+ ---
2158
+
2159
+ ## 7. Drawing Shapes (Non-Image)
2160
+
2161
+ ### 7.1 WordprocessingShape — Basic Shapes (wsp)
2162
+
2163
+ WordprocessingShape uses the `wps` namespace for Word's built-in shape library.
2164
+
2165
+ ```csharp
2166
+ // Shapes require:
2167
+ // - MainDocumentPart.AddNewPart<WordprocessingShapePart>() or
2168
+ // - Embedded via DrawingML inside a Drawing element
2169
+ // The most common approach is embedding shapes directly in a Drawing element
2170
+
2171
+ // Shapes in WordprocessingDrawing are placed like images (inline or anchored)
2172
+ var shapeDrawing = new Drawing(
2173
+ new DW.Inline(
2174
+ new DW.Extent { Cx = 1714500L, Cy = 914400L }, // 1.875" x 1" rectangle
2175
+ new DW.EffectExtent(),
2176
+ new DW.DocProperties { Id = 10U, Name = "Rectangle 1" },
2177
+ new DW.NonVisualGraphicFrameDrawingProperties(
2178
+ new A.GraphicFrameLocks()),
2179
+ // The shape itself
2180
+ new A.Graphic(
2181
+ new A.GraphicData(
2182
+ // WordprocessingShape = wsp:wsp (rectangle, roundedRect, ellipse, etc.)
2183
+ new WSP.WordprocessingShape(
2184
+ // Non-visual properties
2185
+ new WSP.NonVisualDrawingShapeProperties(
2186
+ new A.ShapeLocks { NoChangeAspect = true }
2187
+ ),
2188
+ // Shape properties (fill, outline, geometry)
2189
+ new WSP.ShapeProperties(
2190
+ new A.Transform2D(
2191
+ new A.Offset { X = 0L, Y = 0L },
2192
+ new A.Extents { Cx = 1714500L, Cy = 914400L }
2193
+ ),
2194
+ // PresetGeometry determines the shape type
2195
+ new A.PresetGeometry(new A.AdjustValueList())
2196
+ {
2197
+ Preset = A.ShapeTypeValues.Rectangle
2198
+ // Other values: RoundedRectangle, Ellipse, Triangle, etc.
2199
+ },
2200
+ // Fill color (solid)
2201
+ new A.SolidFill(
2202
+ new A.RgbColorModelHex { Val = "4472C4" }
2203
+ ),
2204
+ // Outline
2205
+ new A.Outline(
2206
+ new A.NoFill() // No outline
2207
+ // Or: new A.SolidFill(new A.RgbColorModelHex { Val = "000000" })
2208
+ // { Width = 12700 } for 1pt border
2209
+ )
2210
+ ),
2211
+ // Text box content
2212
+ new WSP.TextBoxInfo2(
2213
+ new TextBoxContent(
2214
+ new Paragraph(
2215
+ new ParagraphProperties(
2216
+ new Justification { Val = JustificationValues.Center }),
2217
+ new Run(
2218
+ new RunProperties(
2219
+ new Color { Val = "FFFFFF" },
2220
+ new Bold()),
2221
+ new Text("Hello World"))
2222
+ )
2223
+ )
2224
+ )
2225
+ )
2226
+ ) { Uri = "http://schemas.microsoft.com/office/word/2010/wordprocessingShape" }
2227
+ )
2228
+ )
2229
+ { DistanceFromTop = 0U, DistanceFromBottom = 0U,
2230
+ DistanceFromLeft = 0U, DistanceFromRight = 0U }
2231
+ );
2232
+ ```
2233
+
2234
+ **Preset shape types (`A.ShapeTypeValues`):**
2235
+ - `Rectangle`, `RoundedRectangle`, `Ellipse`, `Triangle`, `RightTriangle`
2236
+ - `Parallelogram`, `Trapezoid`, `Pentagon`, `Hexagon`, `Octagon`
2237
+ - `Star4`, `Star5`, `Star6`, `Star8`, `Star10`, `Star12`
2238
+ - `Heart`, `ArrowRight`, `ArrowLeft`, `ArrowUp`, `ArrowDown`
2239
+ - `Callout1`, `Callout2`, `Callout3` (with tail)
2240
+ - `FlowChartProcess`, `FlowChartDecision`, `FlowChartDocument`
2241
+
2242
+ ### 7.2 Shape with Gradient Fill
2243
+
2244
+ ```csharp
2245
+ new WSP.ShapeProperties(
2246
+ new A.Transform2D(...),
2247
+ new A.PresetGeometry(new A.AdjustValueList())
2248
+ { Preset = A.ShapeTypeValues.RoundedRectangle },
2249
+ // Gradient fill
2250
+ new A.GradientFill(
2251
+ new A.LinearGradientFill(
2252
+ new A.Stop { Offset = "0", Color = new A.RgbColorModelHex { Val = "4472C4" } },
2253
+ new A.Stop { Offset = "100000", Color = new A.RgbColorModelHex { Val = "2F5496" } }
2254
+ )
2255
+ { Rotation = 5400000 } // 54° = diagonal
2256
+ )
2257
+ // OR: A.RadialGradientFill for radial gradient
2258
+ );
2259
+ ```
2260
+
2261
+ ### 7.3 Shape Positioning (Anchored)
2262
+
2263
+ ```csharp
2264
+ // Anchored (floating) shapes use DW.Anchor with the shape inside
2265
+ var anchoredShape = new Drawing(
2266
+ new DW.Anchor(
2267
+ new DW.SimplePosition { X = 0L, Y = 0L },
2268
+ new DW.HorizontalPosition(
2269
+ new DW.PositionOffset((914400L * 1).ToString())) // 1 inch from left
2270
+ { RelativeFrom = DW.HorizontalRelativePositionValues.Margin },
2271
+ new DW.VerticalPosition(
2272
+ new DW.PositionOffset((914400L * 2).ToString())) // 2 inches from top
2273
+ { RelativeFrom = DW.VerticalRelativePositionValues.Page },
2274
+ new DW.Extent { Cx = 914400L, Cy = 914400L },
2275
+ new DW.EffectExtent(),
2276
+ new DW.DocProperties { Id = 11U, Name = "AnchoredShape" },
2277
+ new DW.NonVisualGraphicFrameDrawingProperties(new A.GraphicFrameLocks()),
2278
+ new DW.WrapSquare(),
2279
+ new A.Graphic(new A.GraphicData(
2280
+ new WSP.WordprocessingShape(...)
2281
+ ) { Uri = "http://schemas.microsoft.com/office/word/2010/wordprocessingShape" })
2282
+ )
2283
+ { BehindDoc = false, LayoutInCell = true }
2284
+ );
2285
+ ```
2286
+
2287
+ ### 7.4 Grouped Shapes (GroupShape)
2288
+
2289
+ ```csharp
2290
+ // GroupShape combines multiple shapes into one manipulable unit
2291
+ // Uses a different URI: "http://schemas.microsoft.com/office/word/2010/wordprocessingGroupShape"
2292
+
2293
+ var groupShapeDrawing = new Drawing(
2294
+ new DW.Inline(
2295
+ new DW.Extent { Cx = 4572000L, Cy = 2286000L }, // 5" x 2.5"
2296
+ new DW.EffectExtent(),
2297
+ new DW.DocProperties { Id = 12U, Name = "Shape Group" },
2298
+ new DW.NonVisualGraphicFrameDrawingProperties(new A.GraphicFrameLocks()),
2299
+ new A.Graphic(
2300
+ new A.GraphicData(
2301
+ new WPG.GroupShape(
2302
+ // Child shapes are positioned relative to group origin
2303
+ // Shape 1 at (0,0)
2304
+ new WSP.WordprocessingShape(
2305
+ new WSP.NonVisualDrawingShapeProperties(
2306
+ new A.ShapeLocks { NoChangeAspect = true }),
2307
+ new WSP.ShapeProperties(
2308
+ new A.Transform2D(
2309
+ new A.Offset { X = 0L, Y = 0L },
2310
+ new A.Extents { Cx = 914400L, Cy = 914400L }),
2311
+ new A.PresetGeometry(new A.AdjustValueList())
2312
+ { Preset = A.ShapeTypeValues.Ellipse },
2313
+ new A.SolidFill(new A.RgbColorModelHex { Val = "FF0000" })),
2314
+ new WSP.TextBoxInfo2(
2315
+ new TextBoxContent(new Paragraph(
2316
+ new Run(new Text("Red Circle")))))
2317
+ ),
2318
+ // Shape 2 offset to the right
2319
+ new WSP.WordprocessingShape(
2320
+ new WSP.NonVisualDrawingShapeProperties(
2321
+ new A.ShapeLocks { NoChangeAspect = true }),
2322
+ new WSP.ShapeProperties(
2323
+ new A.Transform2D(
2324
+ new A.Offset { X = 914400L, Y = 0L }, // 1" to the right
2325
+ new A.Extents { Cx = 914400L, Cy = 914400L }),
2326
+ new A.PresetGeometry(new A.AdjustValueList())
2327
+ { Preset = A.ShapeTypeValues.Rectangle },
2328
+ new A.SolidFill(new A.RgbColorModelHex { Val = "00FF00" })),
2329
+ new WSP.TextBoxInfo2(
2330
+ new TextBoxContent(new Paragraph(
2331
+ new Run(new Text("Green Square")))))
2332
+ )
2333
+ )
2334
+ ) { Uri = "http://schemas.microsoft.com/office/word/2010/wordprocessingGroupShape" }
2335
+ )
2336
+ )
2337
+ { DistanceFromTop = 0U, DistanceFromBottom = 0U,
2338
+ DistanceFromLeft = 0U, DistanceFromRight = 0U }
2339
+ );
2340
+ ```
2341
+
2342
+ ### 7.5 Shape Effects — Shadow, Reflection
2343
+
2344
+ ```csharp
2345
+ // Shadow effect
2346
+ new WSP.ShapeProperties(
2347
+ new A.Transform2D(...),
2348
+ new A.PresetGeometry(new A.AdjustValueList())
2349
+ { Preset = A.ShapeTypeValues.RoundedRectangle },
2350
+ new A.SolidFill(new A.RgbColorModelHex { Val = "4472C4" }),
2351
+ // Shadow via EffectList
2352
+ new A.EffectList(
2353
+ new A.OuterShadow(
2354
+ new A.RgbColorModelHex { Val = "000000" }
2355
+ )
2356
+ {
2357
+ BlurRadius = 50800L, // 4pt blur (50800 EMU = 4pt at 12700EMU/pt)
2358
+ Distance = 38100L, // 3pt offset
2359
+ Direction = 2700000, // 45° (in 60000ths of a degree)
2360
+ Alignment = A.RectangleAlignmentValues.BottomRight
2361
+ }
2362
+ )
2363
+ );
2364
+
2365
+ // Reflection
2366
+ new A.EffectList(
2367
+ new A.Reflection(
2368
+ new A.ReflectionEffect()
2369
+ {
2370
+ ReflectionBlurRadius = 63500L, // 5pt
2371
+ ReflectionDistance = 76200L, // 6pt
2372
+ ReflectionFade = 50000, // 50% fade
2373
+ ReflectionOverlap = 25000 // 25% overlap
2374
+ }
2375
+ )
2376
+ );
2377
+ ```
2378
+
2379
+ ---
2380
+
2381
+ ## 8. Math / Equations (OMML)
2382
+
2383
+ ### 8.1 OfficeMath Container — Basic Setup
2384
+
2385
+ ```csharp
2386
+ // All math equations must be inside an OfficeMath container
2387
+ // OfficeMath can be inline (in a run) or display (in its own paragraph)
2388
+
2389
+ // Inline equation in a run
2390
+ var inlineMathPara = new Paragraph(
2391
+ new Run(
2392
+ new RunProperties(new RunFonts { Ascii = "Cambria Math", HighAnsi = "Cambria Math" }),
2393
+ // Inline math: use OfficeMath directly in Run
2394
+ new OfficeMath(
2395
+ new M.Fraction(
2396
+ new M.Numerator(
2397
+ new M.Run(new M.Text("1"))
2398
+ ),
2399
+ new M.Denominator(
2400
+ new M.Run(new M.Text("2"))
2401
+ )
2402
+ )
2403
+ )
2404
+ )
2405
+ );
2406
+
2407
+ // Display equation (on its own centered paragraph)
2408
+ var displayMathPara = new Paragraph(
2409
+ new ParagraphProperties(
2410
+ new Justification { Val = JustificationValues.Center }
2411
+ ),
2412
+ new Run(
2413
+ new OfficeMath(
2414
+ new M.Fraction(
2415
+ new M.Numerator(
2416
+ new M.Run(new M.Text("x"))
2417
+ ),
2418
+ new M.Denominator(
2419
+ new M.Run(new M.Text("y"))
2420
+ )
2421
+ )
2422
+ )
2423
+ )
2424
+ );
2425
+
2426
+ // To make a display equation centered with extra spacing:
2427
+ var displayEquationPara = new Paragraph(
2428
+ new ParagraphProperties(
2429
+ new SpacingBetweenLines { Before = "240", After = "240" },
2430
+ new Justification { Val = JustificationValues.Center }
2431
+ ),
2432
+ new Run(new OfficeMath(
2433
+ // Equation content here
2434
+ ))
2435
+ );
2436
+ ```
2437
+
2438
+ ### 8.2 Fraction (M.Fraction)
2439
+
2440
+ ```csharp
2441
+ // \frac{x}{y} pattern
2442
+ new M.Fraction(
2443
+ new M.Numerator(
2444
+ new M.Run(
2445
+ new M.RunText("x") { Space = SpaceProcessingModeValues.Preserve }
2446
+ )
2447
+ ),
2448
+ new M.Denominator(
2449
+ new M.Run(
2450
+ new M.RunText("y") { Space = SpaceProcessingModeValues.Preserve }
2451
+ )
2452
+ )
2453
+ );
2454
+
2455
+ // Nested fraction: (a+b)/(c+d)
2456
+ new M.Fraction(
2457
+ new M.Numerator(
2458
+ new M.Run(new M.Text("a")) { FontSize = 24 },
2459
+ new M.Run(new M.Text("+")) { FontSize = 24 },
2460
+ new M.Run(new M.Text("b")) { FontSize = 24 }
2461
+ ),
2462
+ new M.Denominator(
2463
+ new M.Run(new M.Text("c")) { FontSize = 24 },
2464
+ new M.Run(new M.Text("+")) { FontSize = 24 },
2465
+ new M.Run(new M.Text("d")) { FontSize = 24 }
2466
+ )
2467
+ );
2468
+
2469
+ // Display fraction (skips 1 as numerator/denominator style)
2470
+ new M.Fraction(
2471
+ new M.Numerator(...),
2472
+ new M.Denominator(...),
2473
+ new M.FractionPr(
2474
+ new M.Type { Val = M.FractionValues.Skewed } // or Normal, Linear,丝
2475
+ )
2476
+ );
2477
+ ```
2478
+
2479
+ ### 8.3 Superscript and Subscript
2480
+
2481
+ ```csharp
2482
+ // Superscript: x²
2483
+ new M.Superscript(
2484
+ new M.Base(
2485
+ new M.Run(new M.Text("x")))
2486
+ ),
2487
+ new M.SuperscriptOperand(
2488
+ new M.Run(new M.Text("2")))
2489
+ )
2490
+ );
2491
+
2492
+ // Subscript: x₁
2493
+ new M.Subscript(
2494
+ new M.Base(
2495
+ new M.Run(new M.Text("x")))
2496
+ ),
2497
+ new M.SubscriptOperand(
2498
+ new M.Run(new M.Text("1")))
2499
+ )
2500
+ );
2501
+
2502
+ // Pre-sub/superscript: _b^a (baseline then super)
2503
+ // or use M.SubscriptSuperscript for combined
2504
+
2505
+ // SubscriptSuperscript (both at once): _b^a C
2506
+ new M.SubscriptSuperscript(
2507
+ new M.Base(new M.Run(new M.Text("C"))),
2508
+ new M.Subscript(new M.Run(new M.Text("b"))),
2509
+ new M.Superscript(new M.Run(new M.Text("a")))
2510
+ );
2511
+ ```
2512
+
2513
+ ### 8.4 Square Root and Nth Root
2514
+
2515
+ ```csharp
2516
+ // Square root: √x
2517
+ new M.Radical(
2518
+ new M.Root(
2519
+ new M.Run(new M.Text("x")))
2520
+ )
2521
+ );
2522
+
2523
+ // Square root with degree hidden (just √)
2524
+ new M.Radical(
2525
+ new M.Root(
2526
+ new M.Run(new M.Text("x")))
2527
+ ),
2528
+ new M.RadicalPr(
2529
+ new M.Degree { Val = false } // Hide the root index
2530
+ )
2531
+ );
2532
+
2533
+ // Nth root: ∛(x+1) — cube root of (x+1)
2534
+ new M.Radical(
2535
+ new M.Root(
2536
+ new M.Fraction(
2537
+ new M.Numerator(new M.Run(new M.Text("1"))),
2538
+ new M.Denominator(new M.Run(new M.Text("3")))
2539
+ )
2540
+ ), // This is the "3" for cube root
2541
+ new M.Root(
2542
+ new M.Run(new M.Text("x"))),
2543
+ new M.Run(new M.Text("+"))),
2544
+ new M.Run(new M.Text("1")))
2545
+ )
2546
+ );
2547
+ // Actually, for nth root: first Root is the index (degree), second is the radicand
2548
+ new M.Radical(
2549
+ new M.Root(new M.Run(new M.Text("3"))), // The index: 3rd root
2550
+ new M.Root(
2551
+ new M.Run(new M.Text("x")),
2552
+ new M.Run(new M.Text("+")),
2553
+ new M.Run(new M.Text("1"))
2554
+ )
2555
+ );
2556
+ ```
2557
+
2558
+ ### 8.5 N-ary Operators — Integral, Summation, Product
2559
+
2560
+ ```csharp
2561
+ // Integral ∫ from a to b of f(x) dx
2562
+ new M.Nary(
2563
+ new M.NaryProperties(
2564
+ new M.NaryType { Val = M.NaryValues.Integral } // ∫
2565
+ )
2566
+ {
2567
+ SubSuperscript = M.SubSuperscriptValues.NoSubSuperscript
2568
+ },
2569
+ new M.Base(
2570
+ new M.Run(new M.Text("f(x)")))
2571
+ ),
2572
+ new M.Subscript(
2573
+ new M.Run(new M.Text("a")))
2574
+ ),
2575
+ new M.Superscript(
2576
+ new M.Run(new M.Text("b")))
2577
+ )
2578
+ );
2579
+
2580
+ // Summation Σ from i=1 to n of i²
2581
+ new M.Nary(
2582
+ new M.NaryProperties(
2583
+ new M.NaryType { Val = M.NaryValues.Sum }, // Σ
2584
+ new M.GrowBindings = true
2585
+ ),
2586
+ new M.Base(
2587
+ new M.SubscriptSuperscript(
2588
+ new M.Base(new M.Run(new M.Text("i"))),
2589
+ new M.Subscript(new M.Run(new M.Text("1"))),
2590
+ new M.Superscript(new M.Run(new M.Text("n")))
2591
+ )
2592
+ ),
2593
+ new M.Subscript(
2594
+ new M.Run(new M.Text("i")))
2595
+ ),
2596
+ new M.Superscript(
2597
+ new M.Run(new M.Text("2")))
2598
+ )
2599
+ );
2600
+
2601
+ // Product ∏ from i=1 to n
2602
+ new M.Nary(
2603
+ new M.NaryProperties(
2604
+ new M.NaryType { Val = M.NaryValues.Product } // ∏
2605
+ ),
2606
+ new M.Base(
2607
+ new M.SubscriptSuperscript(
2608
+ new M.Base(new M.Run(new M.Text("i"))),
2609
+ new M.Subscript(new M.Run(new M.Text("1"))),
2610
+ new M.Superscript(new M.Run(new M.Text("n")))
2611
+ )
2612
+ ),
2613
+ new M.Subscript(
2614
+ new M.Run(new M.Text("i")))
2615
+ ),
2616
+ new M.Superscript(
2617
+ new M.Run(new M.Text("2")))
2618
+ )
2619
+ );
2620
+
2621
+ // N-ary type values: Integral, Sum, Product, Union, Intersection, etc.
2622
+ ```
2623
+
2624
+ ### 8.6 Matrix
2625
+
2626
+ ```csharp
2627
+ // 2x2 matrix
2628
+ // [a b]
2629
+ // [c d]
2630
+ new M.Matrix(
2631
+ new M.MatrixRows(
2632
+ // Row 1
2633
+ new M.MatrixRow(
2634
+ new M.MatrixCell(
2635
+ new M.Run(new M.Text("a"))
2636
+ ),
2637
+ new M.MatrixCell(
2638
+ new M.Run(new M.Text("b"))
2639
+ )
2640
+ ),
2641
+ // Row 2
2642
+ new M.MatrixRow(
2643
+ new M.MatrixCell(
2644
+ new M.Run(new M.Text("c"))
2645
+ ),
2646
+ new M.MatrixCell(
2647
+ new M.Run(new M.Text("d"))
2648
+ )
2649
+ )
2650
+ ),
2651
+ new M.MatrixProperties(
2652
+ new M.Jc { Val = M.JustificationValues.Center }, // Centered
2653
+ new M.Structure { Val = M.MathStructureValues.SinglePUNCT },
2654
+ new M.RowSpacing { Val = 120 }, // Row spacing in twips
2655
+ new M.RowSpacing1 { Val = 120 }
2656
+ )
2657
+ )
2658
+ ```
2659
+
2660
+ ### 8.7 Delimiter (Parentheses/Brackets/Braces)
2661
+
2662
+ ```csharp
2663
+ // (a + b) or [a + b] or {a + b}
2664
+ new M.Delimiter(
2665
+ new M.DelimiterProperties(
2666
+ new M.Begin(new M.Text("(")), // Opening char
2667
+ new M.End(new M.Text(")")), // Closing char
2668
+ new M.Separator(new M.Text(",")), // Separator between elements
2669
+ new M.Structure { Val = M.MathStructureValues.Minimal }
2670
+ ),
2671
+ new M.DelimiterContents(
2672
+ new M.Run(new M.Text("a")),
2673
+ new M.Run(new M.Text("+")),
2674
+ new M.Run(new M.Text("b"))
2675
+ )
2676
+ );
2677
+
2678
+ // {a, b, c} with curly braces
2679
+ new M.Delimiter(
2680
+ new M.DelimiterProperties(
2681
+ new M.Begin(new M.Text("{")),
2682
+ new M.End(new M.Text("}")),
2683
+ new M.Separator(new M.Text(","))
2684
+ ),
2685
+ new M.DelimiterContents(
2686
+ new M.Run(new M.Text("a")),
2687
+ new M.Run(new M.Text("b")),
2688
+ new M.Run(new M.Text("c"))
2689
+ )
2690
+ );
2691
+
2692
+ // 2x2 matrix in parentheses
2693
+ new M.Delimiter(
2694
+ new M.DelimiterProperties(
2695
+ new M.Begin(new M.Text("(")),
2696
+ new M.End(new M.Text(")"))
2697
+ ),
2698
+ new M.DelimiterContents(
2699
+ // Inline 2x2 using subscripts
2700
+ new M.SubscriptSuperscript(...)
2701
+ )
2702
+ );
2703
+ ```
2704
+
2705
+ ### 8.8 Equation Array (Aligned Equations)
2706
+
2707
+ ```csharp
2708
+ // EquationArray (M.EquationArray) creates a series of equations aligned at markers
2709
+ // Like \begin{align} in LaTeX
2710
+
2711
+ new M.EquationArray(
2712
+ new M.EquationArrayProperties(
2713
+ new M.Jc { Val = M.JustificationValues.Left },
2714
+ new M.RowSpacing { Val = 240 },
2715
+ new M.RowSpacing1 { Val = 240 }
2716
+ ),
2717
+ // Each equation is a Paragraph inside the array
2718
+ new Paragraph(new Run(new M.Text("x") { Space = SpaceProcessingModeValues.Preserve })),
2719
+ new Paragraph(
2720
+ new Run(new M.Text("+") { Space = SpaceProcessingModeValues.Preserve }),
2721
+ new Run(new M.Text("y") { Space = SpaceProcessingModeValues.Preserve }),
2722
+ new Run(new M.Text("=") { Space = SpaceProcessingModeValues.Preserve }),
2723
+ new Run(new M.Text("z") { Space = SpaceProcessingModeValues.Preserve })
2724
+ )
2725
+ );
2726
+
2727
+ // Or use M.Break with AlignmentTab for manual alignment points
2728
+ ```
2729
+
2730
+ ### 8.9 Greek Letters and Math Symbols
2731
+
2732
+ ```csharp
2733
+ // Greek letters via M.RunText with Symbol font or Unicode
2734
+ // Common Greek letters and their uses:
2735
+
2736
+ // α (alpha)
2737
+ new M.Run(new M.Text("\u03B1")) // or use Unicode directly
2738
+
2739
+ // β (beta)
2740
+ new M.Run(new M.Text("\u03B2"))
2741
+
2742
+ // γ (gamma)
2743
+ new M.Run(new M.Text("\u03B3"))
2744
+
2745
+ // π (pi) — use Greek small letter pi
2746
+ new M.Run(new M.Text("\u03C0"))
2747
+
2748
+ // σ (sigma)
2749
+ new M.Run(new M.Text("\u03C3"))
2750
+
2751
+ // Σ (Sigma, capital) — summation symbol
2752
+ new M.Run(new M.Text("\u03A3"))
2753
+
2754
+ // θ (theta)
2755
+ new M.Run(new M.Text("\u03B8"))
2756
+
2757
+ // ∞ (infinity)
2758
+ new M.Run(new M.Text("\u221E"))
2759
+
2760
+ // ≤ (less than or equal)
2761
+ new M.Run(new M.Text("\u2264"))
2762
+
2763
+ // ≥ (greater than or equal)
2764
+ new M.Run(new M.Text("\u2265"))
2765
+
2766
+ // ≠ (not equal)
2767
+ new M.Run(new M.Text("\u2260"))
2768
+
2769
+ // ± (plus-minus)
2770
+ new M.Run(new M.Text("\u00B1"))
2771
+
2772
+ // × (multiplication)
2773
+ new M.Run(new M.Text("\u00D7"))
2774
+
2775
+ // ÷ (division)
2776
+ new M.Run(new M.Text("\u00F7"))
2777
+
2778
+ // For best results, set the font to "Cambria Math" on math runs
2779
+ new M.Run(
2780
+ new RunFonts { Ascii = "Cambria Math", HighAnsi = "Cambria Math" },
2781
+ new M.Text("\u03C0")
2782
+ )
2783
+ ```
2784
+
2785
+ ---
2786
+
2787
+ ## 9. Numbering System — Deep Dive
2788
+
2789
+ ### 9.1 Architecture Overview
2790
+
2791
+ ```
2792
+ NumberingDefinitionsPart (numbering.xml)
2793
+ └── <w:numbering>
2794
+ ├── <w:abstractNum> (templates)
2795
+ │ ├── <w:lvl> × 9 (levels 0-8)
2796
+ │ └── <w:pPr><w:numPr> links to this abstractNum
2797
+ └── <w:num> (instances)
2798
+ └── <w:abstractNumId val="N"/>
2799
+ ```
2800
+
2801
+ **Key rule**: `AbstractNum` must appear BEFORE `NumberingInstance` in the XML root.
2802
+
2803
+ ### 9.2 AbstractNum with Multi-Level Decimal Numbering
2804
+
2805
+ ```csharp
2806
+ // AbstractNum: the numbering template (what it looks like)
2807
+ // NumberingInstance: a specific use of that template (how it's applied)
2808
+
2809
+ var numberingPart = mainPart.AddNewPart<NumberingDefinitionsPart>();
2810
+ var numbering = new Numbering();
2811
+
2812
+ // ─────────────────────────────────────────────────────────────
2813
+ // Step 1: Define AbstractNum (the template)
2814
+ // ─────────────────────────────────────────────────────────────
2815
+ var abstractNum = new AbstractNum { AbstractNumberId = 1 };
2816
+ // MultiLevelType specifies this is a multi-level list
2817
+ abstractNum.Append(new MultiLevelType { Val = MultiLevelValues.Multilevel });
2818
+
2819
+ // Level 0: "1." — decimal, bold number
2820
+ abstractNum.Append(new Level(
2821
+ new StartNumberingValue { Val = 1 },
2822
+ new NumberingFormat { Val = NumberFormatValues.Decimal },
2823
+ new LevelText { Val = "%1." },
2824
+ new LevelJustification { Val = LevelJustificationValues.Left },
2825
+ new ParagraphProperties(
2826
+ new Indentation { Left = "360", Hanging = "360" } // 0.25" hanging indent
2827
+ ),
2828
+ new NumberingSymbolRunProperties(
2829
+ new Bold(),
2830
+ new RunFonts { Ascii = "Calibri", HighAnsi = "Calibri" },
2831
+ new Color { Val = "2F5496" }
2832
+ )
2833
+ ) { LevelIndex = 0 });
2834
+
2835
+ // Level 1: "1.1." — indent 0.5"
2836
+ abstractNum.Append(new Level(
2837
+ new StartNumberingValue { Val = 1 },
2838
+ new NumberingFormat { Val = NumberFormatValues.Decimal },
2839
+ new LevelText { Val = "%1.%2." },
2840
+ new LevelJustification { Val = LevelJustificationValues.Left },
2841
+ new ParagraphProperties(
2842
+ new Indentation { Left = "720", Hanging = "360" }
2843
+ ),
2844
+ new NumberingSymbolRunProperties(
2845
+ new RunFonts { Ascii = "Calibri", HighAnsi = "Calibri" }
2846
+ )
2847
+ ) { LevelIndex = 1 });
2848
+
2849
+ // Level 2: "1.1.1."
2850
+ abstractNum.Append(new Level(
2851
+ new StartNumberingValue { Val = 1 },
2852
+ new NumberingFormat { Val = NumberFormatValues.Decimal },
2853
+ new LevelText { Val = "%1.%2.%3." },
2854
+ new LevelJustification { Val = LevelJustificationValues.Left },
2855
+ new ParagraphProperties(
2856
+ new Indentation { Left = "1080", Hanging = "360" }
2857
+ ),
2858
+ new NumberingSymbolRunProperties(
2859
+ new RunFonts { Ascii = "Calibri", HighAnsi = "Calibri" }
2860
+ )
2861
+ ) { LevelIndex = 2 });
2862
+
2863
+ // ─────────────────────────────────────────────────────────────
2864
+ // Step 2: Create NumberingInstance (a reference to the template)
2865
+ // ─────────────────────────────────────────────────────────────
2866
+ var numInstance = new NumberingInstance(
2867
+ new AbstractNumId { Val = 1 } // Points to abstractNum above
2868
+ ) { NumberID = 1 };
2869
+
2870
+ // ─────────────────────────────────────────────────────────────
2871
+ // Step 3: Assemble — AbstractNum BEFORE NumberingInstance!
2872
+ // ─────────────────────────────────────────────────────────────
2873
+ numbering.Append(abstractNum);
2874
+ numbering.Append(numInstance);
2875
+ numberingPart.Numbering = numbering;
2876
+ numberingPart.Numbering.Save();
2877
+
2878
+ // ─────────────────────────────────────────────────────────────
2879
+ // Step 4: Apply to a paragraph
2880
+ // ─────────────────────────────────────────────────────────────
2881
+ var numberedPara = new Paragraph(
2882
+ new ParagraphProperties(
2883
+ new NumberingProperties(
2884
+ new NumberingLevelReference { Val = 0 }, // Use level 0 of numId 1
2885
+ new NumberingId { Val = 1 } // Use numbering instance 1
2886
+ )
2887
+ ),
2888
+ new Run(new Text("First item"))
2889
+ );
2890
+
2891
+ // For level 1 sub-item:
2892
+ var subItemPara = new Paragraph(
2893
+ new ParagraphProperties(
2894
+ new NumberingProperties(
2895
+ new NumberingLevelReference { Val = 1 }, // Use level 1
2896
+ new NumberingId { Val = 1 }
2897
+ )
2898
+ ),
2899
+ new Run(new Text("Sub-item"))
2900
+ );
2901
+ ```
2902
+
2903
+ ### 9.3 Bullet Lists with Custom Symbols
2904
+
2905
+ ```csharp
2906
+ // Bullet numbering uses NumberFormatValues.Bullet
2907
+ // The bullet character is defined in LevelText and NumberingSymbolRunProperties
2908
+
2909
+ var bulletAbstractNum = new AbstractNum { AbstractNumberId = 2 };
2910
+ bulletAbstractNum.Append(new MultiLevelType { Val = MultiLevelValues.Multilevel });
2911
+
2912
+ // Level 0 bullet: ● (Unicode bullet)
2913
+ bulletAbstractNum.Append(new Level(
2914
+ new StartNumberingValue { Val = 1 },
2915
+ new NumberingFormat { Val = NumberFormatValues.Bullet }, // Key: Bullet format
2916
+ new LevelText { Val = "\u2022" }, // ● bullet character
2917
+ new LevelJustification { Val = LevelJustificationValues.Left },
2918
+ new ParagraphProperties(
2919
+ new Indentation { Left = "720", Hanging = "360" }
2920
+ ),
2921
+ new NumberingSymbolRunProperties(
2922
+ new RunFonts { Ascii = "Symbol", HighAnsi = "Symbol" }
2923
+ // Symbol font maps ● to character 0xD8 in Symbol encoding
2924
+ )
2925
+ ) { LevelIndex = 0 });
2926
+
2927
+ // Level 1 bullet: ○ (white circle)
2928
+ bulletAbstractNum.Append(new Level(
2929
+ new StartNumberingValue { Val = 1 },
2930
+ new NumberingFormat { Val = NumberFormatValues.Bullet },
2931
+ new LevelText { Val = "\u25CB" }, // ○ Unicode
2932
+ new LevelJustification { Val = LevelJustificationValues.Left },
2933
+ new ParagraphProperties(
2934
+ new Indentation { Left = "1080", Hanging = "360" }
2935
+ ),
2936
+ new NumberingSymbolRunProperties(
2937
+ new RunFonts { Ascii = "Courier New", HighAnsi = "Courier New" }
2938
+ )
2939
+ ) { LevelIndex = 1 });
2940
+
2941
+ // Level 2 bullet: ■ (black square)
2942
+ bulletAbstractNum.Append(new Level(
2943
+ new StartNumberingValue { Val = 1 },
2944
+ new NumberingFormat { Val = NumberFormatValues.Bullet },
2945
+ new LevelText { Val = "\u25A0" }, // ■
2946
+ new LevelJustification { Val = LevelJustificationValues.Left },
2947
+ new ParagraphProperties(
2948
+ new Indentation { Left = "1440", Hanging = "360" }
2949
+ ),
2950
+ new NumberingSymbolRunProperties(
2951
+ new RunFonts { Ascii = "Arial", HighAnsi = "Arial" }
2952
+ )
2953
+ ) { LevelIndex = 2 });
2954
+
2955
+ var bulletNumInstance = new NumberingInstance(
2956
+ new AbstractNumId { Val = 2 }
2957
+ ) { NumberID = 2 };
2958
+
2959
+ numbering.Append(bulletAbstractNum);
2960
+ numbering.Append(bulletNumInstance);
2961
+ ```
2962
+
2963
+ **Common bullet characters:**
2964
+
2965
+ | Symbol | Character | Unicode | Common Font |
2966
+ |--------|-----------|---------|-------------|
2967
+ | ● Filled circle | Bullet | U+2022 | Symbol |
2968
+ | ○ Empty circle | White circle | U+25CB | Arial |
2969
+ | ■ Filled square | Black square | U+25A0 | Arial |
2970
+ | □ Empty square | White square | U+25A1 | Arial |
2971
+ | ➢ Right arrow | Right arrow | U+27A2 | Wingdings |
2972
+ | ✓ Checkmark | Check mark | U+2713 | Wingdings |
2973
+ | ✗ Cross | Ballot X | U+2717 | Wingdings |
2974
+ | ▶ Play | Right triangle | U+25B6 | Arial |
2975
+
2976
+ ### 9.4 Restart Numbering at Specific Point
2977
+
2978
+ ```csharp
2979
+ // Method 1: StartOverride on a specific paragraph
2980
+ // Use LevelOverride + StartOverride to restart at a specific level
2981
+
2982
+ var restartNumInstance = new NumberingInstance(
2983
+ new AbstractNumId { Val = 1 }
2984
+ ) { NumberID = 3 };
2985
+
2986
+ // Override level 0 to start at 5 instead of 1
2987
+ restartNumInstance.Append(new LevelOverride { LevelIndex = 0 },
2988
+ new StartOverrideNumberingValue { Val = 5 }
2989
+ );
2990
+
2991
+ // Apply this to a paragraph — this paragraph starts numbering at 5
2992
+ var restartPara = new Paragraph(
2993
+ new ParagraphProperties(
2994
+ new NumberingProperties(
2995
+ new NumberingLevelReference { Val = 0 },
2996
+ new NumberingId { Val = 3 } // Use the restart instance
2997
+ )
2998
+ ),
2999
+ new Run(new Text("Item 5 (restarted)"))
3000
+ );
3001
+ ```
3002
+
3003
+ ### 9.5 Continue Numbering from Previous List
3004
+
3005
+ ```csharp
3006
+ // By default, Word continues numbering across lists using the same AbstractNum.
3007
+ // To force continuation, ensure the list uses the same numId.
3008
+
3009
+ // If you need explicit continuation control:
3010
+ var continuedNumInstance = new NumberingInstance(
3011
+ new AbstractNumId { Val = 1 }
3012
+ ) { NumberID = 4 };
3013
+
3014
+ // When multiple NumberingInstances share the same AbstractNumId,
3015
+ // they share the same numbering state (continuation)
3016
+
3017
+ // To prevent continuation (start fresh), use a new AbstractNum:
3018
+ var freshAbstractNum = new AbstractNum { AbstractNumberId = 5 };
3019
+ freshAbstractNum.Append(new MultiLevelType { Val = MultiLevelValues.Multilevel });
3020
+ // ... define levels ...
3021
+ var freshNumInstance = new NumberingInstance(
3022
+ new AbstractNumId { Val = 5 }
3023
+ ) { NumberID = 5 };
3024
+ // This starts at 1 again, independent of the previous list
3025
+ ```
3026
+
3027
+ ### 9.6 Link Numbering to Heading Styles (Outline Numbering)
3028
+
3029
+ ```句话说,link numbering to heading styles so that Heading1 starts a new numbering sequence, Heading2 is a sub-item, etc.
3030
+
3031
+ ```csharp
3032
+ // This links styles to numbering levels automatically via StyleLink
3033
+ var abstractNumForOutline = new AbstractNum { AbstractNumberId = 10 };
3034
+ abstractNumForOutline.Append(new MultiLevelType { Val = MultiLevelValues.Multilevel });
3035
+ abstractNumForOutline.Append(new StyleLink { Val = "Heading1" }); // Links level 0 to Heading1
3036
+ abstractNumForOutline.Append(new StyleLink { Val = "Heading2" }); // Links level 1 to Heading2
3037
+ abstractNumForOutline.Append(new StyleLink { Val = "Heading3" }); // Links level 2 to Heading3
3038
+
3039
+ // Level 0 for Heading1
3040
+ abstractNumForOutline.Append(new Level(
3041
+ new StartNumberingValue { Val = 1 },
3042
+ new NumberingFormat { Val = NumberFormatValues.Decimal },
3043
+ new LevelText { Val = "Chapter %1" },
3044
+ new LevelJustification { Val = LevelJustificationValues.Left },
3045
+ new ParagraphProperties(
3046
+ new Indentation { Left = "360", Hanging = "360" }
3047
+ ),
3048
+ new NumberingSymbolRunProperties(
3049
+ new Bold(),
3050
+ new RunFonts { Ascii = "Calibri", HighAnsi = "Calibri" },
3051
+ new FontSize { Val = "28" }
3052
+ )
3053
+ ) { LevelIndex = 0 });
3054
+
3055
+ // Level 1 for Heading2
3056
+ abstractNumForOutline.Append(new Level(
3057
+ new StartNumberingValue { Val = 1 },
3058
+ new NumberingFormat { Val = NumberFormatValues.Decimal },
3059
+ new LevelText { Val = "%1.%2" },
3060
+ new LevelJustification { Val = LevelJustificationValues.Left },
3061
+ new ParagraphProperties(
3062
+ new Indentation { Left = "720", Hanging = "360" }
3063
+ ),
3064
+ new NumberingSymbolRunProperties(
3065
+ new RunFonts { Ascii = "Calibri", HighAnsi = "Calibri" }
3066
+ )
3067
+ ) { LevelIndex = 1 });
3068
+
3069
+ // Level 2 for Heading3
3070
+ abstractNumForOutline.Append(new Level(
3071
+ new StartNumberingValue { Val = 1 },
3072
+ new NumberingFormat { Val = NumberFormatValues.Decimal },
3073
+ new LevelText { Val = "%1.%2.%3" },
3074
+ new LevelJustification { Val = LevelJustificationValues.Left },
3075
+ new ParagraphProperties(
3076
+ new Indentation { Left = "1080", Hanging = "360" }
3077
+ ),
3078
+ new NumberingSymbolRunProperties(
3079
+ new RunFonts { Ascii = "Calibri", HighAnsi = "Calibri" }
3080
+ )
3081
+ ) { LevelIndex = 2 });
3082
+
3083
+ // Now when you apply Heading1/2/3 styles, numbering follows automatically
3084
+ var heading1Para = new Paragraph(
3085
+ new ParagraphProperties(new ParagraphStyleId { Val = "Heading1" }),
3086
+ new Run(new Text("Introduction")) // Automatically gets "Chapter 1" prefix
3087
+ );
3088
+ var heading2Para = new Paragraph(
3089
+ new ParagraphProperties(new ParagraphStyleId { Val = "Heading2" }),
3090
+ new Run(new Text("Background")) // Automatically gets "1.1" prefix
3091
+ );
3092
+ ```
3093
+
3094
+ ### 9.7 NumberingFormat Values Reference
3095
+
3096
+ ```csharp
3097
+ // NumberFormatValues enum — all supported numbering formats:
3098
+ NumberFormatValues.Decimal // 1, 2, 3...
3099
+ NumberFormatValues.LowerRoman // i, ii, iii...
3100
+ NumberFormatValues.UpperRoman // I, II, III...
3101
+ NumberFormatValues.LowerLetter // a, b, c...
3102
+ NumberFormatValues.UpperLetter // A, B, C...
3103
+ NumberFormatValues.Ordinal // 1st, 2nd, 3rd... (locale-dependent)
3104
+ NumberFormatValues.OrdinalText // First, Second, Third... (locale-dependent)
3105
+ NumberFormatValues.Hex // 0, 1, 2... F, 10, 11... (hexadecimal)
3106
+ NumberFormatValues.ChicagoManual // Chapter numbering (I, A, 1, a)
3107
+ NumberFormatValues.Kanji // 漢数字
3108
+ NumberFormatValues.KanjiDigit // 一, 二, 三...
3109
+ NumberFormatValues.DoubleByte // Ideographic: 一, 二, 三
3110
+ NumberFormatValues.ArabicFullWidth // Full-width: 1, 2, 3
3111
+ NumberFormatValues.Bullet // Custom symbol (●, ✓, etc.)
3112
+ NumberFormatValues.None // No number
3113
+ ```
3114
+
3115
+ ### 9.8 IsLegalNumberingStyle — Using Arabic with Nested Levels
3116
+
3117
+ ```csharp
3118
+ // IsLegalNumberingStyle=false (default): each level can have its own format
3119
+ // IsLegalNumberingStyle=true: forces all sub-levels to use Arabic numerals
3120
+
3121
+ // This is important for legal/formal numbering where you want:
3122
+ // 1. level 1 = A, B, C (alphabetic)
3123
+ // 2. level 2 = 1, 2, 3 (Arabic) — NOT a, b, c
3124
+ // Without IsLegalNumberingStyle=false, level 2 would inherit alphabetic
3125
+
3126
+ var legalAbstractNum = new AbstractNum { AbstractNumberId = 20 };
3127
+ legalAbstractNum.Append(new MultiLevelType { Val = MultiLevelValues.Multilevel });
3128
+ legalAbstractNum.Append(new IsLegalNumberingStyle()); // No val = true (default when element present)
3129
+
3130
+ // Level 0: A. B. C.
3131
+ legalAbstractNum.Append(new Level(
3132
+ new StartNumberingValue { Val = 1 },
3133
+ new NumberingFormat { Val = NumberFormatValues.UpperLetter },
3134
+ new LevelText { Val = "%1." },
3135
+ new LevelJustification { Val = LevelJustificationValues.Left },
3136
+ new ParagraphProperties(
3137
+ new Indentation { Left = "360", Hanging = "360" }
3138
+ )
3139
+ ) { LevelIndex = 0 });
3140
+
3141
+ // Level 1: 1. 2. 3. (NOT a. b. c.)
3142
+ legalAbstractNum.Append(new Level(
3143
+ new StartNumberingValue { Val = 1 },
3144
+ new NumberingFormat { Val = NumberFormatValues.Decimal },
3145
+ new LevelText { Val = "%2." },
3146
+ new LevelJustification { Val = LevelJustificationValues.Left },
3147
+ new ParagraphProperties(
3148
+ new Indentation { Left = "720", Hanging = "360" }
3149
+ )
3150
+ ) { LevelIndex = 1 });
3151
+
3152
+ // Level 2: (a) (b) (c)
3153
+ legalAbstractNum.Append(new Level(
3154
+ new StartNumberingValue { Val = 1 },
3155
+ new NumberingFormat { Val = NumberFormatValues.LowerLetter },
3156
+ new LevelText { Val = "(%3)" },
3157
+ new LevelJustification { Val = LevelJustificationValues.Left },
3158
+ new ParagraphProperties(
3159
+ new Indentation { Left = "1080", Hanging = "360" }
3160
+ )
3161
+ ) { LevelIndex = 2 });
3162
+ ```
3163
+
3164
+ ---
3165
+
3166
+ ## 10. Document Protection & Encryption
3167
+
3168
+ ### 10.1 DocumentProtection — Basic Forms
3169
+
3170
+ ```csharp
3171
+ // DocumentProtection is placed in DocumentSettingsPart
3172
+ var settingsPart = mainPart.AddNewPart<DocumentSettingsPart>();
3173
+ settingsPart.Settings = new Settings();
3174
+
3175
+ // ReadOnly: prevents editing, allows reading
3176
+ settingsPart.Settings.Append(new DocumentProtection
3177
+ {
3178
+ Edit = DocumentProtectionValues.ReadOnly,
3179
+ Enforcement = true
3180
+ });
3181
+
3182
+ // Comments: can only add/edit comments (not modify body text)
3183
+ settingsPart.Settings.Append(new DocumentProtection
3184
+ {
3185
+ Edit = DocumentProtectionValues.Comments,
3186
+ Enforcement = true
3187
+ });
3188
+
3189
+ // TrackedChanges: can only edit with track changes ON
3190
+ settingsPart.Settings.Append(new DocumentProtection
3191
+ {
3192
+ Edit = DocumentProtectionValues.TrackedChanges,
3193
+ Enforcement = true
3194
+ });
3195
+
3196
+ // Forms: only form fields are editable
3197
+ settingsPart.Settings.Append(new DocumentProtection
3198
+ {
3199
+ Edit = DocumentProtectionValues.Forms,
3200
+ Enforcement = true
3201
+ });
3202
+ ```
3203
+
3204
+ ### 10.2 Password Hashing for DocumentProtection
3205
+
3206
+ Modern Word uses SHA-512 with salt for password hashing (ECMA-376 standard).
3207
+
3208
+ ```csharp
3209
+ // SHA-512 password hashing for strong protection
3210
+ // CryptographicProviderType must be "rsaAES" or "rsaAES" for SHA-512
3211
+
3212
+ settingsPart.Settings.Append(new DocumentProtection
3213
+ {
3214
+ Edit = DocumentProtectionValues.ReadOnly,
3215
+ Enforcement = true,
3216
+ CryptographicProviderType = CryptProviderValues.RsaAES,
3217
+ CryptographicAlgorithmClass = CryptAlgorithmClassValues.Hash,
3218
+ CryptographicAlgorithmType = CryptAlgorithmValues.TypeAny,
3219
+ CryptographicAlgorithmSid = 14, // SHA-512
3220
+ CryptographicSpinCount = 100000U,
3221
+ Hash = "base64-encoded-hash-here",
3222
+ Salt = "base64-encoded-salt-here"
3223
+ });
3224
+
3225
+ // Generate hash in .NET:
3226
+ public static (string hash, string salt) GeneratePasswordHash(string password, int spinCount = 100000)
3227
+ {
3228
+ byte[] saltBytes = new byte[16];
3229
+ using (var rng = System.Security.Cryptography.RandomNumberGenerator.Create())
3230
+ rng.GetBytes(saltBytes);
3231
+
3232
+ // PBKDF2 with SHA-512, 100000 iterations
3233
+ using var pbkdf2 = new System.Security.Cryptography.Rfc2898DeriveBytes(
3234
+ password, saltBytes, spinCount, System.Security.Cryptography.HashAlgorithmName.SHA512);
3235
+ byte[] hash = pbkdf2.GetBytes(64); // 512 bits
3236
+
3237
+ return (Convert.ToBase64String(hash), Convert.ToBase64String(saltBytes));
3238
+ }
3239
+ ```
3240
+
3241
+ ### 10.3 WriteProtection — Recommend Opening as Read-Only
3242
+
3243
+ ```csharp
3244
+ // WriteProtection is different from DocumentProtection
3245
+ // It recommends (but doesn't enforce) that users open as read-only
3246
+ // Found in extended properties (docProps/custom.xml or settings)
3247
+
3248
+ settingsPart.Settings.Append(new WriteProtection
3249
+ {
3250
+ Recommended = true // "Recommend opening as read-only"
3251
+ });
3252
+
3253
+ // Or force read-only recommendation with a specific application name
3254
+ settingsPart.Settings.Append(new WriteProtection
3255
+ {
3256
+ Recommended = true,
3257
+ ApplicationName = "Microsoft Word"
3258
+ });
3259
+ ```
3260
+
3261
+ ### 10.4 Restrict Editing to Form Fields Only
3262
+
3263
+ ```csharp
3264
+ // This protects the document but allows editing in form field content controls
3265
+ settingsPart.Settings = new Settings(
3266
+ new DocumentProtection
3267
+ {
3268
+ Edit = DocumentProtectionValues.Forms,
3269
+ Enforcement = true
3270
+ },
3271
+ // Also set to allow editing only in form fields
3272
+ new EditingRestrictions { Val = EditingRestrictionValues.Forms }
3273
+ );
3274
+ ```
3275
+
3276
+ ### 10.5 PermStart / PermEnd — Editable Regions in Protected Document
3277
+
3278
+ Allow specific regions (ranges) to be edited even when the document is protected.
3279
+
3280
+ ```csharp
3281
+ // <w:permStart w:id="1" w:editor="everyone"/>
3282
+ // <w:r><w:t>Editable text</w:t></w:r>
3283
+ // <w:permEnd w:id="1"/>
3284
+
3285
+ // Even when document protection is on, this range can be edited by everyone
3286
+
3287
+ var editableRegion = new Paragraph(
3288
+ new ParagraphProperties(
3289
+ new PermStart { Id = 1, EditorGroup = RangePermissionEditingGroupValues.Everyone }
3290
+ ),
3291
+ new Run(new Text("This text can be edited even in a protected document.") { Space = SpaceProcessingModeValues.Preserve }),
3292
+ new ParagraphProperties(
3293
+ new PermEnd { Id = 1 }
3294
+ )
3295
+ );
3296
+
3297
+ // EditorGroup values:
3298
+ // Everyone — anyone can edit
3299
+ // Administrators — only administrators
3300
+ // Contributors — only contributors
3301
+ // Editors — only editors
3302
+ // Owners — only document owners
3303
+ // Nobody — nobody can edit (use with w:perm sbz="1" for "does not include")
3304
+
3305
+ // Use specific user name:
3306
+ // <w:permStart w:id="2" w:author="Alice"/>
3307
+
3308
+ // For tracked changes review scenario (allow comments but not direct editing):
3309
+ var commentableRegion = new Paragraph(
3310
+ new ParagraphProperties(
3311
+ new PermStart { Id = 2, EditorGroup = RangePermissionEditingGroupValues.Everyone }
3312
+ ),
3313
+ new Run(new Text("This region allows comments and tracked changes.") { Space = SpaceProcessingModeValues.Preserve }),
3314
+ new ParagraphProperties(
3315
+ new PermEnd { Id = 2 }
3316
+ )
3317
+ );
3318
+ ```
3319
+
3320
+ ### 10.6 Full Document Protection with Password and Salt
3321
+
3322
+ ```csharp
3323
+ public static void ProtectDocument(
3324
+ WordprocessingDocument doc,
3325
+ string password,
3326
+ DocumentProtectionValues protectionType)
3327
+ {
3328
+ var settingsPart = doc.MainDocumentPart!.AddNewPart<DocumentSettingsPart>();
3329
+ if (settingsPart.Settings == null)
3330
+ settingsPart.Settings = new Settings();
3331
+
3332
+ // Generate password hash
3333
+ byte[] salt = new byte[16];
3334
+ using (var rng = System.Security.Cryptography.RandomNumberGenerator.Create())
3335
+ rng.GetBytes(salt);
3336
+
3337
+ int spinCount = 100000;
3338
+ using var pbkdf2 = new System.Security.Cryptography.Rfc2898DeriveBytes(
3339
+ password, salt, spinCount, System.Security.Cryptography.HashAlgorithmName.SHA512);
3340
+ byte[] hash = pbkdf2.GetBytes(64);
3341
+
3342
+ var protection = new DocumentProtection
3343
+ {
3344
+ Edit = protectionType,
3345
+ Enforcement = true,
3346
+ CryptographicProviderType = CryptProviderValues.RsaAES,
3347
+ CryptographicAlgorithmClass = CryptAlgorithmClassValues.Hash,
3348
+ CryptographicAlgorithmType = CryptAlgorithmValues.TypeAny,
3349
+ CryptographicAlgorithmSid = 14, // SHA-512
3350
+ CryptographicSpinCount = (UInt32Value)spinCount,
3351
+ Hash = Convert.ToBase64String(hash),
3352
+ Salt = Convert.ToBase64String(salt)
3353
+ };
3354
+
3355
+ settingsPart.Settings.Append(protection);
3356
+ settingsPart.Settings.Save();
3357
+ }
3358
+ ```
3359
+
3360
+ ---
3361
+
3362
+ ## Quick Reference: Common Element Order in OpenXML
3363
+
3364
+ When building complex elements, remember these ordering rules:
3365
+
3366
+ ### Run Elements Order (inside RunProperties):
3367
+ `RunFonts` → `Bold`/`Italic` → `Color` → `FontSize` → `Underline` → `VerticalTextAlignment` → `Emphasis` → (any other)
3368
+
3369
+ ### Paragraph Elements Order (inside ParagraphProperties):
3370
+ `ParagraphStyleId` → `KeepNext` → `KeepLines` → `PageBreakBefore` → `FrameProperties` → `WidowControl` → `NumPr` → `Indentation` → `SpacingBetweenLines` → `Justification` → `SectionProperties`
3371
+
3372
+ ### Table Properties Order:
3373
+ `TableWidth` → `TextDirection` → `Borders` → `Shading` → `TableLayout` → `TableCellMarginDefault`
3374
+
3375
+ ### SectionProperties Order:
3376
+ `FootnotePr` → `EndnotePr` → `Type` → `PageSize` → `PageMargin` → `PaperSource` → `PageBorders` → `LineNumberRestart` → `PageNumberFormat` → `TitlePage` → `TextDirection`
3377
+
3378
+ ---
3379
+
3380
+ *Generated for DocumentFormat.OpenXml 3.x / .NET 8+ / C# 12*
3381
+ *Last updated: 2026-03-22*