Expand my Community achievements bar.

SOLVED

AEM - Make the rendered html prettier/more readable

Avatar

Level 4

 

Hello everyone,

does anyone know if there is a way to make the rendered html more readable?. I mean, properly tabulated and ordered.
I'm using sigthly and the markup of each component is well tabulated on the app code.
But I understand that the rendering engine is modifying that indentation and rendering an html that is not ordered and indenting.

an example

<html>
<head></head>
<body>
<div><span>
</span></div>
</body>
<footer>
<div>
</div>
</footer>
</html>

instead of something like this

 

<html>
    <head></head>
    <body>
        <div>
            <span></span>
        </div>
    </body>
    <footer>
        <div></div>
    </footer>
</html>

 

Is that posible without add an extra html processor or something similar to that?

Thanks in advance

1 Accepted Solution

Avatar

Correct answer by
Level 4

Well finally i have a possible approach.

Thanks @Jörg_Hoh and @BrianKasingli for all your help!!
here is the filter that i created and seems to be working. And i would like to share the solution and know your opinions. 

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

@Component(immediate = true)
@Service(Filter.class)
@Properties({ @Property(name = "sling.filter.scope", value = "REQUEST"),
    @Property(name = "sling.filter.pattern", value = ".*\\.html"),
    @Property(name = "service.ranking", value = "700") })
public class HTMLFormatterFilter  extends SlingAllMethodsServlet implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // create a response wrapper to capture the original response
        CharResponseWrapper wrappedResponse = new CharResponseWrapper(
            (HttpServletResponse)response);

        chain.doFilter(request, wrappedResponse);

        // modify the response with the formatted html
        byte[] bytes = wrappedResponse.getByteArray();

        if (wrappedResponse.getContentType().contains("text/html")) {
            String out = new String(bytes);
            Document doc = Jsoup.parse(out);
            response.getOutputStream().write(doc.toString().getBytes());
        }
        else {
            response.getOutputStream().write(bytes);
        }
    }

    @Override
    public void destroy() {
        // Do nothing
    }

    private static class ByteArrayServletStream extends ServletOutputStream
    {
        ByteArrayOutputStream baos;

        ByteArrayServletStream(ByteArrayOutputStream baos)
        {
            this.baos = baos;
        }

        public void write(int param) throws IOException
        {
            baos.write(param);
        }
    }

    private static class ByteArrayPrintWriter
    {

        private ByteArrayOutputStream baos = new ByteArrayOutputStream();

        private PrintWriter pw = new PrintWriter(baos);

        private ServletOutputStream sos = new ByteArrayServletStream(baos);

        public PrintWriter getWriter()
        {
            return pw;
        }

        public ServletOutputStream getStream()
        {
            return sos;
        }

        byte[] toByteArray()
        {
            return baos.toByteArray();
        }
    }

    private static class CharResponseWrapper extends HttpServletResponseWrapper
    {
        private ByteArrayPrintWriter output;
        private boolean usingWriter;

        public CharResponseWrapper(HttpServletResponse response)
        {
            super(response);
            usingWriter = false;
            output = new ByteArrayPrintWriter();
        }

        public byte[] getByteArray()
        {
            return output.toByteArray();
        }

        @Override
        public ServletOutputStream getOutputStream() throws IOException
        {
            // will error out, if in use
            if (usingWriter) {
                super.getOutputStream();
            }
            usingWriter = true;
            return output.getStream();
        }

        @Override
        public PrintWriter getWriter() throws IOException
        {
            // will error out, if in use
            if (usingWriter) {
                super.getWriter();
            }
            usingWriter = true;
            return output.getWriter();
        }

        public String toString()
        {
            return output.toString();
        }
    }
}

 

View solution in original post

11 Replies

Avatar

Employee Advisor

Technically it's possible, but as the indentation is only relevant for a human (for better readability when debugging) and not for a machine/browser, why don't you reformat the HTML when you need it?

Avatar

Level 4
Hi @Jörg_Hoh, yes i know, this is a client requirement, they want to have a "better" html for debugging propose and compare with their Front End code. I just trying to test some posible solution and see if it makes sense or we will have a performance decrease

