Text in vorgegebenes Rechteck optimal einpassen?

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Text in vorgegebenes Rechteck optimal einpassen?

    Hallo,

    ich habe einen string (ohne newlines) bestehend aus einem oder mehreren wörtern.
    dieser text sollte in ein vorgegebenes rect eingepasst werden. erlaubt ist dabei den text umzubrechen und auch die größe zu verändern.

    irgendwie habe ich keinen performanten, sinnvollen ansatz dafür gefunden.
    am besten wäre natürlich eine Lösung mittels CoreText - aber da scheint es schon mal keine passenden funktionen zu geben.

    danke schon mal für euren input!
  • Hm,

    ich wüßte jetzt auch nicht ob es da einen performaten Algorythmus gibt.

    Eigentlich müßte man ja erstmal die Schriftgröße so klein (bzw maximalgroß) machen, dass alles in eine Zeile passt.
    Dann kann man schaun ob man irgendwo umbrechen kann und kann dann den Text größer machen bis die beiden neuen getrennten String in der Breite passen. Passen diese dann auch noch in der Höhe dann gehts weiter mit Zeile 3 etc. Aber ob das schnell genug ist?

    Gruß

    Claus
    2 Stunden Try & Error erspart 10 Minuten Handbuchlesen.

    Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)
  • die zeilen sollten natürlich alle die gleiche größe haben.

    und da die höhe und breite der wörter bzw zeichen ja direkt von der fontgröße abhängen könnte man zuerst mal berechnen wie man den text aufsplittet dass ein bestimmtes seitenverhältnis (das vom rect) entsteht. nachher kann man dann einfach die fontsize daraus berechnen.

    wie man das seitenverhältnis erreicht wäre dann halt zu berechnen.
    ich denke dass man sich raussucht welche blöcke es alles gibt (also wo man einen newline einfügen könnte) und dann die breite dazu ermittelt (einmal die breite wenn es am zeilenende stehen würde un einmal für nicht am zeilenende (space-char brauchts am zeilenende ja nicht).
    dann hätte man zahlenwerte für all die blocks und könnte es durchrechnen.
    nur dazu fehlt mir der algho und ich hab nicht wirklich lust mich da tiefer reinzudenken und das selbst zu implementieren wenn das schon jemand gelöst hat und es getestet/erporbt ist...
    performance-technisch sollte es von daher machbar sein, nur programmieraufwand wäre wohl erheblich!?
  • Musste los und bla. Also noch mal mein Gedanke:

    Das Einzige, was Du variieren kannst, ist die Textgrösse. Umbrechen musst Du, sobald eine Zeile zu lang ist - und wenn Du zuoft umgebrochen hast und zu hoch bist, weisst Du, dass die aktuelle Textgrösse nicht passt. Daher einfach iterativ die Textgrösse verringern, bis es passt - Länge und Höhe einer Zeile solltest Du ja einfach bekommen. Kannst auch binäre Suche machen, wenn Du noch ein wenig performanter sein willst.
    C++
  • Hab so etwas ähnlichen vor ein paar Tagen mal gebaut, allerdings sollte dabei der Zeilenabstand variabel gehalten werden, also mit einem max. Zeilenabstand anfangen und ggf. so lange verringern bis der Text ins Rechteck passt.

    Kann Dir die Methoden dafür gerne zur Verfügung stellen. Du musst dann halt nur die Schriftgröße verringern, wenn die berechnete Höhe nicht mehr ins vorgegebene Rechteck passt.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von MCDan ()

  • Wie typographisch anspruchsvoll und wie optimal soll "optimal" denn sein? Wenn es Zeilenumbruch a la Knuth & Plass und ein absolutes Optimum sein soll, wirst du kaum um viele Versuche herumkommen. Wenn ein einfacherer Umbruchalgorithmus und/oder ein lokales Minimum reicht sollte doch eine einfache Bisektion reichen, oder?
    Multigrad - 360°-Produktfotografie für den Mac
  • zerm schrieb:

    Musste los und bla. Also noch mal mein Gedanke:

    Das Einzige, was Du variieren kannst, ist die Textgrösse. Umbrechen musst Du, sobald eine Zeile zu lang ist - und wenn Du zuoft umgebrochen hast und zu hoch bist, weisst Du, dass die aktuelle Textgrösse nicht passt. Daher einfach iterativ die Textgrösse verringern, bis es passt - Länge und Höhe einer Zeile solltest Du ja einfach bekommen. Kannst auch binäre Suche machen, wenn Du noch ein wenig performanter sein willst.


    ok, so werd ichs mal umsetzen.
  • MCDan schrieb:

    Hab so etwas ähnlichen vor ein paar Tagen mal gebaut, allerdings sollte dabei der Zeilenabstand variabel gehalten werden, also mit einem max. Zeilenabstand anfangen und ggf. so lange verringern bis der Text ins Rechteck passt.

    Kann Dir die Methoden dafür gerne zur Verfügung stellen. Du musst dann halt nur die Schriftgröße verringern, wenn die berechnete Höhe nicht mehr ins vorgegebene Rechteck passt.


    der zeilenabstand soll immer gleich sein. ich denk mal ascent+descent+leading.
  • mattik schrieb:

    Wie typographisch anspruchsvoll und wie optimal soll "optimal" denn sein? Wenn es Zeilenumbruch a la Knuth & Plass und ein absolutes Optimum sein soll, wirst du kaum um viele Versuche herumkommen. Wenn ein einfacherer Umbruchalgorithmus und/oder ein lokales Minimum reicht sollte doch eine einfache Bisektion reichen, oder?


    typografisch korrekt, also so wie es CT rendert (kerning, ligaturen etc)
    umbruch werde ich einfach bei space einfügen.
    die einzelnen zeichen müssen nicht optisch gleich lang sein (zumindest ist das noch keine anforderung...)
  • gritsch schrieb:

    MCDan schrieb:

    Hab so etwas ähnlichen vor ein paar Tagen mal gebaut, allerdings sollte dabei der Zeilenabstand variabel gehalten werden, also mit einem max. Zeilenabstand anfangen und ggf. so lange verringern bis der Text ins Rechteck passt.

    Kann Dir die Methoden dafür gerne zur Verfügung stellen. Du musst dann halt nur die Schriftgröße verringern, wenn die berechnete Höhe nicht mehr ins vorgegebene Rechteck passt.


    der zeilenabstand soll immer gleich sein. ich denk mal ascent+descent+leading.


    Da ich den Text dann auch über CoreText gezeichnet habe reichte mir für die Ermittlung der Gesamthöhe die Anzahl der Zeilen * (Font Size + Zeilenabstand).

    Damit habe ich Text ja dann auch ausgegeben und es hat gepasst. ;)
  • MCDan schrieb:

    gritsch schrieb:

    MCDan schrieb:

    Hab so etwas ähnlichen vor ein paar Tagen mal gebaut, allerdings sollte dabei der Zeilenabstand variabel gehalten werden, also mit einem max. Zeilenabstand anfangen und ggf. so lange verringern bis der Text ins Rechteck passt.

    Kann Dir die Methoden dafür gerne zur Verfügung stellen. Du musst dann halt nur die Schriftgröße verringern, wenn die berechnete Höhe nicht mehr ins vorgegebene Rechteck passt.


    der zeilenabstand soll immer gleich sein. ich denk mal ascent+descent+leading.


    Da ich den Text dann auch über CoreText gezeichnet habe reichte mir für die Ermittlung der Gesamthöhe die Anzahl der Zeilen * (Font Size + Zeilenabstand). Damit habe ich Text ja dann auch ausgegeben und es hat gepasst. ;)


    und was meinst du mit zeilenabstand?
    das passt dann vielleicht bei einigen fonts, lass dirs aber zb mal in zapfino ausgeben... ;)
  • gritsch schrieb:

    typografisch korrekt, also so wie es CT rendert (kerning, ligaturen etc)
    umbruch werde ich einfach bei space einfügen.
    die einzelnen zeichen müssen nicht optisch gleich lang sein (zumindest ist das noch keine anforderung...)

    Dann würde ich einfach Intervalle schachteln und CT den Rest machen lassen, etwa wie angehängt. Oder denke ich da zu einfach?
    Dateien
    • FrameFit.zip

      (77,45 kB, 207 mal heruntergeladen, zuletzt: )
    Multigrad - 360°-Produktfotografie für den Mac
  • mattik schrieb:

    gritsch schrieb:

    typografisch korrekt, also so wie es CT rendert (kerning, ligaturen etc)
    umbruch werde ich einfach bei space einfügen.
    die einzelnen zeichen müssen nicht optisch gleich lang sein (zumindest ist das noch keine anforderung...)

    Dann würde ich einfach Intervalle schachteln und CT den Rest machen lassen, etwa wie angehängt. Oder denke ich da zu einfach?


    aja, das sieht schon mal sehr gut aus.
    und ich hab heut morgen schon angefangen das manuell durchzuackern ;)

    danke dir!
  • bin jetzt doch den manuellen weg zu ende gegangen. ist noch nicht fertig aber so in der art wirds wohl aussehen (falls es jemanden interessiert).

    Quellcode

    1. ​typedef struct TextPartInfo
    2. {
    3. CGFloat width1; // size with space
    4. CGFloat width2; // size without space
    5. } TextPartInfo;
    6. NSInteger remainingTextParts = [textParts count];
    7. TextPartInfo *textPartInfo = textPartInfos;
    8. for (NSString *tmpTextPart in textParts)
    9. {
    10. // fill textPartInfo using CTLineGetOffsetForStringIndex
    11. }
    12. CGFloat currentWidth = 0;
    13. CGFloat currentHeight = lineHeight;
    14. CGFloat factor = 1.0;
    15. textPartInfo = textPartInfos;
    16. TextPartInfo *textPartInfoEnd = textPartInfo + textPartsCount;
    17. while (textPartInfo != textPartInfoEnd)
    18. {
    19. if (currentWidth + (textPartInfo->width2 * factor) < maxWidth) // fits in line with space
    20. {
    21. currentWidth += (textPartInfo->width2 * factor);
    22. textPartInfo++;
    23. }
    24. else if (currentWidth + (textPartInfo->width1 * factor) < maxWidth) // fits in line without space -> go to new line
    25. {
    26. currentWidth = CGFLOAT_MAX; // fits in line but nothing else
    27. textPartInfo++;
    28. }
    29. else // go to new line
    30. {
    31. currentHeight += (lineHeight + leading) * factor;
    32. if (currentHeight > maxHeight || currentWidth < 0.01) // if it does not fit in height or the word does not fit in an empty line
    33. {
    34. textPartInfo = textPartInfos; // start from scratch
    35. factor *= 0.95; // with new factor
    36. currentHeight = lineHeight * factor;
    37. currentWidth = 0;
    38. }
    39. else
    40. {
    41. currentWidth = 0;
    42. }
    43. }
    44. }
    45. // factor is now the number that we needed.
    Alles anzeigen
  • mattik schrieb:

    gritsch schrieb:

    so ganz stimmt der code noch nicht. denn der tritt aus der schleife auch aus wenn der letzte nicht gefittet hat :-/

    Das macht nichts, weil min immer drin bleibt. Wenn mid im letzten Schritt nicht drin ist, wird min auch nicht raufgesetzt.


    nope, es wird nur korrekt gezeichnet weil du für die berechnung ein inset von -1 verwendet hast und es dann trotzdem wieder platz hat.

    aber egal. ich hab es gestern noch manuell hingeschustert.
    ist etwas performanter und ich hab dann auch gleich schon die glyphs (und dessen advances) die ich eh selbst zeichnen will.
  • Der Inset ist nur für die Beispielapp, weil der Text dort eben nicht mit CT, sondern mit den Cocoa-Mechanismen gezeichnet wird. In CT passt das Ergebnis immer, eben nochmal sicherheitshalber getestet. Das ist eben keine lineare Suche bis es passt, sondern Bisektion. Ist prinzipiell effizienter.

    Du kannst das natürlich machen wie du willst, aber ich habe meine Zweifel, das man es mal eben schnell eine händische Layoutlösung hinschreiben kann, die an die Qualität der eingebauten Mechanismen herankommt. Deine Implementierung geht z.B. davon aus, dass das Spacing proportional zur Schriftgröße ist, was nicht der Fall sein muss. Davon gibt's einiges mehr.
    Multigrad - 360°-Produktfotografie für den Mac

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von mattik ()