eZ Community » Blogs » Philippe VINCENT-ROYOL » Syntax Color with eZ Publish 5...


Syntax Color with eZ Publish 5 XmlText (twig)

Tuesday 20 August 2013 5:00:53 pm

  • Currently 5 out of 5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Previous eZ Publish release, it was really easy to add syntax color, thanks to ezgeshi extension. But now it's different with eZ 5 and this blog post will explain how to deal with it and Pygment.

I took a long time before having a working and nicely code like this, because eZ Publish 5 is used a XML parser do display content of your Xml Block. Before starting, just a note: this example explain you how to implement your own syntax color mechanism; of course this code could be optimise :)

Be Careful: this is only for 2013.6 and 5.1 ! If you would like to use 2013.7 and 5.2+ you have an other good way to proceed (https://confluence.ez.no/display/EZP/How+to+implement+a+Custom+Tag+for+XMLText+FieldType)


Before playing with eZ Publish, you have to install Python first, and Pygments. In order to do that just copy and paste:

sudo apt-get install python
sudo easy_install Pygments

Of course, you could install Pygments via source or Mercurial. For this step please have a look to the online documentation.

eZ Publish 5

It's time to play with the "beast" ;) First step is to create a bundle !

Create your Pygment class

We are going to create our Pygment class which will  "communicate" with Pygments library in order to generate your colored code:

 * Lexer documentation: http://pygments.org/docs/lexers/
namespace pvr\PheelitBundle\DependencyInjection;
class Pygment
    public function __construct() {
    static public function highlight_test($string, $lexer = 'bash', $format = 'html')
        // use proc open to start pygmentize
        $descriptorspec = array(
            array( "pipe", "r" ), // stdin
            array( "pipe", "w" ), // stdout
            array( "pipe", "w" ), // stderr
        $cwd = dirname( __FILE__ );
        $env = array();
        //if ( $extra_opts == "" )
            $extra_opts = "-O style=default"; //,linenos=inline";
            if ( $lexer == "php" ) $extra_opts .= ",startinline=True";
        // } else {
        // Just append these to the passed-in args:
        // $extra_opts .= ",full,style=".$style.",cssclass=".$highlight_class;
        // }
        $proc = proc_open( '/usr/local/bin/pygmentize -l ' . $lexer . ' -f ' . $format . " " . $extra_opts, $descriptorspec, $pipes, $cwd, $env );
        if( !is_resource( $proc ) )
            return false;
        // now write $string to pygmentize's input
        fwrite( $pipes[0], $string );
        fclose( $pipes[0] );
        // the result should be available on stdout
        $result = stream_get_contents( $pipes[1] );
        fclose( $pipes[1] );
        // we don't care about stderr in this example
        // just checking the return val of the cmd
        $return_val = proc_close( $proc );
        if( $return_val !== 0 )
            return false;
        return $result;

eZ Publish configuration

We have to "override" two things: service which used converter and xsl templates.

First, put those lines on your service.yml:

    ezpublish.fieldType.ezxmltext.converter.html5.resources: %kernel.root_dir%/../src/Acme/DemoBundle/FieldType/XmlText/Input/Resources/stylesheets/eZXml2Html5.xsl
    ezpublish.fieldType.ezxmltext.converter.literalToHtml5.class: Acme\DemoBundle\FieldType\XmlText\Converter\LiteralToHtml5
        class: %ezpublish.fieldType.ezxmltext.converter.html5.class%
            - %ezpublish.fieldType.ezxmltext.converter.html5.resources%
            - [@ezpublish.fieldType.ezxmltext.converter.embedToHtml5, @ezpublish.fieldType.ezxmltext.converter.ezLinkToHtml5, @ezpublish.fieldType.ezxmltext.converter.literalToHtml5]
        class: %ezpublish.fieldType.ezxmltext.converter.literalToHtml5.class%

We pass a new arguments to tell to eZ Publish that it have to load our new converter on ezpublish.fieldType.ezxmltext.converter.html5 service.

Don't forget to copy files from /vendor/ezsystems/ezpublish-kernel/eZ/Publish/Core/FieldType/XmlText/Input/Resources/stylesheets/ to your bundle.

Modification of eZXml2Html5_core.xsl

Just change your literal part by

<xsl:template match="literal">
<xsl:value-of select="text()" disable-output-escaping="yes"/>

Create a converter

Not really hard to create a converter:  we define it like this:

namespace Acme\DemoBundle\FieldType\XmlText\Converter;
use eZ\Publish\Core\FieldType\XmlText\Converter;
use eZ\Publish\API\Repository\Repository;
use Acme\DemoBundle\DependencyInjection\Pygment;
use DOMDocument;
class LiteralToHtml5 implements Converter
    public function __construct( )
    public function convert( DOMDocument $xmlDoc )
        foreach ( $xmlDoc->getElementsByTagName( "literal" ) as $literal )
            $code = $literal->getAttribute( 'class' );
            $literalString = $literal->nodeValue;
            $pygments = new Pygment();
            $highlight = $pygments->highlight_test( $literalString, $code );
            $literal->nodeValue = htmlentities($highlight);

That's all :)

Just test it via a simple

{{ ez_render_field( content, "my_xml_block" ) }}

Tip: if you would like to keep space on your code (specially after span tag), you have to delete spaceless tag for ezxmltext_field and single_block_field or create new one :)

Have fun !

Proudly Developed with from