Avatar

Employee Advisor
I guess that's it is possible, but I've never done that, also I don't know a good library for it. If you have found one, I would try to implement it in the rewriter pipeline (as the last rewriter). The performance impact is probably negligible (depends on the performance of that library).

Avatar

Level 4
Hi @Jörg_Hoh, And how could I make it last in the rewriter pipeline, perhaps with a java filter? Briankasingli shared me this one https://mkyong.com/java/java-pretty-print-html

Avatar

Employee Advisor
What you can do: Implement a filter, capture the complete output in a string and then apply the reformatting. Just be sure that you do that only for HTML; also it breaks the streaming approach of the result (sending the response to the browser starts when the last byte of the response has been created).

Avatar

Community Advisor

If you wish to pretty print CSS, JS, HTML you can utilise the JavaScript library, js-beautify. Although this solution requires customisation to your JavaScript logic.

Example Code:

let formattedHTML = window.jsbeautifier.beautify('your HTML,CSS,JS string');

Documentation can be found here for js-beautify https://www.npmjs.com/package/js-beautify

 

Avatar

Level 4
Hi @BrianKasingli, thanks for your response. But i would like to it on the render process and not add an extra process on the client side.

Avatar

Level 4
I will try with that lib and see the results, thank you very much!

Avatar

Correct answer by
Level 4

Well finally i have a possible approach.

Thanks @Jörg_Hoh and @BrianKasingli for all your help!!
here is the filter that i created and seems to be working. And i would like to share the solution and know your opinions. 

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

@Component(immediate = true)
@Service(Filter.class)
@Properties({ @Property(name = "sling.filter.scope", value = "REQUEST"),
    @Property(name = "sling.filter.pattern", value = ".*\\.html"),
    @Property(name = "service.ranking", value = "700") })
public class HTMLFormatterFilter  extends SlingAllMethodsServlet implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // create a response wrapper to capture the original response
        CharResponseWrapper wrappedResponse = new CharResponseWrapper(
            (HttpServletResponse)response);

        chain.doFilter(request, wrappedResponse);

        // modify the response with the formatted html
        byte[] bytes = wrappedResponse.getByteArray();

        if (wrappedResponse.getContentType().contains("text/html")) {
            String out = new String(bytes);
            Document doc = Jsoup.parse(out);
            response.getOutputStream().write(doc.toString().getBytes());
        }
        else {
            response.getOutputStream().write(bytes);
        }
    }

    @Override
    public void destroy() {
        // Do nothing
    }

    private static class ByteArrayServletStream extends ServletOutputStream
    {
        ByteArrayOutputStream baos;

        ByteArrayServletStream(ByteArrayOutputStream baos)
        {
            this.baos = baos;
        }

        public void write(int param) throws IOException
        {
            baos.write(param);
        }
    }

    private static class ByteArrayPrintWriter
    {

        private ByteArrayOutputStream baos = new ByteArrayOutputStream();

        private PrintWriter pw = new PrintWriter(baos);

        private ServletOutputStream sos = new ByteArrayServletStream(baos);

        public PrintWriter getWriter()
        {
            return pw;
        }

        public ServletOutputStream getStream()
        {
            return sos;
        }

        byte[] toByteArray()
        {
            return baos.toByteArray();
        }
    }

    private static class CharResponseWrapper extends HttpServletResponseWrapper
    {
        private ByteArrayPrintWriter output;
        private boolean usingWriter;

        public CharResponseWrapper(HttpServletResponse response)
        {
            super(response);
            usingWriter = false;
            output = new ByteArrayPrintWriter();
        }

        public byte[] getByteArray()
        {
            return output.toByteArray();
        }

        @Override
        public ServletOutputStream getOutputStream() throws IOException
        {
            // will error out, if in use
            if (usingWriter) {
                super.getOutputStream();
            }
            usingWriter = true;
            return output.getStream();
        }

        @Override
        public PrintWriter getWriter() throws IOException
        {
            // will error out, if in use
            if (usingWriter) {
                super.getWriter();
            }
            usingWriter = true;
            return output.getWriter();
        }

        public String toString()
        {
            return output.toString();
        }
    }
}