EvotecIT / OfficeIMO

Fast and easy to use cross-platform .NET library that creates or modifies Microsoft Word (DocX) and later also Excel (XLSX) files without installing any software. Library is based on Open XML SDK
MIT License
279 stars 49 forks source link

No way to adjust or create tab stops on WordParagraph objects #89

Closed SprinterDave closed 1 year ago

SprinterDave commented 1 year ago

I don't see a way to adjust tab stops on a WordParagraph object. Am I missing something?

PrzemyslawKlys commented 1 year ago

Probably not. I guess we need to add it.

Some insights on what to work on for anyone that want to add this

image

using DocumentFormat.OpenXml.Wordprocessing;
using DocumentFormat.OpenXml;

namespace GeneratedCode
{
    public class GeneratedClass
    {
        // Creates an Paragraph instance and adds its children.
        public Paragraph GenerateParagraph()
        {
            Paragraph paragraph1 = new Paragraph(){ RsidParagraphAddition = "00FC1A86", RsidParagraphProperties = "00A06F95", RsidRunAdditionDefault = "00FC1A86", ParagraphId = "34A6C600", TextId = "77777777" };

            ParagraphProperties paragraphProperties1 = new ParagraphProperties();

            Tabs tabs1 = new Tabs();
            TabStop tabStop1 = new TabStop(){ Val = TabStopValues.Center, Position = 2880 };

            tabs1.Append(tabStop1);

            paragraphProperties1.Append(tabs1);

            paragraph1.Append(paragraphProperties1);
            return paragraph1;
        }

    }
}

DefaultTabStop

image

using DocumentFormat.OpenXml.Wordprocessing;
using DocumentFormat.OpenXml;

namespace GeneratedCode
{
    public class GeneratedClass
    {
        // Creates an Tabs instance and adds its children.
        public Tabs GenerateTabs()
        {
            Tabs tabs1 = new Tabs();
            TabStop tabStop1 = new TabStop(){ Val = TabStopValues.Left, Position = 1440 };
            TabStop tabStop2 = new TabStop(){ Val = TabStopValues.Right, Position = 2880 };
            TabStop tabStop3 = new TabStop(){ Val = TabStopValues.Bar, Position = 4320 };
            TabStop tabStop4 = new TabStop(){ Val = TabStopValues.Decimal, Leader = TabStopLeaderCharValues.Hyphen, Position = 5760 };

            tabs1.Append(tabStop1);
            tabs1.Append(tabStop2);
            tabs1.Append(tabStop3);
            tabs1.Append(tabStop4);
            return tabs1;
        }

    }
}

image

using DocumentFormat.OpenXml.Wordprocessing;
using DocumentFormat.OpenXml;

namespace GeneratedCode
{
    public class GeneratedClass
    {
        // Creates an SpacingBetweenLines instance and adds its children.
        public SpacingBetweenLines GenerateSpacingBetweenLines()
        {
            SpacingBetweenLines spacingBetweenLines1 = new SpacingBetweenLines(){ Before = "120", After = "120" };
            return spacingBetweenLines1;
        }

    }
}

using DocumentFormat.OpenXml.Wordprocessing;
using DocumentFormat.OpenXml;

namespace GeneratedCode
{
    public class GeneratedClass
    {
        // Creates an Indentation instance and adds its children.
        public Indentation GenerateIndentation()
        {
            Indentation indentation1 = new Indentation(){ Left = "576", Right = "288", FirstLine = "1440" };
            return indentation1;
        }

    }
}

image

using DocumentFormat.OpenXml.Wordprocessing;
using DocumentFormat.OpenXml;

namespace GeneratedCode
{
    public class GeneratedClass
    {
        // Creates an ParagraphProperties instance and adds its children.
        public ParagraphProperties GenerateParagraphProperties()
        {
            ParagraphProperties paragraphProperties1 = new ParagraphProperties();

            Tabs tabs1 = new Tabs();
            TabStop tabStop1 = new TabStop(){ Val = TabStopValues.Left, Position = 1440 };
            TabStop tabStop2 = new TabStop(){ Val = TabStopValues.Right, Position = 2880 };
            TabStop tabStop3 = new TabStop(){ Val = TabStopValues.Bar, Position = 4320 };
            TabStop tabStop4 = new TabStop(){ Val = TabStopValues.Decimal, Leader = TabStopLeaderCharValues.Hyphen, Position = 5760 };

            tabs1.Append(tabStop1);
            tabs1.Append(tabStop2);
            tabs1.Append(tabStop3);
            tabs1.Append(tabStop4);
            SpacingBetweenLines spacingBetweenLines1 = new SpacingBetweenLines(){ Before = "120", After = "120" };
            Indentation indentation1 = new Indentation(){ Left = "576", Right = "288", FirstLine = "1440" };

            paragraphProperties1.Append(tabs1);
            paragraphProperties1.Append(spacingBetweenLines1);
            paragraphProperties1.Append(indentation1);
            return paragraphProperties1;
        }

    }
}
Workaholic01 commented 1 year ago

Hi Guys,

So is there a way to achieve this using Office IMO library. I am trying to create a header with tab spaced content e-g

Logo Image - 'Tab Space' - Contact Information - "Tab Space" - Some other info.

I am trying to achieve this using paragraph. But I am not able to do it. Any help would be appreciated.

