As Ƭᴇcʜιᴇ007's link describes, back in the days of old, this was done by specialized hardware in the graphics card called a character generator. The pixel pattern corresponding to each character would be stored in a ROM (or EPROM), at an address corresponding to the character's ASCII value (or other character code, since non-ASCII character sets were more common back then). You could change your font by replacing the character-ROM chip with one containing different bit patterns.
A fairly simple circuit in the graphics card then generates pixels on the fly by reading a byte from the display buffer, using that as an address to read a byte from the character ROM, and then shifting whatever it found there out the video port one bit at a time. Since the clock that runs that circuit is synched with the motion of the electron beam in the CRT (or vise versa), these bits correspond to visible pixels along one scan line. When the time comes to generate the next scan line, the circuit reads from the next row of character data in each ROM entry; or if it reaches the bottom of the character cell, it advances to the next line in display memory and wraps around to the first row of character memory again.
This maybe sounds more complicated than it is— it can be implemented with counters and simple state machines.
Bit-mapped displays are actually simpler: they simply read whatever's in display memory and stuff it out the video port without the intermediate lookup table. However, that obviously requires a lot more RAM, and RAM was really expensive.