Vertical and horizontal center glyphs

Hi guys,

I’m a noob and I’ve made a sketch where by clicking you can shuffle through an alphabet’s glyphs and they get vertically and horizontally centered according to their size and the size of the sketch. I was stuck for a bit but I stumbled along untill I found some logic that works.

The sketch has an array that stores the different glyphs and I shuffle through them. Since I wanted to vertically center the glyphs and type glyphs have different heights, I figured I would need some code that could determine the height of each glyph. So I searched online and used some of the last code shared in this thread: https://forum.processing.org/two/discussion/19869/is-it-really-not-possible-to-determine-the-precise-height-of-a-text

With the height and width data being determined for each different glyph I then “position their midpoints” on the sketch’s midpoints by doing some simple math. Arriving at this solution took me a while. I worked casually on this over the weekend as an exercise.

Now I’m curious if there are better and simpler ways to making this work because along the way I got lost/confused and was missing some important info about the nuts and bolts of how the type glyphs and Processing work.

Could anyone share any other approaches?

Thanks

``````int i = 0; //change value to start at a different letter

char [] alphabet = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
};

void setup(){
size(600,600);
}

void draw(){

int xPos = width/2;   //horizontal position where you want the glyphs to be centered
int yPos = height/2;  //vertical position where you want the glyphs to be centered

int textSize = 400;
PFont typeface;
typeface = createFont("Arial",textSize);
PFont.Glyph glyph = typeface.getGlyph(alphabet[i]);
int t = glyph.topExtent;
int h = glyph.height;
int l = glyph.leftExtent;
int w = glyph.width;
int yMove = height/2-((yPos-t)+h/2);
int xMove = width/2-((xPos+l)+w/2);

background(255);
fill(0);

textFont(typeface);
textSize(textSize);
text(alphabet[i], width/2+xMove, height/2+yMove);

////////////////// lines for showing top limit and bottom limit of glyph
stroke(255,0,0);
line(0,(yPos-t)+yMove,width,(yPos-t)+yMove); //red line top of glyph
strokeWeight(5);
point((xPos+l)+xMove, (yPos-t)+yMove);
strokeWeight(1);
stroke(0,255,0);
line(0,((yPos-t)+h+yMove),width,((yPos-t)+h)+yMove); //green line height of glyph

//light blue line middle of glyph (helped during coding) comment out blue line (below) to see this one
stroke(0,255,255);
line(0,(((yPos-t)+h/2))+yMove,width,((yPos-t)+h/2)+yMove);

//////////////// line and point for showing center of "canvas"
strokeWeight(1);
stroke(0,0,255);
line(0,yPos,width,yPos); //blue line at vertical midpoint of canvas
strokeWeight(5);
point(width/2, height/2);
strokeWeight(1);

//////////////// glyph bounding box
noFill();
stroke(0,255,0);
rect((xPos+l)+xMove, (yPos-t)+yMove, w, h);

println(alphabet[i]+" "+"t:"+t+" "+" "+"h:"+h+" "+" "+"mouseY:"+mouseY);
println("yMove:"+yMove);
}

void mousePressed() {

////////////////// shuffle through glyphs in array
if(mouseX > width/2){
i = i+1;
} else {
i = i-1;
}

//////////////// ifs for controlling end behaviors when stepping through array
if(i == 26){
i = 0;
}

if(i < 0){
i = 25;
}

}
``````
1 Like

very cool program to demonstrate typography glyphs. A different way to code this would be to create a function for each glyph. For example, you have `strokeWeight()` being used multiple times with 1 parameter. Hope this answers your question. Good luck on your project! Hope to see 2.0

Interesting – thank you for sharing!

Yes, this definitely aligns the glyph differently than if you centered it using `textMode(CENTER,CENTER)`.

Your code has width/2 and height/2 (and many other terms) cancelling out everywhere. You should factor that out. For example:

1. `yPos = height/2` … so:
2. `yMove = height/2 - yPos + t + h/2` is actually…
`yMove = height/2 - height/2 + t + h/2`
`yMove = t + h/2`
3. Now look at this line:
`line(0,((yPos-t)+h+yMove),width,((yPos-t)+h)+yMove);`
[made an error when quickly unpacking, but I think you see the point – t cancels, et cetera]

Followup – I tried wrapping your approaches to glyph bounding boxes up in a class.

``````/**
* GlyphBoxes
* 2018-06 Processing 3.3.6
*/

PFont.Glyph glyph;
GlyphBox glyphbox;

int i = 0; // letter index
char [] alphabet = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
};

void setup() {
size(600, 600);
textFont(createFont("Arial", 400), 400);
glyphbox = new GlyphBox("Arial", 400, alphabet[i]);
glyphbox.align("CENTER"); // CENTER / BASELINE / NONE
}

