DocX icon indicating copy to clipboard operation
DocX copied to clipboard

HeadingType is not applied when using Load()

Open PrzemyslawKlys opened this issue 7 years ago • 22 comments

While below code is powershell it uses your .NET. Essentially when you use .Load(Path) and apply HeaderStyle it's not saved in final document making TOC useless. It behaves as expected on .Create(Path)

Import-Module PSWriteWord -Force

$FilePath = "$Env:USERPROFILE\Desktop\PSWriteWord-Example-TableOfContent3.docx"

$WordDocument = New-WordDocument -FilePath $FilePath
$Toc = Add-WordTOC -WordDocument $WordDocument -Title 'Table of content' -HeaderStyle Heading2
Add-WordSection -WordDocument $WordDocument -PageBreak
Add-WordText -WordDocument $WordDocument -Text 'This is my first title' -HeadingType Heading1
Add-WordSection -WordDocument $WordDocument -PageBreak
Add-WordText -WordDocument $WordDocument -Text 'This is my second title' -HeadingType Heading1 -Color Red -CapsStyle caps
Add-WordSection -WordDocument $WordDocument -PageBreak
Add-WordText  -WordDocument $WordDocument -Text 'This is my third title' -HeadingType Heading2 -Italic $true -Bold $true
Save-WordDocument $WordDocument

### Start Word with file
Invoke-Item $FilePath
Import-Module PSWriteWord -Force

$FilePath = "$Env:USERPROFILE\Desktop\PSWriteWord-Example-TableOfContent7.docx"
$FilePathTemplate = "$PSScriptRoot\Templates\WordTemplate.docx"

$WordDocument = Get-WordDocument -FilePath $FilePathTemplate
$Toc = Add-WordTOC -WordDocument $WordDocument -Title 'Table of content' -HeaderStyle Heading2
Add-WordSection -WordDocument $WordDocument -PageBreak
Add-WordText -WordDocument $WordDocument -Text 'This is my first title' -HeadingType Heading1
Add-WordSection -WordDocument $WordDocument -PageBreak
Add-WordText -WordDocument $WordDocument -Text 'This is my second title' -HeadingType Heading1 -Color Red -CapsStyle caps
Add-WordSection -WordDocument $WordDocument -PageBreak
Add-WordText  -WordDocument $WordDocument -Text 'This is my third title' -HeadingType Heading2 -Italic $true -Bold $true

$WordDocument.Xml
Save-WordDocument $WordDocument -FilePath $FilePath

### Start Word with file
Invoke-Item $FilePath

To reproduce it in pseudo code

Load
Create few Paragraphs
Add text to each paragraph
Make it Heading1 or Heading2
Save
Open

When checking: Document.Paragraphs.StyleName

Heading2
TOC1
Normal
Normal
Heading1
Normal
Heading1
Normal
Heading2
Normal
Heading2
TOC1
Normal
Normal
Heading1
Normal
Heading1
Normal
Heading2

So comparison is almost identical but still save doesn't cover this. Replacing Load with Create - everything works again. I'm using https://github.com/EvotecIT/PSWriteWord/blob/master/Examples/Templates/WordTemplate.docx as a template but shouldn't really matter I guess.

PrzemyslawKlys avatar Jul 19 '18 19:07 PrzemyslawKlys

Hi,

