Entwickler-Ecke

Beschreibungssprachen - Text bricht je nach Zoomstufe unterschiedlich um


Yogu - Mo 19.08.13 17:42
Titel: Text bricht je nach Zoomstufe unterschiedlich um
Hallo,

wir entwickeln eine Webseite, mit der Nutzer eine DIN-A5-Seite gestalten können, vor allem mit formatiertem Text, Bildern und Tabellen. Diese A5-Seiten sollen anschließend auf den verschiedensten Geräten angezeigt werden.

Der offensichtliche Ansatz ist ein WYSIWYG-Editor, der auf eine konstante Größe beschränkt und einfach HTML-Code ausspuckt. Das funktioniert soweit auch schon wunderbar.

Diesen erzeugten HTML-Code möchten wir dynamisch verkleinern oder vergrößern, um die Seite auf das entsprechende Zielgerät (großer Bildschirm, kleiner Bildschirm, Smartphone etc.) anzupassen. Dafür geben wir alle Größen in em an, und modifzieren dann einfach die Schriftgröße des Wurzelelements. Das funktioniert auch.

Das Problem ist nur, dass der Text nicht perfekt proportional mit der Zoomstufe wächst/schrumpft, sondern in unterschiedlich großen Schritten. Das summiert sich auf die Breite einer Zeile zu einigen Pixeln auf, sodass mal ein Wort mehr, mal eines weniger in eine Zeile passt. Am Ende der Seite springen schließlich einige Wörter hin- und her, wenn man die Zoomstufe langsam verändert.

zur Illustration:

small-zoom large-zoom

Das letzte "mehr" passt bei der großen Zoomstufe nicht mehr in die dritte Zeile und wird umgebrochen.

Dieses Phänomen tritt übrigens auch auf, wenn man einfach die Zoomfunktion des Browsers (Firefox oder Chrome) verwendet, und nicht nur bei unserer Seite, sondern auch bei vielen anderen Webseiten. Es ist für uns allerdings besonders relevant, weil dem Beitrag nur ein bestimmter Platz zur Verfügung steht und der Autor diesen optimal ausfüllen will. Wenn der Beitrag bei der Anzeige dann anders umbricht, ist eventuell das gesamte Layout zerstört.

Der offensichtliche Fehler ist, HTML für ein statisches Seitenlayout zu verwenden. Besser geeignet wären dafür natürlich PDF oder SVG. Allerdings möchten wir die WYSIWYG-Funktionen der Browser verwenden, und das geht nunmal nur mit HTML.

Wir müssen also entweder die Browser dazu bringen, die Rundungsfehler beim Zoomen zu vermeiden, oder Zeilenumbrüche irgendwie fixieren.

Den zweiten Ansatz, Zeilenumbrüche fixieren, habe ich schon testweise umgesetzt: HTML -> PDF -> HTML konvertieren. Geht mit wkhtmltopdf [http://code.google.com/p/wkhtmltopdf/] und pdf2htmlEX [https://github.com/coolwanglu/pdf2htmlEX]. Beides sind sehr gute, aktuelle Bibliotheken, und das Ergebnis sieht auch gut aus, aber es entfernt jegliche HTML-Semantik wie z.B. Absätze oder Listen (es wandelt Aufzählungszeichen in Bitmaps um)

Habt ihr weitere Ideen? Seid ihr schonmal über dieses Problem gestolpert? Google kennt es anscheinend überhaupt nicht.

Vielen Dank fürs durchlesen ;)

Grüße,
Yogu


IhopeonlyReader - Mo 19.08.13 21:11

ganz billig, kann man ein "bild" davon machen und das vergrößern/verkleinern.
Nachteil: Jeglicher Text geht verloren, da es nurnoch bildpixel sind. kopieren etc ist somit nicht mehr möglich..

ansonten könntest du testen ob die wörter verschoben wurden und wenn ja die zoom-stufe zurücksetzten..

also z.B. bei zoomstufe 1,2,3,5,6 tritt der "mehr" fehler nicht auf, bei 4 schon dann "überspringst" du die zoomstufe 4..
Wie das möglich ist weiß ich nicht, aber ich versuche mal dir meine Ansätze zu überbringen


