@nguyenphp/antigravity-marketing 1.0.18 → 1.0.20
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.
- package/README.md +130 -78
- package/package.json +4 -3
- package/templates/.agent/skills/marketing-report-expert/SKILL.md +70 -0
- package/templates/.agent/skills/minimax-docx/LICENSE +21 -0
- package/templates/.agent/skills/minimax-docx/SKILL.md +274 -0
- package/templates/.agent/skills/minimax-docx/assets/styles/academic_styles.xml +250 -0
- package/templates/.agent/skills/minimax-docx/assets/styles/corporate_styles.xml +284 -0
- package/templates/.agent/skills/minimax-docx/assets/styles/default_styles.xml +449 -0
- package/templates/.agent/skills/minimax-docx/assets/xsd/aesthetic-rules.xsd +470 -0
- package/templates/.agent/skills/minimax-docx/assets/xsd/business-rules.xsd +130 -0
- package/templates/.agent/skills/minimax-docx/assets/xsd/common-types.xsd +159 -0
- package/templates/.agent/skills/minimax-docx/assets/xsd/wml-subset.xsd +589 -0
- package/templates/.agent/skills/minimax-docx/references/cjk_typography.md +357 -0
- package/templates/.agent/skills/minimax-docx/references/cjk_university_template_guide.md +184 -0
- package/templates/.agent/skills/minimax-docx/references/comments_guide.md +191 -0
- package/templates/.agent/skills/minimax-docx/references/design_good_bad_examples.md +829 -0
- package/templates/.agent/skills/minimax-docx/references/design_principles.md +819 -0
- package/templates/.agent/skills/minimax-docx/references/openxml_element_order.md +308 -0
- package/templates/.agent/skills/minimax-docx/references/openxml_encyclopedia_part1.md +4061 -0
- package/templates/.agent/skills/minimax-docx/references/openxml_encyclopedia_part2.md +2820 -0
- package/templates/.agent/skills/minimax-docx/references/openxml_encyclopedia_part3.md +3381 -0
- package/templates/.agent/skills/minimax-docx/references/openxml_namespaces.md +82 -0
- package/templates/.agent/skills/minimax-docx/references/openxml_units.md +72 -0
- package/templates/.agent/skills/minimax-docx/references/scenario_a_create.md +284 -0
- package/templates/.agent/skills/minimax-docx/references/scenario_b_edit_content.md +295 -0
- package/templates/.agent/skills/minimax-docx/references/scenario_c_apply_template.md +456 -0
- package/templates/.agent/skills/minimax-docx/references/track_changes_guide.md +200 -0
- package/templates/.agent/skills/minimax-docx/references/troubleshooting.md +506 -0
- package/templates/.agent/skills/minimax-docx/references/typography_guide.md +294 -0
- package/templates/.agent/skills/minimax-docx/references/xsd_validation_guide.md +158 -0
- package/templates/.agent/skills/minimax-docx/scripts/doc_to_docx.sh +40 -0
- package/templates/.agent/skills/minimax-docx/scripts/docx_preview.sh +37 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Cli/MiniMaxAIDocx.Cli.csproj +19 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Cli/Program.cs +18 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/AnalyzeCommand.cs +147 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/ApplyTemplateCommand.cs +322 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/CreateCommand.cs +324 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/DiffCommand.cs +155 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/EditContentCommand.cs +487 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/FixOrderCommand.cs +108 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/MergeRunsCommand.cs +122 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/ValidateCommand.cs +107 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/MiniMaxAIDocx.Core.csproj +15 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/CommentSynchronizer.cs +169 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/ElementOrder.cs +80 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/NamespaceConstants.cs +42 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/RunMerger.cs +81 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/StyleAnalyzer.cs +81 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/TrackChangesHelper.cs +99 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/UnitConverter.cs +23 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples.cs +1832 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch1.cs +910 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch2.cs +999 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch3.cs +1048 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch4.cs +1038 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/CharacterFormattingSamples.cs +1020 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/DocumentCreationSamples.cs +1121 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/FieldAndTocSamples.cs +624 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/FootnoteAndCommentSamples.cs +675 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/HeaderFooterSamples.cs +838 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ImageSamples.cs +917 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ListAndNumberingSamples.cs +826 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ParagraphFormattingSamples.cs +1199 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/StyleSystemSamples.cs +1487 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/TableSamples.cs +1163 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/TrackChangesSamples.cs +595 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/CjkHelper.cs +39 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/FontDefaults.cs +24 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/PageSizes.cs +20 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/BusinessRuleValidator.cs +224 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/GateCheckValidator.cs +148 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/ValidationResult.cs +23 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/XsdValidator.cs +69 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.slnx +4 -0
- package/templates/.agent/skills/minimax-docx/scripts/env_check.sh +196 -0
- package/templates/.agent/skills/minimax-docx/scripts/setup.ps1 +274 -0
- package/templates/.agent/skills/minimax-docx/scripts/setup.sh +504 -0
- package/templates/.agent/skills/minimax-multimodal-toolkit/SKILL.md +359 -0
- package/templates/.agent/skills/minimax-pdf/README.md +222 -0
- package/templates/.agent/skills/minimax-pdf/SKILL.md +201 -0
- package/templates/.agent/skills/minimax-pdf/design/design.md +381 -0
- package/templates/.agent/skills/minimax-pdf/scripts/cover.py +1579 -0
- package/templates/.agent/skills/minimax-pdf/scripts/fill_inspect.py +200 -0
- package/templates/.agent/skills/minimax-pdf/scripts/fill_write.py +242 -0
- package/templates/.agent/skills/minimax-pdf/scripts/make.sh +491 -0
- package/templates/.agent/skills/minimax-pdf/scripts/merge.py +112 -0
- package/templates/.agent/skills/minimax-pdf/scripts/palette.py +559 -0
- package/templates/.agent/skills/minimax-pdf/scripts/reformat_parse.py +374 -0
- package/templates/.agent/skills/minimax-pdf/scripts/render_body.py +1055 -0
- package/templates/.agent/skills/minimax-pdf/scripts/render_cover.cjs +111 -0
- package/templates/.agent/skills/minimax-xlsx/SKILL.md +138 -0
- package/templates/.agent/skills/minimax-xlsx/references/create.md +691 -0
- package/templates/.agent/skills/minimax-xlsx/references/edit.md +684 -0
- package/templates/.agent/skills/minimax-xlsx/references/fix.md +37 -0
- package/templates/.agent/skills/minimax-xlsx/references/format.md +768 -0
- package/templates/.agent/skills/minimax-xlsx/references/ooxml-cheatsheet.md +231 -0
- package/templates/.agent/skills/minimax-xlsx/references/read-analyze.md +97 -0
- package/templates/.agent/skills/minimax-xlsx/references/validate.md +772 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/formula_check.py +422 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/libreoffice_recalc.py +248 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/shared_strings_builder.py +163 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/style_audit.py +575 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_add_column.py +395 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_insert_row.py +274 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_pack.py +87 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_reader.py +362 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_shift_rows.py +396 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_unpack.py +130 -0
- package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/[Content_Types].xml +9 -0
- package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/_rels/.rels +6 -0
- package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/_rels/workbook.xml.rels +19 -0
- package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/sharedStrings.xml +33 -0
- package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/styles.xml +160 -0
- package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/workbook.xml +30 -0
- package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/worksheets/sheet1.xml +70 -0
- package/templates/.agent/skills/pptx-generator/SKILL.md +249 -0
- package/templates/.agent/skills/pptx-generator/references/design-system.md +392 -0
- package/templates/.agent/skills/pptx-generator/references/editing.md +162 -0
- package/templates/.agent/skills/pptx-generator/references/pitfalls.md +112 -0
- package/templates/.agent/skills/pptx-generator/references/pptxgenjs.md +420 -0
- package/templates/.agent/skills/pptx-generator/references/slide-types.md +413 -0
- package/templates/.agent/skills/tutorial-video-expert/SKILL.md +88 -0
- package/templates/.agent/skills/ui-ux-pro-max/SKILL.md +170 -585
- package/templates/.agent/skills/vision-analysis/SKILL.md +174 -0
- package/templates/.agent/workflows/analyze.md +3 -0
- package/templates/.agent/workflows/brand-report.md +44 -0
- package/templates/.agent/workflows/report.md +49 -0
- package/templates/.agent/agents/backend-specialist.md +0 -263
- package/templates/.agent/agents/database-architect.md +0 -226
- package/templates/.agent/agents/debugger.md +0 -225
- package/templates/.agent/agents/devops-engineer.md +0 -242
- package/templates/.agent/agents/frontend-specialist.md +0 -527
- package/templates/.agent/agents/game-developer.md +0 -162
- package/templates/.agent/agents/mobile-developer.md +0 -377
- package/templates/.agent/agents/penetration-tester.md +0 -188
- package/templates/.agent/agents/security-auditor.md +0 -170
- package/templates/.agent/agents/test-engineer.md +0 -158
- package/templates/.agent/skills/api-patterns/SKILL.md +0 -81
- package/templates/.agent/skills/api-patterns/api-style.md +0 -42
- package/templates/.agent/skills/api-patterns/auth.md +0 -24
- package/templates/.agent/skills/api-patterns/documentation.md +0 -26
- package/templates/.agent/skills/api-patterns/graphql.md +0 -41
- package/templates/.agent/skills/api-patterns/rate-limiting.md +0 -31
- package/templates/.agent/skills/api-patterns/response.md +0 -37
- package/templates/.agent/skills/api-patterns/rest.md +0 -40
- package/templates/.agent/skills/api-patterns/scripts/api_validator.py +0 -211
- package/templates/.agent/skills/api-patterns/security-testing.md +0 -122
- package/templates/.agent/skills/api-patterns/trpc.md +0 -41
- package/templates/.agent/skills/api-patterns/versioning.md +0 -22
- package/templates/.agent/skills/app-builder/SKILL.md +0 -75
- package/templates/.agent/skills/app-builder/agent-coordination.md +0 -71
- package/templates/.agent/skills/app-builder/feature-building.md +0 -53
- package/templates/.agent/skills/app-builder/project-detection.md +0 -34
- package/templates/.agent/skills/app-builder/scaffolding.md +0 -118
- package/templates/.agent/skills/app-builder/tech-stack.md +0 -40
- package/templates/.agent/skills/app-builder/templates/SKILL.md +0 -39
- package/templates/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +0 -76
- package/templates/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +0 -92
- package/templates/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +0 -88
- package/templates/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +0 -88
- package/templates/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +0 -83
- package/templates/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +0 -90
- package/templates/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +0 -90
- package/templates/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +0 -82
- package/templates/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +0 -100
- package/templates/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +0 -106
- package/templates/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +0 -101
- package/templates/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +0 -83
- package/templates/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +0 -93
- package/templates/.agent/skills/architecture/SKILL.md +0 -55
- package/templates/.agent/skills/architecture/context-discovery.md +0 -43
- package/templates/.agent/skills/architecture/examples.md +0 -94
- package/templates/.agent/skills/architecture/pattern-selection.md +0 -68
- package/templates/.agent/skills/architecture/patterns-reference.md +0 -50
- package/templates/.agent/skills/architecture/trade-off-analysis.md +0 -77
- package/templates/.agent/skills/bash-linux/SKILL.md +0 -199
- package/templates/.agent/skills/behavioral-modes/SKILL.md +0 -242
- package/templates/.agent/skills/clean-code/SKILL.md +0 -201
- package/templates/.agent/skills/code-review-checklist/SKILL.md +0 -109
- package/templates/.agent/skills/database-design/SKILL.md +0 -52
- package/templates/.agent/skills/database-design/database-selection.md +0 -43
- package/templates/.agent/skills/database-design/indexing.md +0 -39
- package/templates/.agent/skills/database-design/migrations.md +0 -48
- package/templates/.agent/skills/database-design/optimization.md +0 -36
- package/templates/.agent/skills/database-design/orm-selection.md +0 -30
- package/templates/.agent/skills/database-design/schema-design.md +0 -56
- package/templates/.agent/skills/database-design/scripts/schema_validator.py +0 -172
- package/templates/.agent/skills/deployment-procedures/SKILL.md +0 -241
- package/templates/.agent/skills/docker-expert/SKILL.md +0 -409
- package/templates/.agent/skills/game-development/2d-games/SKILL.md +0 -119
- package/templates/.agent/skills/game-development/3d-games/SKILL.md +0 -135
- package/templates/.agent/skills/game-development/SKILL.md +0 -167
- package/templates/.agent/skills/game-development/game-art/SKILL.md +0 -185
- package/templates/.agent/skills/game-development/game-audio/SKILL.md +0 -190
- package/templates/.agent/skills/game-development/game-design/SKILL.md +0 -129
- package/templates/.agent/skills/game-development/mobile-games/SKILL.md +0 -108
- package/templates/.agent/skills/game-development/multiplayer/SKILL.md +0 -132
- package/templates/.agent/skills/game-development/pc-games/SKILL.md +0 -144
- package/templates/.agent/skills/game-development/vr-ar/SKILL.md +0 -123
- package/templates/.agent/skills/game-development/web-games/SKILL.md +0 -150
- package/templates/.agent/skills/lint-and-validate/SKILL.md +0 -45
- package/templates/.agent/skills/lint-and-validate/scripts/lint_runner.py +0 -172
- package/templates/.agent/skills/lint-and-validate/scripts/type_coverage.py +0 -173
- package/templates/.agent/skills/mcp-builder/SKILL.md +0 -176
- package/templates/.agent/skills/nestjs-expert/SKILL.md +0 -552
- package/templates/.agent/skills/nextjs-best-practices/SKILL.md +0 -203
- package/templates/.agent/skills/nodejs-best-practices/SKILL.md +0 -333
- package/templates/.agent/skills/parallel-agents/SKILL.md +0 -175
- package/templates/.agent/skills/performance-profiling/SKILL.md +0 -143
- package/templates/.agent/skills/performance-profiling/scripts/lighthouse_audit.py +0 -76
- package/templates/.agent/skills/powershell-windows/SKILL.md +0 -167
- package/templates/.agent/skills/prisma-expert/SKILL.md +0 -355
- package/templates/.agent/skills/python-patterns/SKILL.md +0 -441
- package/templates/.agent/skills/react-patterns/SKILL.md +0 -198
- package/templates/.agent/skills/red-team-tactics/SKILL.md +0 -199
- package/templates/.agent/skills/server-management/SKILL.md +0 -161
- package/templates/.agent/skills/systematic-debugging/SKILL.md +0 -109
- package/templates/.agent/skills/tdd-workflow/SKILL.md +0 -149
- package/templates/.agent/skills/testing-patterns/SKILL.md +0 -178
- package/templates/.agent/skills/testing-patterns/scripts/test_runner.py +0 -219
- package/templates/.agent/skills/typescript-expert/SKILL.md +0 -429
- package/templates/.agent/skills/vue-expert/SKILL.md +0 -374
- package/templates/.agent/skills/vulnerability-scanner/SKILL.md +0 -276
- package/templates/.agent/skills/vulnerability-scanner/checklists.md +0 -121
- package/templates/.agent/skills/vulnerability-scanner/scripts/security_scan.py +0 -458
- package/templates/.agent/skills/webapp-testing/SKILL.md +0 -187
- package/templates/.agent/skills/webapp-testing/scripts/playwright_runner.py +0 -173
- package/templates/.agent/workflows/debug.md +0 -103
- package/templates/.agent/workflows/deploy.md +0 -176
- package/templates/.agent/workflows/enhance.md +0 -63
- package/templates/.agent/workflows/test.md +0 -144
|
@@ -0,0 +1,917 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// ImageSamples.cs — Comprehensive OpenXML image handling reference
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// EMU (English Metric Unit) is the universal measurement in DrawingML:
|
|
5
|
+
// 1 inch = 914400 EMU
|
|
6
|
+
// 1 cm = 360000 EMU
|
|
7
|
+
// 1 px@96dpi = 9525 EMU (914400 / 96 = 9525)
|
|
8
|
+
//
|
|
9
|
+
// Image architecture in OpenXML:
|
|
10
|
+
// Paragraph → Run → Drawing → DW.Inline (or DW.Anchor)
|
|
11
|
+
// → A.Graphic → A.GraphicData → PIC.Picture
|
|
12
|
+
// → PIC.BlipFill → A.Blip (references the image part via r:embed)
|
|
13
|
+
// → PIC.ShapeProperties → A.Transform2D → A.Extents (cx, cy)
|
|
14
|
+
//
|
|
15
|
+
// CRITICAL RULES:
|
|
16
|
+
// 1. Extent.Cx/Cy on DW.Inline/DW.Anchor MUST match A.Extents.Cx/Cy
|
|
17
|
+
// on PIC.ShapeProperties. Mismatch causes rendering issues.
|
|
18
|
+
// 2. Each Drawing element needs a unique DocProperties.Id within the document.
|
|
19
|
+
// 3. ImagePart must be added to the PART that references it:
|
|
20
|
+
// - MainDocumentPart for images in body
|
|
21
|
+
// - HeaderPart for images in headers
|
|
22
|
+
// - FooterPart for images in footers
|
|
23
|
+
// 4. Blip.Embed contains the relationship ID (rId) linking to the ImagePart.
|
|
24
|
+
// ============================================================================
|
|
25
|
+
|
|
26
|
+
using DocumentFormat.OpenXml;
|
|
27
|
+
using DocumentFormat.OpenXml.Packaging;
|
|
28
|
+
using DocumentFormat.OpenXml.Wordprocessing;
|
|
29
|
+
|
|
30
|
+
using A = DocumentFormat.OpenXml.Drawing;
|
|
31
|
+
using DW = DocumentFormat.OpenXml.Drawing.Wordprocessing;
|
|
32
|
+
using PIC = DocumentFormat.OpenXml.Drawing.Pictures;
|
|
33
|
+
|
|
34
|
+
namespace MiniMaxAIDocx.Core.Samples;
|
|
35
|
+
|
|
36
|
+
/// <summary>
|
|
37
|
+
/// Reference implementations for every common image operation in OpenXML.
|
|
38
|
+
/// All methods produce valid, Word-renderable markup.
|
|
39
|
+
/// </summary>
|
|
40
|
+
public static class ImageSamples
|
|
41
|
+
{
|
|
42
|
+
// ── Constants ──────────────────────────────────────────────────────
|
|
43
|
+
private const long EmuPerInch = 914400L;
|
|
44
|
+
private const long EmuPerCm = 360000L;
|
|
45
|
+
private const long EmuPerPixel96Dpi = 9525L; // 914400 / 96
|
|
46
|
+
|
|
47
|
+
// GraphicData URI that tells Word "this is a picture"
|
|
48
|
+
private const string PicGraphicDataUri = "http://schemas.openxmlformats.org/drawingml/2006/picture";
|
|
49
|
+
|
|
50
|
+
// ── 1. Inline Image (most common) ──────────────────────────────────
|
|
51
|
+
|
|
52
|
+
/// <summary>
|
|
53
|
+
/// Inserts an inline image into the body. Inline images flow with text
|
|
54
|
+
/// and do not float. This is the most common image insertion pattern.
|
|
55
|
+
/// </summary>
|
|
56
|
+
/// <param name="mainPart">The MainDocumentPart to add the image relationship to.</param>
|
|
57
|
+
/// <param name="body">The Body element to append the paragraph to.</param>
|
|
58
|
+
/// <param name="imagePath">Filesystem path to the image file (png, jpg, etc.).</param>
|
|
59
|
+
/// <param name="widthPx">Desired display width in pixels (at 96 dpi).</param>
|
|
60
|
+
/// <param name="heightPx">Desired display height in pixels (at 96 dpi).</param>
|
|
61
|
+
public static void InsertInlineImage(
|
|
62
|
+
MainDocumentPart mainPart, Body body,
|
|
63
|
+
string imagePath, int widthPx, int heightPx)
|
|
64
|
+
{
|
|
65
|
+
// Step 1: Add the image file as a part. The ImagePartType must match
|
|
66
|
+
// the actual file format. AddImagePart returns the ImagePart; we then
|
|
67
|
+
// feed data into it.
|
|
68
|
+
var imageType = GetImagePartType(imagePath);
|
|
69
|
+
ImagePart imagePart = mainPart.AddImagePart(imageType);
|
|
70
|
+
|
|
71
|
+
using (FileStream stream = new FileStream(imagePath, FileMode.Open))
|
|
72
|
+
{
|
|
73
|
+
imagePart.FeedData(stream);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Step 2: Get the relationship ID that links the Blip to this ImagePart.
|
|
77
|
+
string relId = mainPart.GetIdOfPart(imagePart);
|
|
78
|
+
|
|
79
|
+
// Step 3: Convert pixel dimensions to EMU.
|
|
80
|
+
// Formula: pixels * 9525 = EMU (at 96 dpi, which is Word's assumption)
|
|
81
|
+
long cx = widthPx * EmuPerPixel96Dpi;
|
|
82
|
+
long cy = heightPx * EmuPerPixel96Dpi;
|
|
83
|
+
|
|
84
|
+
// Step 4: Build the Drawing element using the reusable helper.
|
|
85
|
+
// docPropId must be unique across the entire document.
|
|
86
|
+
Drawing drawing = BuildDrawingElement(
|
|
87
|
+
relId, cx, cy,
|
|
88
|
+
docPropId: 1U,
|
|
89
|
+
name: "Image1",
|
|
90
|
+
description: null);
|
|
91
|
+
|
|
92
|
+
// Step 5: Wrap in Paragraph → Run → Drawing
|
|
93
|
+
Paragraph para = new Paragraph(
|
|
94
|
+
new Run(drawing));
|
|
95
|
+
|
|
96
|
+
body.AppendChild(para);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ── 2. Floating Image (Anchor) ─────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
/// <summary>
|
|
102
|
+
/// Inserts a floating image with absolute positioning using DW.Anchor.
|
|
103
|
+
/// Floating images are positioned relative to a reference point (page,
|
|
104
|
+
/// column, paragraph, etc.) and text wraps around them.
|
|
105
|
+
/// </summary>
|
|
106
|
+
public static void InsertFloatingImage(
|
|
107
|
+
MainDocumentPart mainPart, Body body, string imagePath)
|
|
108
|
+
{
|
|
109
|
+
ImagePart imagePart = mainPart.AddImagePart(GetImagePartType(imagePath));
|
|
110
|
+
using (FileStream stream = new FileStream(imagePath, FileMode.Open))
|
|
111
|
+
{
|
|
112
|
+
imagePart.FeedData(stream);
|
|
113
|
+
}
|
|
114
|
+
string relId = mainPart.GetIdOfPart(imagePart);
|
|
115
|
+
|
|
116
|
+
long cx = (long)(3.0 * EmuPerInch); // 3 inches wide
|
|
117
|
+
long cy = (long)(2.0 * EmuPerInch); // 2 inches tall
|
|
118
|
+
|
|
119
|
+
// DW.Anchor is used instead of DW.Inline for floating images.
|
|
120
|
+
// Key differences from Inline:
|
|
121
|
+
// - Has positioning (SimplePos, HorizontalPosition, VerticalPosition)
|
|
122
|
+
// - Has wrapping mode (WrapSquare, WrapTight, WrapNone, etc.)
|
|
123
|
+
// - Has BehindDoc and LayoutInCell flags
|
|
124
|
+
DW.Anchor anchor = new DW.Anchor(
|
|
125
|
+
// SimplePosition: when SimplePos=true, uses SimplePosition x/y directly.
|
|
126
|
+
// Normally false; we use HorizontalPosition/VerticalPosition instead.
|
|
127
|
+
new DW.SimplePosition { X = 0L, Y = 0L },
|
|
128
|
+
|
|
129
|
+
// HorizontalPosition: where the image sits horizontally.
|
|
130
|
+
// RelativeFrom can be: Column, Page, Margin, Character, LeftMargin, etc.
|
|
131
|
+
new DW.HorizontalPosition(
|
|
132
|
+
new DW.PositionOffset("914400") // 1 inch from reference
|
|
133
|
+
)
|
|
134
|
+
{ RelativeFrom = DW.HorizontalRelativePositionValues.Column },
|
|
135
|
+
|
|
136
|
+
// VerticalPosition: where the image sits vertically.
|
|
137
|
+
new DW.VerticalPosition(
|
|
138
|
+
new DW.PositionOffset("457200") // 0.5 inch from reference
|
|
139
|
+
)
|
|
140
|
+
{ RelativeFrom = DW.VerticalRelativePositionValues.Paragraph },
|
|
141
|
+
|
|
142
|
+
// Extent: overall size of the drawing object
|
|
143
|
+
new DW.Extent { Cx = cx, Cy = cy },
|
|
144
|
+
|
|
145
|
+
// EffectExtent: extra space for shadows, glow, etc. (0 if none)
|
|
146
|
+
new DW.EffectExtent
|
|
147
|
+
{
|
|
148
|
+
LeftEdge = 0L,
|
|
149
|
+
TopEdge = 0L,
|
|
150
|
+
RightEdge = 0L,
|
|
151
|
+
BottomEdge = 0L
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
// WrapSquare: text wraps in a square around the image bounding box.
|
|
155
|
+
new DW.WrapSquare { WrapText = DW.WrapTextValues.BothSides },
|
|
156
|
+
|
|
157
|
+
// DocProperties: unique ID + name for the drawing object
|
|
158
|
+
new DW.DocProperties { Id = 2U, Name = "FloatingImage1" },
|
|
159
|
+
|
|
160
|
+
// Non-visual graphic frame properties (required but usually empty)
|
|
161
|
+
new DW.NonVisualGraphicFrameDrawingProperties(
|
|
162
|
+
new A.GraphicFrameLocks { NoChangeAspect = true }),
|
|
163
|
+
|
|
164
|
+
// The actual graphic content
|
|
165
|
+
new A.Graphic(
|
|
166
|
+
new A.GraphicData(
|
|
167
|
+
new PIC.Picture(
|
|
168
|
+
new PIC.NonVisualPictureProperties(
|
|
169
|
+
new PIC.NonVisualDrawingProperties
|
|
170
|
+
{
|
|
171
|
+
Id = 0U,
|
|
172
|
+
Name = "FloatingImage1.png"
|
|
173
|
+
},
|
|
174
|
+
new PIC.NonVisualPictureDrawingProperties()),
|
|
175
|
+
new PIC.BlipFill(
|
|
176
|
+
new A.Blip { Embed = relId },
|
|
177
|
+
new A.Stretch(new A.FillRectangle())),
|
|
178
|
+
new PIC.ShapeProperties(
|
|
179
|
+
new A.Transform2D(
|
|
180
|
+
new A.Offset { X = 0L, Y = 0L },
|
|
181
|
+
// CRITICAL: These cx/cy MUST match the Extent above
|
|
182
|
+
new A.Extents { Cx = cx, Cy = cy }),
|
|
183
|
+
new A.PresetGeometry(
|
|
184
|
+
new A.AdjustValueList())
|
|
185
|
+
{ Preset = A.ShapeTypeValues.Rectangle }))
|
|
186
|
+
)
|
|
187
|
+
{ Uri = PicGraphicDataUri })
|
|
188
|
+
)
|
|
189
|
+
{
|
|
190
|
+
// Anchor attributes
|
|
191
|
+
DistanceFromTop = 0U,
|
|
192
|
+
DistanceFromBottom = 0U,
|
|
193
|
+
DistanceFromLeft = 114300U, // ~0.125 inch gap between text and image
|
|
194
|
+
DistanceFromRight = 114300U,
|
|
195
|
+
SimplePos = false,
|
|
196
|
+
RelativeHeight = 251658240U, // z-order; higher = in front
|
|
197
|
+
BehindDoc = false, // true = behind text (like a watermark)
|
|
198
|
+
Locked = false,
|
|
199
|
+
LayoutInCell = true,
|
|
200
|
+
AllowOverlap = true
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
Paragraph para = new Paragraph(new Run(new Drawing(anchor)));
|
|
204
|
+
body.AppendChild(para);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ── 3. Image with Various Text Wrapping ────────────────────────────
|
|
208
|
+
|
|
209
|
+
/// <summary>
|
|
210
|
+
/// Demonstrates the four main text wrapping modes for floating images.
|
|
211
|
+
/// Each wrapping mode controls how body text flows around the image.
|
|
212
|
+
/// </summary>
|
|
213
|
+
public static void InsertImageWithTextWrapping(
|
|
214
|
+
MainDocumentPart mainPart, Body body, string imagePath)
|
|
215
|
+
{
|
|
216
|
+
// All wrapping modes require DW.Anchor (not DW.Inline).
|
|
217
|
+
// The wrapping element is a direct child of the Anchor element.
|
|
218
|
+
|
|
219
|
+
ImagePart imagePart = mainPart.AddImagePart(GetImagePartType(imagePath));
|
|
220
|
+
using (FileStream stream = new FileStream(imagePath, FileMode.Open))
|
|
221
|
+
{
|
|
222
|
+
imagePart.FeedData(stream);
|
|
223
|
+
}
|
|
224
|
+
string relId = mainPart.GetIdOfPart(imagePart);
|
|
225
|
+
|
|
226
|
+
long cx = (long)(2.5 * EmuPerInch);
|
|
227
|
+
long cy = (long)(2.0 * EmuPerInch);
|
|
228
|
+
|
|
229
|
+
// ── WrapSquare ──
|
|
230
|
+
// Text wraps in a rectangular bounding box around the image.
|
|
231
|
+
// WrapText controls which sides text appears on.
|
|
232
|
+
var wrapSquare = new DW.WrapSquare
|
|
233
|
+
{
|
|
234
|
+
WrapText = DW.WrapTextValues.BothSides
|
|
235
|
+
// Other options: Left, Right, Largest
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// ── WrapTight ──
|
|
239
|
+
// Text wraps tightly around the actual contour of the image.
|
|
240
|
+
// Uses a WrapPolygon to define the outline; Word can auto-generate this.
|
|
241
|
+
// The coordinates are in EMU relative to the image's top-left.
|
|
242
|
+
var wrapTight = new DW.WrapTight(
|
|
243
|
+
new DW.WrapPolygon(
|
|
244
|
+
new DW.StartPoint { X = 0L, Y = 0L },
|
|
245
|
+
new DW.LineTo { X = 0L, Y = 21600L },
|
|
246
|
+
new DW.LineTo { X = 21600L, Y = 21600L },
|
|
247
|
+
new DW.LineTo { X = 21600L, Y = 0L },
|
|
248
|
+
new DW.LineTo { X = 0L, Y = 0L }
|
|
249
|
+
)
|
|
250
|
+
{ Edited = false }
|
|
251
|
+
)
|
|
252
|
+
{
|
|
253
|
+
WrapText = DW.WrapTextValues.BothSides
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
// ── WrapTopAndBottom ──
|
|
257
|
+
// No text appears beside the image. Text only above and below.
|
|
258
|
+
// This effectively makes the image act as a block-level element
|
|
259
|
+
// but still floating (not inline).
|
|
260
|
+
var wrapTopAndBottom = new DW.WrapTopBottom
|
|
261
|
+
{
|
|
262
|
+
DistanceFromTop = 0U,
|
|
263
|
+
DistanceFromBottom = 0U
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
// ── WrapNone ──
|
|
267
|
+
// No text wrapping at all. Image floats over or behind text.
|
|
268
|
+
// Combined with BehindDoc=true, this creates a watermark effect.
|
|
269
|
+
var wrapNone = new DW.WrapNone();
|
|
270
|
+
|
|
271
|
+
// Example: build anchor with WrapSquare (swap in any wrapping element above)
|
|
272
|
+
DW.Anchor anchor = BuildAnchorElement(
|
|
273
|
+
relId, cx, cy,
|
|
274
|
+
docPropId: 3U,
|
|
275
|
+
name: "WrappedImage",
|
|
276
|
+
wrapElement: wrapSquare,
|
|
277
|
+
behindDoc: false);
|
|
278
|
+
|
|
279
|
+
body.AppendChild(new Paragraph(new Run(new Drawing(anchor))));
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// ── 4. Image with Border ───────────────────────────────────────────
|
|
283
|
+
|
|
284
|
+
/// <summary>
|
|
285
|
+
/// Inserts an image with a visible outline/border. The border is applied
|
|
286
|
+
/// via A.Outline on the PIC.ShapeProperties element.
|
|
287
|
+
/// </summary>
|
|
288
|
+
public static void InsertImageWithBorder(
|
|
289
|
+
MainDocumentPart mainPart, Body body, string imagePath)
|
|
290
|
+
{
|
|
291
|
+
ImagePart imagePart = mainPart.AddImagePart(GetImagePartType(imagePath));
|
|
292
|
+
using (FileStream stream = new FileStream(imagePath, FileMode.Open))
|
|
293
|
+
{
|
|
294
|
+
imagePart.FeedData(stream);
|
|
295
|
+
}
|
|
296
|
+
string relId = mainPart.GetIdOfPart(imagePart);
|
|
297
|
+
|
|
298
|
+
long cx = (long)(3.0 * EmuPerInch);
|
|
299
|
+
long cy = (long)(2.0 * EmuPerInch);
|
|
300
|
+
|
|
301
|
+
// Build PIC.ShapeProperties with an Outline element for the border.
|
|
302
|
+
// Outline width is in EMU. 1pt = 12700 EMU.
|
|
303
|
+
var shapeProperties = new PIC.ShapeProperties(
|
|
304
|
+
new A.Transform2D(
|
|
305
|
+
new A.Offset { X = 0L, Y = 0L },
|
|
306
|
+
new A.Extents { Cx = cx, Cy = cy }),
|
|
307
|
+
new A.PresetGeometry(
|
|
308
|
+
new A.AdjustValueList())
|
|
309
|
+
{ Preset = A.ShapeTypeValues.Rectangle },
|
|
310
|
+
// The Outline element defines the border
|
|
311
|
+
new A.Outline(
|
|
312
|
+
// SolidFill sets the border color
|
|
313
|
+
new A.SolidFill(
|
|
314
|
+
new A.RgbColorModelHex { Val = "2F5496" }), // Dark blue
|
|
315
|
+
// PresetDash sets the line style (solid, dash, dot, etc.)
|
|
316
|
+
new A.PresetDash { Val = A.PresetLineDashValues.Solid }
|
|
317
|
+
)
|
|
318
|
+
{
|
|
319
|
+
Width = 25400, // 2pt border (12700 EMU per pt)
|
|
320
|
+
CompoundLineType = A.CompoundLineValues.Single
|
|
321
|
+
}
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
var picture = new PIC.Picture(
|
|
325
|
+
new PIC.NonVisualPictureProperties(
|
|
326
|
+
new PIC.NonVisualDrawingProperties { Id = 0U, Name = "BorderedImage.png" },
|
|
327
|
+
new PIC.NonVisualPictureDrawingProperties()),
|
|
328
|
+
new PIC.BlipFill(
|
|
329
|
+
new A.Blip { Embed = relId },
|
|
330
|
+
new A.Stretch(new A.FillRectangle())),
|
|
331
|
+
shapeProperties);
|
|
332
|
+
|
|
333
|
+
var drawing = new Drawing(
|
|
334
|
+
new DW.Inline(
|
|
335
|
+
new DW.Extent { Cx = cx, Cy = cy },
|
|
336
|
+
new DW.EffectExtent
|
|
337
|
+
{
|
|
338
|
+
// Must account for border width in effect extent so it is not clipped
|
|
339
|
+
LeftEdge = 25400L,
|
|
340
|
+
TopEdge = 25400L,
|
|
341
|
+
RightEdge = 25400L,
|
|
342
|
+
BottomEdge = 25400L
|
|
343
|
+
},
|
|
344
|
+
new DW.DocProperties { Id = 4U, Name = "BorderedImage" },
|
|
345
|
+
new DW.NonVisualGraphicFrameDrawingProperties(
|
|
346
|
+
new A.GraphicFrameLocks { NoChangeAspect = true }),
|
|
347
|
+
new A.Graphic(
|
|
348
|
+
new A.GraphicData(picture)
|
|
349
|
+
{ Uri = PicGraphicDataUri })
|
|
350
|
+
)
|
|
351
|
+
{
|
|
352
|
+
DistanceFromTop = 0U,
|
|
353
|
+
DistanceFromBottom = 0U,
|
|
354
|
+
DistanceFromLeft = 0U,
|
|
355
|
+
DistanceFromRight = 0U
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
body.AppendChild(new Paragraph(new Run(drawing)));
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// ── 5. Image with Alt Text ─────────────────────────────────────────
|
|
362
|
+
|
|
363
|
+
/// <summary>
|
|
364
|
+
/// Inserts an image with alt text for accessibility. The alt text is set
|
|
365
|
+
/// on the DocProperties.Description attribute. Screen readers use this.
|
|
366
|
+
/// Word also shows it in the "Alt Text" pane.
|
|
367
|
+
/// </summary>
|
|
368
|
+
public static void InsertImageWithAltText(
|
|
369
|
+
MainDocumentPart mainPart, Body body, string imagePath)
|
|
370
|
+
{
|
|
371
|
+
ImagePart imagePart = mainPart.AddImagePart(GetImagePartType(imagePath));
|
|
372
|
+
using (FileStream stream = new FileStream(imagePath, FileMode.Open))
|
|
373
|
+
{
|
|
374
|
+
imagePart.FeedData(stream);
|
|
375
|
+
}
|
|
376
|
+
string relId = mainPart.GetIdOfPart(imagePart);
|
|
377
|
+
|
|
378
|
+
long cx = (long)(3.0 * EmuPerInch);
|
|
379
|
+
long cy = (long)(2.0 * EmuPerInch);
|
|
380
|
+
|
|
381
|
+
// DocProperties.Description is the standard alt text field.
|
|
382
|
+
// DocProperties.Title is an optional short title shown in some UIs.
|
|
383
|
+
Drawing drawing = BuildDrawingElement(
|
|
384
|
+
relId, cx, cy,
|
|
385
|
+
docPropId: 5U,
|
|
386
|
+
name: "AccessibleImage",
|
|
387
|
+
description: "A chart showing quarterly revenue growth from Q1 to Q4 2025");
|
|
388
|
+
|
|
389
|
+
body.AppendChild(new Paragraph(new Run(drawing)));
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// ── 6. Image in Header ─────────────────────────────────────────────
|
|
393
|
+
|
|
394
|
+
/// <summary>
|
|
395
|
+
/// Inserts an image into a header part. The image relationship MUST be
|
|
396
|
+
/// added to the HeaderPart, NOT the MainDocumentPart. If you add it
|
|
397
|
+
/// to MainDocumentPart, Word will show a broken image in the header
|
|
398
|
+
/// because relationship IDs are scoped to their containing part.
|
|
399
|
+
/// </summary>
|
|
400
|
+
public static void InsertImageInHeader(HeaderPart headerPart, string imagePath)
|
|
401
|
+
{
|
|
402
|
+
// CRITICAL: AddImagePart to headerPart, not mainDocumentPart!
|
|
403
|
+
// Each OpenXML part has its own relationship namespace.
|
|
404
|
+
// An rId in the header must point to a relationship in the header's .rels file.
|
|
405
|
+
ImagePart imagePart = headerPart.AddImagePart(GetImagePartType(imagePath));
|
|
406
|
+
using (FileStream stream = new FileStream(imagePath, FileMode.Open))
|
|
407
|
+
{
|
|
408
|
+
imagePart.FeedData(stream);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// GetIdOfPart must also be called on headerPart
|
|
412
|
+
string relId = headerPart.GetIdOfPart(imagePart);
|
|
413
|
+
|
|
414
|
+
long cx = (long)(1.5 * EmuPerInch); // Company logo, typically small
|
|
415
|
+
long cy = (long)(0.5 * EmuPerInch);
|
|
416
|
+
|
|
417
|
+
Drawing drawing = BuildDrawingElement(
|
|
418
|
+
relId, cx, cy,
|
|
419
|
+
docPropId: 6U,
|
|
420
|
+
name: "HeaderLogo",
|
|
421
|
+
description: "Company logo");
|
|
422
|
+
|
|
423
|
+
// Headers use the Header element with Paragraph children (same as Body)
|
|
424
|
+
Header header = headerPart.Header;
|
|
425
|
+
Paragraph para = new Paragraph(
|
|
426
|
+
new ParagraphProperties(
|
|
427
|
+
new Justification { Val = JustificationValues.Center }),
|
|
428
|
+
new Run(drawing));
|
|
429
|
+
|
|
430
|
+
header.AppendChild(para);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// ── 7. Image in Table Cell ─────────────────────────────────────────
|
|
434
|
+
|
|
435
|
+
/// <summary>
|
|
436
|
+
/// Inserts an image into a table cell, sized to fit. Table cells constrain
|
|
437
|
+
/// content width, so we calculate appropriate dimensions to avoid overflow.
|
|
438
|
+
/// The image part is still added to MainDocumentPart (the cell is in the body).
|
|
439
|
+
/// </summary>
|
|
440
|
+
/// <param name="mainPart">MainDocumentPart (owns the relationship).</param>
|
|
441
|
+
/// <param name="cell">The TableCell to insert the image into.</param>
|
|
442
|
+
/// <param name="imagePath">Path to the image file.</param>
|
|
443
|
+
public static void InsertImageInTableCell(
|
|
444
|
+
MainDocumentPart mainPart, TableCell cell, string imagePath)
|
|
445
|
+
{
|
|
446
|
+
ImagePart imagePart = mainPart.AddImagePart(GetImagePartType(imagePath));
|
|
447
|
+
using (FileStream stream = new FileStream(imagePath, FileMode.Open))
|
|
448
|
+
{
|
|
449
|
+
imagePart.FeedData(stream);
|
|
450
|
+
}
|
|
451
|
+
string relId = mainPart.GetIdOfPart(imagePart);
|
|
452
|
+
|
|
453
|
+
// Determine cell width from TableCellWidth if available.
|
|
454
|
+
// TableCellWidth.Width is in DXA (twentieths of a point).
|
|
455
|
+
// If not set, use a reasonable default (e.g., 2 inches).
|
|
456
|
+
long maxWidthEmu = (long)(2.0 * EmuPerInch); // default
|
|
457
|
+
|
|
458
|
+
TableCellProperties? tcPr = cell.GetFirstChild<TableCellProperties>();
|
|
459
|
+
TableCellWidth? tcWidth = tcPr?.GetFirstChild<TableCellWidth>();
|
|
460
|
+
if (tcWidth?.Width is not null && tcWidth.Type?.Value == TableWidthUnitValues.Dxa)
|
|
461
|
+
{
|
|
462
|
+
// Convert DXA to EMU: 1 DXA = 1/20 pt = 1/1440 inch = 914400/1440 EMU
|
|
463
|
+
int dxa = int.Parse(tcWidth.Width);
|
|
464
|
+
maxWidthEmu = (long)(dxa * (EmuPerInch / 1440.0));
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Calculate image dimensions to fit within the cell width
|
|
468
|
+
(long cx, long cy) = CalculateImageDimensions(imagePath, maxWidthEmu / (double)EmuPerInch);
|
|
469
|
+
|
|
470
|
+
Drawing drawing = BuildDrawingElement(
|
|
471
|
+
relId, cx, cy,
|
|
472
|
+
docPropId: 7U,
|
|
473
|
+
name: "CellImage",
|
|
474
|
+
description: null);
|
|
475
|
+
|
|
476
|
+
// A TableCell MUST contain at least one Paragraph.
|
|
477
|
+
// We add the image inside that paragraph.
|
|
478
|
+
Paragraph para = cell.GetFirstChild<Paragraph>() ?? cell.AppendChild(new Paragraph());
|
|
479
|
+
para.AppendChild(new Run(drawing));
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// ── 8. Replace Existing Image ──────────────────────────────────────
|
|
483
|
+
|
|
484
|
+
/// <summary>
|
|
485
|
+
/// Replaces an existing image by updating the ImagePart data behind a
|
|
486
|
+
/// known relationship ID. The Blip.Embed attribute (rId) stays the same;
|
|
487
|
+
/// only the binary content changes. This avoids needing to rebuild the
|
|
488
|
+
/// entire Drawing XML tree.
|
|
489
|
+
/// </summary>
|
|
490
|
+
/// <param name="mainPart">The MainDocumentPart containing the image relationship.</param>
|
|
491
|
+
/// <param name="oldRelId">The existing relationship ID (e.g., "rId5") of the image to replace.</param>
|
|
492
|
+
/// <param name="newImagePath">Path to the replacement image file.</param>
|
|
493
|
+
public static void ReplaceExistingImage(
|
|
494
|
+
MainDocumentPart mainPart, string oldRelId, string newImagePath)
|
|
495
|
+
{
|
|
496
|
+
// Look up the existing ImagePart by its relationship ID
|
|
497
|
+
OpenXmlPart part = mainPart.GetPartById(oldRelId);
|
|
498
|
+
if (part is not ImagePart imagePart)
|
|
499
|
+
{
|
|
500
|
+
throw new InvalidOperationException(
|
|
501
|
+
$"Relationship {oldRelId} does not point to an ImagePart.");
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Feed new image data into the existing part.
|
|
505
|
+
// This replaces the binary content while keeping the same rId.
|
|
506
|
+
using (FileStream stream = new FileStream(newImagePath, FileMode.Open))
|
|
507
|
+
{
|
|
508
|
+
imagePart.FeedData(stream);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// NOTE: If the new image has different dimensions, you should also
|
|
512
|
+
// update the Extent.Cx/Cy and A.Extents.Cx/Cy in the Drawing element.
|
|
513
|
+
// Find all Blip elements referencing this relId:
|
|
514
|
+
//
|
|
515
|
+
// var blips = mainPart.Document.Descendants<A.Blip>()
|
|
516
|
+
// .Where(b => b.Embed == oldRelId);
|
|
517
|
+
// foreach (var blip in blips)
|
|
518
|
+
// {
|
|
519
|
+
// // Navigate up to find the Extent and A.Extents to update dimensions
|
|
520
|
+
// }
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// ── 9. SVG with PNG Fallback ───────────────────────────────────────
|
|
524
|
+
|
|
525
|
+
/// <summary>
|
|
526
|
+
/// Inserts an SVG image with a PNG fallback for compatibility.
|
|
527
|
+
/// Word 2019+ supports SVG natively; older versions show the PNG.
|
|
528
|
+
/// The SVG is referenced via an extension element (SvgBlip) inside the Blip,
|
|
529
|
+
/// while the Blip.Embed itself points to the PNG fallback.
|
|
530
|
+
/// </summary>
|
|
531
|
+
public static void InsertSvgWithPngFallback(
|
|
532
|
+
MainDocumentPart mainPart, Body body,
|
|
533
|
+
string svgPath, string pngFallbackPath)
|
|
534
|
+
{
|
|
535
|
+
// Add PNG fallback as the primary image part
|
|
536
|
+
ImagePart pngPart = mainPart.AddImagePart(ImagePartType.Png);
|
|
537
|
+
using (FileStream pngStream = new FileStream(pngFallbackPath, FileMode.Open))
|
|
538
|
+
{
|
|
539
|
+
pngPart.FeedData(pngStream);
|
|
540
|
+
}
|
|
541
|
+
string pngRelId = mainPart.GetIdOfPart(pngPart);
|
|
542
|
+
|
|
543
|
+
// Add SVG as a separate image part
|
|
544
|
+
ImagePart svgPart = mainPart.AddImagePart(ImagePartType.Svg);
|
|
545
|
+
using (FileStream svgStream = new FileStream(svgPath, FileMode.Open))
|
|
546
|
+
{
|
|
547
|
+
svgPart.FeedData(svgStream);
|
|
548
|
+
}
|
|
549
|
+
string svgRelId = mainPart.GetIdOfPart(svgPart);
|
|
550
|
+
|
|
551
|
+
long cx = (long)(3.0 * EmuPerInch);
|
|
552
|
+
long cy = (long)(3.0 * EmuPerInch);
|
|
553
|
+
|
|
554
|
+
// The Blip.Embed points to the PNG fallback.
|
|
555
|
+
// The SVG is added as an extension element (asvg:svgBlip) inside the Blip.
|
|
556
|
+
// Namespace: http://schemas.microsoft.com/office/drawing/2016/SVG/main
|
|
557
|
+
var blip = new A.Blip { Embed = pngRelId };
|
|
558
|
+
|
|
559
|
+
// Add SVG extension to the Blip using BlipExtensionList
|
|
560
|
+
var svgExtension = new A.BlipExtensionList(
|
|
561
|
+
new A.BlipExtension(
|
|
562
|
+
// The SVG blip element references the SVG image part
|
|
563
|
+
new OpenXmlUnknownElement(
|
|
564
|
+
"asvg", "svgBlip",
|
|
565
|
+
"http://schemas.microsoft.com/office/drawing/2016/SVG/main")
|
|
566
|
+
// NOTE: In production, set the r:embed attribute on this element
|
|
567
|
+
// to svgRelId. OpenXmlUnknownElement requires manual attribute setting.
|
|
568
|
+
)
|
|
569
|
+
{ Uri = "{96DAC541-7B7A-43D3-8B79-37D633B846F1}" }
|
|
570
|
+
);
|
|
571
|
+
blip.Append(svgExtension);
|
|
572
|
+
|
|
573
|
+
var picture = new PIC.Picture(
|
|
574
|
+
new PIC.NonVisualPictureProperties(
|
|
575
|
+
new PIC.NonVisualDrawingProperties { Id = 0U, Name = "SvgImage.svg" },
|
|
576
|
+
new PIC.NonVisualPictureDrawingProperties()),
|
|
577
|
+
new PIC.BlipFill(
|
|
578
|
+
blip,
|
|
579
|
+
new A.Stretch(new A.FillRectangle())),
|
|
580
|
+
new PIC.ShapeProperties(
|
|
581
|
+
new A.Transform2D(
|
|
582
|
+
new A.Offset { X = 0L, Y = 0L },
|
|
583
|
+
new A.Extents { Cx = cx, Cy = cy }),
|
|
584
|
+
new A.PresetGeometry(new A.AdjustValueList())
|
|
585
|
+
{ Preset = A.ShapeTypeValues.Rectangle }));
|
|
586
|
+
|
|
587
|
+
var drawing = new Drawing(
|
|
588
|
+
new DW.Inline(
|
|
589
|
+
new DW.Extent { Cx = cx, Cy = cy },
|
|
590
|
+
new DW.EffectExtent
|
|
591
|
+
{
|
|
592
|
+
LeftEdge = 0L, TopEdge = 0L,
|
|
593
|
+
RightEdge = 0L, BottomEdge = 0L
|
|
594
|
+
},
|
|
595
|
+
new DW.DocProperties { Id = 9U, Name = "SvgImage" },
|
|
596
|
+
new DW.NonVisualGraphicFrameDrawingProperties(
|
|
597
|
+
new A.GraphicFrameLocks { NoChangeAspect = true }),
|
|
598
|
+
new A.Graphic(
|
|
599
|
+
new A.GraphicData(picture)
|
|
600
|
+
{ Uri = PicGraphicDataUri })
|
|
601
|
+
)
|
|
602
|
+
{
|
|
603
|
+
DistanceFromTop = 0U,
|
|
604
|
+
DistanceFromBottom = 0U,
|
|
605
|
+
DistanceFromLeft = 0U,
|
|
606
|
+
DistanceFromRight = 0U
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
body.AppendChild(new Paragraph(new Run(drawing)));
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// ── 10. Calculate Image Dimensions ─────────────────────────────────
|
|
613
|
+
|
|
614
|
+
/// <summary>
|
|
615
|
+
/// Reads the actual pixel dimensions of an image file (PNG or JPEG) and
|
|
616
|
+
/// calculates EMU values that fit within a maximum width while maintaining
|
|
617
|
+
/// the original aspect ratio. Uses raw byte reading to avoid a dependency
|
|
618
|
+
/// on System.Drawing (which is Windows-only on modern .NET).
|
|
619
|
+
/// </summary>
|
|
620
|
+
/// <param name="imagePath">Path to a PNG or JPEG image file.</param>
|
|
621
|
+
/// <param name="maxWidthInches">Maximum allowed width in inches.</param>
|
|
622
|
+
/// <returns>Tuple of (cx, cy) in EMU, scaled to fit maxWidthInches.</returns>
|
|
623
|
+
/// <remarks>
|
|
624
|
+
/// For production use, consider SkiaSharp or SixLabors.ImageSharp for
|
|
625
|
+
/// cross-platform image metadata reading with broader format support.
|
|
626
|
+
/// This implementation handles PNG and JPEG only.
|
|
627
|
+
/// </remarks>
|
|
628
|
+
public static (long cx, long cy) CalculateImageDimensions(
|
|
629
|
+
string imagePath, double maxWidthInches)
|
|
630
|
+
{
|
|
631
|
+
// Read pixel dimensions from the image file header.
|
|
632
|
+
// We parse PNG IHDR or JPEG SOF0 markers directly to avoid
|
|
633
|
+
// pulling in System.Drawing.Common (Windows-only on .NET 6+).
|
|
634
|
+
(int widthPx, int heightPx, double dpiX, double dpiY) = ReadImageMetadata(imagePath);
|
|
635
|
+
|
|
636
|
+
// Calculate actual size in inches based on pixel count and DPI
|
|
637
|
+
double widthInches = widthPx / dpiX;
|
|
638
|
+
double heightInches = heightPx / dpiY;
|
|
639
|
+
|
|
640
|
+
// Scale down if wider than maxWidthInches, preserving aspect ratio
|
|
641
|
+
if (widthInches > maxWidthInches)
|
|
642
|
+
{
|
|
643
|
+
double scale = maxWidthInches / widthInches;
|
|
644
|
+
widthInches = maxWidthInches;
|
|
645
|
+
heightInches *= scale;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
long cx = (long)(widthInches * EmuPerInch);
|
|
649
|
+
long cy = (long)(heightInches * EmuPerInch);
|
|
650
|
+
|
|
651
|
+
return (cx, cy);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/// <summary>
|
|
655
|
+
/// Reads width, height, and DPI from a PNG or JPEG file header.
|
|
656
|
+
/// Returns 96 DPI as default if DPI metadata is not found.
|
|
657
|
+
/// </summary>
|
|
658
|
+
private static (int widthPx, int heightPx, double dpiX, double dpiY) ReadImageMetadata(
|
|
659
|
+
string imagePath)
|
|
660
|
+
{
|
|
661
|
+
const double DefaultDpi = 96.0;
|
|
662
|
+
byte[] header = new byte[32];
|
|
663
|
+
|
|
664
|
+
using var fs = new FileStream(imagePath, FileMode.Open, FileAccess.Read);
|
|
665
|
+
int bytesRead = fs.Read(header, 0, header.Length);
|
|
666
|
+
|
|
667
|
+
// PNG: starts with 0x89 0x50 0x4E 0x47 (‰PNG)
|
|
668
|
+
// IHDR chunk is always first; width and height are at bytes 16-23 (big-endian)
|
|
669
|
+
if (bytesRead >= 24 &&
|
|
670
|
+
header[0] == 0x89 && header[1] == 0x50 &&
|
|
671
|
+
header[2] == 0x4E && header[3] == 0x47)
|
|
672
|
+
{
|
|
673
|
+
int width = (header[16] << 24) | (header[17] << 16) |
|
|
674
|
+
(header[18] << 8) | header[19];
|
|
675
|
+
int height = (header[20] << 24) | (header[21] << 16) |
|
|
676
|
+
(header[22] << 8) | header[23];
|
|
677
|
+
// PNG DPI is in the pHYs chunk (not in IHDR); use default for simplicity
|
|
678
|
+
return (width, height, DefaultDpi, DefaultDpi);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// JPEG: starts with 0xFF 0xD8
|
|
682
|
+
// Scan for SOF0 (0xFF 0xC0) marker to find dimensions
|
|
683
|
+
if (bytesRead >= 2 && header[0] == 0xFF && header[1] == 0xD8)
|
|
684
|
+
{
|
|
685
|
+
fs.Position = 2;
|
|
686
|
+
while (fs.Position < fs.Length - 1)
|
|
687
|
+
{
|
|
688
|
+
int b = fs.ReadByte();
|
|
689
|
+
if (b != 0xFF) continue;
|
|
690
|
+
|
|
691
|
+
int marker = fs.ReadByte();
|
|
692
|
+
if (marker == -1) break;
|
|
693
|
+
|
|
694
|
+
// SOF0 (0xC0) or SOF2 (0xC2, progressive)
|
|
695
|
+
if (marker == 0xC0 || marker == 0xC2)
|
|
696
|
+
{
|
|
697
|
+
byte[] sof = new byte[7];
|
|
698
|
+
if (fs.Read(sof, 0, 7) == 7)
|
|
699
|
+
{
|
|
700
|
+
// SOF structure: length(2) + precision(1) + height(2) + width(2)
|
|
701
|
+
int height = (sof[3] << 8) | sof[4];
|
|
702
|
+
int width = (sof[5] << 8) | sof[6];
|
|
703
|
+
return (width, height, DefaultDpi, DefaultDpi);
|
|
704
|
+
}
|
|
705
|
+
break;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// Skip other markers: read 2-byte length and advance
|
|
709
|
+
if (marker is not (0xD0 or 0xD1 or 0xD2 or 0xD3 or 0xD4 or
|
|
710
|
+
0xD5 or 0xD6 or 0xD7 or 0xD8 or 0xD9 or 0x01))
|
|
711
|
+
{
|
|
712
|
+
byte[] lenBytes = new byte[2];
|
|
713
|
+
if (fs.Read(lenBytes, 0, 2) < 2) break;
|
|
714
|
+
int len = (lenBytes[0] << 8) | lenBytes[1];
|
|
715
|
+
if (len < 2) break;
|
|
716
|
+
fs.Position += len - 2;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// Fallback: cannot determine dimensions; return a reasonable default
|
|
722
|
+
// Caller should handle this gracefully.
|
|
723
|
+
return (300, 200, DefaultDpi, DefaultDpi);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// ── 11. Reusable Drawing Builder (Inline) ──────────────────────────
|
|
727
|
+
|
|
728
|
+
/// <summary>
|
|
729
|
+
/// Builds a complete Drawing element for an inline image. This is the
|
|
730
|
+
/// reusable core that most insertion methods delegate to.
|
|
731
|
+
/// </summary>
|
|
732
|
+
/// <param name="relId">Relationship ID pointing to the ImagePart (e.g., "rId4").</param>
|
|
733
|
+
/// <param name="cx">Image width in EMU. Must be positive.</param>
|
|
734
|
+
/// <param name="cy">Image height in EMU. Must be positive.</param>
|
|
735
|
+
/// <param name="docPropId">Unique ID for DocProperties within the document.
|
|
736
|
+
/// Each Drawing in a document must have a distinct DocProperties.Id.</param>
|
|
737
|
+
/// <param name="name">Name for DocProperties (shows in Word selection pane).</param>
|
|
738
|
+
/// <param name="description">Alt text for accessibility. Null if not needed.</param>
|
|
739
|
+
/// <returns>A fully constructed Drawing element ready to append to a Run.</returns>
|
|
740
|
+
public static Drawing BuildDrawingElement(
|
|
741
|
+
string relId, long cx, long cy,
|
|
742
|
+
uint docPropId, string name, string? description)
|
|
743
|
+
{
|
|
744
|
+
// ── Complete element hierarchy ──
|
|
745
|
+
// Drawing
|
|
746
|
+
// └─ DW.Inline
|
|
747
|
+
// ├─ DW.Extent (cx, cy) ← bounding box size
|
|
748
|
+
// ├─ DW.EffectExtent ← extra space for effects
|
|
749
|
+
// ├─ DW.DocProperties (id, name, descr) ← identity + alt text
|
|
750
|
+
// ├─ DW.NonVisualGraphicFrameDrawingProperties
|
|
751
|
+
// │ └─ A.GraphicFrameLocks ← lock aspect ratio
|
|
752
|
+
// └─ A.Graphic
|
|
753
|
+
// └─ A.GraphicData (uri = picture namespace)
|
|
754
|
+
// └─ PIC.Picture
|
|
755
|
+
// ├─ PIC.NonVisualPictureProperties
|
|
756
|
+
// │ ├─ PIC.NonVisualDrawingProperties
|
|
757
|
+
// │ └─ PIC.NonVisualPictureDrawingProperties
|
|
758
|
+
// ├─ PIC.BlipFill
|
|
759
|
+
// │ ├─ A.Blip (embed = relId)
|
|
760
|
+
// │ └─ A.Stretch → A.FillRectangle
|
|
761
|
+
// └─ PIC.ShapeProperties
|
|
762
|
+
// ├─ A.Transform2D
|
|
763
|
+
// │ ├─ A.Offset (0, 0)
|
|
764
|
+
// │ └─ A.Extents (cx, cy) ← MUST match DW.Extent!
|
|
765
|
+
// └─ A.PresetGeometry (rect)
|
|
766
|
+
|
|
767
|
+
var docProps = new DW.DocProperties
|
|
768
|
+
{
|
|
769
|
+
Id = docPropId,
|
|
770
|
+
Name = name
|
|
771
|
+
};
|
|
772
|
+
if (description is not null)
|
|
773
|
+
{
|
|
774
|
+
docProps.Description = description;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
var picture = new PIC.Picture(
|
|
778
|
+
new PIC.NonVisualPictureProperties(
|
|
779
|
+
new PIC.NonVisualDrawingProperties
|
|
780
|
+
{
|
|
781
|
+
Id = 0U,
|
|
782
|
+
Name = name
|
|
783
|
+
},
|
|
784
|
+
new PIC.NonVisualPictureDrawingProperties()),
|
|
785
|
+
new PIC.BlipFill(
|
|
786
|
+
new A.Blip
|
|
787
|
+
{
|
|
788
|
+
Embed = relId,
|
|
789
|
+
// CompressionState controls image quality vs file size.
|
|
790
|
+
// Print = high quality, Screen = medium, Email = low, None = original
|
|
791
|
+
CompressionState = A.BlipCompressionValues.Print
|
|
792
|
+
},
|
|
793
|
+
new A.Stretch(new A.FillRectangle())),
|
|
794
|
+
new PIC.ShapeProperties(
|
|
795
|
+
new A.Transform2D(
|
|
796
|
+
new A.Offset { X = 0L, Y = 0L },
|
|
797
|
+
new A.Extents { Cx = cx, Cy = cy }), // MUST match DW.Extent
|
|
798
|
+
new A.PresetGeometry(
|
|
799
|
+
new A.AdjustValueList())
|
|
800
|
+
{ Preset = A.ShapeTypeValues.Rectangle }));
|
|
801
|
+
|
|
802
|
+
var inline = new DW.Inline(
|
|
803
|
+
new DW.Extent { Cx = cx, Cy = cy }, // MUST match A.Extents
|
|
804
|
+
new DW.EffectExtent
|
|
805
|
+
{
|
|
806
|
+
LeftEdge = 0L,
|
|
807
|
+
TopEdge = 0L,
|
|
808
|
+
RightEdge = 0L,
|
|
809
|
+
BottomEdge = 0L
|
|
810
|
+
},
|
|
811
|
+
docProps,
|
|
812
|
+
new DW.NonVisualGraphicFrameDrawingProperties(
|
|
813
|
+
new A.GraphicFrameLocks { NoChangeAspect = true }),
|
|
814
|
+
new A.Graphic(
|
|
815
|
+
new A.GraphicData(picture)
|
|
816
|
+
{ Uri = PicGraphicDataUri }))
|
|
817
|
+
{
|
|
818
|
+
DistanceFromTop = 0U,
|
|
819
|
+
DistanceFromBottom = 0U,
|
|
820
|
+
DistanceFromLeft = 0U,
|
|
821
|
+
DistanceFromRight = 0U
|
|
822
|
+
};
|
|
823
|
+
|
|
824
|
+
return new Drawing(inline);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// ── Private Helpers ────────────────────────────────────────────────
|
|
828
|
+
|
|
829
|
+
/// <summary>
|
|
830
|
+
/// Builds a DW.Anchor element for floating images with configurable wrapping.
|
|
831
|
+
/// </summary>
|
|
832
|
+
private static DW.Anchor BuildAnchorElement(
|
|
833
|
+
string relId, long cx, long cy,
|
|
834
|
+
uint docPropId, string name,
|
|
835
|
+
OpenXmlElement wrapElement,
|
|
836
|
+
bool behindDoc)
|
|
837
|
+
{
|
|
838
|
+
return new DW.Anchor(
|
|
839
|
+
new DW.SimplePosition { X = 0L, Y = 0L },
|
|
840
|
+
new DW.HorizontalPosition(
|
|
841
|
+
new DW.PositionOffset("0"))
|
|
842
|
+
{ RelativeFrom = DW.HorizontalRelativePositionValues.Column },
|
|
843
|
+
new DW.VerticalPosition(
|
|
844
|
+
new DW.PositionOffset("0"))
|
|
845
|
+
{ RelativeFrom = DW.VerticalRelativePositionValues.Paragraph },
|
|
846
|
+
new DW.Extent { Cx = cx, Cy = cy },
|
|
847
|
+
new DW.EffectExtent
|
|
848
|
+
{
|
|
849
|
+
LeftEdge = 0L,
|
|
850
|
+
TopEdge = 0L,
|
|
851
|
+
RightEdge = 0L,
|
|
852
|
+
BottomEdge = 0L
|
|
853
|
+
},
|
|
854
|
+
wrapElement,
|
|
855
|
+
new DW.DocProperties { Id = docPropId, Name = name },
|
|
856
|
+
new DW.NonVisualGraphicFrameDrawingProperties(
|
|
857
|
+
new A.GraphicFrameLocks { NoChangeAspect = true }),
|
|
858
|
+
new A.Graphic(
|
|
859
|
+
new A.GraphicData(
|
|
860
|
+
new PIC.Picture(
|
|
861
|
+
new PIC.NonVisualPictureProperties(
|
|
862
|
+
new PIC.NonVisualDrawingProperties
|
|
863
|
+
{
|
|
864
|
+
Id = 0U,
|
|
865
|
+
Name = name
|
|
866
|
+
},
|
|
867
|
+
new PIC.NonVisualPictureDrawingProperties()),
|
|
868
|
+
new PIC.BlipFill(
|
|
869
|
+
new A.Blip { Embed = relId },
|
|
870
|
+
new A.Stretch(new A.FillRectangle())),
|
|
871
|
+
new PIC.ShapeProperties(
|
|
872
|
+
new A.Transform2D(
|
|
873
|
+
new A.Offset { X = 0L, Y = 0L },
|
|
874
|
+
new A.Extents { Cx = cx, Cy = cy }),
|
|
875
|
+
new A.PresetGeometry(
|
|
876
|
+
new A.AdjustValueList())
|
|
877
|
+
{ Preset = A.ShapeTypeValues.Rectangle }))
|
|
878
|
+
)
|
|
879
|
+
{ Uri = PicGraphicDataUri })
|
|
880
|
+
)
|
|
881
|
+
{
|
|
882
|
+
DistanceFromTop = 0U,
|
|
883
|
+
DistanceFromBottom = 0U,
|
|
884
|
+
DistanceFromLeft = 114300U,
|
|
885
|
+
DistanceFromRight = 114300U,
|
|
886
|
+
SimplePos = false,
|
|
887
|
+
RelativeHeight = 251658240U,
|
|
888
|
+
BehindDoc = behindDoc,
|
|
889
|
+
Locked = false,
|
|
890
|
+
LayoutInCell = true,
|
|
891
|
+
AllowOverlap = true
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/// <summary>
|
|
896
|
+
/// Maps file extensions to OpenXML PartTypeInfo values via ImagePartType.
|
|
897
|
+
/// In SDK 3.x, ImagePartType is a static class whose members return PartTypeInfo.
|
|
898
|
+
/// </summary>
|
|
899
|
+
private static PartTypeInfo GetImagePartType(string imagePath)
|
|
900
|
+
{
|
|
901
|
+
string ext = Path.GetExtension(imagePath).ToLowerInvariant();
|
|
902
|
+
return ext switch
|
|
903
|
+
{
|
|
904
|
+
".png" => ImagePartType.Png,
|
|
905
|
+
".jpg" or ".jpeg" => ImagePartType.Jpeg,
|
|
906
|
+
".gif" => ImagePartType.Gif,
|
|
907
|
+
".bmp" => ImagePartType.Bmp,
|
|
908
|
+
".tif" or ".tiff" => ImagePartType.Tiff,
|
|
909
|
+
".svg" => ImagePartType.Svg,
|
|
910
|
+
".emf" => ImagePartType.Emf,
|
|
911
|
+
".wmf" => ImagePartType.Wmf,
|
|
912
|
+
".ico" => ImagePartType.Icon,
|
|
913
|
+
_ => throw new NotSupportedException(
|
|
914
|
+
$"Image format '{ext}' is not supported by OpenXML.")
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
}
|