mirror of
https://github.com/jgm/pandoc.git
synced 2026-09-01 15:38:09 +08:00
ODT writer: image anchoring, percent dimensions, text width resolution (#11746)
Inline images now emit `text:anchor-type=as-char` on their `draw:frame`, anchoring the frame as a character so it flows with the surrounding text. Without this, consumers default to paragraph anchoring and float the frame beside (or after) the following paragraph. When both width and height are given and the width is a percentage, the width is now resolved against the reference document text area width, mirroring how the docx writer derives its print width from the reference docx section properties. The height follows the aspect ratio.
This commit is contained in:
committed by
GitHub
parent
4f5226df4f
commit
57639391fc
@@ -17,7 +17,7 @@ import Control.Monad.Except (catchError, throwError)
|
||||
import Control.Monad.State.Strict (StateT, evalStateT, gets, modify, lift)
|
||||
import Control.Monad (MonadPlus(mplus))
|
||||
import qualified Data.ByteString.Lazy as B
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Maybe (fromMaybe, listToMaybe)
|
||||
import Data.Generics (everywhere', mkT)
|
||||
import Data.List (isPrefixOf)
|
||||
import qualified Data.Map as Map
|
||||
@@ -102,7 +102,8 @@ pandocToODT opts doc@(Pandoc meta _) = do
|
||||
readDataFile "reference.odt"
|
||||
-- handle formulas and pictures
|
||||
-- picEntriesRef <- P.newIORef ([] :: [Entry])
|
||||
doc' <- walkM (transformPicMath opts) $ walk fixDisplayMath doc
|
||||
let refTextWidth = referenceTextWidthPt opts refArchive
|
||||
doc' <- walkM (transformPicMath opts refTextWidth) $ walk fixDisplayMath doc
|
||||
newContents <- lift $ writeOpenDocument opts{writerWrapText = WrapNone} doc'
|
||||
epochtime <- floor `fmap` lift P.getPOSIXTime
|
||||
let contentEntry = toEntry "content.xml" epochtime
|
||||
@@ -303,16 +304,49 @@ addLang lang = everywhere' (mkT updateLangAttr)
|
||||
updateLangAttr x = x
|
||||
|
||||
-- | transform both Image and Math elements
|
||||
transformPicMath :: PandocMonad m => WriterOptions ->Inline -> O m Inline
|
||||
transformPicMath opts (Image attr@(id', cls, _) lab (src,t)) = catchError
|
||||
-- | Width of the text area in points, from the reference document's
|
||||
-- page layout (page width minus horizontal margins). This mirrors how
|
||||
-- the docx writer derives its print width from the reference docx
|
||||
-- section properties.
|
||||
referenceTextWidthPt :: WriterOptions -> Archive -> Maybe Double
|
||||
referenceTextWidthPt opts archive = do
|
||||
entry <- findEntryByPath "styles.xml" archive
|
||||
styles <- either (const Nothing) Just $
|
||||
parseXMLElement $ toTextLazy $ fromEntry entry
|
||||
layout <- listToMaybe $ filterElementsName
|
||||
((== "page-layout-properties") . qName) styles
|
||||
let dim attrName = do
|
||||
v <- findAttrBy ((== attrName) . qName) layout
|
||||
d <- lengthToDim v
|
||||
Just $ 72 * inInch opts d
|
||||
pageWidth <- dim "page-width"
|
||||
let margin = fromMaybe 0 . dim
|
||||
Just $ pageWidth - margin "margin-left" - margin "margin-right"
|
||||
|
||||
transformPicMath :: PandocMonad m
|
||||
=> WriterOptions -> Maybe Double -> Inline -> O m Inline
|
||||
transformPicMath opts mbTextWidthPt (Image attr@(id', cls, _) lab (src,t)) =
|
||||
catchError
|
||||
(do (img, mbMimeType) <- P.fetchItem src
|
||||
(ptX, ptY) <- case imageSize opts img of
|
||||
Right s -> return $ sizeInPoints s
|
||||
Left msg -> do
|
||||
report $ CouldNotDetermineImageSize src msg
|
||||
return (100, 100)
|
||||
let textWidthPt = fromMaybe 420 mbTextWidthPt
|
||||
let dims =
|
||||
case (getDim Width, getDim Height) of
|
||||
-- Percent dimensions are not valid ODF lengths. When both
|
||||
-- dimensions are given and the width is relative, resolve
|
||||
-- it against the reference document's text width (as the
|
||||
-- docx writer does) and let the height follow the image's
|
||||
-- aspect ratio.
|
||||
(Just (Percent wp), Just _) ->
|
||||
let wIn = wp / 100 * textWidthPt / 72
|
||||
in [ ("width", tshow (Inch wIn))
|
||||
, ("height", tshow (Inch (wIn / ratio))) ]
|
||||
(Just w@(Inch i), Just (Percent _)) ->
|
||||
[("width", tshow w), ("height", tshow (Inch (i / ratio)))]
|
||||
(Just w, Just h) -> [("width", tshow w), ("height", tshow h)]
|
||||
(Just w@(Percent _), Nothing) -> [("rel-width", tshow w),("rel-height", "scale"),("width", tshow ptX <> "pt"),("height", tshow ptY <> "pt")]
|
||||
(Nothing, Just h@(Percent _)) -> [("rel-width", "scale"),("rel-height", tshow h),("width", tshow ptX <> "pt"),("height", tshow ptY <> "pt")]
|
||||
@@ -347,7 +381,7 @@ transformPicMath opts (Image attr@(id', cls, _) lab (src,t)) = catchError
|
||||
report $ CouldNotFetchResource src $ T.pack (show e)
|
||||
return $ Emph lab)
|
||||
|
||||
transformPicMath _ (Math t math) = do
|
||||
transformPicMath _ _ (Math t math) = do
|
||||
entries <- gets stEntries
|
||||
let dt = if t == InlineMath then DisplayInline else DisplayBlock
|
||||
case readTeX math of
|
||||
@@ -381,7 +415,7 @@ transformPicMath _ (Math t math) = do
|
||||
, ("xlink:show", "embed")
|
||||
, ("xlink:actuate", "onLoad")]
|
||||
|
||||
transformPicMath _ x = return x
|
||||
transformPicMath _ _ x = return x
|
||||
|
||||
starMathCommentFromTeX :: T.Text -> T.Text
|
||||
starMathCommentFromTeX tex =
|
||||
|
||||
@@ -747,8 +747,14 @@ inlineToOpenDocument o ils
|
||||
getDims (("height", h):xs) = ("svg:height", h) : getDims xs
|
||||
getDims (("rel-height", w):xs) = ("style:rel-height", w) : getDims xs
|
||||
getDims (_:xs) = getDims xs
|
||||
-- Image is an Inline: anchor the frame as a character so
|
||||
-- it flows with the text. Without an explicit anchor,
|
||||
-- consumers default to paragraph anchoring and float the
|
||||
-- frame beside the following text.
|
||||
return $ inTags False "draw:frame"
|
||||
(("draw:name", "img" <> tshow id') : getDims kvs) $
|
||||
(("draw:name", "img" <> tshow id')
|
||||
: ("text:anchor-type", "as-char")
|
||||
: getDims kvs) $
|
||||
selfClosingTag "draw:image" [ ("xlink:href" , s )
|
||||
, ("xlink:type" , "simple")
|
||||
, ("xlink:show" , "embed" )
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||

|
||||
|
||||
^D
|
||||
<text:p text:style-name="FigureWithCaption"><draw:frame draw:name="img1"><draw:image xlink:href="lalune.jpg" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad" /></draw:frame></text:p>
|
||||
<text:p text:style-name="FigureWithCaption"><draw:frame draw:name="img1" text:anchor-type="as-char"><draw:image xlink:href="lalune.jpg" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad" /></draw:frame></text:p>
|
||||
<text:p text:style-name="FigureCaption">Figure <text:sequence text:ref-name="refIllustration0" text:name="Illustration" text:formula="ooow:Illustration+1" style:num-format="1">1</text:sequence>: First
|
||||
image</text:p>
|
||||
<text:p text:style-name="FigureWithCaption"><draw:frame draw:name="img2"><draw:image xlink:href="lalune.jpg" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad" /></draw:frame></text:p>
|
||||
<text:p text:style-name="FigureWithCaption"><draw:frame draw:name="img2" text:anchor-type="as-char"><draw:image xlink:href="lalune.jpg" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad" /></draw:frame></text:p>
|
||||
<text:p text:style-name="FigureCaption">Figure <text:sequence text:ref-name="refIllustration1" text:name="Illustration" text:formula="ooow:Illustration+1" style:num-format="1">2</text:sequence>: Second
|
||||
image</text:p>
|
||||
```
|
||||
|
||||
@@ -21,7 +21,7 @@ Chapter<text:bookmark-end text:name="chapter1" /></text:h>
|
||||
<text:p text:style-name="First_20_paragraph">Chapter 1 references
|
||||
<text:bookmark-ref text:reference-format="text" text:ref-name="chapter1">The
|
||||
Chapter</text:bookmark-ref></text:p>
|
||||
<text:p text:style-name="FigureWithCaption"><draw:frame draw:name="img1"><draw:image xlink:href="lalune.jpg" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad" /></draw:frame></text:p>
|
||||
<text:p text:style-name="FigureWithCaption"><draw:frame draw:name="img1" text:anchor-type="as-char"><draw:image xlink:href="lalune.jpg" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad" /></draw:frame></text:p>
|
||||
<text:p text:style-name="FigureCaption">Voyage dans la Lune</text:p>
|
||||
<text:p text:style-name="Text_20_body">Image 1 references
|
||||
<text:sequence-ref text:reference-format="caption" text:ref-name="lalune">La
|
||||
@@ -38,7 +38,7 @@ Lune</text:sequence-ref></text:p>
|
||||
Chapter<text:bookmark-end text:name="chapter1" /></text:h>
|
||||
<text:p text:style-name="First_20_paragraph">Chapter 1 references
|
||||
<text:bookmark-ref text:reference-format="number" text:ref-name="chapter1"></text:bookmark-ref></text:p>
|
||||
<text:p text:style-name="FigureWithCaption"><draw:frame draw:name="img1"><draw:image xlink:href="lalune.jpg" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad" /></draw:frame></text:p>
|
||||
<text:p text:style-name="FigureWithCaption"><draw:frame draw:name="img1" text:anchor-type="as-char"><draw:image xlink:href="lalune.jpg" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad" /></draw:frame></text:p>
|
||||
<text:p text:style-name="FigureCaption">lalune</text:p>
|
||||
<text:p text:style-name="Text_20_body">Image 1 references
|
||||
<text:sequence-ref text:reference-format="value" text:ref-name="lalune"></text:sequence-ref></text:p>
|
||||
@@ -55,7 +55,7 @@ Chapter<text:bookmark-end text:name="chapter1" /></text:h>
|
||||
<text:p text:style-name="First_20_paragraph">Chapter 1 references
|
||||
<text:bookmark-ref text:reference-format="number" text:ref-name="chapter1"></text:bookmark-ref><text:s /><text:bookmark-ref text:reference-format="text" text:ref-name="chapter1">The
|
||||
Chapter</text:bookmark-ref></text:p>
|
||||
<text:p text:style-name="FigureWithCaption"><draw:frame draw:name="img1"><draw:image xlink:href="lalune.jpg" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad" /></draw:frame></text:p>
|
||||
<text:p text:style-name="FigureWithCaption"><draw:frame draw:name="img1" text:anchor-type="as-char"><draw:image xlink:href="lalune.jpg" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad" /></draw:frame></text:p>
|
||||
<text:p text:style-name="FigureCaption">Voyage dans la Lune</text:p>
|
||||
<text:p text:style-name="Text_20_body">Image 1 references
|
||||
<text:sequence-ref text:reference-format="value" text:ref-name="lalune"></text:sequence-ref><text:s /><text:sequence-ref text:reference-format="caption" text:ref-name="lalune">La
|
||||
|
||||
@@ -923,10 +923,10 @@ link in pointy braces</text:span></text:a>.</text:p>
|
||||
<text:h text:style-name="Heading_20_1" text:outline-level="1"><text:bookmark-start text:name="images" />Images<text:bookmark-end text:name="images" /></text:h>
|
||||
<text:p text:style-name="First_20_paragraph">From “Voyage dans la Lune” by
|
||||
Georges Melies (1902):</text:p>
|
||||
<text:p text:style-name="FigureWithCaption"><draw:frame draw:name="img1"><draw:image xlink:href="lalune.jpg" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad" /></draw:frame></text:p>
|
||||
<text:p text:style-name="FigureWithCaption"><draw:frame draw:name="img1" text:anchor-type="as-char"><draw:image xlink:href="lalune.jpg" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad" /></draw:frame></text:p>
|
||||
<text:p text:style-name="FigureCaption">lalune</text:p>
|
||||
<text:p text:style-name="Text_20_body">Here is a movie
|
||||
<draw:frame draw:name="img2"><draw:image xlink:href="movie.jpg" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad" /></draw:frame>
|
||||
<draw:frame draw:name="img2" text:anchor-type="as-char"><draw:image xlink:href="movie.jpg" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad" /></draw:frame>
|
||||
icon.</text:p>
|
||||
<text:p text:style-name="Horizontal_20_Line" />
|
||||
<text:h text:style-name="Heading_20_1" text:outline-level="1"><text:bookmark-start text:name="footnotes" />Footnotes<text:bookmark-end text:name="footnotes" /></text:h>
|
||||
|
||||
Reference in New Issue
Block a user