summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Kaganski <mike.kaganski@collabora.com>2024-04-30 15:40:41 +0500
committerMike Kaganski <mike.kaganski@collabora.com>2024-04-30 19:11:32 +0200
commitacbb6c98ae1335da5aca4f1a55de5f3ae35a5d02 (patch)
treefe21b7daf69fde6c759e6e726ec5dc50529dba62
parent6104316fd50076d41039c33a5338b016e93259fe (diff)
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.kaganski@collabora.com>
-rw-r--r--include/vcl/imaprect.hxx2
-rw-r--r--svtools/source/svhtml/htmlout.cxx4
-rw-r--r--sw/qa/extras/htmlexport/data/tdf160867_image_with_link.fodt25
-rw-r--r--sw/qa/extras/htmlexport/htmlexport.cxx21
-rw-r--r--sw/source/core/inc/noteurl.hxx34
-rw-r--r--sw/source/core/inc/swfont.hxx3
-rw-r--r--sw/source/core/layout/paintfrm.cxx1
-rw-r--r--sw/source/core/text/atrhndl.hxx2
-rw-r--r--sw/source/core/text/atrstck.cxx15
-rw-r--r--sw/source/core/text/inftxt.cxx21
-rw-r--r--sw/source/core/text/inftxt.hxx7
-rw-r--r--sw/source/core/text/itrform2.cxx2
-rw-r--r--sw/source/core/text/itrpaint.cxx3
-rw-r--r--sw/source/core/text/noteurl.cxx35
-rw-r--r--sw/source/core/text/pormulti.cxx3
-rw-r--r--sw/source/core/txtnode/swfont.cxx2
16 files changed, 176 insertions, 4 deletions
diff --git a/include/vcl/imaprect.hxx b/include/vcl/imaprect.hxx
index 89cb52d80bbe..8abe1ed39960 100644
--- a/include/vcl/imaprect.hxx
+++ b/include/vcl/imaprect.hxx
@@ -25,7 +25,7 @@
class Fraction;
-class UNLESS_MERGELIBS(VCL_DLLPUBLIC) IMapRectangleObject final : public IMapObject
+class VCL_DLLPUBLIC IMapRectangleObject final : public IMapObject
{
tools::Rectangle aRect;
diff --git a/svtools/source/svhtml/htmlout.cxx b/svtools/source/svhtml/htmlout.cxx
index 956546269708..d165a8922f02 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 6a23599184a5..329be9231bb9 100644
--- a/sw/qa/extras/htmlexport/htmlexport.cxx
+++ b/sw/qa/extras/htmlexport/htmlexport.cxx
@@ -3082,6 +3082,27 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_Tdf160390)
ExportToHTML();
}
+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:
+ ExportToHTML();
+ // Parse it as XML (strict!)
+ xmlDocUniquePtr pDoc = parseXml(maTempFile);
+ CPPUNIT_ASSERT(pDoc);
+ assertXPath(pDoc, "/html/body/p"_ostr, 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"_ostr, "name"_ostr);
+ assertXPath(pDoc, "/html/body/p[2]/map/area"_ostr, "shape"_ostr, u"rect"_ustr);
+ assertXPath(pDoc, "/html/body/p[2]/img"_ostr, "usemap"_ostr, "#" + mapName);
+}
+
} // end of anonymous namespace
CPPUNIT_PLUGIN_IMPLEMENT();
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 a8c3320d1cb8..25453744bd3f 100644
--- a/sw/source/core/inc/swfont.hxx
+++ b/sw/source/core/inc/swfont.hxx
@@ -174,6 +174,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 : 1 = false;
public:
SwFont( const SwAttrSet* pSet, const IDocumentSettingAccess* pIDocumentSettingAccess );
@@ -264,6 +265,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 8aa4c3738934..1e8600d83094 100644
--- a/sw/source/core/layout/paintfrm.cxx
+++ b/sw/source/core/layout/paintfrm.cxx
@@ -8136,6 +8136,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 0eb8d74e89c6..82a3da7fbf17 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 b293f18f824a..a1a3bb89a87b 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>
@@ -1461,6 +1463,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 cb11d02b1e8d..9c4126a7a6e9 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()
@@ -418,6 +419,12 @@ public:
void DrawCSDFHighlighting(const SwLinePortion &rPor) 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 2d24e103957a..046aa6bde1ea 100644
--- a/sw/source/core/text/itrform2.cxx
+++ b/sw/source/core/text/itrform2.cxx
@@ -1318,7 +1318,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 5b6bb1288d57..8fa9ca45f5fd 100644
--- a/sw/source/core/text/itrpaint.cxx
+++ b/sw/source/core/text/itrpaint.cxx
@@ -459,6 +459,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 9fb6011f0db5..aaf8339b37ef 100644
--- a/sw/source/core/text/pormulti.cxx
+++ b/sw/source/core/text/pormulti.cxx
@@ -1783,6 +1783,9 @@ void SwTextPainter::PaintMultiPortion( const SwRect &rPaint,
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 98fec0f153e2..20b3062267f7 100644
--- a/sw/source/core/txtnode/swfont.cxx
+++ b/sw/source/core/txtnode/swfont.cxx
@@ -684,6 +684,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,
@@ -873,6 +874,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;
}