diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,4 +1,5 @@
 # Typograffiti
+[GitHub](https://github.com/schell/typograffiti/) [Hackage](https://hackage.haskell.org/package/typograffiti) [Mirror](https://git.argonaut-constellation.org/~alcinnz/typograffiti) [alt issue tracker](https://todo.argonaut-constellation.org/~alcinnz/typograffiti)
 Typograffiti aims to make working with text across a broad range of written languages in multimedia applications easy. Whilst exposing low-level APIs for use by fancier text layout/rendering engines.
 
 Typograffiti is part of [The Argonaut Stack](https://argonaut-constellation.org/) browser engine.
@@ -7,5 +8,6 @@
 * opengl 3.x
 * freetype 2.x
 * harfbuzz 3.3+
+* LibC++ (for Text dependency)
 
 The demo program additionally requires SDL2.
diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -32,14 +32,14 @@
     let text = pack $ case args of
           [] -> unlines [
             "Decoder Ring Theatre brings you the continuing adventures",
-            "of Canada's greatest superhero, that scourage of the underworld,",
+            "of Canada's greatest superhero, that scourge of the underworld,",
             "hunter of those who pray upon the innocent,",
             "that marvelous masked mystery man",
             "known only as The Red Panda!",
             "",
             "The Red Panda, masked crucader for justice, hides his secret identity",
             "as one of the city's wealthiest men in his neverending battle",
-            "against crime & corruption. Only his trust driver, Kit Baxter",
+            "against crime & corruption. Only his trusty driver, Kit Baxter",
             "who joins him in the guise of The Flying Squirrel,",
             "knows who wears the mask of The Red Panda!"]
           _ -> unwords args
diff --git a/src/Typograffiti/Atlas.hs b/src/Typograffiti/Atlas.hs
--- a/src/Typograffiti/Atlas.hs
+++ b/src/Typograffiti/Atlas.hs
@@ -57,8 +57,10 @@
 data GlyphMetrics = GlyphMetrics {
     glyphTexBB :: (V2 Int, V2 Int),
     -- ^ Bounding box of the glyph in the texture.
-    glyphSize :: V2 Int
+    glyphSize :: V2 Int,
     -- ^ Size of the glyph onscreen.
+    glyphOffset :: V2 Int
+    -- ^ Left & top bearings.
 } deriving (Show, Eq)
 
 -- | Cache of rendered glyphs to be composited into place on the GPU.
@@ -67,13 +69,15 @@
     -- ^ The texture holding the pre-rendered glyphs.
     atlasTextureSize :: V2 Int,
     -- ^ The size of the texture.
-    atlasMetrics :: IntMap GlyphMetrics
+    atlasMetrics :: IntMap GlyphMetrics,
     -- ^ Mapping from glyphs to their position in the texture.
+    atlasScale :: (Float, Float)
+    -- ^ Scaling factor for font-units given by Harfbuzz.
 } deriving (Show)
 
 -- | Initializes an empty atlas.
 emptyAtlas :: GLuint -> Atlas
-emptyAtlas t = Atlas t 0 mempty
+emptyAtlas t = Atlas t 0 mempty (0, 0)
 
 -- | Precomputed positioning of glyphs in an `Atlas` texture.
 data AtlasMeasure = AM {
@@ -148,11 +152,10 @@
             canon = floor . (* 0.5) . (* 0.015625) . realToFrac . fromIntegral
             vecsz = canon <$> V2 (gmWidth metrics) (gmHeight metrics)
             vecxb = canon <$> V2 (gmHoriBearingX metrics) (gmHoriBearingY metrics)
-            vecyb = canon <$> V2 (gmVertBearingX metrics) (gmVertBearingY metrics)
-            vecad = canon <$> V2 (gmHoriAdvance metrics) (gmVertAdvance metrics)
             mtrcs = GlyphMetrics {
                 glyphTexBB = (pos, pos + vecwh),
-                glyphSize = vecsz
+                glyphSize = vecsz,
+                glyphOffset = vecxb
               }
         return atlas { atlasMetrics = IM.insert (fromEnum glyph) mtrcs atlasMetrics }
     | otherwise = do
@@ -165,8 +168,8 @@
 -- might need during the life of the 'Atlas'. Character texturization only
 -- happens once.
 allocAtlas :: (MonadIO m, MonadFail m, MonadError TypograffitiError m) =>
-    GlyphRetriever m -> [Word32] -> m Atlas
-allocAtlas cb glyphs = do
+    GlyphRetriever m -> [Word32] -> (Float, Float) -> m Atlas
+allocAtlas cb glyphs scale = do
     AM {..} <- foldM (measure cb 512) emptyAM glyphs
     let V2 w h = amWH
         xymap = amMap
@@ -186,7 +189,7 @@
     glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MIN_FILTER GL_LINEAR
     glBindTexture GL_TEXTURE_2D 0
     glPixelStorei GL_UNPACK_ALIGNMENT 4
-    return atlas { atlasTextureSize = V2 w h }
+    return atlas { atlasTextureSize = V2 w h, atlasScale = scale }
 
 -- | Releases all resources associated with the given 'Atlas'.
 freeAtlas :: MonadIO m => Atlas -> m ()
@@ -202,23 +205,25 @@
     case IM.lookup iglyph atlasMetrics of
         Nothing -> throwError $ TypograffitiErrorNoMetricsForGlyph iglyph
         Just GlyphMetrics {..} -> do
-            let x = penx + f x_offset
-                y = peny + f y_offset
+            let x = penx + f x_offset*fst atlasScale
+                y = peny + f y_offset*snd atlasScale
                 V2 w h = f' <$> glyphSize
+                V2 dx dy = f' <$> glyphOffset
+                (x', y') = (x + dx, y - dy)
                 V2 aszW aszH = f' <$> atlasTextureSize
                 V2 texL texT = f' <$> fst glyphTexBB
                 V2 texR texB = f' <$> snd glyphTexBB
 
-                tl = (V2 (x) (y-h), V2 (texL/aszW) (texT/aszH))
-                tr = (V2 (x+w) (y-h), V2 (texR/aszW) (texT/aszH))
-                br = (V2 (x+w) y, V2 (texR/aszW) (texB/aszH))
-                bl = (V2 (x) y, V2 (texL/aszW) (texB/aszH))
+                tl = (V2 (x') (y'), V2 (texL/aszW) (texT/aszH))
+                tr = (V2 (x'+w) (y'), V2 (texR/aszW) (texT/aszH))
+                br = (V2 (x'+w) (y'+h), V2 (texR/aszW) (texB/aszH))
+                bl = (V2 (x') (y'+h), V2 (texL/aszW) (texB/aszH))
 
-            return (penx + f x_advance/150, peny + f y_advance/150,
+            return (penx + f x_advance*fst atlasScale, peny + f y_advance*snd atlasScale,
                     UV.fromList [tl, tr, br, tl, br, bl] : mLast)
   where
     f :: Int32 -> Float
-    f = fromIntegral
+    f =  fromIntegral
     f' :: Int -> Float
     f' = fromIntegral
 
diff --git a/src/Typograffiti/Store.hs b/src/Typograffiti/Store.hs
--- a/src/Typograffiti/Store.hs
+++ b/src/Typograffiti/Store.hs
@@ -31,8 +31,9 @@
                                         GlyphInfo(..), GlyphPos(..), FontOptions)
 import qualified Data.Text.Glyphize     as HB
 import qualified Data.Text.Lazy         as Txt
+import           Foreign.Storable       (peek)
 import           FreeType.Core.Base
-import           FreeType.Core.Types    (FT_Fixed)
+import           FreeType.Core.Types    (FT_Fixed, FT_UShort)
 import           FreeType.Format.Multiple (ft_Set_Var_Design_Coordinates)
 
 import           Typograffiti.Atlas
@@ -55,8 +56,12 @@
     -- ^ Font as represented by Harfbuzz.
     freetype :: FT_Face,
     -- ^ Font as represented by FreeType.
-    atlases :: TMVar [(IS.IntSet, Atlas)]
+    atlases :: TMVar [(IS.IntSet, Atlas)],
     -- ^ Glyphs from the font rendered into GPU atleses.
+    lineHeight :: Float,
+    -- ^ Default lineheight for this font.
+    fontScale :: (Float, Float)
+    -- ^ Scaling parameters for Harfbuzz layout.
   }
 
 -- | Opens a font sized to given value & prepare to render text in it.
@@ -67,8 +72,11 @@
     m (RichText -> n (AllocatedRendering [TextTransform]))
 makeDrawTextCached store filepath index fontsize SampleText {..} = do
     s <- liftIO $ atomically $ readTMVar $ fontMap store
-    font <- case M.lookup (filepath, fontsize, index, fontOptions) s of
-        Nothing -> allocFont store filepath index fontsize fontOptions
+    let fontOpts' = fontOptions {
+        HB.optionScale = Nothing, HB.optionPtEm = Nothing, HB.optionPPEm = Nothing
+      }
+    font <- case M.lookup (filepath, fontsize, index, fontOpts') s of
+        Nothing -> allocFont store filepath index fontsize fontOpts'
         Just font -> return font
 
     let glyphs = map (codepoint . fst) $
@@ -80,9 +88,10 @@
     a <- liftIO $ atomically $ readTMVar $ atlases font
     atlas <- case [a' | (gs, a') <- a, glyphset `IS.isSubsetOf` gs] of
         (atlas:_) -> return atlas