Yogu - Di 20.08.13 01:42

Hallo,

danke für deine Antwort, IhopeonlyReader. Den ganzen Beitrag in ein Bild umzuwandeln wäre keine schöne Sache.

"Schlechte" Zoomstufen zu überspringen ist prinzipiell eine gute Idee. Allerdings sind diese ganzen Rundungsfehler (man nennt es wohl kerning)* sehr schwer zu berechnen und variieren anscheinend auch zwischen Browsern.

Mein aktueller Ansatz ist es, im Editor <br>s nach jedem (automatischen) Zeilenwechsel einzufügen und den Beitrag dann mit white-space: pre; anzuzeigen. Beim nächsten Bearbeiten werden diese automatisch generierten <br>s dann wieder entfernt. Somit sollten die Zeilenumbrüche unabhängig von Browser und Zoomstufe überall gleich sein. Nur, wenn man einen Beitrag in Browser A erstellt und Browser B editiert, könnte das Problem wieder auftreten. Dann hat man jedoch die Gelegenheit, den Beitrag entsprechend anzupassen.

Ich melde mich wieder, wenn ich Fortschritte gemacht habe.

Nach bessren Alternativen suche ich weiterhin, denn das ist eher ein Workaround als eine saubere Lösung.

Grüße,
Yogu

