git.iko.soy/typeable/pandoc
on master help sign in
Handle relative lengths (e.g. `2*`) in HTML column widths.

See <https://www.w3.org/TR/html4/types.html#h-6.6>. "A relative length has the form "i*", where "i" is an integer. When allotting space among elements competing for that space, user agents allot pixel and percentage lengths first, then divide up remaining available space among relative lengths. Each relative length receives a portion of the available space that is proportional to the integer preceding the "*". The value "*" is equivalent to "1*". Thus, if 60 pixels of space are available after the user agent allots pixel and percentage space, and the competing relative lengths are 1*, 2*, and 3*, the 1* will be alloted 10 pixels, the 2* will be alloted 20 pixels, and the 3* will be alloted 30 pixels." Closes #4063.

commit1af2cfb2873c5bb6ddd9fc00d076088b2e62af30 authorJohn MacFarlane <jgm@berkeley.edu> dateSat 22 May 2021, 21:56 (5 years ago) parent80b4b3f
2 files changed+62−14
src/Text/Pandoc/Readers/HTML/Table.hs+33−14 test/command/4063.md+29
src/Text/Pandoc/Readers/HTML/Table.hs+33−14
@@ -17,6 +17,7 @@ module Text.Pandoc.Readers.HTML.Table (pTable) where
1717
1818import Control.Applicative ((<|>))
1919import Data.Maybe (fromMaybe)
20+import Data.Either (lefts, rights)
2021import Data.List.NonEmpty (nonEmpty)
2122import Data.Text (Text)
2223import Text.HTML.TagSoup
@@ -33,34 +34,51 @@ import Text.Pandoc.Shared (onlySimpleTableCells, safeRead)
3334import qualified Data.Text as T
3435import qualified Text.Pandoc.Builder as B
3536
36−-- | Parses a @<col>@ element, returning the column's width. Defaults to
37−-- @'ColWidthDefault'@ if the width is not set or cannot be determined.
38−pCol :: PandocMonad m => TagParser m ColWidth
37+-- | Parses a @<col>@ element, returning the column's width.
38+-- An Either value is used: Left i means a "relative length" with
39+-- integral value i (see https://www.w3.org/TR/html4/types.html#h-6.6);
40+-- Right w means a regular width. Defaults to @'Right ColWidthDefault'@
41+-- if the width is not set or cannot be determined.
42+pCol :: PandocMonad m => TagParser m (Either Int ColWidth)
3943pCol = try $ do
4044 TagOpen _ attribs' <- pSatisfy (matchTagOpen "col" [])
4145 let attribs = toStringAttr attribs'
4246 skipMany pBlank
4347 optional $ pSatisfy (matchTagClose "col")
4448 skipMany pBlank
45− let width = case lookup "width" attribs of
49+ return $ case lookup "width" attribs of
4650 Nothing -> case lookup "style" attribs of
4751 Just (T.stripPrefix "width:" -> Just xs) | T.any (== '%') xs ->
48− fromMaybe 0.0 $ safeRead (T.filter
49− (`notElem` (" \t\r\n%'\";" :: [Char])) xs)
50− _ -> 0.0
52+ maybe (Right ColWidthDefault) (Right . ColWidth)
53+ $ safeRead (T.filter
54+ (`notElem` (" \t\r\n%'\";" :: [Char])) xs)
55+ _ -> Right ColWidthDefault
56+ Just (T.unsnoc -> Just (xs, '*')) ->
57+ maybe (Left 1) Left $ safeRead xs
5158 Just (T.unsnoc -> Just (xs, '%')) ->
52− fromMaybe 0.0 $ safeRead xs
53− _ -> 0.0
54− if width > 0.0
55− then return $ ColWidth $ width / 100.0
56− else return ColWidthDefault
59+ maybe (Right ColWidthDefault)
60+ (Right . ColWidth . (/ 100.0)) $ safeRead xs
61+ _ -> Right ColWidthDefault
5762
58−pColgroup :: PandocMonad m => TagParser m [ColWidth]
63+pColgroup :: PandocMonad m => TagParser m [Either Int ColWidth]
5964pColgroup = try $ do
6065 pSatisfy (matchTagOpen "colgroup" [])
6166 skipMany pBlank
6267 manyTill pCol (pCloses "colgroup" <|> eof) <* skipMany pBlank
6368
69+resolveRelativeLengths :: [Either Int ColWidth] -> [ColWidth]
70+resolveRelativeLengths ws =
71+ let remaining = 1 - sum (map getColWidth $ rights ws)
72+ relatives = sum $ lefts ws
73+ relUnit = remaining / fromIntegral relatives
74+ toColWidth (Right x) = x
75+ toColWidth (Left i) = ColWidth (fromIntegral i * relUnit)
76+ in map toColWidth ws
77+
78+getColWidth :: ColWidth -> Double
79+getColWidth ColWidthDefault = 0
80+getColWidth (ColWidth w) = w
81+
6482data CellType
6583 = HeaderCell
6684 | BodyCell
@@ -182,7 +200,8 @@ pTable :: PandocMonad m
182200pTable block = try $ do
183201 TagOpen _ attribs <- pSatisfy (matchTagOpen "table" []) <* skipMany pBlank
184202 caption <- option mempty $ pInTags "caption" block <* skipMany pBlank
185− widths <- ((mconcat <$> many1 pColgroup) <|> many pCol) <* skipMany pBlank
203+ widths <- resolveRelativeLengths <$>
204+ ((mconcat <$> many1 pColgroup) <|> many pCol) <* skipMany pBlank
186205 thead <- pTableHead block <* skipMany pBlank
187206 topfoot <- optionMaybe (pTableFoot block) <* skipMany pBlank
188207 tbodies <- many (pTableBody block) <* skipMany pBlank
test/command/4063.mdnew file+29
@@ -0,0 +1,29 @@
1+```
2+% pandoc -f html -t native
3+<table>
4+<colgroup>
5+ <col width="30%" />
6+ <col width="*" />
7+</colgroup>
8+<tr>
9+ <td>1</td>
10+ <td>2</td>
11+</tr>
12+</table>
13+^D
14+[Table ("",[],[]) (Caption Nothing
15+ [])
16+ [(AlignDefault,ColWidth 0.3)
17+ ,(AlignDefault,ColWidth 0.7)]
18+ (TableHead ("",[],[])
19+ [])
20+ [(TableBody ("",[],[]) (RowHeadColumns 0)
21+ []
22+ [Row ("",[],[])
23+ [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1)
24+ [Plain [Str "1"]]
25+ ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1)
26+ [Plain [Str "2"]]]])]
27+ (TableFoot ("",[],[])
28+ [])]
29+```