#include <SPI.h>
#include <Adafruit_GFX.h>
#include <text.h>
#include <Fonts/FreeMonoBold9pt7b.h>
#include <Fonts/FreeSansBold9pt7b.h>
#define SCREEN_WIDTH 400
#define SCREEN_HEIGHT 300
#define BUFFER_SIZE (SCREEN_WIDTH * SCREEN_HEIGHT / 8)
#define CS_PIN 10
GFXcanvas1 display(SCREEN_WIDTH, SCREEN_HEIGHT);
size_t renderWrappedText(GFXcanvas1& display, const String& text, int16_t x, int16_t y, uint16_t maxWidth, uint16_t screenHeight, uint16_t leftMargin, uint16_t rightMargin, bool measureOnly = false) {
String currentLine = "";
String currentWord = "";
int16_t x1, y1;
uint16_t w, h;
size_t lastDisplayedIndex = 0;
maxWidth -= (leftMargin + rightMargin);
x += leftMargin;
// Get the line height once, outside the loop
display.getTextBounds("Ay", 0, 0, &x1, &y1, &w, &h);
int16_t lineHeight = h + 2;
for (size_t i = 0; i < text.length(); i++) {
if (text[i] == ' ' || text[i] == '\n' || i == text.length() - 1) {
if (i == text.length() - 1 && text[i] != ' ' && text[i] != '\n') {
currentWord += text[i];
}
display.getTextBounds(currentLine + currentWord, x, y, &x1, &y1, &w, &h);
// Check if we've run out of vertical space
if (y + lineHeight > screenHeight) {
return lastDisplayedIndex;
}
if (w > maxWidth) {
if (currentLine != "") {
if (!measureOnly) {
display.setCursor(x, y);
display.print(currentLine);
}
y += lineHeight;
lastDisplayedIndex = i - currentWord.length() - 1;
currentLine = "";
}
if (w > maxWidth && currentWord != "") {
// Word is too long, need to split it
String partialWord = "";
for (char c : currentWord) {
display.getTextBounds(partialWord + c, x, y, &x1, &y1, &w, &h);
if (w > maxWidth) {
// Check if we've run out of vertical space
if (y + lineHeight > screenHeight) {
return lastDisplayedIndex;
}
if (!measureOnly) {
display.setCursor(x, y);
display.print(partialWord);
}
y += lineHeight;
lastDisplayedIndex = i - (currentWord.length() - partialWord.length());
partialWord = String(c);
} else {
partialWord += c;
}
}
currentLine = partialWord + " ";
} else {
currentLine = currentWord + " ";
}
} else {
currentLine += currentWord + " ";
}
currentWord = "";
if (text[i] == '\n') {
// Check if we've run out of vertical space
if (y + lineHeight > screenHeight) {
return lastDisplayedIndex;
}
if (!measureOnly) {
display.setCursor(x, y);
display.print(currentLine);
}
y += lineHeight;
lastDisplayedIndex = i;
currentLine = "";
}
} else {
currentWord += text[i];
}
}
if (currentLine != "") {
// Final check if we've run out of vertical space
if (y + lineHeight <= screenHeight) {
if (!measureOnly) {
display.setCursor(x, y);
display.print(currentLine);
}
lastDisplayedIndex = text.length() - 1;
}
}
return lastDisplayedIndex;
}
size_t findPageStartIndex(GFXcanvas1& display, const String& text, size_t endIndex, int16_t x, int16_t y, uint16_t maxWidth, uint16_t screenHeight, uint16_t leftMargin, uint16_t rightMargin) {
size_t low = 0;
size_t high = endIndex;
size_t mid;
size_t bestFit = 0;
while (low <= high) {
mid = low + (high - low) / 2;
// Measure text from mid to endIndex
size_t lastRendered = renderWrappedText(display, text.substring(mid), x, y, maxWidth, screenHeight, leftMargin, rightMargin, true);
if (lastRendered + mid < endIndex) {
low = mid + 1;
} else {
high = mid - 1;
bestFit = mid;
if(mid == 0) break;
}
}
// Fine-tune the result, looking for whitespace
while (bestFit > 0) {
// Check if current position is whitespace
if (isspace(text[bestFit])) {
break;
}
bestFit--;
}
return bestFit;
}
void setup() {
Serial.begin(115200);
SPI.begin();
pinMode(CS_PIN, OUTPUT);
digitalWrite(CS_PIN, HIGH);
// Clear display
display.setRotation(3);
display.fillScreen(1);
display.setFont(&FreeSansBold9pt7b);
// Draw some text and shapes
display.setTextSize(1);
display.setTextColor(0);
display.setCursor(0, 20);
// display.println(TEXT);
size_t offset = 667 + 1;
size_t lastIndex =offset+ renderWrappedText(display, TEXT + offset, 0, 20, SCREEN_HEIGHT, SCREEN_HEIGHT, 10, 10);
Serial.print("Last Index: ");
Serial.println(lastIndex);
size_t pageStart = findPageStartIndex(display, TEXT, lastIndex, 0, 20, SCREEN_HEIGHT, SCREEN_HEIGHT, 10, 10);
Serial.print("Page start: ");
Serial.println(pageStart);
// Transmit buffer to the framebuffer chip
digitalWrite(CS_PIN, LOW);
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
SPI.transfer(display.getBuffer(), BUFFER_SIZE);
SPI.endTransaction();
digitalWrite(CS_PIN, HIGH);
Serial.println("Display updated");
}
void loop() {
// Nothing to do in the loop for this demo
}