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; }