GithubHelp home page GithubHelp logo

typography's Introduction

Typography - Fonts, Typesetting and Rasterization.

⚠️ Typography has been integrated into Pixie, a full-featured 2D graphics library for Nim. This repo is no longer being actively worked on. ⚠️

Pixie supports more TrueType / OpenType font features as well as rich text layout and rasterizizing through spans.

Consider migrating projects from Typography to Pixie for additional functionality and future improvements. 🚀🚀🚀🚀🚀


nimble install typography

Run tests

API reference

About

Typography is pure nim implementation for font rasterization (letter drawing) and text typesetting (text layout). It does not rely on any external library such as FreeType, stb_truetype, pango or HarfBuzz.

See api reference: https://treeform.github.io/typography/typography.html

Font file formats:

  • SVG fonts - Most features are supported.
  • TTF fonts - Fair support. Most modern features are supported but font format came out in 1994 and has a bunch of formats for different OSes that are not supported.
  • OTF fonts - Basic TTF outline support only. No support for CFF or SVG outlines.

Requred Packages

  • vmath - vector stuff, vec2 and matrices.
  • pixie - image stuff, saving and loading PNGs.
  • chroma - color stuff, mostly to save and add rgba colors.
  • print - better logging.
  • bumpy - geometry stuff.
  • flatty - dealing with binary encoding.

Basic usage

var font = readFontSvg("fonts/Ubuntu.svg")
font.drawText(image, vec2(10, 50), "The quick brown fox jumps over the lazy dog.")

example output

var font = readFontTtf("fonts/Ubuntu.ttf")
font.drawText(image, vec2(10, 50), "The quick brown fox jumps over the lazy dog.")

example output

font.size = 8
font.drawText(image, vec2(10, 10), "The quick brown fox jumps over the lazy dog.")
font.size = 10
font.drawText(image, vec2(10, 25), "The quick brown fox jumps over the lazy dog.")
font.size = 14
font.drawText(image, vec2(10, 45), "The quick brown fox jumps over the lazy dog.")
font.size = 22
font.drawText(image, vec2(10, 75), "The quick brown fox jumps over the lazy dog.")

example output

font.drawText(image, vec2(10, 10), readFile("examples/sample.ru.txt"))

example output

Dealing with Glyphs

Each font has an table of glyphs.

font.glyphs["Q"]

And for each glyphs, you can see what the SVG path of a glyph looks like:

echo font.glyphs["Q"].path
M754,236 Q555,236 414,377  Q273,518 273,717  Q273,916 414,1057  Q555,1198 754,1198  Q953,1198 1094,1057  Q1235,916 1235,717  Q1235,593 1175,485  L1096,565  Q1062,599 1013,599  Q964,599 929,565  Q895,530 895,481  Q895,432 929,398  L1014,313  Q895,236 754,236  Z M1347,314  Q1471,496 1471,717  Q1471,1014 1261,1224  Q1051,1434 754,1434  Q458,1434 247,1224 Q37,1014 37,717  Q37,421 247,210  Q458,0 754,0  Q993,0 1184,143  L1292,35  Q1327,0 1376,0  Q1425,0 1459,35  Q1494,69 1494,118  Q1494,167 1459,201  Z

You can also draw this path to see all of the paths and all of the curve contorl points:

font.getGlyphOutlineImage("Q")

example output

Most of the time you would like to just get the image instead:

font.getGlyphImage("Q")

example output

You can then use this image in openGL, canvas, or even HTML.

Subpixel glyphs with subpixel layout:

Each glyphs can be rendered with a subpixel offset, so that it fits into the layout:

example output

Note how many of the "o"s and "m"s are different from each other. This happens because spaces between letters are not an integer number of pixels so glyphs must be rendred shifted by fraction of a pixel.

Here is how glyph changes with different subpixel offsets:

example output

var glyphOffset # this is an offset of the image from the 0,0 position
var image = font.getGlyphImage(glyph, glyphOffset, subPixelShift=X)

Typesetting

Before glyphs can be rendered they need to be typeset:

var layout = font.typeset("""
Two roads diverged in a yellow wood,
And sorry I could not travel both
And be one traveler, long I stood
And looked down one as far as I could
To where it bent in the undergrowth;""")

This produces a layout.

