Forum Samples, Tips and Tricks

Here, have an automatic TOC script

StoneCypher
So, I slapped together a quick TOC generator this morning. Yay coffee.

If you have a document that makes a few small requirements, then this script will pull a table of contents out of your document automatically, using the text of headers as the table of contents text. All table of contents items will be generated as links to their position in the document, then will provide a dot leader and a generated page number. The example below styles them to look pretty okay.

This code will be part of my utility library, http://scutil.com/ (also available at github,) probably by the end of the day or so.

The way this works is to scan the DOM and build a list of every <h1>, <h2> .. <h6> that is not marked notoc (that is, <h1 notoc="notoc"> will be ignored.) Then, it goes to the location you pass it as a target, and builds a nested <ol> containing <li><a href="header id"></li> to represent the ToC. It is assumed that CSS will be used to fill the TOC out; that way you don't have to screw with the code to set what your ToC looks like. An example of how to do that is given below.

This script assumes two requirements of any header that isn't marked notoc. Headers marked notoc will be ignored.

The first is that every header must have a unique id. That ID is how this script hooks the links, page numbers and text content in the ToC to the element. If you forget to set a header's id, there will be a broken row in your table of contents, with an empty text spot and a zero page number.

The other is that this script will fail, and halt generation of your document, if headers jump by more than one step upwards. So, using <h1> then <h3> and then invoking this script will Cause Much Fail (tm). You could get around that with notoc if you're using headers for styling purposes, but then, if you're using headers for styling purposes, bad!

function LinkTo(Tgt) {

  var InA = document.createElement('a');
  InA.setAttribute('href', '#' + Tgt);   // prince bug: foo.href = bar; doesn't work!

  return InA;

}





function TocItem(Tgt) {

  var InLI = document.createElement('li');
  InLI.appendChild(LinkTo(Tgt));

  return InLI;

}





function ToCwalk(Source) {

  var InOL   = document.createElement('ol');
  var DStack = [];

  var Walker = function(lSource) {

    switch (lSource.nodeName.toString().toUpperCase()) {
      case 'H1': if (lSource.getAttribute('notoc') !== 'notoc') { DStack.push({d:1, t:lSource.id}); } break;
      case 'H2': if (lSource.getAttribute('notoc') !== 'notoc') { DStack.push({d:2, t:lSource.id}); } break;
      case 'H3': if (lSource.getAttribute('notoc') !== 'notoc') { DStack.push({d:3, t:lSource.id}); } break;
      case 'H4': if (lSource.getAttribute('notoc') !== 'notoc') { DStack.push({d:4, t:lSource.id}); } break;
      case 'H5': if (lSource.getAttribute('notoc') !== 'notoc') { DStack.push({d:5, t:lSource.id}); } break;
      case 'H6': if (lSource.getAttribute('notoc') !== 'notoc') { DStack.push({d:6, t:lSource.id}); } break;
      default  : break;
    }

    if (lSource.hasChildNodes()) {

      var tNode = lSource.firstChild;
      do {
        Walker(tNode);
      } while (tNode = tNode.nextSibling);

    }

  }

  Walker(Source);

  var curDepth   = 1;
  var curStack   = [InOL];
  var childStack = [];
  var lastChild  = null;

  for (var i=0, iC=DStack.length; i<iC; ++i) {

    var gap = DStack[i].d - curDepth;

    switch (gap) {

      case 0:
        lastChild = TocItem(DStack[i].t)
        curStack[curStack.length-1].appendChild(lastChild);
        break;

      case 1:
        ++curDepth;
        childStack.push(lastChild);
        var newList = document.createElement('ol');
        curStack.push(newList);
        lastChild.appendChild(newList);
        lastChild = TocItem(DStack[i].t)
        curStack[curStack.length-1].appendChild(lastChild);
        break;

      default:
        if (gap > 0) {
          Log.error("Header depth increased by more than one!");
        } else {
          for (z=0; z>gap; --z) {
            --curDepth;
            childStack.pop();
            curStack.pop();
          }
          lastChild = TocItem(DStack[i].t)
          curStack[curStack.length-1].appendChild(lastChild);
        }
        break;

    }


  }

  return InOL;

}


That's enough to build a table of contents magically. An example of using it is given below, assuming that you've cut and pasted that code into a file called toc.js.

<?xml version="1.0" standalone="no" ?>
<!DOCTYPE html>

