Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TinyMCE display as A4

I have a TinyMCE editor on my website and I would like to have the editable area (or the whole thing) displayed in A4 format.

Basically, I would like to view the document in the same way as in MS Word. (width, pagebreaks etc.)

Is that even possible? Please point me in the right direction.

like image 517
jakub.petr Avatar asked Apr 23 '15 15:04

jakub.petr


People also ask

How do I resize TinyMCE?

min_width. This option sets the minimum width that a user can stretch the entire TinyMCE interface (by grabbing the draggable area in the bottom right of the editor interface). This behavior is different from the autoresize plugin, which controls the resizing of the editable area only, not the entire editor.

How do I change the default font size in TinyMCE?

Font size selection The TinyMCE rich text editor comes with 7 font size options by default, ranging from 8pt to 36pt. Depending on how TinyMCE is configured, users can select a font from the menubar or toolbar (via the fontsizeselect dropdown). A user selects a font size from the fontsizeselect toolbar menu.

How do I get rid of powered by tiny?

Use the branding option to disable the “Powered by Tiny” link displayed in the status bar for product attribution. Important: Product attribution is required for free and open source users.


3 Answers

Everybody says it's difficult, but Google already done it in Google Docs (TIP: you could use Google API and even get the PDF version of your document. I didn't do this, because we needed extra functions in the editor.)

Here's my solution:

  1. I have resized the page to A4 width
  2. Added a ruler, that shows how much page is left (obviously not 100% reliable, but close). And even page numbers! Yes!

Thoughts:

Ruler is much easier than trying to show each page, which would mean to split up the contents... IT WOULD BE DOPE THOUGH... I had my attempts to do full A4 pages even using css clip, but it was messing with text selection, so I don't know... I wish I could do it, but...

The reason I used SVG inside HTML tag is because it's the only thing I can place there... if you select all text in TinyMCE you could erase my ruler, or even copy and paste... even if you used contenteditable="false"... the choices were limited.

See here my solution:

https://jsfiddle.net/mzvarik/59smpdv8/

// plugin pravítko
    tinymce.PluginManager.add('ruler', function(editor) {

        var domHtml;
        var lastPageBreaks;

        function refreshRuler()
        {
  console.log("ddd");
            try {
        domHtml = $( editor.getDoc().getElementsByTagName('HTML')[0] );

       // HACK - erase this, I have to put my CSS here
       console.log($('tinystyle').html() );
       domHtml.find('head').append( $('<style>'+$('tinystyle').html()+'</style>'));

            } catch (e) {
                return setTimeout(refreshRuler, 50);
            }

            var dpi = 96
            var cm = dpi/2.54;
            var a4px = cm * (29.7-5.7); // A4 height in px, -5.5 are my additional margins in my PDF print

            // ruler begins (in px)
            var startMargin = 4;

            // max size (in px) = document size + extra to be sure, idk, the height is too small for some reason
            var imgH = domHtml.height() + a4px*5;

            var pageBreakHeight = 14; // height of the pagebreak line in tinyMce

            var pageBreaks = [];
            domHtml.find('.mce-pagebreak').each(function(){
                pageBreaks[pageBreaks.length] = $(this).offset().top;
            });
            pageBreaks.sort();

            // if pageBreak is too close next page, then ignore it

            if (lastPageBreaks == pageBreaks) {
                return; // no change
            }
            lastPageBreaks = pageBreaks;

            console.log("Redraw ruler");

            var s = '';
            s+= '<svg width="100%" height="'+imgH+'" xmlns="http://www.w3.org/2000/svg">';

            s+= '<style>';
            s+= '.pageNumber{font-weight:bold;font-size:19px;font-family:verdana;text-shadow:1px 1px 1px rgba(0,0,0,.6);}';
            s+= '</style>';

            var pages = Math.ceil(imgH/a4px);

            var i, j, curY = startMargin;
            for (i=0; i<pages; i++)
            {
                var blockH = a4px;

                var isPageBreak = 0;
                for (var j=0; j<pageBreaks.length; j++) {
                    if (pageBreaks[j] < curY + blockH) {
                        // musime zmensit velikost stranky
                        blockH = pageBreaks[j] - curY;
                        // pagebreak prijde na konec stranky
                        isPageBreak = 1;
                        pageBreaks.splice(j, 1);
                    }
                }

                s+= '<line x1="0" y1="'+curY+'" x2="100%" y2="'+curY+'" stroke-width="1" stroke="red"/>';

                // zacneme pravitko
                s+= '<pattern id="ruler'+i+'" x="0" y="'+curY+'" width="37.79527559055118" height="37.79527559055118" patternUnits="userSpaceOnUse">';
                s+= '<line x1="0" y1="0" x2="100%" y2="0" stroke-width="1" stroke="black"/>';
                s+= '</pattern>';
                s+= '<rect x="0" y="'+curY+'" width="100%" height="'+blockH+'" fill="url(#ruler'+i+')" />';

                // napiseme cislo strany
                s+= '<text x="10" y="'+(curY+19+5)+'" class="pageNumber" fill="#ffffff">'+(i+1)+'.</text>';

                curY+= blockH;
                if (isPageBreak) {
                    //s+= '<rect x="0" y="'+curY+'" width="100%" height="'+pageBreakHeight+'" fill="#FFFFFF" />';
                    curY+= pageBreakHeight;
                }
            }

            s+= '</svg>';

            domHtml.css('background-image', 'url("data:image/svg+xml;utf8,'+encodeURIComponent(s)+'")');
        }
        editor.on('NodeChange', refreshRuler);
        editor.on("init", refreshRuler);

    });

