1 /**
2 * Copyright © Novelate 2020
3 * License: MIT (https://github.com/Novelate/NovelateEngine/blob/master/LICENSE)
4 * Author: Jacob Jensen (bausshf)
5 * Website: https://novelate.com/
6 * ------
7 * Novelate is a free and open-source visual novel engine and framework written in the D programming language.
8 * It can be used freely for both personal and commercial projects.
9 * ------
10 * Module Description:
11 * This module handles text wrapping which is an essential feature when working with text that has to fit into a specific width. It's currently not a very performant algorithm but it gets the job done.
12 */
13 module novelate.textwrap;
14 
15 import std.uni : isWhite;
16 import std.conv : to;
17 
18 import dsfml.graphics : Text;
19 
20 import novelate.fonts;
21 
22 /**
23 * Creates text that wraps when it exceeds a given width. It does so by adding a new line at the last space given before the text exceeds the width and does so for all of the text.
24 * Params:
25 *   text = The text to wrap.
26 *   fontName = The font used for the text.
27 *   fontSize = The size of the font used.
28 *   width = The width to wrap the text at when it exceeds.
29 * Returns:
30 *   A string that is wrapable based on the original given string and conditions.
31 */
32 dstring wrapableText(dstring text, string fontName, uint fontSize, size_t width)
33 {
34   if (!text || !text.length)
35   {
36     return "";
37   }
38 
39   width -= (fontSize / 2);
40 
41   auto font = retrieveFont(fontName, FontStyle.normal);
42 
43   bool[] splitIndexes = new bool[text.length];
44   bool[] includeSplitters = new bool[text.length];
45 
46   dstring calculateText = "";
47 
48   auto lastWhiteIndex = 0;
49   bool hasForeignCharacters = false;
50 
51   foreach (ref i; 0 .. text.length)
52   {
53     dchar c = text[i];
54     bool isForeighnCharacter = cast(ushort)c > 128;
55 
56     if (!hasForeignCharacters && isForeighnCharacter)
57     {
58       width -= cast(uint)(cast(double)fontSize * 1.2);
59 
60       hasForeignCharacters = true;
61     }
62 
63     if (c.isWhite || isForeighnCharacter)
64     {
65       lastWhiteIndex = i;
66     }
67 
68     calculateText ~= c;
69 
70     Text textInstance = new Text();
71     textInstance.setFont(font);
72     textInstance.setString(calculateText);
73     textInstance.setCharacterSize(fontSize);
74 
75     auto textWidth = textInstance.getLocalBounds().width;
76 
77     if (textWidth >= width)
78     {
79       splitIndexes[lastWhiteIndex] = true;
80       includeSplitters[lastWhiteIndex] = isForeighnCharacter;
81       calculateText = "";
82 
83       i = lastWhiteIndex + 1;
84     }
85   }
86 
87   calculateText = "";
88 
89   foreach (i; 0 .. text.length)
90   {
91     dchar c = text[i];
92 
93     if (splitIndexes[i])
94     {
95       if (includeSplitters[i])
96       {
97         calculateText ~= c ~ to!dstring("\r\n");
98       }
99       else
100       {
101         calculateText ~= "\r\n";
102       }
103     }
104     else
105     {
106       calculateText ~= c;
107     }
108   }
109 
110   return calculateText;
111 }