<html>

  <head>

    <script type="text/javascript" src="toc.js"></script>



    <style type="text/css">

      breaker         { display: block; page-break-after: always; }

      #ToCol          { margin: 1.5em 0 0 1.5em; }
      #ToCol a        { color: black; text-decoration: none; content: target-content(attr(href)); }
      #ToCol a::after { content: " " leader(".") " " target-counter(attr(href), page); }

      #ToCol ol       { margin-left: 2em; }
      #ToCol li       { margin-top: 0.3em; }

      .lorem          { color: #bbb; }

    </style>



    <script type="text/javascript">

      function init() {

        var Body = document.getElementById('bodyid');           // this is what we're scanning

        var ToC  = ToCwalk(Body);                               // make the ToC
        ToC.id   = 'ToCol';                                     // give it an id, for CSS

        document.getElementById('ToCbox').appendChild(ToC);     // put the ToC in place.

      }

    </script>



  </head>

  <body id="bodyid" onload="init();">

    <div id="ToCbox"></div>

    <breaker/>

    <h1 id="firstId">First</h1>

    <p>Hi, hello</p>
    <p>How are you</p>
    <p class="lorem">Lorem ipsum dolor sit amet, consectetur adipisicing 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.</p>
    <p class="lorem">Lorem ipsum dolor sit amet, consectetur adipisicing 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.</p>

    <h2 id="firstDetailId">Details</h2>

    <p>Just fine, thanks</p>
    <p class="lorem">Lorem ipsum dolor sit amet, consectetur adipisicing 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.</p>
    <p class="lorem">Lorem ipsum dolor sit amet, consectetur adipisicing 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.</p>

    <h2 id="firstSiblingDetailId">More Details</h2>

    <p>Nice weather we're having</p>
    <p class="lorem">Lorem ipsum dolor sit amet, consectetur adipisicing 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.</p>
    <p class="lorem">Lorem ipsum dolor sit amet, consectetur adipisicing 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.</p>

    <h1 id="secondId">Second</h1>

    <p>Hi, hello</p>
    <p>How are you</p>
    <p class="lorem">Lorem ipsum dolor sit amet, consectetur adipisicing 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.</p>
    <p class="lorem">Lorem ipsum dolor sit amet, consectetur adipisicing 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.</p>

    <h2 id="secondDetailId">Details</h2>

    <p>Just fine, thanks</p>
    <p class="lorem">Lorem ipsum dolor sit amet, consectetur adipisicing 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.</p>
    <p class="lorem">Lorem ipsum dolor sit amet, consectetur adipisicing 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.</p>

    <h3 id="inceptionGoDeeperBwomp">Deeper this time</h3>

    <p>Would like a coffee though</p>
    <p class="lorem">Lorem ipsum dolor sit amet, consectetur adipisicing 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.</p>
    <p class="lorem">Lorem ipsum dolor sit amet, consectetur adipisicing 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.</p>

    <h3 id="theJokeIsGettingOld">More TOC please</h3>

    <p>And maybe a bagel?</p>
    <p class="lorem">Lorem ipsum dolor sit amet, consectetur adipisicing 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.</p>
    <p class="lorem">Lorem ipsum dolor sit amet, consectetur adipisicing 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.</p>

    <h2 id="Eisenhower">More Details</h2>

    <p>Bagel bagel bagel.</p>
    <p class="lorem">Lorem ipsum dolor sit amet, consectetur adipisicing 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.</p>
    <p class="lorem">Lorem ipsum dolor sit amet, consectetur adipisicing 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.</p>

    <h1 id="ThatsAWrap">Postscript</h1>

    <p>was a nice language</p>

  </body>

</html>

John Haugeland is http://fullof.bs/

  1. ToC.png378.0 kB
mikeday
Nice work! JavaScript support is finally paying off. :D
BadSanta
Hi.

Why when I try to generate PDF from file index.php(where is clear html now) I have errors?
O_o but when I rename this to index.html everything is good.
1.jpg
  1. 1.jpg42.2 kB
mikeday
If your document is not XML, please call $prince->setHTML(1); to ensure that it is parsed as HTML.
BadSanta
Of course, I did that.

I save result of *.php to temp.html and then covert it to pdf. It works :)

But I've two questions:

1) Now we use attribute "id" for bind. But my users edit texts in wysiwyg editors, and they only select h1,h2 and so on. Can I bind another way? not use ids pr another attributes.

2) I use jquery for changing in html then convert pdf. Now I want to use ajax for loading some data to div block. Can prince do that? :) render with ajax-loading content?
mikeday
I'm not sure what you mean by binding to an ID. Is this related to the TOC script described in this forum topic? If not, it may be better to start a new forum topic for the new problem.

Prince 8.1 does not support AJAX, but we are planning to support it in the next major release of Prince.
BadSanta
if i use:
 <h1 id="firstId">First</h1>

    <p>Hi, hello</p>

pdf result:
work.jpg

but when i use
 <h1>First</h1>

    <p>Hi, hello</p>

pdf result:
not_work.jpg


I only delete id attribute... how change TOC-script for working without attribute-id? :) thx.
  1. not_work.jpg13.5 kB
  2. work.jpg14.5 kB
mikeday
You will need to change the script to add an automatically generated id attribute to heading elements that don't have it.
BadSanta
I forgot clear javacsript :)

on jquery:
$('h1,h2,h3,h4,h5,h6').each(function (i,obj) {
        if ($(obj).attr('id')==undefined) { $(obj).attr('id','mineID_'+i); }
    });


mikeday, can you help me to do this without jquery?
mikeday
How about getElementsByTagName("h1"), iterate over the node list, and set the ID? eg.
var hs = document.getElementsByTagName("h1");

for (var i = 0; i < hs.length; ++i)
{
    if (!hs[i].id) hs[i].id = "h1_"+i;
}
BadSanta
mikeday, perfect! thx! :wink:

When will you add ajax-loading? In what version? :)
mikeday
Whatever the next big version is called, presumably 9.0. There are many other DOM interfaces that also need to be implemented for compatibility with browsers to support popular JavaScript libraries.
jqin
Mike, what’s the time frame for 9.0?

Our company is looking for a Web-to-Print tool and PrinceXML looks very promising. However our webpages are mostly AJAX loaded. We really hope AJAX support is coming soon since we like what Prince has to offer so far.

Is there any workarounds to convert an AJAX loaded web page for now?

Thank you. Nice job.
mikeday
No workarounds within Prince, you could use an external tool perhaps. We should be able to release a test build of Prince with AJAX support within the next couple of months.
mikeday
I forgot to update this thread, but Prince has supported a subset of XMLHttpRequest for some time now. :)
howcome
Also, we have a quick guide for making ToCs in Prince:

https://www.princexml.com/howcome/2021/guides/toc