{"id":17788704,"url":"https://github.com/devel0/netcore-docx","last_synced_at":"2025-03-16T09:33:35.723Z","repository":{"id":122983010,"uuid":"167382079","full_name":"devel0/netcore-docx","owner":"devel0","description":"net core wrapper around DocumentFormat.OpenXml","archived":false,"fork":false,"pushed_at":"2024-02-28T21:48:00.000Z","size":7768,"stargazers_count":7,"open_issues_count":0,"forks_count":4,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-02-27T07:12:33.610Z","etag":null,"topics":["document","docx","netcore","openxml-sdk"],"latest_commit_sha":null,"homepage":"https://devel0.github.io/netcore-docx/api/SearchAThing.DocX.html","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/devel0.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2019-01-24T14:36:37.000Z","updated_at":"2024-10-16T09:55:22.000Z","dependencies_parsed_at":"2024-02-28T22:50:53.030Z","dependency_job_id":null,"html_url":"https://github.com/devel0/netcore-docx","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devel0%2Fnetcore-docx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devel0%2Fnetcore-docx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devel0%2Fnetcore-docx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devel0%2Fnetcore-docx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devel0","download_url":"https://codeload.github.com/devel0/netcore-docx/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243809888,"owners_count":20351407,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["document","docx","netcore","openxml-sdk"],"created_at":"2024-10-27T10:20:54.715Z","updated_at":"2025-03-16T09:33:35.404Z","avatar_url":"https://github.com/devel0.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# netcore-docx\n\n[![NuGet Badge](https://buildstats.info/nuget/netcore-docx)](https://www.nuget.org/packages/netcore-docx/)\n\n.NET core docx\n\n- [API Documentation](https://devel0.github.io/netcore-docx/html/class_search_a_thing_1_1_doc_x_1_1_doc_x_ext.html)\n- [Changelog](https://github.com/devel0/netcore-docx/commits/master)\n\n\u003chr/\u003e\n\n\u003c!-- TOC --\u003e\n- [description](#description)\n- [quickstart](#quickstart)\n  - [document creation](#document-creation)\n  - [document default, main section properties, library](#document-default-main-section-properties-library)\n  - [title and subtitle](#title-and-subtitle)\n  - [toc](#toc)\n  - [font family, size, color, paragraph properties](#font-family-size-color-paragraph-properties)\n  - [custom paragraph style](#custom-paragraph-style)\n  - [find and replace](#find-and-replace)\n  - [images](#images)\n  - [tables](#tables)\n  - [numbering](#numbering)\n- [TODO](#todo)\n- [debugging](#debugging)\n- [unit test](#unit-test)\n- [install](#install)\n- [how this project was built](#how-this-project-was-built)\n- [references](#references)\n\u003c!-- TOCEND --\u003e\n\n\u003chr/\u003e\n\n## description\n\nThis is an extension methods set for [OpenXML SDK](https://github.com/OfficeDev/Open-XML-SDK).\n\nThis mean that there aren't wrapping classes around document, paragraph, run, style, etc... but extension methods that cohexists within OpenXML SDK.\n\nThis allow you to use any OpenXML SDK infrastructure in addition to provided simplified extensions methods without these interfere with a custom approach.\n\n## quickstart\n\n[example](src/examples/0001/Program.cs) shows basic usage through [sampledoc](src/sampledocs/SampleDoc2.cs)\n\n### document creation\n\nYou can open an existing doc or create an empty docx eventually as a copy of an existing prestyled template document:\n\n```cs\nusing DocumentFormat.OpenXml.Wordprocessing;\nusing DColor = System.Drawing.Color;\n\nusing SearchAThing.DocX;\nusing SearchAThing;\n\nnamespace examples;\n\nclass Program\n{\n    static void Main(string[] args)\n    {        \n        var outputPathfilename = System.IO.Path.Combine(\n            Environment.GetFolderPath(Environment.SpecialFolder.Desktop), \"out.docx\");\n                \n        // note: initialize new empty document into given pathfilename saving for first time\n        using (var doc = DocXToolkit.Create(outputPathfilename))\n        {\n            doc.AddParagraph(\"Sample paragraph\");\n\n            // note: remember to call doc.Finalize() to release associated dynamic resources\n            doc.Finalize();\n        }\n\n        var psi = new ProcessStartInfo(outputPathfilename);\n        psi.UseShellExecute = true;\n        Process.Start(psi);\n    }\n}\n\n```\n\n- is highly suggested to call `Finalize()` method at the end of document usage to release resources allocated per document ( style libraries, state variables, ... ).\n- `DColor` is an alias for `System.Drawing.Color`\n\n### document default, main section properties, library\n\n```cs\ndoc\n    .SetDocDefaults(runFontName: \"Arial\");\n\ndoc\n    .MainSectionProperties().SetPageSize(PaperSize.A4, PageOrientationValues.Portrait);\n\nvar titleStyle = doc.GetPredefinedStyle(LibraryStyleEnum.Title);\nvar subTitleStyle = doc.GetPredefinedStyle(LibraryStyleEnum.Subtitle);\n\nvar heading1Style = doc.GetPredefinedStyle(LibraryStyleEnum.Heading1);\nvar heading2Style = doc.GetPredefinedStyle(LibraryStyleEnum.Heading2);\n\nvar tableStyle = doc.GetPredefinedStyle(LibraryStyleEnum.Table);\nvar figureStyle = doc.GetPredefinedStyle(LibraryStyleEnum.Figure);\n```\n\n- `SetDocDefaults()` change default font, size, spacing, indentation, justification\n- `MainSectionProperties()` allow access to base Section Properties; these will be used as backing store for actual section properties until you introduce a new one\n- `GetPredefinedStyle()` retrieve one of predefined style ( see LibraryStyleEnum ) ; these styles came from a library that is instantiated foreach document through per document resources object ( because of specific number id, and other properties ).\n\n### title and subtitle\n\n![](data/img/demo_01.png)\n\n```cs\ndoc\n    .AddParagraph(\"netcore-docx\", titleStyle);\n\ndoc\n    .AddParagraph(\"Demo document\", subTitleStyle);\n```\n\n- AddParagraph allow to insert a text and specify style to use for paragraph\n\n### toc\n\n![](data/img/demo_02.png)\nnote: TOC field updated from wordprocessor at first open ( not by the api )\n\n![](data/img/demo_03.png)\n\n```cs\ndoc\n    .AddParagraph()\n    .SectionProperties() // here a new section property will instantiated                        \n        .SetHeader(header =\u003e header.AddParagraph(\"netcore-docx - Demo document\"))\n        .SetFooter(footer =\u003e footer\n            .AddParagraph(\"Page \")\n            .AddField(FieldEnum.PAGE)\n            .AddText(\" of \")\n            .AddField(FieldEnum.NUMPAGES)\n            .SetJustification(JustificationValues.Center)\n        );\n\ndoc\n    .AddToc(\"Table of Contents\")\n    .AddParagraph();\n\ndoc\n    .AddBreak();\n```\n\n- `AddToc()` insert a predefined sdt block ( see `GenerateSdtBlock()` ) and trigger `UpdateFieldsOnOpen` because the TOC couldn't created at this model level without having exact page number position of elements that depends on document renderer program.\n\n### font family, size, color, paragraph properties\n\n![](data/img/demo_04.png)\n\n```cs\ndoc\n    .AddParagraph(\"Font family, size, color, paragraph properties\", heading1Style)\n    .EnableAutoNumbering()\n\n    .AddParagraph(\"some normal paragraph\")\n    .AddParagraph(\"line with space before 5mm\").SetSpacingBetweenLines(beforeMM: 5)\n    .AddParagraph(\"line with space after 5mm\").SetSpacingBetweenLines(afterMM: 5)\n    .AddParagraph(\"bold mode\").SetBold()\n    .AddParagraph(\"italic mode\").SetItalic()\n    .AddParagraph(\"underline mode\").SetUnderline()\n\n    .AddParagraph(\"some color \")\n        .AddRun(\"red\", action: run =\u003e run.SetColor(DColor.Red))\n        .AddRun(\" green\", action: run =\u003e run.SetColor(DColor.Green))\n        .AddRun(\" blue\", action: run =\u003e run.SetColor(DColor.Blue))\n\n    .AddParagraph(\"paragraph shading\")\n    .IncAutoNumbering()\n        .AddParagraph(\"orange, clear\").SetShading(DColor.Orange)\n    .DecAutoNumbering()\n\n    .AddParagraph(\"run shading\")\n        .IncAutoNumbering()\n        .AddParagraph(\"clear \")\n            .AddRun(\"red\", action: run =\u003e run.SetShading(DColor.Red, ShadingPatternValues.Clear))\n            .AddSpace().AddRun(\"green\", action: run =\u003e run.SetShading(DColor.Green, ShadingPatternValues.Clear))\n            .AddSpace().AddRun(\"blue\", action: run =\u003e run.SetShading(DColor.Blue, ShadingPatternValues.Clear))\n        .DecAutoNumbering()\n\n    .DisableAutoNumbering();\n\ndoc\n    .AddBreak();\n```\n\n- `EnableAutoNumbering()` is an helper to auto set numbering mode on any paragraph inserted after this command succeed; this command flag a value into per document resources object allowing DocX methods extensions to know that `SetNumbering()` has to be applied silently until `DisableAutoNumbering()` clear this behavior; during auto numbering `IncAutoNumbering()` and `DecAutoNumbering()` allow to increase/decrease numbering level (indentation). If don't want to use auto numbering you can set per paragraph numbering using `SetNumbering()`.\n- `SetShading()` can applied with different behavior to a single Run or to an entire Paragraph.\n- `AddBreak()` adds a manual page break ( if want to change section use `.SectionProperties()` instead )\n\n### custom paragraph style\n\n![](data/img/demo_05.png)\n\n```cs\ndoc\n    .AddParagraph(\"Custom paragraph properties\", heading1Style);\n\n{\n    var myStyle1 = doc.AddParagraphSyle(\"myStyle1\",\n        runFontName: \"Times New Roman\",\n        runFontColor: DColor.Blue,\n        runFontSizePt: 14,\n        spacingBetweenLinesOpts: new SpacingBetweenLinesOptions { AfterMM = 5 },\n        indentationOpts: new IndentationOptions { },\n        justification: JustificationValues.Left);\n\n    doc.AddParagraph(\"paragraph with myStyle1\", myStyle1);\n\n    var myStyle2 = doc.AddParagraphSyle(\"myStyle2\",\n        runFontColor: DColor.Red,\n        basedOn: myStyle1);\n\n    doc.AddParagraph(\"paragraph with myStyle2 based on myStyle1\", myStyle2);\n}\n```\n\n- with `AddParagraphStyle()` you can add a new paragraph style from scratch ; if you can starts from a template docx may you find easily to prepare styles needed instead of creating them programmatically.\n\n### find and replace\n\n![](data/img/demo_06.png)\n\n```cs\ndoc\n    .AddParagraph(\"Find replace\", heading1Style);\n\nParagraph? pref = null;\n\ndoc\n    .AddParagraph(\"This \")\n        .AddRun(\"wor\", run =\u003e run.SetColor(DColor.Red))\n        .AddRun(\"ld\", run =\u003e run.SetColor(DColor.Green))\n        .AddRun(\"s\", run =\u003e run.SetColor(DColor.Blue))\n        .AddRun(\" text\")\n        .Act(p =\u003e pref = p)\n    .AddParagraph($\"Is composed by {pref!.GetRuns().Count()} runs\")\n    .AddParagraph(\"where the word \\\"worlds\\\" is composed by 3 runs\");\n\nvar search = doc.FindText(\"worlds\");\n\ndoc\n    .AddParagraph(\n        $\"Searching the word \\\"worlds\\\" in previous text results in {search.Count} occurrences:\");\n\nforeach (var match in search)\n{\n    doc\n        .AddParagraph($\"paragraph: [\")\n            .AddRuns(match.Paragraph.GetRuns().Select(r =\u003e (Run)r.Clone()))\n            .AddRun($\"] contains {match.Runs.Count} matching runs:\")\n            .SetNumbering(0, NumberFormatValues.Decimal);\n\n    foreach (var matchingrun in match.Runs)\n    {\n        doc.AddParagraph()\n            .AddRun(((Run)matchingrun.Clone()).Act(run =\u003e\n                run.SetShading(DColor.Yellow)))\n            .SetNumbering(1, NumberFormatValues.Bullet);\n    }\n}\n\ndoc\n    .AddBreak();\n```\n\n- `FindText()` actually works by searching text occurrence in all the document and returns a list of paragraphs that contains occurrence.\n- `FindAndReplace()` like FindText() works over all document contents by searching text and replacing with text substitution specified; specifically it can replace existing text runs by maintaining their run properties until replaced text length not exceeded the existant one and using last run properties for remaining addictional text; an option `SplitAndCreateSingleRun` allow to replace matching multiple run into single run by trimming and stitching in order to ensure that replaced text will exists in a single run ( could used to uniform as a single run some template document keyword edited by hand causing multiple run across a single keyword to be used subsequently for an automated document creation )\n\n### images\n\n![](data/img/demo_07.png)\n\n![](data/img/demo_08.png)\n\n![](data/img/demo_09.png)\n\n```cs\ndoc\n    .AddParagraph(\"Images\", heading1Style)\n    .AddParagraph(\"Image with original size\", heading2Style)\n    .AddImage(img01pathfilename)\n    .AddParagraph()\n\n    .AddParagraph(\"Image keep aspect Width = 50mm\", heading2Style)\n    .AddImage(img01pathfilename, widthMM: 50)\n    .AddParagraph()\n\n    .AddParagraph(\"Image keep aspect Height = 50mm\", heading2Style)\n    .AddImage(img01pathfilename, heightMM: 50)\n    .AddParagraph()\n\n    .AddParagraph(\"Image unconstrained width:50mm height:50mm\", heading2Style)\n    .AddImage(img01pathfilename, widthMM: 50, heightMM: 50)\n    .AddParagraph()\n\n    .AddTable()\n        .AddColumns(3, 1)\n        .AddRow(row =\u003e\n        {\n            row.GetCell(0)\n                .SetParagraph(\"Left\")\n                .AddImage(img01pathfilename, widthMM: 30)\n                .SetJustification(JustificationValues.Left);\n\n            row.GetCell(1)\n                .SetParagraph(\"Center\")\n                .AddImage(img01pathfilename, widthMM: 30)\n                .SetJustification(JustificationValues.Center);\n\n            row.GetCell(2)\n                .SetParagraph(\"Right\")\n                .AddImage(img01pathfilename, widthMM: 30)\n                .SetJustification(JustificationValues.Right);\n\n            var cellsCount = row.Elements\u003cTableCell\u003e().Count();\n            for (int i = 0; i \u003c cellsCount; ++i) row.GetCell(i).SetPadding(2);\n        })\n        .SetBorderAll()\n    .AddBreak();\n```\n\n- `AddImage()` adds an inline image into a paragraph or a run ; if not specified the type if auto states type from file extension ; `dpi` if present are used to state original dimension and aspect ratio ; can be inserted without specify width, height to use original dimension or constraining one of them keeping aspect ratio or constraining all of them to force stretched version.\n\n### tables\n\n![](data/img/demo_10.png)\n\n```cs\ndoc\n    .AddParagraph(\"Table with Field types\", heading1Style);\n\nvar tbl = doc\n    .AddTable(tableWidthPercent: 50)\n    .AddColumn(1).AddColumn(1);\n\ntbl.AddRow(row =\u003e\n{\n    row.GetCell(0)\n        .SetShading(DColor.Navy)\n        .SetParagraph(\"FIELD NAME\", run =\u003e run.SetBold().SetColor(DColor.White))\n        .SetUniformMargin(2);\n\n    row.GetCell(1)\n        .SetShading(DColor.Navy)\n        .SetParagraph(\"SAMPLE\", run =\u003e run.SetBold().SetColor(DColor.White))\n        .SetUniformMargin(2);\n});\n\nforeach (var t in Enum.GetValues\u003cFieldEnum\u003e())\n{\n    tbl.AddRow(row =\u003e\n    {\n        row.GetCell(0).SetParagraph(t.ToString(), run =\u003e run.SetBold()).SetUniformMargin(2);\n        row.GetCell(1).GetFirstChild\u003cParagraph\u003e()!.AddField(t).SetUniformMargin(2);\n    });\n}\n\n// short version\n//tbl.SetBordersOutside(BorderValues.Single);                \n\n// custom version\ntbl.SetBorders((args) =\u003e\n{\n    if (args.colIdx == 0) \n        args.leftBorder = new LeftBorder() { Val = BorderValues.Single };\n\n    if (args.rowIdx == 0) \n        args.topBorder = new TopBorder() { Val = BorderValues.Single };\n\n    if (args.colIdx == args.colCount - 1) \n        args.rightBorder = new RightBorder() { Val = BorderValues.Single };\n\n    if (args.rowIdx == args.rowCount - 1) \n        args.bottomBorder = new BottomBorder() { Val = BorderValues.Single };\n});\n\ndoc\n    .AddBreak();\n```\n\n- paragraph `AddTable()` adds a table with specified % width or if null auto computed from columns\n- table `AddColumn()` adds column to the table specifying an exact MM measure if table work with fixed width ( tableWidthPercent: null ); instead of table uses a % width the column widthMM will normalized across all columns to reach table width extents.\n- table `AddRow()` adds an empty row already filled with enough cells to cover actual table columns\n- row `GetCell()` retrieve Table Cell from which you may use `.SetParagraph()` in place of `.AddParagraph()` because empty cells prepared with an empty paragraph.\n    - an helper row `SetUniformMargin()` allow to set margin around cell contents\n- table `SetBordersOutside()` sets outer table cells border to Single or specified Border type. Alternatively `SetBordersAll()` or `SetBorders()` can be used to set all cells outer/inner or custom border.\n\n### numbering\n\n![](data/img/demo_11.png)\n\n```cs\ndoc\n    .AddParagraph(\"Numbering\", heading1Style)\n\n    .AddParagraph(\"Bullet list\", heading2Style)\n    .AddParagraph(\"sample1\").SetNumbering(0)\n    .AddParagraph(\"sample2\").SetNumbering(1)\n    .AddParagraph(\"sample2\").SetNumbering(0)\n\n    .AddParagraph(\"Decimal list\", heading2Style)\n    .AddParagraph(\"sample1\").SetNumbering(0, NumberFormatValues.Decimal, restartNumbering: true)\n    .AddParagraph(\"sample2x\").SetNumbering(1, NumberFormatValues.Decimal)\n    .AddParagraph(\"sample2\").SetNumbering(0, NumberFormatValues.Decimal)\n\n    .AddParagraph(\"Structured Decimal list\", heading2Style)\n    .AddParagraph(\"sample1\").SetNumbering(0, NumberFormatValues.Decimal, structured: true)\n    .AddParagraph(\"sample2\").SetNumbering(1, NumberFormatValues.Decimal, structured: true)\n    .AddParagraph(\"sample2\").SetNumbering(0, NumberFormatValues.Decimal, structured: true);\n\ndoc\n    .AddBreak();\n```\n\n- `SetNumbering(level, [format:bullet], [structured:false])` allow to specify that a paragraph is marked in its property to be a numbering ; `level` specify indentation level 0...9 ; `format` specify the type of numering ( actually library supports None, Bullet, Decimal ) ; `structured: true` used in conjunction with Decimal format allow to enumerate inside levels with recall of parent levels.\n\n## TODO\n\n- double check document validate errors\n\n## debugging\n\n- useful tools : [OpenXML SDK Tools 2.5](https://github.com/OfficeDev/Open-XML-SDK/releases/tag/v2.5)\n\n## unit test\n\n```sh\ndotnet test\n```\n\n## install\n\n- [nuget package](https://www.nuget.org/packages/netcore-docx/)\n\n## how this project was built\n\n```sh\nmkdir netcore-docx\ncd netcore-docx\n\ndotnet new sln\ndotnet new classlib -n netcore-docx\n\ncd netcore-docx\ndotnet add package DocumentFormat.OpenXml --version 2.8.1\ncd ..\n\ndotnet sln netcore-docx.sln add netcore-docx/netcore-docx.csproj\ndotnet restore\ndotnet build\n```\n\n## references\n\n- [open xml sdk doc](https://github.com/OfficeDev/office-content/tree/master/en-us/OpenXMLCon)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevel0%2Fnetcore-docx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevel0%2Fnetcore-docx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevel0%2Fnetcore-docx/lists"}