-        _ -> allocAtlas' (atlases font) (freetype font) glyphset
+        _ -> allocAtlas' (atlases font) (freetype font) glyphset (fontScale font)
 
-    return $ drawLinesWrapper tabwidth minLineHeight $
+    let lh = if minLineHeight == 0 then lineHeight font else minLineHeight
+    return $ drawLinesWrapper tabwidth lh $
         \RichText {..} -> drawGlyphs store atlas $
             shape (harfbuzz font) defaultBuffer { HB.text = text } []
 
@@ -104,8 +113,14 @@
     let designCoords = map float2fixed $ HB.fontVarCoordsDesign font'
     unless (null designCoords) $ liftIO $ ft_Set_Var_Design_Coordinates font designCoords
 
+    font_ <- liftIO $ peek font
+    size <- srMetrics <$> liftIO (peek $ frSize font_)
+    let lineHeight = fixed2float $ smHeight size
+    let upem = short2float $ frUnits_per_EM font_
+    let scale = (short2float (smX_ppem size)/upem/2, short2float (smY_ppem size)/upem/2)
+
     atlases <- liftIO $ atomically $ newTMVar []
-    let ret = Font font' font atlases
+    let ret = Font font' font atlases lineHeight scale
 
     atomically $ do
         map <- takeTMVar fontMap
