7.8 KiB
Font Bounds System for Jumperless OLED
This system provides comprehensive font measurement and text positioning capabilities for the 128x32 pixel OLED display on the Jumperless V5.
Features
✅ Pixel-perfect text positioning
✅ Automatic text centering and alignment
✅ Text fit validation
✅ Smart word wrapping
✅ Multi-font support
✅ Character-level analysis
✅ Font metrics calculation
Quick Start
- Include the header in your firmware:
#include "examples/font_bounds_example.h"
- Basic usage:
// Check if text fits
if (oled.textFits("Hello World")) {
// Center the text
int16_t x, y;
oled.getCenteredPosition("Hello World", &x, &y);
oled.setCursor(x, y);
oled.print("Hello World");
oled.show();
}
Core Functions
Text Measurement
getTextBounds(const char* str)
Returns detailed measurements for any text string.
TextBounds bounds = oled.getTextBounds("Sample Text");
// bounds.width, bounds.height, bounds.x1, bounds.y1, bounds.x2, bounds.y2
// bounds.baseline, bounds.ascent, bounds.descent
getCharBounds(char c)
Get metrics for individual characters.
CharBounds bounds = oled.getCharBounds('A');
// bounds.width, bounds.height, bounds.xAdvance, bounds.xOffset, bounds.yOffset
Text Positioning
getCenteredPosition(const char* str, int16_t* x, int16_t* y)
Calculate perfect center position for text.
int16_t x, y;
oled.getCenteredPosition("Centered Text", &x, &y);
oled.setCursor(x, y);
oled.print("Centered Text");
getCenteredX(const char* str, int16_t* x)
Center horizontally only.
int16_t x;
oled.getCenteredX("Horizontal Center", &x);
oled.setCursor(x, 16); // Your Y position
oled.print("Horizontal Center");
Text Fitting
textFits(const char* str)
Check if text fits anywhere on display.
if (oled.textFits("Long text message")) {
// Display normally
} else {
// Use word wrapping or smaller font
}
textFitsAt(const char* str, int16_t x, int16_t y)
Check if text fits at specific position.
if (oled.textFitsAt("Status", 0, 24)) {
oled.setCursor(0, 24);
oled.print("Status");
}
Word Wrapping
wrapText(const char* text)
Automatically break long text into lines.
std::vector<String> lines = oled.wrapText("Very long text that needs to be wrapped");
int lineHeight = oled.getFontHeight(oled.currentFont);
for (int i = 0; i < lines.size(); i++) {
oled.setCursor(0, i * lineHeight);
oled.print(lines[i]);
}
Font Analysis
getFontMetrics()
Get comprehensive font information.
FontMetrics metrics = oled.getFontMetrics();
// metrics.lineHeight, metrics.ascent, metrics.descent
// metrics.maxWidth, metrics.avgWidth, metrics.spaceWidth
getMaxLines()
and getCharsPerLine()
Calculate layout constraints.
int maxLines = oled.getMaxLines(); // How many lines fit
int charsPerLine = oled.getCharsPerLine(); // Approximate chars per line
Data Structures
TextBounds
struct TextBounds {
int16_t width, height; // Total dimensions
int16_t x1, y1, x2, y2; // Bounding box corners
int16_t baseline; // Baseline position
int16_t ascent, descent; // Font metrics
};
CharBounds
struct CharBounds {
int16_t width, height; // Character dimensions
int16_t xAdvance; // Cursor movement
int16_t xOffset, yOffset; // Position offsets
int16_t x1, y1, x2, y2; // Bounding box
};
FontMetrics
struct FontMetrics {
int16_t lineHeight; // Line spacing
int16_t ascent; // Max height above baseline
int16_t descent; // Max depth below baseline
int16_t maxWidth; // Widest character
int16_t avgWidth; // Average character width
int16_t spaceWidth; // Space character width
};
Practical Examples
Smart Status Display
void displayStatus(const char* title, const char* status, int value) {
oled.clear();
// Centered title at top
int16_t x, y;
oled.getCenteredX(title, &x);
TextBounds titleBounds = oled.getTextBounds(title);
oled.setCursor(x, titleBounds.ascent + 2);
oled.print(title);
// Left-aligned status
oled.setCursor(0, 16);
oled.print(status);
// Right-aligned value
String valueStr = String(value);
TextBounds valueBounds = oled.getTextBounds(valueStr.c_str());
oled.setCursor(SCREEN_WIDTH - valueBounds.width, 16);
oled.print(valueStr);
oled.show();
}
Adaptive Font Selection
void smartDisplay(const char* message) {
// Try fonts from largest to smallest
bool displayed = false;
for (int fontIndex = numFonts - 1; fontIndex >= 0 && !displayed; fontIndex--) {
oled.setFont(fontIndex);
if (oled.textFits(message)) {
int16_t x, y;
oled.getCenteredPosition(message, &x, &y);
oled.setCursor(x, y);
oled.print(message);
displayed = true;
}
}
if (!displayed) {
// Use word wrapping with smallest font
oled.setFont(0);
std::vector<String> lines = oled.wrapText(message);
// ... display wrapped lines
}
}
Menu with Proper Spacing
void displayMenu(const char* items[], int count, int selected) {
oled.clear();
FontMetrics metrics = oled.getFontMetrics();
int lineHeight = metrics.lineHeight;
int maxItems = SCREEN_HEIGHT / lineHeight;
for (int i = 0; i < count && i < maxItems; i++) {
int y = i * lineHeight;
// Highlight selected item
if (i == selected) {
oled.setCursor(0, y);
oled.print(">");
}
oled.setCursor(8, y);
oled.print(items[i]);
}
oled.show();
}
Integration into Firmware
Option 1: Direct Integration
Add to your main firmware files:
#include "examples/font_bounds_example.h"
void setup() {
// Your setup code...
// Quick test
if (quickFontTest()) {
// Font bounds working
}
}
void loop() {
// Use font bounds functions as needed
if (oled.textFits(currentMessage)) {
int16_t x, y;
oled.getCenteredPosition(currentMessage, &x, &y);
oled.setCursor(x, y);
oled.print(currentMessage);
}
}
Option 2: App Integration
Add as an app in your app system:
void fontBoundsApp() {
fontBoundsMenu();
// Handle encoder input for menu selection
// Run different demonstrations based on selection
}
Example Functions Available
fontBoundsExample()
- Complete demonstrationtestAllFonts()
- Font comparisondisplayTextInfo(const char* text)
- Text analysisquickFontTest()
- Validation testfontBoundsMenu()
- Interactive menudemonstrateTextWrapping(const char* text)
- Wrapping demodemonstrateTextPositioning(const char* text)
- Alignment demo
Tips for Best Results
- Always check fit before displaying: Use
textFits()
ortextFitsAt()
- Use appropriate fonts: Different fonts for different purposes
- Consider word wrapping: For long messages
- Center important text: Use
getCenteredPosition()
- Account for baselines: Use
TextBounds.ascent
for proper positioning - Test with different content: Fonts behave differently with different characters
Display Constraints
- Screen: 128x32 pixels
- Available fonts: 4 (Eurostile, Berkeley, Jokerman, Jumperless)
- Font sizes: Various heights from ~10px to ~47px
- Max lines: Depends on font (2-3 lines typically)
- Max characters: Varies by font (~10-20 chars per line)
This system ensures your text always displays perfectly on the OLED, regardless of content length or font choice!