* Kerning [http://de.wikipedia.org/wiki/Unterschneidung_%28Typografie%29] ist doch etwas anderes.


jasocul - Di 20.08.13 11:58

Ich habe gerade ein ähnliches Problem, allerdings mit TRichEdit-Feldern in Delphi. Das Grundproblem ist aber exakt das selbe. Alle Ansätze die ich bisher angefangen habe, scheitern in der Regel an den Rundungsproblemen. Also genau, wie bei dir.

Jetzt habe ich das ein paar Tage ruhen lassen und hatte spontan eine neue Idee, die mir vielversprechend erscheint und auch bei dir zur Lösung führen kann. (Denke ich zumindest)

Vor dem Zoom merke ich mir die Anzahl der Zeilen und für jede Zeile die Anzahl der Zeichen pro Zeile.
Dann sehe ich die Zeilen durch und verändere je nach Veränderung die Breite des Text-Bereiches pixelweise, bis es für die betroffene Zeile passt. Danach muss ich natürlich wieder von vorne anfangen.

Wie gesagt, ist das bisher nur eine Idee, aber ich wollte da heute Nachmittag ein paar Tests machen, ob das funktioniert. Vielleicht muss ich mit Toleranzen arbeiten, damit ich da nicht in einer Endlos-Schleife lande.

Vielleicht hilft dir die Idee ja auch weiter. Kenne mich mit Web-Programmierung eher nur rudimentär aus. :wink:


Tilo - Di 20.08.13 12:29

Hallo Yogu,

Zur Vermeidung von ungewollten Umbrüchen könntest Du es mal mit geschützten Leerzeichen [http://de.wikipedia.org/wiki/Gesch%C3%BCtztes_Leerzeichen] versuchen.

Eine weitere Möglichkeit wäre beim Editieren einen "virtuellen" Rahmen um den Text zu erstellen und etwas früher einen Umbruch zu erwzingen. Damit wäre bei einer Darstellungskombination die den Text etwas breiter darstellt noch genügend Platz vorhanden um einen ungewollten Umbruch zu vermeiden.

MFG,
Tilo


jasocul - Di 20.08.13 13:47

Nur zur Info:
Meine Idee hat funktioniert. Es gibt aber trotzdem vereinzelt Grenzfälle, die nicht 100%ig passen.


Yogu - Mo 26.08.13 21:58

user profile iconjasocul hat folgendes geschrieben Zum zitierten Posting springen:
Ich habe gerade ein ähnliches Problem, allerdings mit TRichEdit-Feldern in Delphi. Das Grundproblem ist aber exakt das selbe. Alle Ansätze die ich bisher angefangen habe, scheitern in der Regel an den Rundungsproblemen. Also genau, wie bei dir.

Ah! Ich dachte schon, ich wäre alleine mit diesem Problem. Habe die Frage auch auf StackOverflow [http://stackoverflow.com/questions/18321659/line-break-depends-on-font-size] gepostet, und dort praktisch nur zu hören bekommen, dass der Ansatz zum Scheitern verurteilt ist.

user profile iconjasocul hat folgendes geschrieben Zum zitierten Posting springen:
Vor dem Zoom merke ich mir die Anzahl der Zeilen und für jede Zeile die Anzahl der Zeichen pro Zeile.
Dann sehe ich die Zeilen durch und verändere je nach Veränderung die Breite des Text-Bereiches pixelweise, bis es für die betroffene Zeile passt. Danach muss ich natürlich wieder von vorne anfangen.

Das ist meine Lösungr sehr ähnlich. Wenn meine scheitert, probiere ich sie vielleicht mal aus.

Falls jemand an meinem Code interessiert ist:


JavaScript-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
      function getTextNodesIn(node, includeWhitespaceNodes) {
          var textNodes = [], whitespace = /^\s*$/;

          function getTextNodes(node) {
              if (node.nodeType == 3) {
                  if (includeWhitespaceNodes || !whitespace.test(node.nodeValue)) {
                      textNodes.push(node);
                  }
              } else {
                  for (var i = 0, len = node.childNodes.length; i < len; ++i) {
                      getTextNodes(node.childNodes[i]);
                  }
              }
          }

          getTextNodes(node);
          return textNodes;
      }
      
      function addLinebreaks(root) {
        var elems = $('p, div, li, blockquote, td, th, h1, h2, h3', root);
        elems.each(function() {
          var elem = $(this);

          // Wrap words into <span>s
          var textNodes = getTextNodesIn(elem[0]);
          for (var i = 0; i < textNodes.length; i++) {
            var node = textNodes[i];
            var text = node.textContent;
            var letters = [];
            for (var j = 0; j < text.length; j++) {
              letters.push($('<span>').addClass('letter').text(text[j]));
            }
            $(node).replaceWith(letters);
            var html = '';
            var oldY = -1;
              for (var j = 0; j < letters.length; j++) {
                var letter = letters[j];
                var y = letter.offset().top;
                if (oldY >= 0 && y != oldY) {
                  html += '<br class="auto" />';
                }
                html += letter.html(); // escape
              oldY = letter.offset().top;
              }
              $(letters[0]).replaceWith(html);
              $(letters).each(function() { $(this).remove()});
          }
        });
        console.log('added linebreaks');
      }
      
      function removeLinebreaks(root) {
        $('br.auto', root).remove();
      }

Die root-Parameter sind jQuery-Objekte des HTML-Elements, dessen Zeilenumbrüche fixiert werden sollen.

Es funktioniert bei allen bisher getesteten Dokumenten perfekt; vor allem bei sehr vielen Bildern bin ich mir aber nicht sicher, ob es immer klappt.


user profile iconTilo hat folgendes geschrieben Zum zitierten Posting springen:
Zur Vermeidung von ungewollten Umbrüchen könntest Du es mal mit geschützten Leerzeichen [http://de.wikipedia.org/wiki/Gesch%C3%BCtztes_Leerzeichen] versuchen.

Ist im Prinzip äquivalent zu <br>-Tags und white-space: nowrap;.

user profile iconTilo hat folgendes geschrieben Zum zitierten Posting springen:
Eine weitere Möglichkeit wäre beim Editieren einen "virtuellen" Rahmen um den Text zu erstellen und etwas früher einen Umbruch zu erwzingen. Damit wäre bei einer Darstellungskombination die den Text etwas breiter darstellt noch genügend Platz vorhanden um einen ungewollten Umbruch zu vermeiden.

Das Problem ist, dass dann möglicherweise zu wenig umgebrochen wird und das Layout doch wieder anders aussieht. Wir möchten, dass die Autoren ihren Beitrag in einem echten WYSIWYG-Editor gestalten können.

Danke für alle Meldungen!

Viele Grüße,
Yogu