Forum


Replies: 13   Views: 3229
Add section and change page orientation with htmlextended
Topic closed:
Please note this is an old forum thread. Information in this post may be out-to-date and/or erroneous.
Every phpdocx version includes new features and improvements. Previously unsupported features may have been added to newer releases, or past issues may have been corrected.
We encourage you to download the current phpdocx version and check the Documentation available.

Posted by gmakstutis  · 23-05-2019 - 07:49

Hi,

I can see that addSection is available via htmlExtended, but I'm not having much luck in getting the syntax correct.

I would like to be able to do something like:

<phpdocx_addSection dataPaperType="A4-landscape" data-marginRight="1077" data-marginBottom="850" data-marginLeft="1077" />

but this isn't working.

Is there a way to do this in the HTML?

I'm using the namespaced version of the Drupal plugin (for Drupal 8) and the Premium version of phpdocx.

Many thanks

 

Posted by admin  · 23-05-2019 - 08:14

Hello,

The problem is that your sample is not using the correct tag. On https://www.phpdocx.com/documentation/introduction/html-extended-to-word-PHP you can read the list of supported contents and their default tags (that can be overwriten if needed).

For example, to generate a new section you need to use the following tag:

<phpdocx_section data-sectionType="nextPage" data-paperType="A3-landscape" />

This is using phpdocx_section (not phpdocx_addSection). Also please note you need to use data-paperType (not dataPaperType as your code is using).

In the samples included in the Premium package you can find a script that creates a section with HTML Extended: sample_6.php

Regards.

Posted by gmakstutis  · 23-05-2019 - 19:02

I must be doing something wrong...

I have setup a test content-type in Drupal, and have a Drupal twig template that has this:

<phpdocx_modifypagelayout data-paperType="A4" data-marginTop="850" data-marginRight="1077" data-marginBottom="850" data-marginLeft="1077" />

