Reimplement getAllTextDimensions

Create SVG text elements so that the dimensions are representative
This commit is contained in:
Nathan McRae
2025-08-19 19:05:50 -07:00
parent d26f7778ba
commit 99cc71e330
2 changed files with 59 additions and 40 deletions

View File

@@ -15,7 +15,7 @@ import Effect (Effect)
import Effect.Console (log)
import Effect.Class (liftEffect)
import Effect.Exception (throw)
import TernaryGraph (Dimension, ternaryGraph, TextStyle, tickLabelStrings)
import TernaryGraph (Dimension, svgTextID, ternaryGraph, ternaryGraphSvg, TextStyle, tickLabelStrings)
import Web.DOM.Document (contentType
, createElement
, Document
@@ -54,47 +54,55 @@ getNodeById doc id = do
Nothing -> throw $ "Unable to find element with ID '" <> id <> "'"
Just e -> pure $ Element.toNode e
getTextDimensions :: HTMLDoc.HTMLDocument -> TextStyle -> String -> Effect Dimension
getTextDimensions htmlDoc style text = do
let doc = HTMLDoc.toDocument htmlDoc
el :: Element.Element <- createElement "span" doc
getAllTextDimensions :: forall f. Foldable.Foldable f => Document -> Node -> f (Tup.Tuple String TextStyle) -> Effect (Map.Map (Tup.Tuple String TextStyle) Dimension)
getAllTextDimensions document svgContainer strings = do
myDomParser <- makeDOMParser
bodyMay <- HTMLDoc.body htmlDoc
bodyNode <- case bodyMay of
Nothing -> throw "Could not find body of HTML document"
Just e -> pure $ Element.toNode (HTMLElement.toElement e)
-- Create svg fragments
let textID = \i -> "text-" <> (show i)
let (Tup.Tuple idMap _) = Foldable.foldr (\strTup (Tup.Tuple map i) -> Tup.Tuple (Map.insert (textID i) strTup map) (i + 1)) (Tup.Tuple Map.empty 0) strings
let frag = \str style i -> svgTextID (Just (textID i)) str { x: 0.0, y: 0.0 } 0.0 style { widthPx: 0.0, heightPx: 0.0 }
let fragments = Foldable.foldr (\(Tup.Tuple str style) arr -> Array.cons (frag str style (Array.length arr)) arr) [] strings
appendChild (Element.toNode el) bodyNode
-- create svg string
let svg = ternaryGraphSvg fragments
let styleText = (
"font" <> style.typeface
<> ";font-family: " <> style.typeface
<> ";font-size: " <> (toString style.sizePx) <> "px"
<> ";height: " <> "auto"
<> ";width: " <> "auto"
<> ";position: " <> "absolute"
<> ";white-space: " <> "nowrap"
)
-- add to container
svgDocMay <- parseSVGFromString svg myDomParser
svgDoc <- case svgDocMay of
Left error -> throw error
Right doc -> pure doc
Element.setAttribute "style" styleText el
setTextContent text (Element.toNode el)
width <- Element.clientWidth el
height <- Element.clientHeight el
elMay <- firstElementChild $ toParentNode svgDoc
svgNode <- case elMay of
Nothing -> throw "no child in svg doc"
Just el -> pure $ Element.toNode el
removeChild (Element.toNode el) bodyNode
newNode <- importNode svgNode true document
appendChild newNode svgContainer
pure { widthPx: width, heightPx: height }
-- get dimensions for all text elements
myMap <- Foldable.foldM (\map i -> do
let id = textID i
textElement <- myGetElementById document id
boundingRect <- Element.getBoundingClientRect textElement
let textNFontMay = Map.lookup id idMap
textNFont <- case textNFontMay of
Nothing -> throw $ "Missing '" <> id <> "' from map"
Just x -> pure x
log $ id <> ": " <> (toString boundingRect.width) <> ", " <> (toString boundingRect.height)
pure $ Map.insert textNFont { widthPx: boundingRect.width, heightPx: boundingRect.height } map
) Map.empty (Array.range 0 ((Array.length fragments) - 1))
converter :: HTMLDoc.HTMLDocument -> (Map.Map (Tup.Tuple String TextStyle) Dimension) -> (Tup.Tuple String TextStyle) -> Effect (Map.Map (Tup.Tuple String TextStyle) Dimension)
converter doc map textTuple = do
let text = Tup.fst textTuple
let style = Tup.snd textTuple
dimension <- getTextDimensions doc style text
pure $ Map.insert textTuple dimension map
-- remove the svg
containerKids <- (childNodes svgContainer) >>= NodeList.toArray
Foldable.for_ containerKids \kid -> do
removeChild kid svgContainer
getAllTextDimensions :: forall f. Foldable.Foldable f => HTMLDoc.HTMLDocument -> f (Tup.Tuple String TextStyle) -> Effect (Map.Map (Tup.Tuple String TextStyle) Dimension)
getAllTextDimensions doc strings =
Foldable.foldM (converter doc) Map.empty strings
pure myMap
update :: Event -> Effect Unit
update e = do
@@ -134,7 +142,11 @@ update e = do
, (Tup.Tuple graphDef.axis2Label graphDef.axisTitleTextStyle)
, (Tup.Tuple graphDef.axis3Label graphDef.axisTitleTextStyle)
]
textDimensions <- getAllTextDimensions d textStyles
textDimensions <- getAllTextDimensions document svgContainer textStyles
log $ case Map.lookup (Tup.Tuple "axis 1" graphDef.axisTitleTextStyle) textDimensions of
Nothing -> "Not found"
Just { widthPx: width, heightPx: height} -> (toString width) <> ", " <> (toString height)
let mySVGErr = ternaryGraph 100.0 50.0 50.0 graphDef textDimensions
mySVG <- case mySVGErr of
@@ -191,7 +203,7 @@ main = do
, (Tup.Tuple graphDef.axis2Label graphDef.axisTitleTextStyle)
, (Tup.Tuple graphDef.axis3Label graphDef.axisTitleTextStyle)
]
textDimensions <- getAllTextDimensions d textStyles
textDimensions <- getAllTextDimensions dd svgContainer textStyles
log $ Foldable.foldr (\dim str -> str <> "\n" <> (toString dim.widthPx) <> ", " <> (toString dim.heightPx)) "" $ Map.values textDimensions