tinymce.init({
plugins: "ruler pagebreak",
toolbar1: "pagebreak",
selector: 'textarea',
height: 300
});

Btw. Imagine Google would make free rich text editor!

CKEditor also can't do it and is paid, what a shame!

like image 178
Martin Zvarík Avatar answered Sep 18 '22 10:09

Martin Zvarík


It is possible, but hard, error prone and you won't get near MS Word. Maybe you can get it right for one font or so.

What you need to do is a custom CSS and a custom template. The template should resemble a grey background with the white page (with a shadow :). Define some buttons that will add custom classes to the template with Javascript and you will get the margin settings (narrow, wide, normal, no values). For the page break, you can insert a special <hr> that styles the underlying page template as if it ends and another one begins. Bear in mind you will have to replace almost all of your custom CSS in order to make it print-ready. Also, you should make tinymce fullscreen.

Another (very weird) approach that I've seen is a combination between tinymce and a PDF renderer library or equivalent. This way you'll get the WYSIWYG right.

Hope that helps.

like image 29
Alex Avatar answered Sep 18 '22 10:09

Alex


I modified the Martin's ruler. Thanks ruler

// plugin pravítko, modified by SerhatSoylemez
tinymce.PluginManager.add("editor-ruler", function(editor) {

  var domHtml;
  var lastPageBreaks;
  var pagen= tinymce.util.I18n.translate("p.");

  function refreshRuler() {
    try {
      domHtml = $(editor.getDoc().getElementsByTagName('HTML')[0]);
    } catch (e) {
      return setTimeout(refreshRuler, 50);
    }

    var dpi = 96
    var cm = dpi/2.54;
    var a4px = cm * (29.7); // A4 height in px, -5.5 are my additional margins in my PDF print

    // ruler begins (in px)
    var startMargin = 0;

    // max size (in px) = document size + extra to be sure, idk, the height is too small for some reason
    var imgH = domHtml.height() + a4px*5;

    var pageBreakHeight = 4; // height of the pagebreak line in tinyMce

    var pageBreaks = [];  // I changed .mce-pagebreak with .page-break !!!
    domHtml.find('.page-break').each(function() {
      pageBreaks[pageBreaks.length] = $(this).offset().top;
    });

    pageBreaks.sort();

    // if pageBreak is too close next page, then ignore it
    if (lastPageBreaks == pageBreaks) {
      return; // no change
    }

    lastPageBreaks = pageBreaks;

    // console.log("Redraw ruler");

    var s = '';
    s+= '<svg width="100%" height="'+imgH+'" xmlns="http://www.w3.org/2000/svg">';

    s+= '<style>';
    s+= '.pageNumber{font-weight:bold;font-size:20px;font-family:verdana;text-shadow:1px 1px 1px rgba(0,0,0,.6);}';
    s+= '</style>';

    var pages = Math.ceil(imgH/a4px);

    var i, j, curY = startMargin;
    for (i=0; i<pages; i++) {
      var blockH = a4px;

      var isPageBreak = 0;
      for (var j=0; j<pageBreaks.length; j++) {
        if (pageBreaks[j] < curY + blockH) {

          // musime zmensit velikost stranky
          blockH = pageBreaks[j] - curY;

          // pagebreak prijde na konec stranky
          isPageBreak = 1;
          pageBreaks.splice(j, 1);
        }
      }

      curY2 = curY+38;
      s+= '<line x1="0" y1="'+curY2+'" x2="100%" y2="'+curY2+'" stroke-width="1" stroke="red"/>';

      // zacneme pravitko
      s+= '<pattern id="ruler'+i+'" x="0" y="'+curY+'" width="37.79527559055118" height="37.79527559055118" patternUnits="userSpaceOnUse">';
      s+= '<line x1="0" y1="0" x2="100%" y2="0" stroke-width="1" stroke="black"/>';
      s+= '<line x1="24" y1="0" x2="0" y2="100%" stroke-width="1" stroke="black"/>';
      s+= '</pattern>';
      s+= '<rect x="0" y="'+curY+'" width="100%" height="'+blockH+'" fill="url(#ruler'+i+')" />';

      // napiseme cislo strany
      s+= '<text x="10" y="'+(curY2+19+5)+'" class="pageNumber" fill="#e03e2d">'+pagen+(i+1)+'.</text>';

      curY+= blockH;
      if (isPageBreak) {
        //s+= '<rect x="0" y="'+curY+'" width="100%" height="'+pageBreakHeight+'" fill="#ffffff" />';
        curY+= pageBreakHeight;
      }
    }

    s+= '</svg>';

    domHtml.css('background-image', 'url("data:image/svg+xml;utf8,'+encodeURIComponent(s)+'")');
  }

  function deleteRuler() {

    domHtml.css('background-image', '');
  }

  var toggleState = false;

  editor.on("NodeChange", function () {
    if (toggleState == true) {
      refreshRuler();
    }
  });


  editor.on("init", function () {
    if (toggleState == true) {
      refreshRuler();
    }
  });

  editor.ui.registry.addIcon("square_foot", '<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24" viewBox="0 0 24 24" width="24">'+
  '<g><rect fill="none" height="24" width="24"/></g><g><g><path d="M17.66,17.66l-1.06,1.06l-0.71-0.71l1.06-1.06l-1.94-1.94l-1.06,1.06l-0.71-0.71'+
  'l1.06-1.06l-1.94-1.94l-1.06,1.06 l-0.71-0.71l1.06-1.06L9.7,9.7l-1.06,1.06l-0.71-0.71l1.06-1.06L7.05,7.05L5.99,8.11L5.28,7.4l1.06-1.06L4,4'+
  'v14c0,1.1,0.9,2,2,2 h14L17.66,17.66z M7,17v-5.76L12.76,17H7z"/></g></g></svg>');

  editor.ui.registry.addToggleMenuItem("ruler", {
    text: "Show ruler",
    icon: "square_foot",
    onAction: function() {
      toggleState = !toggleState;
      if (toggleState == false) {
        deleteRuler();
      } else {
        refreshRuler();
      }
    },
    onSetup: function(api) {
      api.setActive(toggleState);
      return function() {};
    }
  });

});

function loadJavascript(url) {
    var script = document.createElement("script");
    script.src = url;
    document.head.appendChild(script);
}
loadJavascript("https://code.jquery.com/jquery-3.5.1.min.js");
like image 23
SerhatSoylemez Avatar answered Sep 20 '22 10:09

SerhatSoylemez