{{ node.field_test1.value|raw }} {# outputs a series of paragraphs #}

<phpdocx_section data-paperType="A4-landscape" data-sectionType="nextPage" />

{{ node.field_test2.value|raw }} {# outputs a series of paragraphs #}

<phpdocx_section data-paperType="A4" data-sectionType="nextPage" />

{{ node.field_test3.value|raw }} {# outputs a series of paragraphs #}

<phpdocx_section data-paperType="A4-landscape" data-sectionType="nextPage" />

{{ node.field_test4.value|raw }} {# outputs a series of paragraphs #}

I am expecting that I will see the page orientation change after each block of text. However, what I'm getting is:

  • The docx starts with a blank A4 (portrait) page (with correct margins), followed by
  • A blank A4-landscape page (with different margins), followed by
  • A blank A4 (portrait) page, followed by
  • A series of A4-landscape pages with the text output from the Drupal fields

Why am I getting blank pages, changed margins, and why is the text not appearing in the sections that preceed them?

Thanks

 

Posted by admin  · 24-05-2019 - 06:46

Hello,

You need to set custom margins if you don't want to use the default values of the chosen paper type when creating sections using addSection (phpdocx_section tag):

<phpdocx_section data-paperType="A4-landscape" data-sectionType="nextPage" data-marginTop="850" data-marginRight="1077" data-marginBottom="850" data-marginLeft="1077" />

About your other question, because of how sections work you only can generate one section for each embedHTML method call, for example:

$html = '
    <phpdocx_modifypagelayout data-paperType="A4" data-marginTop="850" data-marginRight="1077" data-marginBottom="850" data-marginLeft="1077" />

    <p>Paragraph 1</p>
    <p>Paragraph 2</p>
';
$docx->embedHTML($html, array('useHTMLExtended' => true));
$html = '
    <phpdocx_section data-paperType="A4-landscape" data-sectionType="nextPage" data-marginTop="850" data-marginRight="1077" data-marginBottom="850" data-marginLeft="1077" />

    <p>Paragraph A</p>
    <p>Paragraph B</p>
';
$docx->embedHTML($html, array('useHTMLExtended' => true));
$html = '
    <phpdocx_section data-paperType="A4" data-sectionType="nextPage" data-marginTop="850" data-marginRight="1077" data-marginBottom="850" data-marginLeft="1077" />

    <p>Paragraph I</p>
    <p>Paragraph II</p>';
$docx->embedHTML($html, array('useHTMLExtended' => true));
$html = '
    <phpdocx_section data-paperType="A4-landscape" data-sectionType="nextPage" data-marginTop="850" data-marginRight="1077" data-marginBottom="850" data-marginLeft="1077" />

    <p>Paragraph a</p>
    <p>Paragraph b</p>
';
$docx->embedHTML($html, array('useHTMLExtended' => true));

We have moved a request to add support to generating multiple sections in the same embedHTML method to the dev team.

Regards.

Posted by admin  · 24-05-2019 - 07:24

Hello,

An alternative code to generate custom sections in the same embedHTML is using WordFragments with raw WordML example, for example the following code generate two section types and add them into the workflow:

$section1Fragment = new Phpdocx\Elements\WordFragment($docx);
$section1Fragment->addWordML('<w:p xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:pPr><w:sectPr><w:pgSz w:code="9" w:h="16838" w:orient="portrait" w:w="11906"/><w:pgMar w:bottom="850" w:footer="708" w:gutter="0" w:header="708" w:left="1077" w:right="1077" w:top="850"/><w:cols w:num="1" w:space="708"/><w:docGrid w:linePitch="360"/></w:sectPr></w:pPr></w:p>');
$section2Fragment = new Phpdocx\Elements\WordFragment($docx);
$section2Fragment->addWordML('<w:p xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:pPr><w:sectPr><w:type w:val="nextPage"/><w:pgSz w:code="9" w:h="11906" w:orient="landscape" w:w="16838"/><w:pgMar w:bottom="850" w:footer="708" w:gutter="0" w:header="708" w:left="1077" w:right="1077" w:top="850"/><w:cols w:num="1" w:space="708"/><w:docGrid w:linePitch="360"/></w:sectPr></w:pPr></w:p>');

$html = '
    <phpdocx_modifypagelayout data-paperType="A4" data-marginTop="850" data-marginRight="1077" data-marginBottom="850" data-marginLeft="1077" />
    <p>Paragraph 1</p>
    <p>Paragraph 2</p>

    <phpdocx_wordfragment data-content="'.base64_encode(serialize($section1Fragment)).'" />
    <p>Paragraph A</p>
    <p>Paragraph B</p>

    <phpdocx_wordfragment data-content="'.base64_encode(serialize($section2Fragment)).'" />
    <p>Paragraph I</p>
    <p>Paragraph II</p>
';
$docx->embedHTML($html, array('useHTMLExtended' => true));

Following this approach, please note you need to generate sections as raw WordML contents setting margins, orientation, cols and other values and then add them as WordFragments into the HTML. For example you could generate two (or more) raw WordML fragments: A4, A4-landscape with your custom margins, and then use them as needed (one or multiple times):

$sectionA4Fragment = new Phpdocx\Elements\WordFragment($docx);
$sectionA4Fragment->addWordML('...');
$sectionA4LandscapeFragment = new Phpdocx\Elements\WordFragment($docx);
$sectionA4LandscapeFragment->addWordML('...');

$html = '
    <phpdocx_modifypagelayout data-paperType="A4" data-marginTop="850" data-marginRight="1077" data-marginBottom="850" data-marginLeft="1077" />
    <p>Paragraph</p>
    <p>Paragraph</p>

    <phpdocx_wordfragment data-content="'.base64_encode(serialize($sectionA4Fragment)).'" />
    <p>Paragraph</p>
    <p>Paragraph</p>

    <phpdocx_wordfragment data-content="'.base64_encode(serialize($sectionA4LandscapeFragment)).'" />
    <p>Paragraph</p>
    <p>Paragraph</p>

    <phpdocx_wordfragment data-content="'.base64_encode(serialize($sectionA4Fragment)).'" />
    <p>Paragraph</p>
    <p>Paragraph</p>
';
$docx->embedHTML($html, array('useHTMLExtended' => true));

Regards.

Posted by gmakstutis  · 24-05-2019 - 08:27

Hmmm... this is difficult. We are generating a very complex document (many pages, different styles, etc.) from a Drupal 8 site. The Word file is the output of a complex Twig template, that includes many entity_references, and; therefore, a complex relationship of different Twig templates get embedded. We can't really use the code feature of phpdocx, as our team doesn't have sufficient PHP/Drupal coding experience.

Thinking about future development, would it be possible to do something like have an opening and closing phpdocx tag that integrates the call to $docx->embedHTML($html, array('useHTMLExtended' => true)); when the closing tag is called?

Effectively, this would use everything between the opening <phpdocx_section> and </phpdocx_section> is the value of $html. Something like:

<phpdocx_section data-paperType="A4-landscape" data-sectionType="nextPage">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</phpdocx_section>

Might this allow for using HTMLextended to achieve multiple sections in a document?

Posted by admin  · 24-05-2019 - 09:48

Hello,

phpdocx_section is an inline tag (https://www.phpdocx.com/documentation/introduction/html-extended-to-word-PHPTag types section) that should be rewritten to work as block tag. We move the request to the dev team.

Anyway, we think the previous approach is not complex. You just need to generate the section tags as WordML and use a phpdocx_wordfragment tag instead of phpdocx_section.

Regards.

Posted by gmakstutis  · 26-05-2019 - 10:15

I'm trying, as you suggested, to use 'fragments'. However, this is still proving difficult.

I've setup the Twig template to do this:

{# set the portrait variable to raw WordML #}
{% set portrait = '<w:p xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:pPr><w:sectPr><w:pgSz w:code="9" w:h="16838" w:orient="portrait" w:w="11906"/><w:pgMar w:bottom="850" w:footer="708" w:gutter="0" w:header="708" w:left="1077" w:right="1077" w:top="850"/><w:cols w:num="1" w:space="708"/><w:docGrid w:linePitch="360"/></w:sectPr></w:pPr></w:p>' %}

{# generate a variable with the result of phpdocx_wordML tag #}
{% set portfrag = "<phpdocx_wordML data-content='{{ portrait }}' />" %}

{# output #}
<phpdocx_wordfragment data-content="{{ portfrag|base64encode }}" />

This has required the creation of a custom Twig Filter (base64encode).

So far, this isn't working. 

The problem might be my custom twig filter, since I'm not very familiar with developing for twig; but it is a relatively simple filter so I don't think it is the problem.

However, this has raised another question. If the <phpdocx_wordfragment /> always requires the calling of the PHP functions base64_encode and serialize, couldn't this be included in the phpdocx function that parses the <phpdocx_wordfragment /> tag?

Many thanks for your assistance.

 

 

Posted by admin  · 26-05-2019 - 12:58

Hello,

The value of data-content needs to be a WordFragment object serialized and encoded using base64, not a string encoded using base64. This is the most simple example:

$sectionFragment = new Phpdocx\Elements\WordFragment($docx);
$sectionFragment->addWordML('<w:p xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:pPr><w:sectPr><w:pgSz w:code="9" w:h="16838" w:orient="portrait" w:w="11906"/><w:pgMar w:bottom="850" w:footer="708" w:gutter="0" w:header="708" w:left="1077" w:right="1077" w:top="850"/><w:cols w:num="1" w:space="708"/><w:docGrid w:linePitch="360"/></w:sectPr></w:pPr></w:p>');

$html = '<phpdocx_wordfragment data-content="'.base64_encode(serialize($sectionFragment)).'" />';

So your twig filter must generate a serialized WordFragment object. You could also generate WordFragments in Drupal and use them in twig.

As it's a serialized object,  it can't be generated using phpdocx_wordfragment directly, it needs to be created previously.

Regards.

Posted by gmakstutis  · 26-05-2019 - 18:19

OK. Trying to generate the tags, with encoding in a Drupal theme preprocess function. In this, we pass the final tag value as a Twig theme variable.

use Drupal\views\Views;
use Drupal\Core\Url;
use Drupal\node\Entity\Node;

/**
 * @param $variables
 */
function starkprint_preprocess_node(&$variables){

    $landscape_fragment = new Phpdocx\Elements\WordFragment($docx);
    $landscape_fragment->addWordML('<w:p xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:pPr><w:sectPr><w:type w:val="nextPage"/><w:pgSz w:code="9" w:h="11906" w:orient="landscape" w:w="16838"/><w:pgMar w:bottom="850" w:footer="708" w:gutter="0" w:header="708" w:left="1077" w:right="1077" w:top="850"/><w:cols w:num="1" w:space="708"/><w:docGrid w:linePitch="360"/></w:sectPr></w:pPr></w:p>');
        
     $landscape = '<phpdocx_wordfragment data-content="'.base64_encode(serialize($landscape_fragment)).'" />';
     $variables['landscape'] = $landscape;

     $portrait_fragment = new Phpdocx\Elements\WordFragment($docx);
     $portrait_fragment->addWordML('<w:p xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:pPr><w:sectPr><w:pgSz w:code="9" w:h="16838" w:orient="portrait" w:w="11906"/><w:pgMar w:bottom="850" w:footer="708" w:gutter="0" w:header="708" w:left="1077" w:right="1077" w:top="850"/><w:cols w:num="1" w:space="708"/><w:docGrid w:linePitch="360"/></w:sectPr></w:pPr></w:p>');
        
     $portrait = '<phpdocx_wordfragment data-content="'.base64_encode(serialize($portrait_fragment)).'" />';
     $variables['portrait'] = $portrait;
}

In the Twig template, we put:

{{ portrait }} {# this should give us the phpdocx tag for portrait layout #}

...Text to be portrait orientation...

{{ landscape }} {# this should give us the phpdocx tag for landscape layout #}

...Text to be landscape orientation...

But... this isn't working either. Do I need a 'use' statement, in order to call the 'new Phpdocx\...'?

Is there something else I should be trying?

Thanks

Posted by admin  · 26-05-2019 - 18:50

Hello,

Yes, using PHP namespaces you need to add a use sentence or set the global namespace. For example, in the plugin, you can find the file NodeToDocxHandler.php, where phpdocx is used as:

$docx = new \Phpdocx\Create\CreateDocx();
$docx->embedHTML($html);

as you can check, it's using the global namespace to instantiate the main class: \Phpdocx\Create\CreateDocx . Or you can add a use sentence.

We also recommend you to check if you get any PHP error and if you need to get the raw value of the wordfragment tag in twig to avoid it's escaped.

Regards.

Posted by gmakstutis  · 29-05-2019 - 22:57

I am very happy to inform you that it works!

You can create the necessary WordML fragments in a Drupal theme_preprocess_node function, in order to define a variable that can be added to your template as a {{ twig }} variable. I did, as you suggested, add 'raw' to the twig variable ( {{ twig|raw }} to ensure that the result of the WordML encoding and serializing did not get 'escaped'.

Thank you, once again, for your assistance.

Posted by gmakstutis  · 03-06-2019 - 14:23

I've got my sections working, with:

  #Landscape section - Next Page
  $landscape_fragment = new Phpdocx\Elements\WordFragment($docx);
  $landscape_fragment->addWordML('<w:p xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:pPr><w:sectPr><w:type w:val="nextPage"/><w:pgSz w:code="9" w:h="11906" w:orient="landscape" w:w="16838"/><w:pgMar w:bottom="850" w:footer="708" w:gutter="0" w:header="708" w:left="1077" w:right="1077" w:top="850"/><w:cols w:num="1" w:space="708"/><w:docGrid w:linePitch="360"/></w:sectPr></w:pPr></w:p>');
  $landscape = '<phpdocx_wordfragment data-content="'.base64_encode(serialize($landscape_fragment)).'" />';
  $variables['landscape'] = $landscape;

  #Portrait section - Next Page
  $portrait_fragment = new Phpdocx\Elements\WordFragment($docx);
  $portrait_fragment->addWordML('<w:p xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:pPr><w:sectPr><w:type w:val="nextPage"/><w:pgSz w:code="9" w:h="16838" w:orient="portrait" w:w="11906"/><w:pgMar w:bottom="850" w:footer="708" w:gutter="0" w:header="708" w:left="1077" w:right="1077" w:top="850"/><w:cols w:num="1" w:space="708"/><w:docGrid w:linePitch="360"/></w:sectPr></w:pPr></w:p>');
  $portrait = '<phpdocx_wordfragment data-content="'.base64_encode(serialize($portrait_fragment)).'" />';
  $variables['portrait'] = $portrait;

However, I'm losing my footer; which was being added by:

<phpdocx_footer>
    <div class="print-footer">
        <table border="-" style="border-collapse: collapse; width:100%;">
            <tbody>
                <tr>
                    <td class="text-left font-8"><phpdocx_pagenumber data-target="defaultFooter" data-type="numerical" data-fontSize="8" data-font="Open Sans" data-textAlign="left" /></td>
                    <td class="text-right font-8">
                        Pearson BTEC Levels 4 and 5 Higher Nationals in {{ subject }}<br/>
                        Specification - Issue {{ issue }} - {{ issmonth }} {{ issyear }} &copy; 2019
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
</phpdocx_footer>

I'm assuming that I need to have footers defined as part of my sections. Is this correct?