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 novelate.fonts; 19 import novelate.external : ExternalText; 20 21 /** 22 * 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. 23 * Params: 24 * text = The text to wrap. 25 * fontName = The font used for the text. 26 * fontSize = The size of the font used. 27 * width = The width to wrap the text at when it exceeds. 28 * Returns: 29 * A string that is wrapable based on the original given string and conditions. 30 */ 31 dstring wrapableText(dstring text, string fontName, size_t fontSize, size_t width) 32 { 33 if (!text || !text.length) 34 { 35 return ""; 36 } 37 38 width -= (fontSize / 2); 39 40 auto font = retrieveFont(fontName, FontStyle.normal); 41 42 bool[] splitIndexes = new bool[text.length]; 43 bool[] includeSplitters = new bool[text.length]; 44 45 dstring calculateText = ""; 46 47 size_t lastWhiteIndex = 0; 48 bool hasForeignCharacters = false; 49 50 foreach (ref i; 0 .. text.length) 51 { 52 dchar c = text[i]; 53 bool isForeighnCharacter = cast(ushort)c > 128; 54 55 if (!hasForeignCharacters && isForeighnCharacter) 56 { 57 width -= cast(size_t)(cast(double)fontSize * 1.2); 58 59 hasForeignCharacters = true; 60 } 61 62 if (c.isWhite || isForeighnCharacter) 63 { 64 lastWhiteIndex = i; 65 } 66 67 calculateText ~= c; 68 69 auto textInstance = new ExternalText; 70 textInstance.setFont(font); 71 textInstance.setString(calculateText); 72 textInstance.setCharacterSize(fontSize); 73 74 auto textWidth = textInstance.bounds.x; 75 76 if (textWidth >= width) 77 { 78 splitIndexes[lastWhiteIndex] = true; 79 includeSplitters[lastWhiteIndex] = isForeighnCharacter; 80 calculateText = ""; 81 82 i = lastWhiteIndex + 1; 83 } 84 } 85 86 calculateText = ""; 87 88 foreach (i; 0 .. text.length) 89 { 90 dchar c = text[i]; 91 92 if (splitIndexes[i]) 93 { 94 if (includeSplitters[i]) 95 { 96 calculateText ~= c ~ to!dstring("\r\n"); 97 } 98 else 99 { 100 calculateText ~= "\r\n"; 101 } 102 } 103 else 104 { 105 calculateText ~= c; 106 } 107 } 108 109 return calculateText; 110 }