Also does paragraph text understands \n character?

PrzemyslawKlys commented 1 year ago

In my opinion, it's better to build this kind of stuff using the invisible table. You control the table size, the column sizes and it's perfectly planned.

At least that's how I do it manually when using Word, I have never seen anyone using TabSpaces to position stuff in Word, but maybe I am wrong. As for new lines I am not sure, requires testing and then if it's not supported probably opening another issue with ideas on how this should work. Creating another run, creating another paragraph, just inserting new line in openxml way. But first confirmation is required.

Workaholic01 commented 1 year ago

Thank you for the quick response @PrzemyslawKlys.

Yes, I ended up going for the invisible table as well. And for new lines I tried it but unfortunately it didn't work.

aknaton commented 1 year ago

I have never seen anyone using TabSpaces to position stuff in Word, but maybe I am wrong.

I have used tabs in Word. A lot. But I have worked professionally as a technical writer. Right now though I have the same problem as @Workaholic01 so I may also use the workaround.

PrzemyslawKlys commented 1 year ago

Can any of you send me a small template (docx) with minimal amount of data that shows the use case? While I can add myself some tab stops I dont understand how would you use them so on design side of things I am not sure what would be right way. Also if you have some "idea" in dummy code how would you like to use it - it would be cool.

aknaton commented 1 year ago

If you create a new document directly in the Word-app and go to the header you can se that the program added an center tab in the middle of the text area and a right tab at the right side of the text area (as is shown in my attached file, if it is not removed). The same is true for the footer. I guess this is because this is the default use case. It then is easy to add a logo, a company name in the middle and maybe a pagination at the right.

Maybe it would be nice to have an option to replicate that:

var head = document.Sections[0].Header.Default.AddParagraphWithDefaultTabstops();

Usable for headers and footers.

Another usage I see is if you should construct an area where some people should sign a document. See the attached file.

I don't know if one could do something similar have some sort of default tab stops if one don't want to implement a full tab implementation in your library.

Den tis 28 feb. 2023 kl 13:35 skrev Przemysław Kłys < @.***>:

Can any of you send me a small template (docx) with minimal amount of data that shows the use case? While I can add myself some tab stops I dont understand how would you use them so on design side of things I am not sure what would be right way. Also if you have some "idea" in dummy code how would you like to use it - it would be cool.

— Reply to this email directly, view it on GitHub https://github.com/EvotecIT/OfficeIMO/issues/89#issuecomment-1448106745, or unsubscribe https://github.com/notifications/unsubscribe-auth/AANSGPUF7XAKGJVGWW4WPYLWZXWIZANCNFSM6AAAAAASVSPUYY . You are receiving this because you commented.Message ID: @.***>

PrzemyslawKlys commented 1 year ago

I will probably go for full implementation, I just need to understand use cases. I see other "vendors" do it like this so I guess I will sort of replicate more or less.

image

Although I am not sure I understand all functionality :-)

PrzemyslawKlys commented 1 year ago

Btw the examples/docs were stripped out

PrzemyslawKlys commented 1 year ago

So I have added a PR with both DefaultTabStop which is set to 720 (it was kind of always there), but then you can also add tab stops to paragraphs. Please remember that WordParagraph is basically a Run, which means even tho there are 5 WordParagraphs, it's actually 2 Paragraphs, that contain 5 runs, and that means if you ask 3rd,4th and 5th WordParagraph for TabStops it will have them exactly the same because in this case they are sharing single Paragraph/ParagraphProperties.

internal static void Example_BasicTabStops(string folderPath, bool openWord) {
    Console.WriteLine("[*] Creating standard document with paragraphs");
    string filePath = System.IO.Path.Combine(folderPath, "Basic Document with some tab stops.docx");
    using (WordDocument document = WordDocument.Create(filePath)) {
        var paragraph = document.AddParagraph("\tFirst Line");

        document.Settings.DefaultTabStop = 2880;
        document.Settings.CharacterSpacingControl = CharacterSpacingValues.DoNotCompress;

        Console.WriteLine("Tabs count: " + paragraph.TabStops.Count);

        var tab1 = paragraph.AddTabStop(1440);

        var tab2 = paragraph.AddTabStop(1440);
        tab2.Alignment = TabStopValues.Left;
        tab2.Leader = TabStopLeaderCharValues.Hyphen;
        tab2.Position = 1440;

        paragraph.AddText("\tMore text");

        Console.WriteLine($"Tabs count: " + paragraph.TabStops.Count);

        var paragraph1 = document.AddParagraph("\tNext Line");

        var tab3 = paragraph1.AddTabStop(5000);
        tab3.Leader = TabStopLeaderCharValues.Hyphen;

        var tab4 = paragraph1.AddTabStop(1440 * 2);
        paragraph1.AddText("\tEven more text");

        Console.WriteLine("Tabs for Paragraph2 count: " + paragraph.TabStops.Count);
        Console.WriteLine("Tabs for Paragraph1 count: " + paragraph1.TabStops.Count);
        Console.WriteLine("Default tab stop: " + document.Settings.DefaultTabStop);
        Console.WriteLine("Default tab stop: " + document.Settings.CharacterSpacingControl);

        document.Save();
    }

    using (WordDocument document = WordDocument.Load(filePath)) {

        document.Save(openWord);
    }
}

Maybe it needs further improvements, but this is what I came up with.