diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,14 @@
 # Revision history for skylighting
 
+## 0.3.2 -- 2017-04-01
+
+  * Parse xml definitions without resolving entities from
+    language.dtd.  This allows xml definitions to be used even
+    when language.dtd isn't available.  Existing definitions
+    don't rely on the two or three entities definied in
+    language.dtd, so this is harmless.
+  * Small optimizations.
+
 ## 0.3.1 -- 2017-02-28
 
   * Use handwritten parser for Float instead of regex.  Fixes
diff --git a/skylighting.cabal b/skylighting.cabal
--- a/skylighting.cabal
+++ b/skylighting.cabal
@@ -1,5 +1,5 @@
 name:                skylighting
-version:             0.3.1
+version:             0.3.2
 synopsis:            syntax highlighting library
 description:         Skylighting is a syntax highlighting library with
                      support for over one hundred languages.  It derives
diff --git a/src/Skylighting/Parser.hs b/src/Skylighting/Parser.hs
--- a/src/Skylighting/Parser.hs
+++ b/src/Skylighting/Parser.hs
@@ -67,7 +67,8 @@
 
 application :: String -> IOSArrow b Syntax
 application fp
-    = readDocument [withValidate no, withInputEncoding utf8] fp
+    = readDocument [withValidate no, withSubstDTDEntities no,
+                    withInputEncoding utf8] fp
       >>>
       multi (hasName "language")
       >>>
diff --git a/src/Skylighting/Tokenizer.hs b/src/Skylighting/Tokenizer.hs
--- a/src/Skylighting/Tokenizer.hs
+++ b/src/Skylighting/Tokenizer.hs
@@ -221,8 +221,7 @@
                 HlCStringChar -> withAttr attr $ parseCStringChar inp
                 HlCChar -> withAttr attr $ parseCChar inp
                 Float -> withAttr attr $ parseFloat inp
-                Keyword kwattr kws ->
-                  withAttr attr $ keyword kwattr kws inp
+                Keyword kwattr kws -> withAttr attr $ keyword kwattr kws inp
                 StringDetect s -> withAttr attr $
                                     stringDetect (rCaseSensitive rule) s inp
                 WordDetect s -> withAttr attr $
@@ -269,15 +268,6 @@
      then return Nothing
      else return $ Just (tt, res)
 
-{-
--- Try a tokenizer and return to original state if it fails.
--- This is used when one tokenizer calls another.
-try :: TokenizerM a -> TokenizerM a
-try tokenizer' = do
-  origstate <- get
-  tokenizer' <|> (put origstate >> mzero)
--}
-
 wordDetect :: Bool -> Text -> ByteString -> TokenizerM Text
 wordDetect caseSensitive s inp = do
   wordBoundary inp
@@ -426,6 +416,7 @@
   reStr <- if dynamic
               then subDynamic (reString re)
               else return (reString re)
+  when (BS.take 2 reStr == "\\b") $ wordBoundary inp
   regex <- if dynamic
               then return $ compileRegex (reCaseSensitive re) reStr
               else do
@@ -437,12 +428,10 @@
                              Map.insert re cre (compiledRegexes st) }
                        return cre
                      Just cre -> return cre
-  when (BS.take 2 reStr == "\\b") $ wordBoundary inp
   case matchRegex regex inp of
        Just (match:capts) -> do
-         match' <- decodeBS match
          modify $ \st -> st{ captures = capts }
-         takeChars (Text.length match')
+         takeChars (UTF8.length match)
        _ -> mzero
 
 wordBoundary :: ByteString -> TokenizerM ()
