fontwhich 0.1.0 → 0.2
raw patch · 4 files changed
+104/−43 lines, 4 filesdep +unicode-data-namesdep +unicode-data-scripts
Dependencies added: unicode-data-names, unicode-data-scripts
Files
- ChangeLog.md +6/−0
- README.md +36/−7
- fontwhich.cabal +8/−9
- src/Main.hs +54/−27
ChangeLog.md view
@@ -1,4 +1,10 @@ # fontwhich releases +## 0.2 (2026-03-12)+- add --unicode for unicode data output (uses `unicode-data`)+- rename --hex to --utf8 (for hex bytes)+- show number of pango items with --utf8 or --unicode+- only print number of bytes with --utf8 or --unicode+ ## 0.1.0 (2026-02-27) - initial release with --font --lang and --hex
README.md view
@@ -8,12 +8,19 @@ ## Usage +`$ fontwhich --version`++```+0.2+```+ `$ fontwhich --help` ``` fontwhich -Usage: fontwhich [--version] [-f|--font FONT] [-l|--lang LANG] [-x|--hex] [TEXT]+Usage: fontwhich [--version] [-f|--font FONT] [-l|--lang LANG] [-b|--utf8] + [-u|--unicode] [TEXT] Describes the fonts used to render text with pango @@ -22,13 +29,13 @@ --version Show version -f,--font FONT Base font [default: Sans] -l,--lang LANG Language code- -x,--hex Output UTF-8 hex codes+ -b,--utf8 Output UTF-8 hex codes+ -u,--unicode Output Unicode data ``` `$ fontwhich Hello 🌍 World 世界` ```-23 bytes 'Hello ' : Noto Sans '🌍' : Noto Color Emoji ' World ' : Noto Sans@@ -38,7 +45,6 @@ `$ fontwhich -f Serif "こんにちは 😀 世界"` ```-27 bytes 'こんにちは ' : Noto Serif CJK JP '😀' : Noto Color Emoji ' ' : Noto Serif CJK JP@@ -51,13 +57,36 @@ Primary font for ja is: "Noto Sans CJK JP" ``` -`$ fontwhich --hex 🍊`+`$ fontwhich --utf8 🌳` ```-4 bytes+4 bytes;+'🌳' [f0 9f 8c b3] : Noto Color Emoji+```++`$ fontwhich --unicode αβ१२`++```+10 bytes; 2 pango items+'αβ' : Noto Sans+α <U+03B1>: GREEK SMALL LETTER ALPHA [Greek]+β <U+03B2>: GREEK SMALL LETTER BETA [Greek]+'१२' : Noto Sans Devanagari+१ <U+0967>: DEVANAGARI DIGIT ONE [Devanagari]+२ <U+0968>: DEVANAGARI DIGIT TWO [Devanagari]+```++One can use both options together:++`$ fontwhich --utf8 🍊 --unicode`++```+4 bytes; '🍊' [f0 9f 8d 8a] : Noto Color Emoji+🍊 <U+1F34A>: TANGERINE [Common] ``` + ## Building and installation On Fedora: ```@@ -68,7 +97,7 @@ There is a copr repo: <https://copr.fedorainfracloud.org/coprs/petersen/fontwhich/> ## Misc-Code was assisted with Gemini Pro 3.1.+Code assisted with Gemini 3.1. The tool is related conceptually to <https://github.com/sudipshil9862/whichfont> (C codebase).
fontwhich.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: fontwhich-version: 0.1.0+version: 0.2 synopsis: Determine fonts used to render text description: Fontwhich is a commandline tool that used pango to determine which@@ -17,13 +17,10 @@ build-type: Simple extra-doc-files: README.md ChangeLog.md--- tested-with: GHC == 8.10.7--- || == 9.0.2--- || == 9.2.8--- || == 9.4.8--- || == 9.6.7--- || == 9.8.4--- || == 9.10.3+tested-with: GHC == 9.6.7+ || == 9.8.4+ || == 9.10.3+ || == 9.12.3 source-repository head type: git@@ -41,7 +38,9 @@ gi-pangocairo, simple-cmd, simple-cmd-args,- text+ text,+ unicode-data-names,+ unicode-data-scripts default-language: Haskell2010 ghc-options: -Wall if impl(ghc >= 8.0)
src/Main.hs view
@@ -1,17 +1,19 @@ -- SPDX-License-Identifier: BSD-3-Clause -import Control.Monad.Extra (whenJust)+import Control.Monad.Extra (forM_, when, whenJust) import qualified Data.ByteString as B+import Data.Char (ord) import Data.Maybe (fromMaybe) import qualified Data.Text as T import qualified Data.Text.Encoding as TE+import qualified GI.Pango as Pango+import qualified GI.PangoCairo.Interfaces.FontMap as PangoCairo import SimpleCmd (error', (+-+))-import SimpleCmdArgs+import SimpleCmdArgs hiding (str) import System.Environment (getArgs, withArgs) import Text.Printf (printf)--import qualified GI.Pango as Pango-import qualified GI.PangoCairo.Interfaces.FontMap as PangoCairo+import qualified Unicode.Char.General.Names as UN+import qualified Unicode.Char.General.Scripts as US import Paths_fontwhich (version) @@ -29,11 +31,12 @@ run <$> optional (strOptionWith 'f' "font" "FONT" "Base font [default: Sans]") <*> optional (strOptionWith 'l' "lang" "LANG" "Language code")- <*> switchWith 'x' "hex" "Output UTF-8 hex codes"+ <*> switchWith 'b' "utf8" "Output UTF-8 hex codes"+ <*> switchWith 'u' "unicode" "Output Unicode data" <*> many (strArg "TEXT") -run :: Maybe String -> Maybe String -> Bool -> [String] -> IO ()-run mfont mlang hex txt = do+run :: Maybe String -> Maybe String -> Bool -> Bool -> [String] -> IO ()+run mfont mlang hex unicode txt = do -- Get a default Font Map and Context fontMap <- PangoCairo.fontMapGetDefault context <- Pango.fontMapCreateContext fontMap@@ -66,33 +69,57 @@ Pango.contextSetLanguage context mplang let utf8Bytes = TE.encodeUtf8 myText- putStrLn $ show (B.length utf8Bytes) +-+ "bytes"+ when (hex || unicode) $+ putStr $ show (B.length utf8Bytes) +-+ "bytes;" -- start_index, length, cached_iter items <- Pango.itemize context myText 0 (fromIntegral $ B.length utf8Bytes) attr Nothing- mapM_ (printItemInfo hex utf8Bytes) items+ when (hex || unicode) $+ putStrLn $+ if length items > 1+ then ' ' : show (length items) +-+ "pango items"+ else ""+ mapM (itemString utf8Bytes) items >>=+ mapM_ (printItemInfo hex unicode)+ where+ itemString :: B.ByteString -> Pango.Item -> IO (String, Pango.Item)+ itemString utf8Bytes item = do+ -- Offsets in Pango are bytes+ offset <- Pango.getItemOffset item+ len <- Pango.getItemLength item+ let itemBytes = B.take (fromIntegral len) $ B.drop (fromIntegral offset) utf8Bytes+ return (T.unpack $ TE.decodeUtf8 itemBytes, item) -printItemInfo :: Bool -> B.ByteString -> Pango.Item -> IO ()-printItemInfo hex utf8Bytes item = do+printItemInfo :: Bool -> Bool -> (String, Pango.Item) -> IO ()+printItemInfo hex unicode (str,item) = do -- Get the Analysis struct from the Item analysis <- Pango.getItemAnalysis item -- Extract the Font used for this specific item maybeFont <- Pango.getAnalysisFont analysis - case maybeFont of- Nothing -> putStrLn "No font assigned to this segment."- Just font -> do- desc <- Pango.fontDescribe font- family <- Pango.fontDescriptionGetFamily desc+ mfamily <-+ case maybeFont of+ Nothing -> do+ putStrLn "No font assigned"+ return Nothing+ Just font ->+ Pango.fontDescribe font >>= Pango.fontDescriptionGetFamily - -- Offsets in Pango are byte offsets- offset <- Pango.getItemOffset item- len <- Pango.getItemLength item- let itemBytes = B.take (fromIntegral len) $ B.drop (fromIntegral offset) utf8Bytes- itemText = TE.decodeUtf8 itemBytes- hexStr = if hex- then "[" ++ unwords [printf "%02x" b | b <- B.unpack itemBytes] ++ "]"- else ""- putStrLn $- '\'' : T.unpack itemText ++ "'" +-+ hexStr +-+ ":" +-+ maybe "Unknown" T.unpack family+ let hexStr = if hex+ then unwords $ map hexify str+ else ""+ putStrLn $+ '\'' : str ++ "'" +-+ hexStr +-+ ":" +-+ maybe "Unknown" T.unpack mfamily+ when unicode $+ forM_ str $ \char -> do+ putChar char+ let mname = UN.name char+ script = US.script char+ codepoint = printf "U+%04X" (ord char)+ putStrLn $ " <" ++ codepoint ++ ">:" +-+ fromMaybe "unknown codepoint" mname +-+ '[' : show script ++ "]" -- +-+ '(' : US.scriptShortName script ++ ")"+ where+ hexify :: Char -> String+ hexify char =+ let bytes = TE.encodeUtf8 $ T.singleton char+ in '[' : unwords [printf "%02x" b | b <- B.unpack bytes] ++ "]"