example output

Drawing the layout

You can then use the simple drawing included to draw to an image, or use some other graphical librarry like openGL, canvas, or even HTML:

image.drawText(layout)

example output

Wrapping and Clipping

You can also give the typeset region width and height so that text wraps and clips:

font.typeset(
  readFile("sample.wrap.txt"),
  pos=vec2(100, 20),
  size=vec2(300, 160)
)

example output

Alignment

There are 3 horizontal and 3 vertical alignment modes:

font.typeset("Center, Bottom",
  pos=vec2(20, 20),
  size=vec2(460, 160),
  hAlign=Center,
  vAlign=Bottom
)

example output

Selection

When selecting text is useful to know where to highlighting rectangles.

layout.getSelection(23, 120) # selects char 23 to char 120 (not glyphs)

example output

Picking

When clicking on text is useful to know where to highlighting what glyph and what is the string index.

layout.pickGlyphAt(vec2(120, 48)) # selects glyph at cordiantes

example output

Comparison to different OSs.

At the large font sizes (more then 24 pixels) the fonts on most operating system looks nearly identical. But when you scale the font below 24px different OSs take different approaches.

  • OSX - tries to render fonts most true to how font designer intended even if they look a blurry.
  • Windows - tries to render fonts to a pixel grid making them look sharper.
  • Linux - configurable and somewhere between the two.
  • iOS, Android - it really does not matter how the font is rendered because its almost always above 24px because of high resolution screens phones have.
   var font = readFontSvg("fonts/DejaVuSans.svg")
   font.size = 11 # 11px or 8pt
   font.drawText(image, vec2(10, 15), "The quick brown fox jumps over the lazy dog.")

Typography renderer - this library (4x):

example output

Apple Core Text renderer (4x):

example output

Paint.net renderer (4x):

example output

Bohemian Sketch renderer (4x):

example output

Window ClearType renderer (4x):

example output

How the font should look on screen is very subjective, some people love the crisp windows fonts, others swear by the apples adherence to design. But my opinion is it's all related a lot with familiarity. What you are used to is what you would like best, and when a person switches to a different screen with a different font rendering style brain immediately rejects it.

Subpixel Antialising is on its way out

About a decade ago use of Subpixel Antialising improved readability of fonts. It would leak a bit of color to the left and right of text because color pixels were not square. The monitors followed predictable pixel patterns first in CRTs then in LCDs.

example output

Then everything changed. Today our pixels small and they don't follow a typical CRT or LCD orientation.

example output

The fact that there is no standard pixel layout grid anymore. And the fact that high resolution displays are everywhere makes subpixeling obsolete. Apple, Adobe, Bohemian and others in the typography space are abandoning subpixeling.

This library does not support Subpixel Antialising.

Neat tricks with Subpixel rendering

Apple removes Subpixel Antialising

Text Boxes

Full backend implementation of a text area. You need to connect your own rendering, keyboard and mouse input.

Often when displaying text you also need to edit text. This is where the textbox part of this library comes in. This implemented the backend of a text box/text area/input element/text field. Text boxes are surprisingly hard to implement right because the users are very familiar with how they work, so any missing features or inconsistencies are painfully obvious.

Here is a small list of some of the features:

  • Typing, arrow keys, backspace and delete.
  • Mouse clicking, dragging to select.
  • Almost everything can be used to select with shift.
  • Double click to select word, triple click to select paragraph, quad click to select all.
  • Page up and page down.
  • Move left/right by word.
  • Move to start or end of lines.
  • Copy, Cut and Paste.
  • Scrolling and scroll to cursor when typing or selecting.
  • Resizing of the text box while it’s being added.
  • Remember your horizontal position when going up or down with short lines.

example output

example output

How to convert any font to SVG font using FontForge:

SVG fonts are really nice. The are simple to parse and understand and debug. They are very uncommon though. But they are good as a debug input, output, or intermediate step.

$ fontforge -c 'Open($1); Generate($2)' foo.ttf foo.svg

typography's People

Contributors

anuken avatar chr-1x avatar cxong avatar dom96 avatar guzba avatar jiro4989 avatar rthai-stripe avatar treeform avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

typography's Issues

Text width computation

It would be very useful to have an API that could easily compute the width of a string of text in pixels.

