From c8ee9dc545557117f8bef47bbf18ccce7f61ce08 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 14 Mar 2026 10:52:26 +0000 Subject: [PATCH] Docx reader: preserve non-textbox content when unwrapping textboxes. Previously, when a w:p paragraph contained runs with textboxes, the entire paragraph was replaced by just the textbox content, discarding all other runs (including image-bearing runs). Now we walk the paragraph's children in order, grouping non-textbox content into copies of the original w:p and splicing unwrapped textbox content in place, preserving the original order. We also treat text inside a textbox containing an image as a figure caption. (One often finds captioned images of this kind in docx files.) Closes #11510. Closes #6893. Closes #11412. Closes #5394. Closes #9633. Co-Authored-By: Claude --- src/Text/Pandoc/Readers/Docx/Parse.hs | 62 ++++++++++++++++++-- test/Tests/Readers/Docx.hs | 7 +++ test/docx/image_with_textbox_caption.docx | Bin 0 -> 19882 bytes test/docx/image_with_textbox_caption.native | 44 ++++++++++++++ 4 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 test/docx/image_with_textbox_caption.docx create mode 100644 test/docx/image_with_textbox_caption.native diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index 0a9c2ca0e..6e9889db1 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -160,8 +160,10 @@ unwrapElement ns element | isElem ns "w" "smartTag" element = concatMap (unwrapElement ns) (elChildren element) | isElem ns "w" "p" element - , textboxes@(_:_) <- findChildrenByName ns "w" "r" element >>= findTextboxes - = concatMap (unwrapElement ns) (concatMap elChildren textboxes) -- handle #9214, #11053 + , _:_ <- findChildrenByName ns "w" "r" element >>= findTextboxes + = let result = splitP (elContent element) [] -- handle #9214, #11053 + in [element{ elName = QName "textbox-image" Nothing Nothing + , elContent = map Elem result }] | isElem ns "w" "r" element , Just fallback <- findChildByName ns "mc" "AlternateContent" element >>= findChildByName ns "mc" "Fallback" @@ -169,6 +171,26 @@ unwrapElement ns element | otherwise = [element{ elContent = concatMap (unwrapContent ns) (elContent element) }] where + -- Split a w:p's children around textbox-bearing runs, preserving order. + -- Non-textbox content is grouped into a copy of the original w:p; + -- textbox content is unwrapped into sibling elements in place. + splitP [] acc = [wrapP acc | hasContent acc] + splitP (c:cs) acc + | Elem el <- c + , isElem ns "w" "r" el + , tbs@(_:_) <- findTextboxes el + = [wrapP acc | hasContent acc] + ++ concatMap (unwrapElement ns) (concatMap elChildren tbs) + ++ splitP cs [] + | otherwise + = splitP cs (acc ++ [c]) + wrapP cs = element{ elContent = concatMap (unwrapContent ns) cs } + hasContent = any (\case Elem el -> isElem ns "w" "r" el + && any (isContentElem . elName) + (elChildren el) + _ -> False) + isContentElem n = qName n `elem` + ["t", "drawing", "pict", "br", "tab", "sym", "fldChar", "instrText"] -- Search textbox content in the run's effective children. -- If AlternateContent is present, use only the fallback branch, -- matching the w:r unwrapping logic and avoiding duplicate textbox @@ -475,9 +497,17 @@ archiveToDocument zf = do return $ Document namespaces body elemToBody :: NameSpaces -> Element -> D Body -elemToBody ns element | isElem ns "w" "body" element = - Body . addCaptioned <$> mapD (elemToBodyPart ns) (elChildren element) -elemToBody _ _ = throwError WrongElem +elemToBody ns element + | isElem ns "w" "body" element = + Body . addCaptioned <$> concatMapM (elemToBodyParts ns) (elChildren element) + | otherwise = throwError WrongElem + where + elemToBodyParts ns' el + | qName (elName el) == "textbox-image" + = addSplitGroupCaptioned . splitMixedParagraphs + <$> mapD (elemToBodyPart ns') (elChildren el) + | otherwise + = ((:[]) <$> elemToBodyPart ns' el) `catchError` (\_ -> return []) archiveToStyles :: Archive -> (CharStyleMap, ParStyleMap) archiveToStyles = archiveToStyles' getStyleId getStyleId @@ -803,6 +833,28 @@ addCaptioned (x : Paragraph parstyle parparts : xs) = Captioned parstyle parparts x : addCaptioned xs addCaptioned (x:xs) = x : addCaptioned xs +-- Split paragraphs that contain both a Drawing and text into two body parts, +-- so that the captioning logic can combine them. +splitMixedParagraphs :: [BodyPart] -> [BodyPart] +splitMixedParagraphs [] = [] +splitMixedParagraphs (Paragraph ps parts : rest) + | (drawings@(_:_), texts@(_:_)) <- partition isDrawingPart parts + = Paragraph ps drawings : Paragraph ps texts : splitMixedParagraphs rest + where isDrawingPart Drawing{} = True + isDrawingPart _ = False +splitMixedParagraphs (x:xs) = x : splitMixedParagraphs xs + +-- For textbox-image groups: combine image + any adjacent text into Captioned +addSplitGroupCaptioned :: [BodyPart] -> [BodyPart] +addSplitGroupCaptioned [] = [] +addSplitGroupCaptioned (Paragraph parstyle parparts : x : xs) + | isCaptionable x + = Captioned parstyle parparts x : addSplitGroupCaptioned xs +addSplitGroupCaptioned (x : Paragraph parstyle parparts : xs) + | isCaptionable x + = Captioned parstyle parparts x : addSplitGroupCaptioned xs +addSplitGroupCaptioned (x:xs) = x : addSplitGroupCaptioned xs + isCaptionable :: BodyPart -> Bool isCaptionable (Paragraph _ [Drawing{}]) = True isCaptionable (Tbl{}) = True diff --git a/test/Tests/Readers/Docx.hs b/test/Tests/Readers/Docx.hs index f2c05332a..c4b19b6d7 100644 --- a/test/Tests/Readers/Docx.hs +++ b/test/Tests/Readers/Docx.hs @@ -223,6 +223,10 @@ tests = [ testGroup "document" "image inside textbox content with duplicate encoding" "docx/textbox_image_duplicate_encoding.docx" "docx/textbox_image_duplicate_encoding.native" + , testCompare + "image with textbox caption in same paragraph" + "docx/image_with_textbox_caption.docx" + "docx/image_with_textbox_caption.native" ] , testGroup "blocks" [ testCompare @@ -487,6 +491,9 @@ tests = [ testGroup "document" , testMediaBag "image inside textbox content with duplicate encoding populates media bag" "docx/textbox_image_duplicate_encoding.docx" + , testMediaBag + "image with textbox caption in same paragraph populates media bag" + "docx/image_with_textbox_caption.docx" ] , testGroup "custom styles" [ testCompare diff --git a/test/docx/image_with_textbox_caption.docx b/test/docx/image_with_textbox_caption.docx new file mode 100644 index 0000000000000000000000000000000000000000..6e7da567f28e3c784936262d5135b26efce599e8 GIT binary patch literal 19882 zcmeFZ1CuRLwk}$(UADE$w!O=?ZJWDn+qP}nwr$(St?ttuw_lv|e!)F6Vr8tDbBxKM z{BmT50Tj-YL@j>`j_wYm{26DZ z?Y&L!TT`IAC9vZ4K+E_0; z3nR#ZxwQpNf&~|wvo;hqrEn(`Aw&<}Z*a<`TCqTbo^``Ak#fA3KJByl>;@;vmfllD zUaNEnV% z=$MsE1|{Bg&8{ea6*k<;DbI})7_o1JtLXKHE}BN`?^H*9Jz2G^Cib4wGj#~l$JI%2 z(%-@#(!$ABgSJM`A0q7fYo;N|#ikzZ`}oAjn!vip8=*CRr^kzAUa5cm7E(BO6=0Hw zO)EL^5wp}yt3K=5n^=koY8|jMqFX@wc!##|Rx4<1b>Z+!=e@PkLzYaY!CkRLW5DZH zok?N6eBlz3BPUv<5Tl=&RtrG{hv4)>q7zzdpHER)Dll^+d+!=HqAF95RHR zb`rF1Fq4#E!a50Op=_n^~{o{T@o2*t?9{KM5y!lp67VqZd1 zCVP+$DFmMmi=oU2$fp+!i0bpR?39+DQ@j{+Qt20$%tbZa!OOUXx`J@prZBu9jd^R6 z8L4BU`?l(|1{BZYTNonE$segOE$L{9Gh+2CATe(`X3yO6yNd@y1F0Z#Awp^2J&ZLG z)xVCzjxcyxuJP3xbarj9GPL;af1K2R*AJWsOvDv<000Rl004xaP5EnMZ%AusW8h@< zpye>3Ot@lOazPfw5{C5DIeeK#^L=!}vRA9)&7d&;6YI=NF$zvtMnj2O;pCTpY z>$IE~TQ)lVadR}u$nV}5sv<%PPPf03+^vb3`$qV_jyaEzJXPpDU{0fKJi9ci55T{w z5C_AmDQWH^r~jG|w!N<>dw(}?*A+!I3^0DQ0Kt1LOaNWpXW|U=#pe-BvTzZ<9k8p1 z5Eu?+Gabl{FyTz*Fd&~HOE5C!`oWtCBlmj6?G=kVs`Qz?zRx_!j_T5bRmi1P2Gqv# z>e2B+_sIbbc7y5j0)QaaO<0ti!3ivqPb^aAtOfh{l&gqt499H2SAEeFJu%Dg8;CA0 zi+&8pXt%~cul365zR;QG%_!)b2B993@6toy0Ue3X1YE~4q^qY z%Cx(%FwO!L9E#?%RpH62f0oSadGoYhhmk|%%4P3{tNca|V9tnvOPncu!GV z69=drolo-|7s<_Q>p@_10R5=tr}E+Lt#}EK5!UYY-MNBFprx3be&O_qRcDmtFNDhy zXeLUHEvw|5gX-lS>Qu@qHsaIIZH6d}>+%U@(kdTg_(0N_0Ftml_^zBNujN2RxK(<>ykELDDwABMtwM8peJ{|$3fua?}{ z7FrHg9f&3%T6+tP5ud*jEYZv#kgW1qn5u++Iw^s0&Oyaw(a=aZWdNfXVhR@|hxF_~ z@fv>LlXGh1;hnnzG>UUI)m~p{rb|lP7AICk$zxT$F{d@6z^oo;CiZ|^wUjDJh7%djUaT;->AmH6pHtqpWfG1(>ZJ66^d~;PBU-AvcCC)Vj+_- zii|C_pbOW+_w%XwdZ~86igK9YU6>Zi6Eqc0nFf2Sr5Kpd3q&RTVUHv7nY|}@e^vl;Z zlHUsOEcX{quyDm{3KQQ_UA7y`ALRQ2sEsfny6CjX+?P*}Vj_Biv60D{{_UO}mDv*6 z>Az9zj}08Z@>?u^SNlQ8GSdmpBq9%x6=@gmSmWt6jo?;&=lN5({d!N?jSKiTyjZn! z*`k@!M+l_?M^oPZ4yQ^Ag2=w1M$kH87n<1tXnhsJO_hmv-#4&mI zftQ+Mp0`mpuK!p*AP9vc7RPasO+p_CS$leZG4Kof(|vckrDRM6d4&Lq-KDRa7u%W7MSje7WRnKU>HmI z#bqZX=KN&&CJLO0m5RXBn6p;SuRG;265yT)yl78z^7eFSyP59BNlepb`7}xMyn`wf z>u@?et|fw&)*n{N0a5E+T1+!)LR*k*$Io^ZdYl&HC0w@*bx_yTmV4SaubcEORsb5L zZi+uZl3xTZMGSf1ZqU>*_T~)Dvn*<#@boU$Mprlwik=MKHxGjtCKxBs=+37o6z5$T z8Uj|qbM!KquvqViHS&Vbu8c{%;~HII`49BK@6dRMhv8ZUGtRI;kHNscX~m0~8(t-r zk?IJT7OvNB#U-AV87HQ9(?4zxl%drsO<^#&hy5quLaC=m-}(IAb*AYi1J3ykWL35V zWOj8_2E6QEa>VSzGR}87{D2VY7j$@?Ln(>Avsne>nxfMYRVUa5(Hqa z;`_^}Q(+Z!d~9h&Y045D>r+8LpPvU62e1hx#{*GsakyD}=h8piN7Z=>;a@h>hBS}k zwmy&AM$4mIigv8c_TbMM2M`Z=Nu6~yS1G=b+&p8exOhjar462e0PjqW9;Oy~<7fXx z0cq=H<>CT=u=4-Y-7=`FD(M(rja|+*`0VP)L;)Uv^n}5?^tgi^{GwVY1uY9$c?#=7 zqlbtSnu;Lkxg9Lu#uiafGF0S3GA2dS_g+5^rFWakg&&RZHYJ+2t)ATBQpi8m#c=Iorgxq3nCn1lk0MSF6jXbK92#a0(9}<_N)G7DK6&b z`3W;o6KdQ@MTyHNm<{b0dDuEtRsS}oS4$gft4v2m;C?_p;6S|oPYv*%6m?A3`bHl` zcM+V+tibMqlq`TV!1#m{TVnS0QfA4f`Q%^{@srTY=``_D8=J(K-%!K6-!#hA_FTN; zBO1*ID=RnaIuJ@%ONf{jG{Gk7?@aHtf>lkY>Zio3$vd8N_2- z;J&!L-}+r$UdGAwmB8l3VgQ(Ahap7)}u`{kj!{9Q#Y+pCAa%>|(i zJu0;G{$#{bK~jeEDa95GY^g&{0E82XkN?T+j4o-fL$n$gG!tLwKCZjaMEw-_$ z4#jzxNZHDK@u$M{Wy#$38KtE2$ox!C`@)Lx*4-1LlBus0o8l=m5dbyk;|l&j?yDh) zeTLf(twM6ABc*yv)?A06<^@dBGY53Y7GRW7YG=bYp*HFh`#OSxqvxx&x$&KoMbk}#Vh&Ym1?u%t2Bl_^PIxe2WoDlKWrFx;#|%_ z7!RY4;Ms6oE!QwKgwCGIr$TL<4$MyOQ7rpc3EbiLjs{uA$IkXEGN%C$Piw|_meRvW zfSM(8Y%|S44eOT$fy~$s3vD&JjNaqQCh3*77h_rU4gnDDBjv4}t%9H05=t2%&r3Mf z{qzQg(51hn$yq4@%&NUpFNT%7whA~kENzX|xf#cbf?7svP?nCe2F4JvvcTf|Yf#x^ zORiX^rpzOorFjnihFd4v!&&fdJV7h`dvnXF$1aUaAj3I7PZ@B`L$?O2X+u2 zE!8$xiCKUb8pu5l`-)s*R?cWhqLdm_mtD;rn@sF(oiMKq8g|~YA`NT&6%lG+hY>j+ zkfZUpWw6uzZD01_-s@-*F5XY9;C`hlWD_)IX+j`?`dftn` zAy~*0R?o98D*bHXYCO5`vQ2M5!yLmt6_t78R_+iHuri*a&HT`Z6=CO?2lldAcCU=J zSsY6HX47=uSK!rvK60!Sxy|QRDpnC|h!P1jn7>P7S@+=Kz(b8TX}V%}Z{r?!?yQA; z9+701emBV0z$sem;FgfRYQi)qmAkf5B%*!&$bdx>N!P*65Bv@Pr=HL>x573~Z;EhH zuc)I5u>e^7bEQfhy8Wc`E~;tzdA_kNmp^|tTFILo{SA?^Zf>*G^B3G@Rcjr&fdqhi zZ1NTq+R>L|w%nc^vI#P=apfxm)?Y)pi!g#Hl{{)^MH@E^j28URmp{9*%N*ukY@2@r z%iLge+We!HczAB^koi|U=uzGjy~n5yEvsmEFQ|yV4|3F5G&%cRnAl6)wvt=5v1QuH zZRYe1PqZRCc`p+sdULHCCHv=+%R0BC3tyiKSkI;i%Iu%b@|O~2a7ZUm?{2(0kfs$! zg70P)qusdkhs(*E@nFHja|1U#7I>z9>9$-2sWD|x4R>)E1x}iq7L7`ByYACXwLK>* z$v{RDGQd^-R#|MyVQaT-PQ5vMsS9=I|Fo+;l1c#d4r_%#g4rw??YX4e{9lm4_v-nz*l^%k!0b;7N73s z^9t>*x&`ms!{zgS_woMN$y*=8i>Kr3_2%uk|J>&K{-<_2cp1;d#j_kwS0aeShBhJk zYR`n0p356u^-nBm=8N3P{JqSE+o$=&{pM~3raD*qsN`6{bqADqf)%x${Payb9nhD8 zPlXw<)^WKJ3tR;~`*;uS!*WU<6S;OoD@(YEHF};n1>%S3wHb0Zjd4#GOd2U4aPEuR zytw`x)s{ss0+>kUT%8N-=~CM#TL30Z1Bw0RXT8egXay2Kg_n@IS$jf8&OLKXCpJ_5a(YB7R)D{~vI+=!ZYE z`(DAHK-o-d*+Of44<9TU#PAn>`tE^gL(fKe2~B zGfUED>yR#$EUa)n1Y+fe*Hn<`bbW0dUFzHhwB*!-((faPgTjC~vEBzmgKyDM6=0to zIGCp7iT{3%?6aoDwWKTXr#r%t0{V-O8>kwZrrCgdIa?m0{T%;)2h<+mu5cG%0RTD# z00B^bDE=u)Rz`+qdbDO%dL~BnG)7j&7nx=iC_<<{*X|v@WC21fYJLMDF~1QKa6qsF zM1eSvB(ZUM4WaOXfcSrDsCti9L4-r01L=k00|F1E;OoWhnoY!j6K86dwY9aKWpFe)If+purX{?E*Dq?YbKAs-cWQGcnsb`2 z>?oEIJzo0~m#UMq-PPAfQ!Lg~CBCUN_Io2Y1Al%ewW%tly_r1PZTos}mhwQct8_+I zY<hGVbJuu}8^xB?*Ru zZ2#*%?EZ8Sd*uTAm2$Gf5XDzQ(+Umo# z1Lo6JsangA+iW_^?|8YU)Ykjh>HfUtr_b=GF;uKJ)^a=_bog4SJ)~_VR{*?PTil9Z zYuHdm-stV?^SvUP9q3HV^t(jM5?rc3T==5WIc`9;=pcyD6)`e&uKwO`uIfl_+5UF?Md`K zec`1yYn^sC_mg>KrPe@bv(3rKd-Cn^+z9VXL57>ysm5eFYs*ve>BKUZMrJQ%%Y5T{ zqqX0&4vShtJrdz5X}|r?&C07XUu^4qh(@nb`qwq zgyUHa#OZoVYa)}AbyBN28n{>{`{~J!l5v4+;GW&#XvA^p6}&F? zT(hwEN9GQ;T_h)K7rzcVew+(z4M0fbn7YHTPY`qEe37T7O zBe#xJY>%*)wtDn)yO^!@rszM{B9TnP(5=K-x|I84C=#o~!XRI%@fr@LKn_{Y z^4=y|mZTszwIleiq|C~z9)b54VKABCx4qsgr**L38^aMYGv{7&nameNJ>E`>hd8Rr zwP(44oCz!Lky$e%#u@E#t4&_ww6}=8DbLHA2a$_*`?x zEwtYGSyld^v1ls1+e4DWong1|4--qSN4_;~nvBs745Bgyg5DqgD84=RU5_ttmIjM~ z9jnb2(-Xpx2CAcH$7=*I57eIB^%i%2JA~HT^>()oljVvmHn*GN`7$!evRu5tkHI`> zUZIE?uHwL0AjJdMnqY2qfenIq6*x}NPpk7n0q1))EpJ~#r`HciVYW!(Dl&4$J|H<` zOF6q{G?_}+?pXgw+mO^N8|Hh85d&-c3Y_M7m-NbxY&oixCeif*tWbbN&ij^UW#v*~ zC=Y74-sDhQo@ot^1U?uJg26hd6%$cIi;phV1ZCgq@@#MPh~j+3)N!nJN?oA+{W(51 z=CSPfT#t(io_dgfimN2;H+2s<&?svV&gZ;Bd`S=syVaeh=v~#; zXfO-R63q9yJj{kG7LA;KF8YqY8 zbpO(KY8qf7_@g`fia&G~=iez#xOJN{3oIds#n_TdYuQfZ&k`p&M_z}c2s>@~D!qZX zk>4C@Gg|)MFDDOU%2P7wEK<zmVHD5Ak#Nz!@7xV?lcC~=%@nPRApApT|MIgQ7&?SmG{3M;5mhK@P9 zh5`DkTC;VJ6Alvi&0g|5AH)=0&roqOS#s!A$}<~Y=dC+Crw*jWcGqu-8toCVh^=-s zRIE#BJvW}+4cbB3`|I_%!B(-GrcKJ%i4*`cSTbk&pp|g= z?Ypy>Zd%Ud@Bwx`K1n4y%xiPQLJh(7=UkVU8IOKApAyc-2E)+~H|XLe{k-ElA>!QM zu+zN=t=1ZDC$k4z%BIyCOk-VsY|`R71_^4*6xuT2HbB?QUbytrgdyqs(`EVnmE`eM zrsp=n+1^gxbk!AfJk*T!$8%F4vQ z;t{X?S*Q*VM&s|^Dm9u9TB|%>eiZkRH&fB&BTEdi?d@=HFnn;`raSYHc_n+~A{V93+U*a)oZ&2@^LGd_du|8$a+dHk7hKx^#)QbfeFErl%>j%L;tU z``fdX2I(Z~YWsMBHiEVfbG@cG#SyfRikw_*-fUyL#ZI8Xd{LZAr;BS+i*;(6&JOfZ zc5#xy&Uv2qONjQA!`2d$MY5ktfOesf;k z{iMZde-NT+vGWrk>i-Cax~CzxVHiT0NLwqwLUxpjw(HEqWFM})q!>0H>daCjab;BP3g$M&BI(oDcvn$b z*?bedXeH;z3k}V@8ddE>(xkj;S$XMd$5_L{^&?X@WIlgBZx^phE{sp6vyQWy4oBhK zxAt@Ydbsx4Rw$NsUCyBv1^kAG4FI3%Qbh?ts=}Y3V+KwBn*LefIClkk{ydx}*4^FV zxS2ITl#)IhpTQ$t(=LbhD4kf~8RE&W%jnuwd|Q8JBjHF);%Pk`-4sUu8MiCW>yG_K zN|9(>YPaXB%lh^OlC&Y37P#P22;EP(1Fc3Qnn!+3sKy@x^=BT>w-N47Yk8s1rMN7E z4fLWSc%o~$=H;)4v1~Ey*0*~?FO^>Y3>ORwy`8? zjSf4wO;zY+tyF#dh)IF&GU=Y@x9YC9vX`5FxI*opz)1aT8nS!8*np_$DG$Ppwi68G z@smz615m3M<|uuaIc(}n*0Qd;zBYBq?v@9$Xsvn7ZG;G+gf%HCY0Th*M>SXqt+ukb z>NfhsGIO-5)IA*!r0bj^5eGURd2DP<@W%pl-q(00td4lZ@T`5vD_5xYI>(A}%@oho z8A)7k6ot3WaH9Er^tJuwn(8IWY;!sA+E=F@ngsD#9ge5sbY|bLEcLYIqpL3Q`V|oMOZ{27mWg1WVHQ-%Zj5`aed~%kd_P zGmCwS^$!pK&wxt7j8)(q7yv;3Z-D=M8|-LmWMxGA&nx{ub4%1F!?9SAyV2fwp&kBy zFz-jh+tds@vy56J)}<$4H#TwQHyRrfxw4Xgh})kP%8E+B!8|Mpki#&|%t(Bv1O~8t z622AGauaG?k*vZ5)LE#W?>}dGxm};0x*m-!2Gcu5(V+zpQ#X5*9&oT>)ZmNtCAr&l z>zKAg#gJhTj3I8=o2a&^H1m94K(!OEBSo$HQVA10e zLTKTl24my+Z>6A87Rpzm6UMbj`{~QNyE730UszKwJdu4!6vzStk3A>IcSPYtjh{(O z2>xkx2rwNFL4?5MCr>qeDud`KCfNlT6r9dr?|T<$madb zV27E^%FW`%i+98_1-MYk8|M0NT4q(Lj{lso6^ssTW4rcdkAV-1| z27Q}VAggD!PW8ktMp;UDl_F1Ux<0jVaKtlUPG&ff7&d$Q4gw|s&7TBp+r5hzI=`Ou zoB#-P8YS^hd6cj6=wgkv{1XOY_ESgG-%CNrcp}hjsUu15zzuEeBj+mB(peo`u6OUF z^TnhxUYXUMG|iE5bhOV;#Wr8AUxoM2HoRS)Pn9^dcQ35n9*;-Syx$U?Os1aCdz0g6 z)!pB3)!ErAh2D=-5!&6$Q{b1BkJHTIFi0=pluA$C|kO{wu4fvi74RdTJ z;e}8&!PA{Prt!5_e3pxX!FD@CWQc14Y=mn8H;f{j(VMSMi_|G?0?iQD(2nQ1u4wp0 zni~C4XrGIaLfrKgFa%d}T!a0_#qjClN~+ z)d;uugP<(fV$ z9A@R99~m{p#Ki9oZV(nV3vY0|<%E?b1)XkEU?Ui#)Kojk!Wx57UY4!vi=(xniY*a< zQ0LBOi1l(YrHnWYO-#R+&a{o0jz8~TPSZ3*i4zg2DBi-+;EdQTWO-qezr=~a&?o*F z$gOR%akJf?K_A2Z0f6zb#SpWyy~^?)f(c~^SRb7;R#T_}Prs2JTC!g-Sftz<>uSo# zimVcrl|odk{D-nJ`uo-#uY^wf`)0$BJKJp-mP?|ro;zR47=-xwaR8x@wEipptfKZV^?KTe&kQ(>Fl=^n4n1_)6G%G`{ zgV1r6aOVQFE5jFxeaEU^>P|cQ9B6~gb-osiBkEt$eV;bfYa)pZd5>}p|Y9&OqQ5YtA_bK{})-pJA zFw;4Ek(+pkv|Eq|h{RHOnD`{l7zY7wQR7rw{B}`6a!HxO_vp}pja;H2)#-L$5{zd1 z!m6j2&^U`E9`5ne>W=tZ(*SV$e$l1CR>e@KrwH9eJ%!($XfV;4z#NBv5zxn~lM?}h z1xE^*g9DEJIP3Ss+tToa_B|z>1rag@O}+9D;$0z^FNrfZc$nrFwNgJ{jbZr@3mCWC zilItFHRm>LeM>avvv_&7h_ek64?X>r(%MuUv>am3SvC<(y5a2?>IR|B!)gQ7SiZXz6eVpRxNw*5wRD!i)fTrma@hJV#6Q7%@5ltS@pv_+=U7As2q6HQY$ zE^sQzZq^zt?zIj@;U!DP{Gr`j?pDa(Z=(WbwOyU$?$x?*sfg<@XNa8q>3GTWu~JTEx=3Dor zHOgsRep&5BK3-^pdWjtJAu`T1J%kLB$Y{9z#QQo1kuduJ#xOZnRL-FmfoR@-AWz^**xL%RxK6&_`~SFh)K~91$^SY;moDT5pX~e^Gy0 zvR%2ppx98Vu12Y#e$!?gbn|xvh9 zmt!a}HT3puleX<9;r?sJXp2b5N&ASeB)Z@8wgNI!efT5>KZ4 zRkyV7y(C}E?(p(yN(0BxZ`Z2egX(!Cu2Xz1-OC-TsSI-X7bgmj{b07)E8#qsQ7qOO z95MDiDbPkZ$@&*(6^5uepbg((+#U^>hQDXDnrff(A~misfgg$V9Be}~@+o>`fY{oSLscUE!j62vB0QO0`K{1W5p`)&>|{*s{0aIqaYQ|4-j{as_Sf*=wX z#0NsFG@i7t;HqYKeN`X_wAy5me!|qB5oRB~?F{=6&VtPFxyQOgnI8L4pe6#0d^`H? z_s3%gumc^>##gzLL6N45V-7gYj5tjeg_N&^IF8s(Vn3K_#~@s%KGOC1LsNy{`O8EE z=NK!(K1KW5uo?A*RyRoA=^CFJ??_Ty*rpBJt6fV=fB*g=5F-Q1;iWIdL%*@FCu{O3 zvKaCbe%v9xKx+ItrnOx(Pumc~WvdQNOw(cx^_y6q^K0X}alax%spUjqBk-EIGlcav zuVfjR7NVjC4Z`?Jf6h5p;-EVpiRL14K3p;b9=8!LG(&gq*$tjr%VyR+JM~h{+GfMp zI;Ex4E?uf(K<5!3YF+%O6PL=8Q>>52un}@osaleuIe*4bBi(>Wd3_9TBSSGCb4FO- z*K`a5vtp5lQ5l=uJ0*T@hdhWrU^-SR6VsN-9Dq+wj!2dPYzB6QjWkbvuaXDNV&HG) zR+9g-yk41y+gxE0@=>>)vI>mbL5VqLj9x8wbs|w=2jRkuP^NW+B82}vblLdFz3(}j z(=PiEBBVO&9cKZS|Iy^F=_-$w@=YX#TZgdH;zN|9iA*r)>SbZ&m{y+(tVQ*g=wIn@ zc1#CMN$(uVP3V?&0TBo2%ZO=arsjOtpd%-y?U|1#RCM(mQ@VKi;Mp$#s|jor&+#xn z96PW1-Dy!N_yJrcmsLqGQQ9T=HKjk$u=HL5oj}Keebu312gtogCK}Q?0~%LAnX1&Z zE+|DQS~uFo&TCa!yrh97Km;p2~+TX{Xej z%bSR+b2+oR!#+w%9oCoI!xkI>w?3h^reb?s!!hK#5k8Y%ve1O1SkqpY>sVM4aKj=21&u^_)KcXKR)lYd$y!1pv_P0002@?oN)v!i~mc zM5ej0K5uukzh6FYJ(GBcxuBSSPi~R!o|>i}mp|{@q0+ea)v?+SDtzwpmjxWY6}DlK zN^YW+cgm7bCWW_#2z_z!tZZjTQAC^x#D)j{dKJMyg^+Q;;#KBa5==Fgs2ww3Gr(KR zSDY0ejWJ2xIwhiUg+MyRWMl`*5eyJFwvX+el0@97(uHDxu57zWG!=3>#Y0sNHWU;jlNbxg|sVwFWe2Cxzx-mOhrGH zVH>}zm#kTWOFoKTlIf$e>F}f>pvO!{t=D<$2S?40f`vW%OCt%_v=y%ArqYde+Zmmm z#?6j%>v%mp(ZRgDF^{0&UeAccR^?pr(OlbJo?LC&Pl*b-4y$pX>2Alc$vUHoevXIT zU9RInJCRaPkgPrO?g3AKHQ}H!wdGbSf|SaU567%YbXO^?hFd`1etmEL`#1(}kh2{`XX#j10a` zs%0*(RO;+OIYh|hQs4@cb^o_YKS?5@)vd&oWJ-#K5ajx=$AB&z%rGkuZ>NJ?dW#S z?svPt#SXkzR~MJVo0%228}_9rnr7*Rue!<91rrLX_rH&2`pu{osWe39ZYKDmDdRGU zdJm_%tOG`M9V!0k#CjYD2SQeU2yFhJy^D@8)3VDGSq zi#-gcCBWiQ21Cq1vzrK2)iEndF@mXvp)z(c)1~jH1P;g0MrUHm$IJ^8W|BvVH0`0l zoNYDe*uH|ObrO9R{R5W9tm$uEd9xu@A=sWeY7^yI8wIGZyJ%#uCXH zCwpc9rj4@310vKiAwDiiv%kZO{7u?^NoR*XJw=>6!~|;n^7EGzi$g#Vqh%DL5RJ-B zdM5SGk|-j;T#MX0G~!?y}*_v~`l{?1sTZ}pU3?|o`f|5%;O9EM9vr0j^ z|Gu+zDSM{?j$;(q4%^7`ZjjKzk_yJ^l3eM7(IHS}e%$Sy(DBRM?1R}}rN&;I(jK51 z54fjPw834xTH-qU#L@mx;=+(sFwesAf_~A@MeB78P63NEpJI-21W#i+9u=o3$_kCi*7q9z zB^{I0fJOY1~~k!s8fAp^n4j*57;wQ&+~SlC*p zy@_&SYm~kPij^MfRelITCCbx&_{_~!c`NC*?!5`CQ`+4N;qN$C3iYzddwR<0*VIUx z?l$E5rtW%*%=x?8T3?kbQCPthSx2hA+Y0Z>)Jf$|{$_7_;(+pj-L&Y+k>lS39!#U$*%Y+wFX#gt0oJAO2YM`9kFGqwK{QI z)S#nvpb}}`>6ZQX)M1=xyV!KU+W7RjbroyDUx z_hq3F%!xE#4hR0>uoDiv{Rao$f3`s)nCA0?{DYK%;@2|+ML|yFzpD-_gT8)P*z)^a zepteyy%xJk)c{@wZOSbht@VGhriqj+Q!52=e>?*KkI~C|fZd=q=`mn@ejFpft_U{7o ze0r?zqU%DUXL{@ooy$U^Vm@@KLrZ#?~Nx zfLCL~kv;o8M90aq!`#DWZCzc+@NGo~{R*uK+5K#J>uLqJ{JQo5-@UGKYZB>wsygZn27&sy@|ur{U{6~ zv-x3^F_$IIFAM^yScviYq4CIt&Xq;5|<$bqpiRSR9=bvnUV^1qu9TeQKfD#9$B8m zQ8=U|ktx4lM%=vc!zyL2*|gNB|&A*oP?=Gx=XynuX zZx=s=|Ns5}yBGF<5B|>~<{DuUK%#xph$RmJH}Cgo)S4z%Z-SA&L{X*y~vZ%IZgi02tvhO_3aT>lix3Xcmui0wDT%D{* zT(^QzH#v4-yyoQ4h6P)G+Ns^D@vIPkZh3pD5(jVRC~aqo9>;iY#a4D^ zanmOj_x8$tcbFWjJRIvZn?Q4YJvH3s>DU=OzQ1Glm~t^fYC@vhx{`dYdjn)0j!Bj| zs;(lkg?`W0EZ86_YQk?$Zi2?S%({2o%*VQH(UCwsf4+==*Kvm0NY;v?nXmL@v~I>~ zwenfVN`zqKydZghC!HT%U3Dc^VfW>aA3CSrx=R11XgtflDR=JrqI|#kpWmdu;cOGE z`bl~m_yIJK{~iASHPTo34?H6yVN-ga4nFWY)+Ko4nF+#Aocwj&Ho;8GT|^kf>;+sd5)^+V*m5YvJVlj zrL=_~SfL4U+S;U;lvQf>&npUh=n9@HF4s867K0b`g6J~R^$o`@*UVgaeoN8Yz|pL!Tsd9H=N2>m`XkswP2u* zVy}j^JEmmg=uz6JKkAUdxaV|v#vCFDLU&x@vC7~*hjcx!2)a}|4(JAR^z!mg>~)w! z7}xA|XLeuHl5jhE=zI3Z{Wgy4v8aRleL4Gu1c;e({OcKlw(msEm+%4<*|RD?@9SR= z6_Braj?{m=QH+zIsnq5Sm^+}MEo;tFNUT$NHps{ZYq6}G=p5?5Mn7<_?H^zZ{P|SL zikP5W7T(RFcU(o6`cTnW21|lE(f5Tsjk;0lq2_n{L9@ zTYiCD?Oc|TxxbEH9B&?1S2~I~IDw#Mj?!@u#IZ9Q$nw_?cgJS#^+X74MNrCE)b{p> zf38$?x;Glt%o_ZE-7_189T(VFjqahl5F`7CngtB$npNn-COhL%8fU{z!myUjZ%Jb=a2nb3o`vyGP2RKF1?HwFv8>%xINO2vF9b}u*-x=BSyZ0W{ z1o#snFfr}aBle0#p7pt5VX#^yjQExR%PIG1V9niKQZmlTsi@ zZ_P)P&e_mO+El#RF)H8N#PLi7@jbFbA2lG@5zCA;S#tEvQ8_meKr1IUO2y=Jq~c-F z89&Ay8e3Py;Vwott*TjLj$BOIIAQM5SXSO!*`@1o%u%*P#bqbJXwK-i2jgic)$X__ z5lKcKBE&14#f>TKloea-6eu$#A8#x5luX2OC(1sc%h}L>?9x8TBZ$z`MzH&!%ihL0 z0xRrLp;ujoY8EC?Nc#NB`tR%i7!6 zI?x)}*#A>Be!ivfzd6xAN|O;MC*x0t9Jnp@0atJ>t2|qwp@r!z}HETo`3=r zl*3Qx{_c&Um?J;YmsG)wF3KOKBfbt}5p<2Cm@$HS(~pSdF5qsjjUkS0I}K(j;A#ZS z7IRpEsuw{RyEjs=1YE?i&d{-D!JSYYSeX7-Oisq2Si%nDd5@^OU5=7hOk8_@P`D{A z>_hS)ux)Y?PBx%Hw_hlmRpqvnHo^eCGQ}&%B>%^MgwJQIB<0H}3c2p-H+U!eg()Q0*|91-(Ys$(s{UfDIKS})X|0$(~-~h>ocn>q6y;_^cyZs;}k4P}na^IVPc#`*&#y zt+hE+aOjP&KGdm<9prLZb8Ef2QQ;n!%ct|kK)L|YxT*#r>RV>G zOaF%M%o}!x1xkg|XT7O8W_{iFg;K@4^)iCyKf>!eN_Xg#2gjp&DoAhhlWHYzZV z7D3mJx(NrNnStRLaIXcvEjj2Wp>M)Km?a>EV@n3Q5$Fq85Jre=LXALQ&Vp_P`t%>d z2pI#Y5y+E)=-Sc8+Y#Dd0S{{d2M1#G9bGeeYaOBar2|wmTH77n1oZYG!i1AfP!mvE zhv@oI+YAWZ3=B_Q7#J{G5a`-b3v*=c-R?-*p+$RuH!CoOF);7|Aqy~(PVfTp0BTG% AVgLXD literal 0 HcmV?d00001 diff --git a/test/docx/image_with_textbox_caption.native b/test/docx/image_with_textbox_caption.native new file mode 100644 index 000000000..9880537da --- /dev/null +++ b/test/docx/image_with_textbox_caption.native @@ -0,0 +1,44 @@ +[ Figure + ( "" , [] , [] ) + (Caption + Nothing + [ Para + [ Str "1" + , Space + , Str "Daniel" + , Space + , Str "Schliebner:" + , Space + , Str "Cantor'sches" + , Space + , Str "Diagonalverfahren." + , Space + , Str "Von" + , Space + , Str "Mengen," + , Space + , Str "Unendlichkeiten" + , Space + , Str "und" + , Space + , Str "Wahnsinn," + , Space + , Str "Pdf:" + , Space + , Str + "https://www2.informatik.hu-berlin.de/~kossahl/Uni/Ma1/Cantor.pdf" + ] + ]) + [ Plain + [ Image + ( "" + , [] + , [ ( "width" , "2.3680555555555554in" ) + , ( "height" , "0.9340277777777778in" ) + ] + ) + [] + ( "media/image1.emf" , "" ) + ] + ] +]