void draw() {
background(255);
stroke(0);

// screen crosshairs
line(0, height/2.0, width, height/2.0);
line(width/2.0, 0, width/2.0, height);

// center the screen coordinates
translate(width/2, height/2);

// draw letter
fill(0);
glyphbox.text(0, 0);

// draw some glyph lines
noFill();
stroke(0, 0, 255);
glyphbox.boxbaseline();
stroke(255, 0, 0);
glyphbox.box();
stroke(0, 255, 0);
glyphbox.reticule();
}

class GlyphBox {
String fontName;
float textSize;
PFont typeface;
PFont.Glyph glyph;
char character;
String alignment = "";
float x = 0; // x offset based on extent
float y = 0; // y offset based on extent

GlyphBox (String fontName, float textSize, char c) {
this.fontName = fontName;
this.textSize = textSize;
this.typeface = createFont(fontName, textSize);
this.character = c;
this.glyph = typeface.getGlyph(c);
this.alignUpdate();
}

void align(String s) {
alignment = s;
alignUpdate();
}

void alignUpdate() {
if (alignment.equals("CENTER")) {
// adjust center by glyph width/height
x = -glyph.width/2.0;
y = -glyph.height/2.0;
// adjust center by glyph extent
x -= glyph.leftExtent;
y += glyph.topExtent;
} else if (alignment.equals("BASELINE")) {
y = 0;
// adjust center by glyph width/height
x = -glyph.width/2.0;
// adjust center by glyph extent
x -= glyph.leftExtent;
} else if (alignment.equals("NONE")) {
x = 0;
y = 0;
} else {
x = 0;
y = 0;
}
}

void setChar(char c) {
character = c;
glyph = typeface.getGlyph(c);
alignUpdate();
}

void text() {
this.text(0, 0);
}

void text(float tx, float ty) {
pushMatrix();
translate(x, y);
translate(0, 0);
pushStyle();
textFont(typeface);
textSize(400);
g.text(character, tx, ty);
popStyle();
popMatrix();
}

void box() {
rect(x + glyph.leftExtent, y + -glyph.topExtent, glyph.width, glyph.height);
}

void boxbaseline() {
topline();
baseline();
leftbaseline();
rightbaseline();
}

void reticule() {
midline();
centerline();
}

void reticulebaseline() {
midline();
centerbaseline();
}

void topline() {
line(x + glyph.leftExtent, y + -glyph.topExtent,
x + glyph.leftExtent + glyph.width, y + -glyph.topExtent);
}

void midline() {
line(x + glyph.leftExtent, y + -glyph.topExtent + glyph.height/2.0,
x + glyph.leftExtent + glyph.width, y + -glyph.topExtent + glyph.height/2.0);
}

void baseline() {
line(x + glyph.leftExtent, y,
x + glyph.leftExtent + glyph.width, y);
}

void bottomline() {
line(x + glyph.leftExtent, y + glyph.height-glyph.topExtent,
x + glyph.leftExtent + glyph.width, y + glyph.height-glyph.topExtent);
}

void leftbaseline() {
line(x + glyph.leftExtent, y + -glyph.topExtent,
x + glyph.leftExtent, y);
}

void leftline() {
line(x + glyph.leftExtent, y + -glyph.topExtent,
x + glyph.leftExtent, y + -glyph.topExtent + glyph.height);
}

void centerline() {
line(x + glyph.leftExtent + glyph.width/2.0, y + -glyph.topExtent,
x + glyph.leftExtent + glyph.width/2.0, y + -glyph.topExtent + glyph.height);
}

void centerbaseline() {
line(x + glyph.leftExtent + glyph.width/2.0, y + -glyph.topExtent,
x + glyph.leftExtent + glyph.width/2.0, y);
}

void rightbaseline() {
line(x + glyph.leftExtent + glyph.width, y + -glyph.topExtent,
x + glyph.leftExtent + glyph.width, y);
}

void rightline() {
line(x + glyph.leftExtent + glyph.width, y + -glyph.topExtent,
x + glyph.leftExtent + glyph.width, y + -glyph.topExtent + glyph.height);
}
}

void mousePressed() {
// shuffle through glyphs in array
if (mouseX > width/2) {
i = (i+1) % 26;
} else {
i = i-1;
if (i < 0) {
i = 25;
}
}
glyphbox.setChar(alphabet[i]);
}
``````

RE: textAlign – Note that I believe your approach (centering the glyph on its height) is not compatible with setting `textAlign(CENTER, CENTER)` in the main sketch. If you wanted to make the glyph boxes responsive to the global textAlign style you could actually use g.textAlign / textAlignY to detect the current mode – e.g. `if (g.textAlignY==CENTER)` – but I’m not actually sure how you would then alter GlyphBox.align to make assign correct offsets to a textAlign centered glyph so that the bounding box would line up.

1 Like