Error when calling up() while in first line of an empty textbox.

I got this error while using fidget.
When up() is called while the cursor is in the first line of an empty textbox an error occurs.
In fidget this happens when pressing the up key in an empty box before writing anything.

D:\Programming\nim\nhGui\src\nhGui.nim(159) nhGui
C:\Users\manni\.nimble\pkgs\fidget-0.2.1\fidget\backendopengl.nim(284) startFidget
C:\Users\manni\.nimble\pkgs\fidget-0.2.1\fidget\openglbackend\base.nim(57) tick
C:\Users\manni\.nimble\pkgs\fidget-0.2.1\fidget\openglbackend\base.nim(238) onSetKey
C:\Users\manni\.nimble\pkgs\typography-0.2.3\typography\textboxes.nim(327) up
C:\Users\manni\.choosenim\toolchains\nim-1.0.4\lib\system\fatal.nim(39) sysFatal
Error: unhandled exception: index out of bounds, the container is empty [IndexError]

The error seems to be that up() tries to acces the first glyph (layout[0]) wich doesn't exist.

 elif textBox.cursorPos.y == textBox.layout[0].selectRect.y:
    # are we on the first line? then jump to start location 0
    textBox.cursor = 0

The same thing happens with down().
In that case checking if the layout seq is empty will probably be enough.

elif textbox.layout.len != 0 and textBox.cursorPos.y == textBox.layout[^1].selectRect.y:
    # are we on the last line? then jump to start location last

I would submit a PR but i don't know how to fix the first problem.

Compiler error while trying to launch fidget

trying to launch fidget with: nim c -r minimal
getting:
/home/artemonster/.nimble/pkgs/typography-0.7.12/typography/font.nim(96, 28) Error: type mismatch: got
but expected one of:
macro .(v: GVec234; fields: untyped): untyped
first type mismatch at position: 1
required type for v: GVec234
but expression 'glyph.path' is of type: Path
proc commandsToShapes(glyph: Glyph)
first type mismatch at position: 1
required type for glyph: Glyph
but expression 'glyph.path' is of type: Path

expression: commandsToShapes(glyph.path)

Nim version:
artemonster@artemonster:~/nim/fidget/examples/minimal$ nim -v
Nim Compiler Version 1.6.0 [Linux: amd64]
Compiled at 2021-10-19
Copyright (c) 2006-2021 by Andreas Rumpf

git hash: 727c6378d2464090564dbcd9bc8b9ac648467e38
active boot switches: -d:release

Unsatisfied dependency: nim (>= 0.18.1), latest release is 0.18.0

Hello @treeform, nice package you're working on.
I'm trying to see if I could use it in a little project I have in mind..

> nimble install typography
Downloading https://github.com/treeform/typography.git using git
  Verifying dependencies for [email protected]
       Tip: 3 messages have been suppressed, use --verbose to show them.
     Error: Unsatisfied dependency: nim (>= 0.18.1)

Is this intentional?

`drawText()` creates white on transparent text

I noticed you used the alphaWhite() procedure in the examples, but I can't find it in flippy (at least not anymore). The text appears white inside of a transparent rectangle (I'd like for it to be black in a white rectangle).

Am I doing something wrong?

Edit: I just realized that I glossed right over the "custom" alpha white proc in the example, haha. Sorry

how to get itterable objects like seq

hi, i am new nim user. As a practice, i am making a simple drawing library "github NimDraw" using nimPNG
i want to add text support for it. I have learned that i need to make rasterization of a text before applying it to an image. in the package i have Canvas type which is actually seq of pixels. if i can get pixel coordination (e.g. start point (0,0) ) from typography i can use for loop to place pixel with selected color on the canvas.

so how can get pixel coordination using typography.
thanks in advance.

Unable to import typography threw fidget

Hi, when I try to import fidget or compile one of the fidget examples, the compiler yells at me

/home/gabriel/.nimble/pkgs/typography-0.7.11/typography/rasterizer.nim(214, 23) Error: type mismatch: got <PathCommand>
but expected one of:
macro `.`(v: GVec234; fields: untyped): untyped
  first type mismatch at position: 1
  required type for v: GVec234
  but expression 'c' is of type: PathCommand

expression: numbers(c)

Im using an updated artix linux

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.