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
263 stars 47 forks source link

How to align page number in Footer #153

Open hisuwh opened 1 year ago

hisuwh commented 1 year ago

I have seen how you can add page numbers to the footer using .AddPageNumber() but how can I position this with other content I have in the footer.

We have done this in the header by using a table (I'm not sure if there is a better way):

wordDocument.AddHeadersAndFooters();
var headerTable = wordDocument.Header.Default.AddTable(1, 3, WordTableStyle.TableNormal);
headerTable.WidthType = TableWidthUnitValues.Pct;
headerTable.Width = WordTableGenerator.OneHundredPercentWidth;

var para = headerTable.Rows[0].Cells[0].AddParagraph();
para.AddImage(headerLogo, "logo.png", targetWidth, targetHeight);
para.ParagraphAlignment = JustificationValues.Left;

var header = headerTable.Rows[0].Cells[1].AddParagraph(headerText);
header.ParagraphAlignment = JustificationValues.Center;

I can't do this in the Footer as the AddPageNumber method is only available on the WordFooter type. So I have to do this:

var footer = wordDocument.Footer.Default.AddParagraph(footerText);
footer.ParagraphAlignment = JustificationValues.Center;

var pageNumber = wordDocument.Footer.Default.AddPageNumber(WordPageNumberStyle.PageNumberXofY);
pageNumber.ParagraphAlignment = JustificationValues.Right;

Meaning I end up with my footer text on one line and the Page Number on another - rather than side by side in one Row as I would prefer.

image

PrzemyslawKlys commented 1 year ago

Well, the way to do it is to expand AddPageNumber to work on Paragraphs, rather then on Documents directly, and use same method as you use for header with a table. I guess poor implementation on my side, that would require fixing. Or you can use Fields, as after all it's all field codes

image

There's no direct way now. So you either have to fix my implementation that will work with both document and paragraph or build your own Paragraph with fields

namespace OfficeIMO.Examples.Word {
    internal static partial class Fields {
        internal static void Example_DocumentWithFields02(string folderPath, bool openWord) {
            Console.WriteLine("[*] Creating standard document with tables");
            string filePath = System.IO.Path.Combine(folderPath, "Document with Fields02.docx");
            using (WordDocument document = WordDocument.Create(filePath)) {
                var paragraph = document.AddParagraph("Basic paragraph");
                paragraph.ParagraphAlignment = JustificationValues.Center;

                document.AddParagraph();

                document.AddHeadersAndFooters();

                // added page number using fields which triggers fields refresh
                document.AddField(WordFieldType.NumPages);

                document.AddField(WordFieldType.Author);

                document.AddField(WordFieldType.GreetingLine);

                // added page number using dedicated way
                var pageNumber = document.Header.Default.AddPageNumber(WordPageNumberStyle.Roman);

                document.Save(openWord);
            }
        }
    }
}

for example:

namespace OfficeIMO.Examples.Word {
    internal static partial class Fields {
        internal static void Example_DocumentWithFields02(string folderPath, bool openWord) {
            Console.WriteLine("[*] Creating standard document with tables");
            string filePath = System.IO.Path.Combine(folderPath, "Document with Fields02.docx");
            using (WordDocument document = WordDocument.Create(filePath)) {
                var paragraph = document.AddParagraph("Basic paragraph");
                paragraph.ParagraphAlignment = JustificationValues.Center;

                document.AddParagraph();

                document.AddHeadersAndFooters();

                // added page number using fields which triggers fields refresh
                document.AddField(WordFieldType.Page).AddText(" of ").AddField(WordFieldType.NumPages);

                document.AddField(WordFieldType.Author);

                document.AddField(WordFieldType.GreetingLine);

                // added page number using dedicated way
                var pageNumber = document.Header.Default.AddPageNumber(WordPageNumberStyle.Roman);

                document.Save(openWord);
            }
        }
    }
}

image

And you can use fields anywhere

hisuwh commented 1 year ago

Thanks for your prompt and thorough response.

I have tried doing it like this:

var footerTable = wordDocument.Footer.Default.AddTable(1, 3, WordTableStyle.TableNormal);
footerTable.WidthType = TableWidthUnitValues.Pct;
footerTable.Width = WordTableGenerator.OneHundredPercentWidth;

var footer = footerTable.Rows[0].Cells[1].AddParagraph(footerText);
footer.ParagraphAlignment = JustificationValues.Center;

var pageNumber = footerTable.Rows[0].Cells[2].AddParagraph();
pageNumber.ParagraphAlignment = JustificationValues.Right;

pageNumber
    .AddText("Page ")
    .AddField(WordFieldType.Page)
    .AddText(" of ")
    .AddField(WordFieldType.NumPages);

Which works: image

Except I get this pop up when I open the document: image

PrzemyslawKlys commented 1 year ago

Those are fields, which I guess need to be updated on open to be able to tell which page are those. I would guess this is by design. But if the same doesn't happen with the other way, there must be some difference for that.

hisuwh commented 1 year ago

Yh with the standard .AddPageNumber method I didn't get that popup.

PrzemyslawKlys commented 1 year ago

Well then that needs investigation what is different and why one triggers and the other one doesn't. Probably better to fix 1st method to add to paragraph, but that requires some work.

hisuwh commented 1 year ago

Do you know what's needed to achieve that - is it just a lot of refactoring? I would offer to help but I likely won't have much time - though with some guidance on what's needed it might be more possible.

PrzemyslawKlys commented 1 year ago

The code starts here:

https://github.com/EvotecIT/OfficeIMO/blob/470cef22a54c3d736e88c91bffc955b515e635c7/OfficeIMO.Word/WordFooter.cs#L57-L60

When you use it, it does this:

                document.AddHeadersAndFooters();
                document.Footer.Default.AddPageNumber(WordPageNumberStyle.PageNumberXofY);

image

Then if you add paragraphs there:

                document.AddHeadersAndFooters();
                document.Footer.Default.AddParagraph("Test1");
                document.Footer.Default.AddParagraph("Test2");
                document.Footer.Default.AddPageNumber(WordPageNumberStyle.PageNumberXofY);

image

That means we are basically applying it to last element in footer. Looking at how it behaves I would say you need to modify this line, and instead of appending it to footer directly, you would need to allow assigning it to last paragraph wherever you want..

https://github.com/EvotecIT/OfficeIMO/blob/470cef22a54c3d736e88c91bffc955b515e635c7/OfficeIMO.Word/WordPageNumber.cs#L101C47-L101C47

At least that's my guess, not tested.