@@ -114,15 +129,20 @@
   where
     x2 = (*2)
     float2fixed :: Float -> FT_Fixed
-    float2fixed = toEnum . fromEnum . (*65536)
+    float2fixed = toEnum . fromEnum . (*bits16)
+    fixed2float :: FT_Fixed -> Float
+    fixed2float = (/bits16) . toEnum . fromEnum
+    bits16 = 2**16
+    short2float :: FT_UShort -> Float
+    short2float = toEnum . fromEnum
 
 -- | Allocates a new Atlas for the given font & glyphset,
 -- loading it into the atlas cache before returning.
 allocAtlas' :: (MonadIO m, MonadFail m, MonadError TypograffitiError m) =>
-    TMVar [(IS.IntSet, Atlas)] -> FT_Face -> IS.IntSet -> m Atlas
-allocAtlas' atlases font glyphset = do
+    TMVar [(IS.IntSet, Atlas)] -> FT_Face -> IS.IntSet -> (Float, Float) -> m Atlas
+allocAtlas' atlases font glyphset scale = do
     let glyphs = map toEnum $ IS.toList glyphset
-    atlas <- allocAtlas (glyphRetriever font) glyphs
+    atlas <- allocAtlas (glyphRetriever font) glyphs scale
 
     liftIO $ atomically $ do
         a <- takeTMVar atlases
diff --git a/src/Typograffiti/Text.hs b/src/Typograffiti/Text.hs
--- a/src/Typograffiti/Text.hs
+++ b/src/Typograffiti/Text.hs
@@ -25,11 +25,12 @@
                                         FontOptions (..), defaultFontOptions)
 import qualified Data.Text.Glyphize     as HB
 import           FreeType.Core.Base
-import           FreeType.Core.Types    (FT_Fixed)
+import           FreeType.Core.Types    (FT_Fixed, FT_UShort)
 import           FreeType.Format.Multiple (ft_Set_Var_Design_Coordinates)
 import           Data.Text.Lazy         (Text, pack)
 import qualified Data.Text.Lazy         as Txt
 import           Data.Word              (Word32)
+import           Foreign.Storable       (peek)
 
 import           Typograffiti.Atlas
 import           Typograffiti.Cache
@@ -58,12 +59,12 @@
     -- ^ Additional font options offered by Harfbuzz.
     minLineHeight :: Float
     -- ^ Number of pixels tall each line should be at minimum.
-    -- Defaults to 10px.
+    -- Defaults to 0 indicate to use the font's default lineheight.
 }
 
 -- | Constructs a `SampleText` with default values.
 defaultSample :: SampleText
-defaultSample = SampleText [] (pack $ map toEnum [32..126]) 4 defaultFontOptions 10
+defaultSample = SampleText [] (pack $ map toEnum [32..126]) 4 defaultFontOptions 0
 -- | Appends an OpenType feature callers may use to the `Sample` ensuring its
 -- glyphs are available. Call after setting `sampleText`.
 addSampleFeature :: String -> Word32 -> SampleText -> SampleText
@@ -133,8 +134,17 @@
                                                     (floor $ 26.6 * 2 * h)
                                                     (toEnum dpix) (toEnum dpiy)
 