We cannot reproduce this in v1.2. Here's the sample we use : ` var doc = DocX.Load( "WordTemplate.docx" ); doc.InsertParagraph( "Paragraph2" ).Heading( HeadingType.Heading1 ); doc.InsertParagraph( "Paragraph3" ).Heading( HeadingType.Heading2 ); doc.SaveAs("output.docx");

  var doc2 = DocX.Load( "output.docx" );
  var paragraphCount = doc2.Paragraphs.Count;
  Debug.Assert( doc2.Paragraphs[ paragraphCount - 1 ].StyleName == HeadingType.Heading2.ToString() );
  Debug.Assert( doc2.Paragraphs[ paragraphCount - 2 ].StyleName == HeadingType.Heading1.ToString() );`

Are we missing anything ? Thank you.

XceedBoucherS avatar Jul 25 '18 18:07 XceedBoucherS

Yes. I use $Paragraph.StyleName = $HeadingType instead of the way you do. I guess I'll switch to your approach if it works. So in C# it would be

var doc = DocX.Load( "WordTemplate.docx" );
doc.InsertParagraph( "Paragraph2" ).StyleName = HeadingType.Heading1;
doc.InsertParagraph( "Paragraph3" ).StyleName = HeadingType.Heading2;
doc.SaveAs("output.docx");

More or less. And what I actually do in code is

var doc = DocX.Load( "WordTemplate.docx" );
var paragraph = doc.InsertParagraph( "Paragraph2" );
var paragraph1 = doc.InsertParagraph( "Paragraph3" );
paragraph.StyleName = HeadingType.Heading2;
paragraph1.StyleName = HeadingType.Heading1;
doc.SaveAs("output.docx");

PrzemyslawKlys avatar Jul 25 '18 18:07 PrzemyslawKlys

And maybe I'm using it wrong but I don't see... Heading property for Paragraph

image

But I do see it in examples

 public static void Heading()
    {
      Console.WriteLine( "\tHeading()" );

      // Create a document.
      using( DocX document = DocX.Create( ParagraphSample.ParagraphSampleOutputDirectory + @"Heading.docx" ) )
      {
        // Add a title.
        document.InsertParagraph( "Heading types" ).FontSize( 15d ).SpacingAfter( 50d ).Alignment = Alignment.center;

        var headingTypes = Enum.GetValues( typeof( HeadingType ) );

        foreach( HeadingType heading in headingTypes )
        {
          // Set a text containing the current Heading type.
          var text = string.Format( "This Paragraph is using \"{0}\" heading type.", heading.EnumDescription() );
          // Add a paragraph.
          var p = document.InsertParagraph().AppendLine( text );
          // Set the paragraph's heading type.
          p.Heading( heading );
        }

        document.Save();
        Console.WriteLine( "\tCreated: Heading.docx\n" );
      }
    }

So I'll verify why I don't see it. Anyways I use it via StyleName instead of Heading property.

PrzemyslawKlys avatar Jul 25 '18 19:07 PrzemyslawKlys

If you want to use it your way, you should use it this way :

`var doc = DocX.Load( "WordTemplate.docx" ); //doc.InsertParagraph( "Paragraph2" ).Heading( HeadingType.Heading1 ); //doc.InsertParagraph( "Paragraph3" ).Heading( HeadingType.Heading2 ); var paragraph = doc.InsertParagraph( "Paragraph2" ); var paragraph1 = doc.InsertParagraph( "Paragraph3" ); paragraph.StyleName = HeadingType.Heading1.ToString(); paragraph1.StyleName = HeadingType.Heading2.ToString(); doc.SaveAs("output.docx");

  var doc2 = DocX.Load( "output.docx" );
  var paragraphCount = doc2.Paragraphs.Count;
  Debug.Assert( doc2.Paragraphs[ paragraphCount - 1 ].StyleName == HeadingType.Heading2.ToString() );
  Debug.Assert( doc2.Paragraphs[ paragraphCount - 2 ].StyleName == HeadingType.Heading1.ToString() );`

Paragraph only contains a StyleName property, which is a string. It doesn't contain a Heading property.

XceedBoucherS avatar Jul 25 '18 19:07 XceedBoucherS

Well I do have that

image

Yet the result is:

image

PrzemyslawKlys avatar Jul 25 '18 20:07 PrzemyslawKlys

In c#

        public static void InsertTableOfContentWithReference1()
        {
            Console.WriteLine("\tInsertTableOfContentWithReference()");

            // Create a document.
            using (DocX document = DocX.Load(TableOfContentSample.TableOfContentSampleOutputDirectory + @"WordTemplate.docx"))
            {
                var paragraph = document.InsertParagraph("Paragraph2");
                var paragraph1 = document.InsertParagraph("Paragraph3");
                paragraph.StyleName = HeadingType.Heading1.ToString();
                paragraph1.StyleName = HeadingType.Heading2.ToString();

                document.SaveAs(TableOfContentSample.TableOfContentSampleOutputDirectory + @"InsertTableOfContentWithReference1.docx");
                Console.WriteLine("\tCreated: InsertTableOfContentWithReference.docx\n");
            }
        }

Notice how it's not set

image

And Create


        public static void InsertTableOfContentWithReference1()
        {
            Console.WriteLine("\tInsertTableOfContentWithReference()");

            // Create a document.
            using (DocX document = DocX.Create(TableOfContentSample.TableOfContentSampleOutputDirectory + @"InsertTableOfContentWithReference1.docx"))
            //using (DocX document = DocX.Load(TableOfContentSample.TableOfContentSampleOutputDirectory + @"WordTemplate.docx"))
            {
                var paragraph = document.InsertParagraph("Paragraph2");
                var paragraph1 = document.InsertParagraph("Paragraph3");
                paragraph.StyleName = HeadingType.Heading1.ToString();
                paragraph1.StyleName = HeadingType.Heading2.ToString();

                document.SaveAs(TableOfContentSample.TableOfContentSampleOutputDirectory + @"InsertTableOfContentWithReference1.docx");
                Console.WriteLine("\tCreated: InsertTableOfContentWithReference.docx\n");
            }
        }

image

Diffence is just Create vs Load.

PrzemyslawKlys avatar Jul 25 '18 21:07 PrzemyslawKlys

And your code doesn't give better results either:

        public static void InsertTableOfContentWithReference1()
        {
            Console.WriteLine("\tInsertTableOfContentWithReference()");

            // Create a document.
            //using (DocX document = DocX.Create(TableOfContentSample.TableOfContentSampleOutputDirectory + @"InsertTableOfContentWithReference1.docx"))
            using (DocX document = DocX.Load(TableOfContentSample.TableOfContentSampleOutputDirectory + @"WordTemplate.docx"))
            {
                var paragraph = document.InsertParagraph("Paragraph2").Heading(HeadingType.Heading1);
                var paragraph1 = document.InsertParagraph("Paragraph3").Heading(HeadingType.Heading1);
               // paragraph.StyleName = HeadingType.Heading1.ToString();
               // paragraph1.StyleName = HeadingType.Heading2.ToString();

                document.SaveAs(TableOfContentSample.TableOfContentSampleOutputDirectory + @"InsertTableOfContentWithReference1.docx");
                Console.WriteLine("\tCreated: InsertTableOfContentWithReference.docx\n");
            }
        }

Headings are not set.

PrzemyslawKlys avatar Jul 25 '18 21:07 PrzemyslawKlys

Hello, Thank you for the precision.

When creating a document, the style used will be the the default styles (including Heading1, Heading2...), but when loading a document, its defined styles will be used. In this case, WordTemplate.docx do not contains the HeadingX styles.

We will investigate on this.

XceedBoucherS avatar Jul 26 '18 15:07 XceedBoucherS

Hello After investigations, I come to the same conclusion as Klys. Anyway thanks for the great job done, & I am looking forward for the fix.

Matdegraye avatar Aug 10 '18 12:08 Matdegraye

I worked around it by creating empty doc, saving and then adding my flavors making it my template. Works

PrzemyslawKlys avatar Aug 10 '18 12:08 PrzemyslawKlys

Hi @PrzemyslawKlys , i'm experiencing the same issue and would be very helpful if you can be more detailed on your solution proposal.

Thanks in advance

ruizdidier avatar Oct 16 '18 05:10 ruizdidier

Just create an empty doc with DocX and work from there. It will have proper format that is required.

PrzemyslawKlys avatar Oct 16 '18 05:10 PrzemyslawKlys

Just create an empty doc with DocX and work from there. It will have proper format that is required.

@PrzemyslawKlys I tried, but if i make some change on it using Office like edit the header it keep doing the same. I have to put a logo just like u show on your screenshots, but i cant find a way to put an image as a header using DocX

ruizdidier avatar Oct 16 '18 06:10 ruizdidier

What I did is exactly as I said:

  1. Empty document with DocX (well PSWriteWord in PowerShell)
  2. Open it up in Word
  3. Go into headers, pasted an image of logo (actually it's A4 sized logo so it covers full page along with footer as 1 image.
  4. Saved document
  5. Using that document as Template for things I do in DocX (PSWriteWord to be exact)

PrzemyslawKlys avatar Oct 16 '18 06:10 PrzemyslawKlys

Thanks for your replay, i followed your suggestion but i changed the editor for the Doc Drive downloading as a .docx and it worked perfectly.

I found that if you save in latest version of Word it changes the schemas from 2006 to 2015 or later in the "document.xml" and i think that's the problem, because trying with other editors they use http://schemas.openxmlformats.org/markup-compatibility/2006

ruizdidier avatar Oct 16 '18 19:10 ruizdidier

Hello everybody, Is there a planned version to solve this issue ? The proposed workaround cannot be applied inmy case. Regards,

mastertnt avatar Apr 27 '20 20:04 mastertnt

After some investigations, the property "Stylename" is a little bit confusing. It should be "Styleid", the element name in the schema is not the same thing for a style.

In my point of view, the easiest way could be to retrieve styles by alias and to apply the retrieved style id on a property named "Styleid"

mastertnt avatar Apr 27 '20 21:04 mastertnt

Hi mastertnt, This changed is already done. In the future relase v1.8, "StyleName" is considered obsolete while "StyleId" is the new property to use. Thank you

XceedBoucherS avatar Apr 28 '20 12:04 XceedBoucherS

Hi,

Thanks for the answer.

If you use StyleId, the Load function has no trouble. The difficulty is to know the correct StyleId (the aliases displayed in Word seems more natural) or i don't the way to retrieve it from the API.

Regards,

mastertnt avatar Apr 28 '20 21:04 mastertnt

Hi, You are right, these are not easy to know. Keep in mind that styleIds don't have spaces and use Camel case. So "Heading 1" will have the styleId "Heading1", "Normal" will have styleId "Normal"....

XceedBoucherS avatar Apr 29 '20 11:04 XceedBoucherS

Hi,

Thanks for the precision, i have already seen this. I use a french template and also all the special characters present in Latin1 character set are removed for example, "Référence" will have styleId "Rfrence".

For other languages (like chinese), i don't know if it is possible to infer the styleId.

Regards,

mastertnt avatar Apr 29 '20 11:04 mastertnt

Hi,

For Heading types, you can always use the Paragraph.Heading() method, which take in parameter a HeadingType. Therefor, you don't have to use the styleId, at least for the heading types.

In v1.8, you will also have access to a static method in Document called "GetParagraphStyleIdFromStyleName" which will take in argument a styleName and return the corresponding styleId.

XceedBoucherS avatar May 04 '20 12:05 XceedBoucherS