retypeapp / retype

Retype is an ✨ ultra-high-performance✨ static site generator that builds a website based on simple text files.
https://retype.com
Other
1.06k stars 204 forks source link

Assistance in defining a custom print style for Retype #628

Closed ApolonTorq closed 1 year ago

ApolonTorq commented 1 year ago

I'm attempting to reproduce the print styling functionality of the following VS Code extension when the user performs a print preview on a Retype page: https://marketplace.visualstudio.com/items?itemName=yzane.markdown-pdf

Here are the styles it uses: https://github.com/yzane/vscode-markdown-pdf/tree/master/styles That VS Code extension generates html from a single markup file and its inbuilt styles and then pdf renders it.

Any pointers for how to achieve a Retype print preview that looks more like a pdf "book"? Note that this query is just for one page and not the entire retype documentation set?

The sort of questions I need help with include:

geoffreymcgill commented 1 year ago

We have creating a print stylesheet logged as a feature request and it is a relatively high priority feature we would like to add. At the moment, we cannot work on this issue but will try and get to it as soon as possible.

Unfortunately, I do not have any advice or recommendations for making a custom print stylesheet. This feature request is covered by issue #85, so I'm going to close this issue in favor of keeping the discussion regarding the print stylesheet centralized within issue #85.

ApolonTorq commented 1 year ago

Okay. As a workaround for the interim I hacked together the following <script> for inclusion in the Retype _includes/head.html file. Its been posted here in case its of use to anyone else. The css style sheets added are the ones mentioned in the first post in this issue thread.

The suffixing of ?print to a retype url causes the DOM to be re-written at runtime with a more print style rendering for which pdf generation can be initiated. It works "good enough" for now until you have a retype feature equivalent. It gets a bit weird when working with retype start and refreshes. This probably relates to the auto-refresh functionality in that mode.

<script id="print-style-mutator">
    function adjustForPrint() {
        return document.location.href.includes('?print');
    }

    document.addEventListener('DOMContentLoaded', function() {
        if (adjustForPrint()) {
            removeAll('link');
            removeAll('script[src*="retype.js"]');
            removeAll('script[src*="config.js"]');
            removeAll('aside');
            removeAll('#docs-site-header');
            removeAll('.breadcrumb');
            removeAll('.header-anchor-trigger');
            removeAll('.docs-icon');
            removeFirst('#docs-overlay-target');
            removeFirst('#docs-sidebar-right-toggle');

            removeFirst('#retype-connector-js');
            removeFirst('#retype-reloader-js');
            removeFirst('#retype-editor-js');
            removeAfter('#print-style-mutator');
            addStylesheet('../../../../resources/css/pdf/markdown.css');
            addStylesheet('../../../../resources/css/pdf/tomorrow.css');
            addStylesheet('../../../../resources/css/pdf/markdown-pdf.css');     

            var docsContent = document.getElementById('docs-content');
            var docsApp = document.getElementById('docs-app');
            if (docsApp && docsContent) {
                console.log("Promoting docs-content children to the body and removing docs-app");
                while (docsContent.firstChild) {
                    document.body.appendChild(docsContent.firstChild);
                }
                docsApp.parentNode.removeChild(docsApp);
                removeFirst('.break-words')
            }

            document.querySelectorAll('doc-anchor-target').forEach(function(element) {
                id = element.id;
                element.removeAttribute('id');
                element.firstChild.id = id;
                // insert children before element in parent
                while (element.firstChild) {
                    element.parentNode.insertBefore(element.firstChild, element);
                }
                element.parentNode.removeChild(element);
            });
        }
    });

    window.addEventListener('load', function () {
        applyMermaidWorkarounds();

        if (adjustForPrint()) {
            removeAll('.simplebar-placeholder');

            var startElement = null;
            var url = new URL(window.location.href);
            if (url.hash) {
                var id = url.hash.split('?print')[0].replace('#', '');
                var startElement = document.getElementById(id);                 
            }

            if (startElement != null) {
                requestAnimationFrame(function() {                    
                    console.log("Scrolling to element: " + startElement.id);
                    startElement.scrollIntoView({ block: 'start' });
                });
            }               
        }
    });

    function removeAll(selector) {
        var elements = document.querySelectorAll(selector);
        if (elements) {
            console.log("Removing " + selector + " matches: " + elements.length + " elements");
            Array.from(elements).forEach(function(element) {
                element.parentNode.removeChild(element);
            });
        }
    }

    function removeFirst(selector) {
        var element = document.querySelector(selector);
        if (element) {
            console.log("Removing first " + selector + " match");
            element.parentNode.removeChild(element);
        }
        else console.log("No first " + selector + " match");
    }

    function removeAfter(selector) {
        var element = document.querySelector(selector);
        if (element) {
            var nextElement = element.nextElementSibling;
            while (nextElement) {
                var nextNextElement = nextElement.nextElementSibling;
                nextElement.parentNode.removeChild(nextElement);
                nextElement = nextNextElement;
            }
        }
    }

    function addStylesheet(href) {
        var linkElement = document.createElement('link');
        linkElement.rel = 'stylesheet';
        linkElement.href = href;
        var descriptionProperty = document.head.querySelector('meta[name="description"]');
        if (descriptionProperty != null)
            document.head.insertBefore(linkElement, descriptionProperty);
        else document.head.appendChild(linkElement);
    }    

    function applyMermaidWorkarounds() {
        document.querySelectorAll('svg[aria-roledescription="journey"]').forEach(function(svg) {
            console.log("Applying mermaid workaround for journey diagram: " + svg.id);
            svg.style.height = 'auto';
        });
    }
</script>