+    font_ <- liftIO $ peek font
+    size <- srMetrics <$> liftIO (peek $ frSize font_)
+    let lineHeight = if minLineHeight == 0 then fixed2float $ smHeight size else minLineHeight
+    let upem = short2float $ frUnits_per_EM font_
+    let scale = (short2float (smX_ppem size)/upem/2, short2float (smY_ppem size)/upem/2)
+
     bytes <- liftIO $ B.readFile filepath
-    let font' = HB.createFontWithOptions fontOptions $ HB.createFace bytes $ toEnum index
+    let fontOpts' = fontOptions {
+            HB.optionScale = Nothing, HB.optionPtEm = Nothing, HB.optionPPEm = Nothing
+      }
+    let font' = HB.createFontWithOptions fontOpts' $ HB.createFace bytes $ toEnum index
     let glyphs = map (codepoint . fst) $
             shape font' defaultBuffer {
                 HB.text = Txt.replicate (toEnum $ succ $ length sampleFeatures) sampleText
@@ -145,17 +155,22 @@
     unless (null designCoords) $
         liftFreetype $ ft_Set_Var_Design_Coordinates font designCoords
 
-    atlas <- allocAtlas (glyphRetriever font) glyphs'
+    atlas <- allocAtlas (glyphRetriever font) glyphs' scale
     liftFreetype $ ft_Done_Face font
 
     drawGlyphs <- makeDrawGlyphs
-    return $ freeAtlasWrapper atlas $ drawLinesWrapper tabwidth minLineHeight
+    return $ freeAtlasWrapper atlas $ drawLinesWrapper tabwidth lineHeight
         $ \RichText {..} ->
             drawGlyphs atlas $ shape font' defaultBuffer { HB.text = text } features
   where
     x2 = (*2)
     float2fixed :: Float -> FT_Fixed
-    float2fixed = toEnum . fromEnum . (*65536)
+    float2fixed = toEnum . fromEnum . (*bits16)
+    fixed2float :: FT_Fixed -> Float
+    fixed2float = (/bits16) . toEnum . fromEnum
+    bits16 = 2**16
+    short2float :: FT_UShort -> Float
+    short2float = toEnum . fromEnum
 
 -- | Variant of `makeDrawText` which initializes FreeType itself.
 makeDrawText' a b c d =
diff --git a/typograffiti.cabal b/typograffiti.cabal
--- a/typograffiti.cabal
+++ b/typograffiti.cabal
@@ -1,7 +1,7 @@
 cabal-version: 1.12
 
 name:           typograffiti
-version:             0.2.0.0
+version:             0.2.0.1
 synopsis:       Just let me draw nice text already
 description:    This is a text rendering library that uses OpenGL and freetype2 to render TTF font strings quickly. It is fast enough to render large chunks of text in real time. This library exists because text rendering is one of the biggest hurdles in Haskell graphics programming - and it shouldn't be!
                 Typograffiti includes an MTL style typeclass and a default monad transformer. It does not assume you are using any specific windowing solution. It does assume you are using OpenGL 3.3+.
@@ -10,9 +10,9 @@
 category:       Graphics
 homepage:       https://github.com/schell/typograffiti#readme
 bug-reports:    https://github.com/schell/typograffiti/issues
-author:         Schell Scivally
-maintainer:     schell@takt.com
-copyright:      2018 Schell Scivally & others
+author:         Schell Scivally & Adrian Cochrane
+maintainer:     alcinnz@argonaut-constellation.org
+copyright:      2018 Schell Scivally, 2023 Adrian Cochrane
 license:        BSD3
 license-file:   LICENSE
 build-type:     Simple
@@ -33,7 +33,7 @@
       Typograffiti.Store
       Typograffiti.Text
       Typograffiti.Rich
-  build-depends:       base >=4.12 && <4.13, linear>=1.20, containers >= 0.6,
+  build-depends:       base >=4.12 && <4.16, linear>=1.20, containers >= 0.6,
                         freetype2 >= 0.2, gl >= 0.8, mtl >= 2.2, stm >= 2.5, text,
                         vector >= 0.12, harfbuzz-pure >= 1.0.2, bytestring >= 0.10
   hs-source-dirs:      src
@@ -42,7 +42,7 @@
 
 executable typograffiti
   main-is:             Main.hs
-  build-depends:       base >=4.12 && <4.13, typograffiti, sdl2 >= 2.5.4, text, gl, mtl
+  build-depends:       base >=4.12 && <4.16, typograffiti, sdl2 >= 2.5.4, text, gl, mtl
   hs-source-dirs:      app
   default-language:    Haskell2010
 
