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.ui.textwrap; 14 15 import std.uni : isWhite; 16 import std.conv : to; 17 18 import novelate.fonts; 19 import novelate.external : ExternalText; 20 import novelate.buildstate; 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, size_t 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 size_t 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(size_t)(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 auto textInstance = new ExternalText; 71 textInstance.setFont(font); 72 textInstance.setString(calculateText); 73 textInstance.setCharacterSize(fontSize); 74 75 auto textWidth = textInstance.bounds.x; 76 77 static if (isManualMemory) 78 { 79 textInstance.clean(); 80 } 81 82 if (textWidth >= width) 83 { 84 splitIndexes[lastWhiteIndex] = true; 85 includeSplitters[lastWhiteIndex] = isForeighnCharacter; 86 calculateText = ""; 87 88 i = lastWhiteIndex + 1; 89 } 90 } 91 92 calculateText = ""; 93 94 foreach (i; 0 .. text.length) 95 { 96 dchar c = text[i]; 97 98 if (splitIndexes[i]) 99 { 100 if (includeSplitters[i]) 101 { 102 calculateText ~= c ~ to!dstring("\r\n"); 103 } 104 else 105 { 106 calculateText ~= "\r\n"; 107 } 108 } 109 else 110 { 111 calculateText ~= c; 112 } 113 } 114 115 return calculateText; 116 }