svtools/source/svhtml/htmlout.cxx                           |    4 -
 sw/qa/extras/htmlexport/data/tdf160867_image_with_link.fodt |   25 +++++++
 sw/qa/extras/htmlexport/htmlexport.cxx                      |   41 ++++++++++++
 sw/source/core/inc/noteurl.hxx                              |   36 ++++++++++
 sw/source/core/inc/swfont.hxx                               |    3 
 sw/source/core/layout/paintfrm.cxx                          |    5 -
 sw/source/core/text/atrhndl.hxx                             |    2 
 sw/source/core/text/atrstck.cxx                             |   15 ++++
 sw/source/core/text/inftxt.cxx                              |   37 ++++++++++
 sw/source/core/text/inftxt.hxx                              |    7 ++
 sw/source/core/text/itrform2.cxx                            |    2 
 sw/source/core/text/itrpaint.cxx                            |   20 +++++
 sw/source/core/text/noteurl.cxx                             |   37 ++++++++++
 sw/source/core/text/porfly.hxx                              |    1 
 sw/source/core/text/pormulti.cxx                            |    3 
 sw/source/core/txtnode/swfont.cxx                           |    2 
 sw/source/filter/html/htmlflywriter.cxx                     |   31 ++++++---
 17 files changed, 256 insertions(+), 15 deletions(-)

New commits:
commit a2cb4a27f2e56d042b1f0dd1eaead49228b98197
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Wed May 1 12:10:23 2024 +0500
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Wed May 1 16:13:46 2024 +0500

    tdf#160867: only output first element of the map in ReqIF case
    
    It should be investigated, how the whole image map can be output in
    that case - something to be done separately.
    
    Change-Id: I6543c0d238205fabdb0a688e32a2d08423d7a5d3
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166948
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>
    Tested-by: Jenkins

diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx 
b/sw/qa/extras/htmlexport/htmlexport.cxx
index 148285680043..f1e32f9d110e 100644
--- a/sw/qa/extras/htmlexport/htmlexport.cxx
+++ b/sw/qa/extras/htmlexport/htmlexport.cxx
@@ -3078,6 +3078,23 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, 
testHTML_160867)
     assertXPath(pDoc, "/html/body/p[2]/img", "usemap", "#" + mapName);
 }
 
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_160867)
+{
+    // Given a document with an image with hyperlink, and text with hyperlink, 
both in a frame:
+    createSwDoc("tdf160867_image_with_link.fodt");
+    // When exporting to reqif:
+    ExportToReqif();
+    // For now, we don't (yet) output the whole map in ReqIF case.
+    // Make sure that the first hyperlink from the objects in the frame is 
output as an <a> element
+    // around the whole image of the frame.
+    SvMemoryStream aStream;
+    WrapReqifFromTempFile(aStream);
+    xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+    assertXPath(pXmlDoc, 
"//reqif-xhtml:p[2]/reqif-xhtml:a/reqif-xhtml:object");
+    CPPUNIT_ASSERT(
+        getXPath(pXmlDoc, "//reqif-xhtml:p[2]/reqif-xhtml:a", 
"href").endsWith("foo/bar"));
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlflywriter.cxx 
b/sw/source/filter/html/htmlflywriter.cxx
index 624d992fb273..32b97fe16bdc 100644
--- a/sw/source/filter/html/htmlflywriter.cxx
+++ b/sw/source/filter/html/htmlflywriter.cxx
@@ -1117,17 +1117,17 @@ OUString lclWriteOutImap(SwHTMLWriter& rHTMLWrt, const 
SfxItemSet& rItemSet, con
     OUString aIMapName;
 
     // Only consider the URL attribute if no ImageMap was supplied
-    if (!pAltImgMap)
-        pURLItem = rItemSet.GetItemIfSet( RES_URL );
 
     // write ImageMap
     const ImageMap* pIMap = pAltImgMap;
-    if( !pIMap && pURLItem )
+    if( !pIMap  )
     {
-        pIMap = pURLItem->GetMap();
+        pURLItem = rItemSet.GetItemIfSet(RES_URL);
+        if (pURLItem)
+            pIMap = pURLItem->GetMap();
     }
 
-    if (pIMap)
+    if (pIMap && !rHTMLWrt.mbReqIF)
     {
         // make the name unique
         aIMapName = pIMap->GetName();
@@ -1135,10 +1135,10 @@ OUString lclWriteOutImap(SwHTMLWriter& rHTMLWrt, const 
SfxItemSet& rItemSet, con
         if (!aIMapName.isEmpty())
             aNameBase = aIMapName;
         else
+        {
             aNameBase = OOO_STRING_SVTOOLS_HTML_map;
-
-        if (aIMapName.isEmpty())
             aIMapName = aNameBase + OUString::number(rHTMLWrt.m_nImgMapCnt);
+        }
 
         bool bFound;
         do
@@ -1309,7 +1309,7 @@ Writer& OutHTML_ImageStart( HtmlWriter& rHtml, Writer& 
rWrt, const SwFrameFormat
     // URL -> <a>...<img ... >...</a>
     const SvxMacroItem *pMacItem = rItemSet.GetItemIfSet(RES_FRMMACRO);
 
-    if (pURLItem || pMacItem)
+    if (pURLItem || pMacItem || (rHTMLWrt.mbReqIF && pAltImgMap))
     {
         OUString aMapURL;
         OUString aName;
@@ -1321,6 +1321,21 @@ Writer& OutHTML_ImageStart( HtmlWriter& rHtml, Writer& 
rWrt, const SwFrameFormat
             aName = pURLItem->GetName();
             aTarget = pURLItem->GetTargetFrameName();
         }
+        else if (rHTMLWrt.mbReqIF && pAltImgMap)
+        {
+            // Get first non-empty map element
+            for (size_t i = 0; i < pAltImgMap->GetIMapObjectCount(); ++i)
+            {
+                if (auto* pIMapObject = pAltImgMap->GetIMapObject(i))
+                {
+                    aMapURL = pIMapObject->GetURL();
+                    aName = pIMapObject->GetName();
+                    aTarget = pIMapObject->GetTarget();
+                    if (!aMapURL.isEmpty() || !aName.isEmpty() || 
!aTarget.isEmpty())
+                        break;
+                }
+            }
+        }
 
         bool bEvents = pMacItem && !pMacItem->GetMacroTable().empty();
 
commit 22ce686df6bd8b2cb2e8048206f3dd86c5a98d68
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Wed May 1 10:00:25 2024 +0500
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Wed May 1 13:53:04 2024 +0500

    Make pNoteURL thread-local, just in case
    
    Change-Id: I27247a85f0d7497f70c5d97a8955b68a29be1176
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166946
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>

diff --git a/sw/source/core/inc/noteurl.hxx b/sw/source/core/inc/noteurl.hxx
index eeae15642a43..b575b60d20a0 100644
--- a/sw/source/core/inc/noteurl.hxx
+++ b/sw/source/core/inc/noteurl.hxx
@@ -59,7 +59,7 @@ public:
 };
 
 // globale Variable, in NoteURL.Cxx angelegt
-extern SwNoteURL* pNoteURL;
+extern thread_local SwNoteURL* pNoteURL;
 
 #endif
 
diff --git a/sw/source/core/layout/paintfrm.cxx 
b/sw/source/core/layout/paintfrm.cxx
index f11e6c4a1a82..3d11229acb9f 100644
--- a/sw/source/core/layout/paintfrm.cxx
+++ b/sw/source/core/layout/paintfrm.cxx
@@ -7627,7 +7627,7 @@ Graphic SwFlyFrameFormat::MakeGraphic( ImageMap* pMap, 
const sal_uInt32 /*nMaxim
             SfxItemState::SET != GetAttrSet().GetItemState( RES_URL );
         if( bNoteURL )
         {
-            OSL_ENSURE( !pNoteURL, "MakeGraphic: pNoteURL already used? " );
+            assert(!pNoteURL);
             pNoteURL = new SwNoteURL;
         }
         SwFlyFrame *pFly = static_cast<SwFlyFrame*>(pFirst);
@@ -7708,7 +7708,7 @@ Graphic SwFlyFrameFormat::MakeGraphic( ImageMap* pMap, 
const sal_uInt32 /*nMaxim
 
         if( bNoteURL )
         {
-            OSL_ENSURE( pNoteURL, "MakeGraphic: Good Bye, NoteURL." );
+            assert(pNoteURL);
             pNoteURL->FillImageMap(pMap, pFly->getFrameArea().Pos(), aMap);
             delete pNoteURL;
             pNoteURL = nullptr;
diff --git a/sw/source/core/text/noteurl.cxx b/sw/source/core/text/noteurl.cxx
index ae52e1c29a91..d66736280bb5 100644
--- a/sw/source/core/text/noteurl.cxx
+++ b/sw/source/core/text/noteurl.cxx
@@ -25,7 +25,7 @@
 #include <vcl/outdev.hxx>
 
 // Global variable
-SwNoteURL* pNoteURL = nullptr;
+thread_local SwNoteURL* pNoteURL = nullptr;
 
 void SwNoteURL::InsertURLNote(const OUString& rURL, const OUString& rTarget, 
const SwRect& rRect)
 {
commit ff0c1b51e35a552094438c3dec16053ebf4df1ed
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Tue Apr 30 22:16:12 2024 +0500
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Wed May 1 13:52:47 2024 +0500

    tdf#160867: export as-char frames' hyperlinks to image map
    
    Change-Id: Idc8d41a27c8ee9cdd12fb5e17a328ec6aa104a16
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166935
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>

diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx 
b/sw/qa/extras/htmlexport/htmlexport.cxx
index 5edae5a45085..148285680043 100644
--- a/sw/qa/extras/htmlexport/htmlexport.cxx
+++ b/sw/qa/extras/htmlexport/htmlexport.cxx
@@ -3065,13 +3065,16 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, 
testHTML_160867)
     CPPUNIT_ASSERT(pDoc);
     assertXPath(pDoc, "/html/body/p", 2);
 
-    // Test export of text hyperlink in the image map. TODO: implement export 
of image hyperlink.
+    // Test export of image and text hyperlinks in the image map.
     // Without the fix, the test would fail with
     // - Expected: 1
     // - Actual  : 0
     // - In <>, XPath '/html/body/p[2]/map' number of nodes is incorrect
     const OUString mapName = getXPath(pDoc, "/html/body/p[2]/map", "name");
-    assertXPath(pDoc, "/html/body/p[2]/map/area", "shape", "rect");
+    assertXPath(pDoc, "/html/body/p[2]/map/area[1]", "shape", "rect");
+    CPPUNIT_ASSERT(getXPath(pDoc, "/html/body/p[2]/map/area[1]", 
"href").endsWith("foo/bar"));
+    assertXPath(pDoc, "/html/body/p[2]/map/area[2]", "shape", "rect");
+    CPPUNIT_ASSERT(getXPath(pDoc, "/html/body/p[2]/map/area[2]", 
"href").endsWith("baz"));
     assertXPath(pDoc, "/html/body/p[2]/img", "usemap", "#" + mapName);
 }
 
diff --git a/sw/source/core/text/inftxt.cxx b/sw/source/core/text/inftxt.cxx
index 54d0506c4db0..2cc1b0329f62 100644
--- a/sw/source/core/text/inftxt.cxx
+++ b/sw/source/core/text/inftxt.cxx
@@ -44,6 +44,7 @@
 #include <viewsh.hxx>
 #include <viewopt.hxx>
 #include <frmtool.hxx>
+#include <fmturl.hxx>
 #include <IDocumentSettingAccess.hxx>
 #include <IDocumentDeviceAccess.hxx>
 #include <IDocumentMarkAccess.hxx>
@@ -51,6 +52,7 @@
 #include <rootfrm.hxx>
 #include "inftxt.hxx"
 #include <noteurl.hxx>
+#include "porfly.hxx"
 #include "porftn.hxx"
 #include "porrst.hxx"
 #include "itratr.hxx"
@@ -1381,6 +1383,20 @@ void SwTextPaintInfo::NotifyURL_(const SwLinePortion& 
rPor) const
             const SwFormatINetFormat& rFormat = pAttr->GetINetFormat();
             pNoteURL->InsertURLNote(rFormat.GetValue(), 
rFormat.GetTargetFrame(), aIntersect);
         }
+        else if (rPor.IsFlyCntPortion())
+        {
+            if (auto* pFlyContentPortion = dynamic_cast<const 
sw::FlyContentPortion*>(&rPor))
+            {
+                if (auto* pFlyFtame = pFlyContentPortion->GetFlyFrame())
+                {
+                    if (auto* pFormat = pFlyFtame->GetFormat())
+                    {
+                        auto& url = pFormat->GetURL(); // TODO: url.GetMap() ?
+                        pNoteURL->InsertURLNote(url.GetURL(), 
url.GetTargetFrameName(), aIntersect);
+                    }
+                }
+            }
+        }
     }
 }
 
diff --git a/sw/source/core/text/itrpaint.cxx b/sw/source/core/text/itrpaint.cxx
index 382c56f4cf01..1f1959b06c66 100644
--- a/sw/source/core/text/itrpaint.cxx
+++ b/sw/source/core/text/itrpaint.cxx
@@ -32,6 +32,7 @@
 #include <txtfrm.hxx>
 #include <swfont.hxx>
 #include "txtpaint.hxx"
+#include "porfly.hxx"
 #include "portab.hxx"
 #include <txatbase.hxx>
 #include <charfmt.hxx>
@@ -39,6 +40,7 @@
 #include "porrst.hxx"
 #include "pormulti.hxx"
 #include <doc.hxx>
+#include <fmturl.hxx>
 
 // Returns, if we have an underline breaking situation
 // Adding some more conditions here means you also have to change them
@@ -406,6 +408,21 @@ void SwTextPainter::DrawTextLine( const SwRect &rPaint, 
SwSaveClip &rClip,
 
         if (GetFnt()->IsURL() && pPor->InTextGrp())
             GetInfo().NotifyURL(*pPor);
+        else if (pPor->IsFlyCntPortion())
+        {
+            if (auto* pFlyContentPortion = 
dynamic_cast<sw::FlyContentPortion*>(pPor))
+            {
+                if (auto* pFlyFrame = pFlyContentPortion->GetFlyFrame())
+                {
+                    if (auto* pFormat = pFlyFrame->GetFormat())
+                    {
+                        auto& url = pFormat->GetURL();
+                        if (!url.GetURL().isEmpty()) // TODO: url.GetMap() ?
+                            GetInfo().NotifyURL(*pPor);
+                    }
+                }
+            }
+        }
 
         bFirst &= !pPor->GetLen();
         if( pNext || !pPor->IsMarginPortion() )
diff --git a/sw/source/core/text/porfly.hxx b/sw/source/core/text/porfly.hxx
index a519c1109c87..2c56563a4436 100644
--- a/sw/source/core/text/porfly.hxx
+++ b/sw/source/core/text/porfly.hxx
@@ -76,6 +76,7 @@ namespace sw
             FlyContentPortion(SwFlyInContentFrame* pFly);
             static FlyContentPortion* Create(const SwTextFrame& rFrame, 
SwFlyInContentFrame* pFly, const Point& rBase, tools::Long nAscent, tools::Long 
nDescent, tools::Long nFlyAsc, tools::Long nFlyDesc, AsCharFlags nFlags);
             SwFlyInContentFrame* GetFlyFrame() { return m_pFly; }
+            const SwFlyInContentFrame* GetFlyFrame() const { return m_pFly; }
             void GetFlyCursorOfst(Point& rPoint, SwPosition& rPos, 
SwCursorMoveState* pCMS) const { m_pFly->GetModelPositionForViewPoint(&rPos, 
rPoint, pCMS); };
             virtual void Paint(const SwTextPaintInfo& rInf) const override;
             virtual ~FlyContentPortion() override;
commit be55b84165dcad78eb72619e0737acd85602aee3
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Tue Apr 30 15:40:41 2024 +0500
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Wed May 1 13:27:07 2024 +0500

    tdf#160867: restore HTML map export for text hyperlinks in frames
    
    The most exciting was to discover that this functionality was actually
    already implemented prior to 2001, and then accidentally dropped, and
    nobody noticed, until Noel did his great cleanups, and made an amazing
    investigation in commit ed2ae3c3bb0a708cafc3de6a01adc9ddc43fb859 (remove
    dead SwNoteURL, 2018-03-14). The detailed commit message made my task
    so much easier: I knew where and what to restore.
    
    So this change restores relevant pieces removed over the time in commits
    * 1b666235f6b0b0f0b13f473bf3b639f4f5f0b12f (loplugin:singlevalfields
      improve copy constructor check, 2018-01-03),
    * be8c414567f49242164b1fdfb12764b16be355c1 (loplugin:unusedmethods also
      check for functions returning bool, 2018-01-19),
    * 73139fe600fc1399ae828077981a2498cb0a0b0c (loplugin:unusedmethods,
      2018-01-20)
    * bb7ade140df807b6a0f12766a1365b8f8d0fd342 (loplugin:unusedmethods,
      2018-03-08),
    * ed2ae3c3bb0a708cafc3de6a01adc9ddc43fb859 (remove dead SwNoteURL,
      2018-03-14),
    * fd1cfd25b48cb4bd5c87e9cb317b37699ca3a1d6 (PortionType::Url is unused,
      2019-01-18).
    
    It re-implements the functionality accidentally removed in commit
    da7671e4f7482110ecd0cfbfd7dbd9e0b873c81c (Opt.(FME): The new attribute
    handler makes a lot of code superfluous, 2001-03-15), moving it into
    SwAttrHandler, which replaced the ChgFnt in SwTxtAttr.
    
    It also fixes the code writing the HTML image map, to output valid HTML.
    
    And finally, it adds a unit test, to avoid repeating the story :-)
    
    Change-Id: I72ae3cf30f0e9689f50a2c877e1622e4ae46de49
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166924
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>

diff --git a/svtools/source/svhtml/htmlout.cxx 
b/svtools/source/svhtml/htmlout.cxx
index 3c9090f1062a..97d69198bbbf 100644
--- a/svtools/source/svhtml/htmlout.cxx
+++ b/svtools/source/svhtml/htmlout.cxx
@@ -702,7 +702,7 @@ SvStream& HTMLOutFuncs::Out_ImageMap( SvStream& rStream,
 
                 sOut.append(OString::Concat("<") + OOO_STRING_SVTOOLS_HTML_area
                         " " OOO_STRING_SVTOOLS_HTML_O_shape
-                        "=" + pShape + " "
+                        "=\"" + pShape + "\" "
                         OOO_STRING_SVTOOLS_HTML_O_coords "=\"" +
                         aCoords + "\" ");
                 rStream.WriteOString( sOut );
@@ -756,7 +756,7 @@ SvStream& HTMLOutFuncs::Out_ImageMap( SvStream& rStream,
                     Out_Events( rStream, rMacroTab, pEventTable,
                                 bOutStarBasic );
 
-                rStream.WriteChar( '>' );
+                rStream.WriteOString("/>");
             }
         }
 
diff --git a/sw/qa/extras/htmlexport/data/tdf160867_image_with_link.fodt 
b/sw/qa/extras/htmlexport/data/tdf160867_image_with_link.fodt
new file mode 100644
index 000000000000..43c35cdff13e
--- /dev/null
+++ b/sw/qa/extras/htmlexport/data/tdf160867_image_with_link.fodt
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" 
xmlns:xlink="http://www.w3.org/1999/xlink"; 
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" 
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" 
xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" 
xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" 
xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0"
 office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:styles>
+  <style:style style:name="Frame" style:family="graphic">
+   <style:graphic-properties text:anchor-type="as-char" svg:x="0" svg:y="0" 
style:number-wrapped-paragraphs="no-limit" style:wrap-contour="false" 
fo:background-color="transparent" draw:fill="none" fo:margin-left="0" 
fo:margin-right="0" fo:margin-top="0" fo:margin-bottom="0" style:wrap="none" 
style:vertical-pos="middle" style:vertical-rel="baseline" 
style:horizontal-pos="from-left" style:horizontal-rel="paragraph" 
fo:padding="0" fo:border="none" loext:rel-width-rel="paragraph"/>
+  </style:style>
+ </office:styles>
+ <office:body>
+  <office:text>
+   <text:p><draw:a xlink:type="simple" xlink:href="foo/bar"><draw:frame 
draw:style-name="Frame" draw:name="image1" svg:width="17cm" svg:height="25mm" 
style:rel-height="scale"><draw:image draw:mime-type="image/png">
+       
<office:binary-data>iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsIAAA7CARUoSoAAAAAMSURBVBhXY/jPwAAAAwEBAGMkVdMAAAAASUVORK5C</office:binary-data>
+      </draw:image>
+     </draw:frame></draw:a>image1 with a hyperlink, and this text with <text:a 
xlink:type="simple" xlink:href="baz" text:style-name="Internet_20_link" 
text:visited-style-name="Visited_20_Internet_20_Link">another 
hyperlink</text:a></text:p>
+   <text:p><draw:frame draw:style-name="Frame" draw:name="frame" 
svg:width="17cm">
+     <draw:text-box fo:min-height="1pt">
+      <text:p><draw:a xlink:type="simple" xlink:href="foo/bar"><draw:frame 
draw:style-name="Frame" draw:name="image2" svg:width="17cm" 
svg:height="25mm"><draw:image draw:mime-type="image/png">
+          
<office:binary-data>iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsIAAA7CARUoSoAAAAAMSURBVBhXY/jPwAAAAwEBAGMkVdMAAAAASUVORK5C</office:binary-data>
+         </draw:image>
+        </draw:frame></draw:a>image2 with a hyperlink, and this text with 
<text:a xlink:type="simple" xlink:href="baz" text:style-name="Internet_20_link" 
text:visited-style-name="Visited_20_Internet_20_Link">another 
hyperlink</text:a>, in a frame</text:p>
+     </draw:text-box>
+    </draw:frame></text:p>
+  </office:text>
+ </office:body>
+</office:document>
\ No newline at end of file
diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx 
b/sw/qa/extras/htmlexport/htmlexport.cxx
index 6c06299fb6df..5edae5a45085 100644
--- a/sw/qa/extras/htmlexport/htmlexport.cxx
+++ b/sw/qa/extras/htmlexport/htmlexport.cxx
@@ -3054,6 +3054,27 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, 
testHTML_Tdf160390)
     save("HTML (StarWriter)");
 }
 
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_160867)
+{
+    // Given a document with an image with hyperlink, and text with hyperlink, 
both in a frame:
+    createSwDoc("tdf160867_image_with_link.fodt");
+    // When exporting to HTML:
+    save("HTML (StarWriter)");
+    // Parse it as XML (strict!)
+    xmlDocUniquePtr pDoc = parseXml(maTempFile);
+    CPPUNIT_ASSERT(pDoc);
+    assertXPath(pDoc, "/html/body/p", 2);
+
+    // Test export of text hyperlink in the image map. TODO: implement export 
of image hyperlink.
+    // Without the fix, the test would fail with
+    // - Expected: 1
+    // - Actual  : 0
+    // - In <>, XPath '/html/body/p[2]/map' number of nodes is incorrect
+    const OUString mapName = getXPath(pDoc, "/html/body/p[2]/map", "name");
+    assertXPath(pDoc, "/html/body/p[2]/map/area", "shape", "rect");
+    assertXPath(pDoc, "/html/body/p[2]/img", "usemap", "#" + mapName);
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/noteurl.hxx b/sw/source/core/inc/noteurl.hxx
index 1e5775d3c099..eeae15642a43 100644
--- a/sw/source/core/inc/noteurl.hxx
+++ b/sw/source/core/inc/noteurl.hxx
@@ -20,8 +20,42 @@
 #ifndef INCLUDED_SW_SOURCE_CORE_INC_NOTEURL_HXX
 #define INCLUDED_SW_SOURCE_CORE_INC_NOTEURL_HXX
 
+#include <swrect.hxx>
+
+#include <rtl/ustring.hxx>
+
+#include <vector>
+
+class ImageMap;
+class MapMode;
+
+class SwURLNote
+{
+    OUString aURL;
+    OUString aTarget;
+    SwRect aRect;
+
+public:
+    SwURLNote(const OUString& rURL, const OUString& rTarget, const SwRect& 
rRect)
+        : aURL(rURL)
+        , aTarget(rTarget)
+        , aRect(rRect)
+    {
+    }
+    const OUString& GetURL() const { return aURL; }
+    const OUString& GetTarget() const { return aTarget; }
+    const SwRect& GetRect() const { return aRect; }
+};
+
 class SwNoteURL
 {
+private:
+    std::vector<SwURLNote> m_List;
+
+public:
+    SwNoteURL() {}
+    void InsertURLNote(const OUString& rURL, const OUString& rTarget, const 
SwRect& rRect);
+    void FillImageMap(ImageMap* pMap, const Point& rPos, const MapMode& rMap);
 };
 
 // globale Variable, in NoteURL.Cxx angelegt
diff --git a/sw/source/core/inc/swfont.hxx b/sw/source/core/inc/swfont.hxx
index 38936574a471..233b933175c2 100644
--- a/sw/source/core/inc/swfont.hxx
+++ b/sw/source/core/inc/swfont.hxx
@@ -169,6 +169,7 @@ class SwFont
     bool m_bFontChg       :1;
     bool m_bOrgChg        :1;  // nOrgHeight/Ascent are invalid
     bool m_bGreyWave      :1;  // for the extended TextInput: gray waveline
+    bool m_bURL = false;
 
 public:
     SwFont( const SwAttrSet* pSet, const IDocumentSettingAccess* 
pIDocumentSettingAccess );
@@ -259,6 +260,8 @@ public:
     inline void SetGreyWave( const bool bNew );
     bool IsGreyWave() const { return m_bGreyWave; }
     bool IsPaintBlank() const { return m_bPaintBlank; }
+    void SetURL(const bool bURL) { m_bURL = bURL; }
+    bool IsURL() const { return m_bURL; }
 
     // setting of the base class font for SwTextCharFormat
     void SetDiffFnt( const SfxItemSet* pSet,
diff --git a/sw/source/core/layout/paintfrm.cxx 
b/sw/source/core/layout/paintfrm.cxx
index be694a390350..f11e6c4a1a82 100644
--- a/sw/source/core/layout/paintfrm.cxx
+++ b/sw/source/core/layout/paintfrm.cxx
@@ -7709,6 +7709,7 @@ Graphic SwFlyFrameFormat::MakeGraphic( ImageMap* pMap, 
const sal_uInt32 /*nMaxim
         if( bNoteURL )
         {
             OSL_ENSURE( pNoteURL, "MakeGraphic: Good Bye, NoteURL." );
+            pNoteURL->FillImageMap(pMap, pFly->getFrameArea().Pos(), aMap);
             delete pNoteURL;
             pNoteURL = nullptr;
         }
diff --git a/sw/source/core/text/atrhndl.hxx b/sw/source/core/text/atrhndl.hxx
index 851615325a06..efe1ae954958 100644
--- a/sw/source/core/text/atrhndl.hxx
+++ b/sw/source/core/text/atrhndl.hxx
@@ -46,6 +46,8 @@ private:
     // a template, if we have to restart the attribute evaluation
     std::optional<SwFont> m_oFnt;
 
+    int m_nINETFMT = 0; // for font's SetURL
+
     bool m_bVertLayout;
     bool m_bVertLayoutLRBT;
 
diff --git a/sw/source/core/text/atrstck.cxx b/sw/source/core/text/atrstck.cxx
index 7fab6da10c5e..c6744d50e780 100644
--- a/sw/source/core/text/atrstck.cxx
+++ b/sw/source/core/text/atrstck.cxx
@@ -367,6 +367,13 @@ void SwAttrHandler::PushAndChg( const SwTextAttr& rAttr, 
SwFont& rFnt )
                 }
             }
         }
+
+        if (rAttr.Which() == RES_TXTATR_INETFMT)
+        {
+            if (m_nINETFMT == 0)
+                rFnt.SetURL(true);
+            ++m_nINETFMT;
+        }
     }
     // this is the usual case, we have a basic attribute, push it onto the
     // stack and change the font
@@ -433,6 +440,14 @@ void SwAttrHandler::PopAndChg( const SwTextAttr& rAttr, 
SwFont& rFnt )
         const SfxItemSet* pSet = CharFormat::GetItemSet( rAttr.GetAttr() );
         if ( !pSet ) return;
 
+        if (rAttr.Which() == RES_TXTATR_INETFMT)
+        {
+            assert(m_nINETFMT > 0);
+            --m_nINETFMT;
+            if (m_nINETFMT == 0)
+                rFnt.SetURL(false);
+        }
+
         for ( sal_uInt16 i = RES_CHRATR_BEGIN; i < RES_CHRATR_END; i++)
         {
             const SfxPoolItem* pItem;
diff --git a/sw/source/core/text/inftxt.cxx b/sw/source/core/text/inftxt.cxx
index 737b6b3b2700..54d0506c4db0 100644
--- a/sw/source/core/text/inftxt.cxx
+++ b/sw/source/core/text/inftxt.cxx
@@ -21,7 +21,9 @@
 
 #include <unotools/linguprops.hxx>
 #include <unotools/lingucfg.hxx>
+#include <fmtinfmt.hxx>
 #include <hintids.hxx>
+#include <txatbase.hxx>
 #include <svl/ctloptions.hxx>
 #include <sfx2/infobar.hxx>
 #include <sfx2/printer.hxx>
@@ -1363,6 +1365,25 @@ void SwTextPaintInfo::DrawViewOpt( const SwLinePortion 
&rPor,
         DrawBackground( rPor, pColor );
 }
 
+void SwTextPaintInfo::NotifyURL_(const SwLinePortion& rPor) const
+{
+    assert(pNoteURL);
+
+    SwRect aIntersect;
+    CalcRect(rPor, nullptr, &aIntersect);
+
+    if (aIntersect.HasArea())
+    {
+        SwTextNode* pNd = 
const_cast<SwTextNode*>(GetTextFrame()->GetTextNodeFirst());
+        SwTextAttr* const pAttr = pNd->GetTextAttrAt(sal_Int32(GetIdx()), 
RES_TXTATR_INETFMT);
+        if (pAttr)
+        {
+            const SwFormatINetFormat& rFormat = pAttr->GetINetFormat();
+            pNoteURL->InsertURLNote(rFormat.GetValue(), 
rFormat.GetTargetFrame(), aIntersect);
+        }
+    }
+}
+
 static void lcl_InitHyphValues( PropertyValues &rVals,
             sal_Int16 nMinLeading, sal_Int16 nMinTrailing,
             bool bNoCapsHyphenation, bool bNoLastWordHyphenation,
diff --git a/sw/source/core/text/inftxt.hxx b/sw/source/core/text/inftxt.hxx
index 8011b29b442a..1ab5e3496f6b 100644
--- a/sw/source/core/text/inftxt.hxx
+++ b/sw/source/core/text/inftxt.hxx
@@ -358,6 +358,7 @@ class SwTextPaintInfo : public SwTextSizeInfo
                    const bool bGrammarCheck = false );
 
     SwTextPaintInfo &operator=(const SwTextPaintInfo&) = delete;
+    void NotifyURL_(const SwLinePortion& rPor) const;
 
 protected:
     SwTextPaintInfo()
@@ -416,6 +417,12 @@ public:
 
     void DrawCheckBox(const SwFieldFormCheckboxPortion &rPor, bool bChecked) 
const;
 
+    void NotifyURL(const SwLinePortion& rPor) const
+    {
+        if (URLNotify())
+            NotifyURL_(rPor);
+    }
+
     /**
      * Calculate the rectangular area where the portion takes place.
      * @param[in]   rPor        portion for which the method specify the 
painting area
diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx
index dbf5765ef1b6..5d88b6e960b6 100644
--- a/sw/source/core/text/itrform2.cxx
+++ b/sw/source/core/text/itrform2.cxx
@@ -1286,7 +1286,7 @@ SwTextPortion *SwTextFormatter::WhichTextPor( 
SwTextFormatInfo &rInf ) const
             }
             if( !pPor )
             {
-                if( !rInf.X() && !m_pCurr->GetNextPortion() && 
!m_pCurr->GetLen() )
+                if( !rInf.X() && !m_pCurr->GetNextPortion() && 
!m_pCurr->GetLen()  && !GetFnt()->IsURL() )
                     pPor = m_pCurr;
                 else
                     pPor = new SwTextPortion;
diff --git a/sw/source/core/text/itrpaint.cxx b/sw/source/core/text/itrpaint.cxx
index a66d358645f0..382c56f4cf01 100644
--- a/sw/source/core/text/itrpaint.cxx
+++ b/sw/source/core/text/itrpaint.cxx
@@ -404,6 +404,9 @@ void SwTextPainter::DrawTextLine( const SwRect &rPaint, 
SwSaveClip &rClip,
         // reset (for special vertical alignment)
         GetInfo().Y( nOldY );
 
+        if (GetFnt()->IsURL() && pPor->InTextGrp())
+            GetInfo().NotifyURL(*pPor);
+
         bFirst &= !pPor->GetLen();
         if( pNext || !pPor->IsMarginPortion() )
             pPor->Move( GetInfo() );
diff --git a/sw/source/core/text/noteurl.cxx b/sw/source/core/text/noteurl.cxx
index fa91ea252d5f..ae52e1c29a91 100644
--- a/sw/source/core/text/noteurl.cxx
+++ b/sw/source/core/text/noteurl.cxx
@@ -19,7 +19,42 @@
 
 #include <noteurl.hxx>
 
+#include <vcl/imap.hxx>
+#include <vcl/imaprect.hxx>
+#include <vcl/mapmod.hxx>
+#include <vcl/outdev.hxx>
+
 // Global variable
 SwNoteURL* pNoteURL = nullptr;
 
+void SwNoteURL::InsertURLNote(const OUString& rURL, const OUString& rTarget, 
const SwRect& rRect)
+{
+    const size_t nCount = m_List.size();
+    for (size_t i = 0; i < nCount; ++i)
+        if (rRect == m_List[i].GetRect())
+            return;
+
+    m_List.emplace_back(rURL, rTarget, rRect);
+}
+
+void SwNoteURL::FillImageMap(ImageMap* pMap, const Point& rPos, const MapMode& 
rMap)
+{
+    assert(pMap && "FillImageMap: No ImageMap, no cookies!");
+    const size_t nCount = m_List.size();
+    if (nCount)
+    {
+        MapMode aMap(MapUnit::Map100thMM);
+        for (size_t i = 0; i < nCount; ++i)
+        {
+            const SwURLNote& rNote = m_List[i];
+            SwRect aSwRect(rNote.GetRect());
+            aSwRect -= rPos;
+            tools::Rectangle 
aRect(OutputDevice::LogicToLogic(aSwRect.SVRect(), rMap, aMap));
+            IMapRectangleObject aObj(aRect, rNote.GetURL(), OUString(), 
OUString(),
+                                     rNote.GetTarget(), OUString(), true, 
false);
+            pMap->InsertIMapObject(aObj);
+        }
+    }
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/pormulti.cxx b/sw/source/core/text/pormulti.cxx
index 0f00db0d8c26..c725a81c2320 100644
--- a/sw/source/core/text/pormulti.cxx
+++ b/sw/source/core/text/pormulti.cxx
@@ -1765,6 +1765,9 @@ void SwTextPainter::PaintMultiPortion( const SwRect 
&rPaint,
         else
             pPor->Paint( GetInfo() );
 
+        if (GetFnt()->IsURL() && pPor->InTextGrp())
+            GetInfo().NotifyURL(*pPor);
+
         bFirst &= !pPor->GetLen();
         if( pNext || !pPor->IsMarginPortion() )
             pPor->Move( GetInfo() );
diff --git a/sw/source/core/txtnode/swfont.cxx 
b/sw/source/core/txtnode/swfont.cxx
index c0149d9a6573..c18070ef493e 100644
--- a/sw/source/core/txtnode/swfont.cxx
+++ b/sw/source/core/txtnode/swfont.cxx
@@ -668,6 +668,7 @@ SwFont::SwFont( const SwFont &rFont )
     m_bOrgChg = rFont.m_bOrgChg;
     m_bPaintBlank = rFont.m_bPaintBlank;
     m_bGreyWave = rFont.m_bGreyWave;
+    m_bURL = rFont.m_bURL;
 }
 
 SwFont::SwFont( const SwAttrSet* pAttrSet,
@@ -857,6 +858,7 @@ SwFont& SwFont::operator=( const SwFont &rFont )
         m_bOrgChg = rFont.m_bOrgChg;
         m_bPaintBlank = rFont.m_bPaintBlank;
         m_bGreyWave = rFont.m_bGreyWave;
+        m_bURL = rFont.m_bURL;
     }
     return *this;
 }

Reply via email to