1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <SfxRedactionHelper.hxx>
11 #include <autoredactdialog.hxx>
13 #include <com/sun/star/beans/XPropertySet.hpp>
14 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
15 #include <com/sun/star/drawing/LineStyle.hpp>
16 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
17 #include <com/sun/star/graphic/XGraphic.hpp>
18 #include <com/sun/star/frame/XLayoutManager.hpp>
20 // For page margin related methods
21 #include <com/sun/star/style/XStyle.hpp>
22 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
23 #include <com/sun/star/text/XPageCursor.hpp>
24 #include <com/sun/star/text/XTextViewCursorSupplier.hpp>
25 #include <com/sun/star/sheet/XSpreadsheetView.hpp>
28 #include <i18nutil/searchopt.hxx>
29 #include <com/sun/star/util/SearchAlgorithms.hpp>
30 #include <com/sun/star/util/SearchAlgorithms2.hpp>
31 #include <com/sun/star/util/SearchFlags.hpp>
32 #include <vcl/svapp.hxx>
33 #include <vcl/settings.hxx>
34 #include <i18nlangtag/languagetag.hxx>
35 #include <unotools/textsearch.hxx>
37 #include <sfx2/request.hxx>
38 #include <sfx2/sfxsids.hrc>
39 #include <sfx2/viewfrm.hxx>
41 #include <svl/eitem.hxx>
42 #include <svl/stritem.hxx>
44 #include <svtools/DocumentToGraphicRenderer.hxx>
46 #include <tools/gen.hxx>
47 #include <tools/stream.hxx>
48 #include <tools/diagnose_ex.h>
50 #include <vcl/gdimtf.hxx>
51 #include <vcl/graph.hxx>
52 #include <sal/log.hxx>
54 #include <vcl/wmf.hxx>
55 #include <vcl/metaact.hxx>
56 #include <vcl/outdev.hxx>
57 #include <vcl/vcllayout.hxx>
59 using namespace ::com::sun::star
;
60 using namespace ::com::sun::star::lang
;
61 using namespace ::com::sun::star::uno
;
63 bool SfxRedactionHelper::isRedactMode(const SfxRequest
& rReq
)
65 const SfxItemSet
* pArgs
= rReq
.GetArgs();
68 const SfxBoolItem
* pIsRedactMode
= rReq
.GetArg
<SfxBoolItem
>(SID_IS_REDACT_MODE
);
69 if (pIsRedactMode
&& pIsRedactMode
->GetValue())
76 OUString
SfxRedactionHelper::getStringParam(const SfxRequest
& rReq
, sal_uInt16 nParamId
)
78 OUString sStringParam
;
80 const SfxItemSet
* pArgs
= rReq
.GetArgs();
84 const SfxStringItem
* pStringArg
= rReq
.GetArg
<SfxStringItem
>(nParamId
);
88 sStringParam
= pStringArg
->GetValue();
95 * Roundtrip the gdimetafile to and from WMF
96 * to get rid of the position and size irregularities
97 * We better check the conversion method to see what it
98 * actually does to correct these issues, and do it ourselves.
100 void fixMetaFile(GDIMetaFile
& tmpMtf
)
102 SvMemoryStream
aDestStrm(65535, 65535);
103 ConvertGDIMetaFileToWMF(tmpMtf
, aDestStrm
, nullptr, false);
108 ReadWindowMetafile(aDestStrm
, tmpMtf
);
112 * Sets page margins for a Draw page. Negative values are considered erroneous
114 void setPageMargins(const uno::Reference
<beans::XPropertySet
>& xPageProperySet
,
115 const PageMargins
& aPageMargins
)
117 if (aPageMargins
.nTop
< 0 || aPageMargins
.nBottom
< 0 || aPageMargins
.nLeft
< 0
118 || aPageMargins
.nRight
< 0)
121 xPageProperySet
->setPropertyValue("BorderTop", css::uno::makeAny(aPageMargins
.nTop
));
122 xPageProperySet
->setPropertyValue("BorderBottom", css::uno::makeAny(aPageMargins
.nBottom
));
123 xPageProperySet
->setPropertyValue("BorderLeft", css::uno::makeAny(aPageMargins
.nLeft
));
124 xPageProperySet
->setPropertyValue("BorderRight", css::uno::makeAny(aPageMargins
.nRight
));
127 // #i10613# Extracted from ImplCheckRect::ImplCreate
128 tools::Rectangle
ImplCalcActionBounds(const MetaAction
& rAct
, const OutputDevice
& rOut
,
129 sal_Int32 nStrStartPos
, sal_Int32 nStrEndPos
)
131 tools::Rectangle aActionBounds
;
133 switch (rAct
.GetType())
135 case MetaActionType::TEXTARRAY
:
137 const MetaTextArrayAction
& rTextAct
= static_cast<const MetaTextArrayAction
&>(rAct
);
138 const OUString
aString(rTextAct
.GetText().copy(rTextAct
.GetIndex(), rTextAct
.GetLen()));
140 if (!aString
.isEmpty())
142 // #105987# ImplLayout takes everything in logical coordinates
143 std::unique_ptr
<SalLayout
> pSalLayout1
= rOut
.ImplLayout(
144 aString
, 0, nStrStartPos
, rTextAct
.GetPoint(), 0, rTextAct
.GetDXArray());
145 std::unique_ptr
<SalLayout
> pSalLayout2
= rOut
.ImplLayout(
146 aString
, 0, nStrEndPos
, rTextAct
.GetPoint(), 0, rTextAct
.GetDXArray());
149 tools::Rectangle
aBoundRect2(
150 const_cast<OutputDevice
&>(rOut
).ImplGetTextBoundRect(*pSalLayout2
));
151 aActionBounds
= rOut
.PixelToLogic(aBoundRect2
);
153 if (pSalLayout1
&& nStrStartPos
> 0)
155 tools::Rectangle
aBoundRect1(
156 const_cast<OutputDevice
&>(rOut
).ImplGetTextBoundRect(*pSalLayout1
));
157 aActionBounds
.SetLeft(rOut
.PixelToLogic(aBoundRect1
).getX()
158 + rOut
.PixelToLogic(aBoundRect1
).getWidth());
168 if (!aActionBounds
.IsEmpty())
170 // fdo#40421 limit current action's output to clipped area
171 if (rOut
.IsClipRegion())
172 return rOut
.GetClipRegion().GetBoundRect().Intersection(aActionBounds
);
174 return aActionBounds
;
177 return aActionBounds
;
180 } // End of anon namespace
182 void SfxRedactionHelper::getPageMetaFilesFromDoc(std::vector
<GDIMetaFile
>& aMetaFiles
,
183 std::vector
<::Size
>& aPageSizes
, sal_Int32 nPages
,
184 DocumentToGraphicRenderer
& aRenderer
)
186 for (sal_Int32 nPage
= 1; nPage
<= nPages
; ++nPage
)
188 ::Size aDocumentSizePixel
= aRenderer
.getDocumentSizeInPixels(nPage
);
190 ::Point aCalcPageLogicPos
;
191 ::Size aCalcPageContentSize
;
192 ::Size aLogic
= aRenderer
.getDocumentSizeIn100mm(nPage
, &aLogicPos
, &aCalcPageLogicPos
,
193 &aCalcPageContentSize
);
195 aPageSizes
.push_back(aLogic
);
197 Graphic aGraphic
= aRenderer
.renderToGraphic(nPage
, aDocumentSizePixel
, aDocumentSizePixel
,
198 COL_TRANSPARENT
, true);
199 auto& rGDIMetaFile
= const_cast<GDIMetaFile
&>(aGraphic
.GetGDIMetaFile());
201 // Set preferred map unit and size on the metafile, so the Shape size
202 // will be correct in MM.
204 aMapMode
.SetMapUnit(MapUnit::Map100thMM
);
206 rGDIMetaFile
.SetPrefMapMode(aMapMode
);
207 rGDIMetaFile
.SetPrefSize(aLogic
);
209 fixMetaFile(rGDIMetaFile
);
211 aMetaFiles
.push_back(rGDIMetaFile
);
215 void SfxRedactionHelper::addPagesToDraw(
216 const uno::Reference
<XComponent
>& xComponent
, sal_Int32 nPages
,
217 const std::vector
<GDIMetaFile
>& aMetaFiles
, const std::vector
<::Size
>& aPageSizes
,
218 const PageMargins
& aPageMargins
,
219 const std::vector
<std::pair
<RedactionTarget
*, OUString
>>& r_aTableTargets
, bool bIsAutoRedact
)
221 // Access the draw pages
222 uno::Reference
<drawing::XDrawPagesSupplier
> xDrawPagesSupplier(xComponent
, uno::UNO_QUERY
);
223 uno::Reference
<drawing::XDrawPages
> xDrawPages
= xDrawPagesSupplier
->getDrawPages();
225 uno::Reference
<css::lang::XMultiServiceFactory
> xFactory(xComponent
, uno::UNO_QUERY
);
227 for (sal_Int32 nPage
= 0; nPage
< nPages
; ++nPage
)
229 GDIMetaFile rGDIMetaFile
= aMetaFiles
[nPage
];
230 Graphic
aGraphic(rGDIMetaFile
);
232 sal_Int32
nPageHeight(aPageSizes
[nPage
].Height());
233 sal_Int32
nPageWidth(aPageSizes
[nPage
].Width());
235 uno::Reference
<graphic::XGraphic
> xGraph
= aGraphic
.GetXGraphic();
236 uno::Reference
<drawing::XDrawPage
> xPage
= xDrawPages
->insertNewByIndex(nPage
);
238 // Set page size & margins
239 uno::Reference
<beans::XPropertySet
> xPageProperySet(xPage
, uno::UNO_QUERY
);
240 xPageProperySet
->setPropertyValue("Height", css::uno::makeAny(nPageHeight
));
241 xPageProperySet
->setPropertyValue("Width", css::uno::makeAny(nPageWidth
));
243 setPageMargins(xPageProperySet
, aPageMargins
);
245 // Create and insert the shape
246 uno::Reference
<drawing::XShape
> xShape(
247 xFactory
->createInstance("com.sun.star.drawing.GraphicObjectShape"), uno::UNO_QUERY
);
248 uno::Reference
<beans::XPropertySet
> xShapeProperySet(xShape
, uno::UNO_QUERY
);
249 xShapeProperySet
->setPropertyValue("Graphic", uno::Any(xGraph
));
250 xShapeProperySet
->setPropertyValue("MoveProtect", uno::Any(true));
251 xShapeProperySet
->setPropertyValue("SizeProtect", uno::Any(true));
255 awt::Size(rGDIMetaFile
.GetPrefSize().Width(), rGDIMetaFile
.GetPrefSize().Height()));
259 if (bIsAutoRedact
&& !r_aTableTargets
.empty())
261 for (const auto& targetPair
: r_aTableTargets
)
263 autoRedactPage(targetPair
.first
, rGDIMetaFile
, xPage
, xComponent
);
268 // Remove the extra page at the beginning
269 uno::Reference
<drawing::XDrawPage
> xPage(xDrawPages
->getByIndex(0), uno::UNO_QUERY_THROW
);
270 xDrawPages
->remove(xPage
);
273 void SfxRedactionHelper::showRedactionToolbar(const SfxViewFrame
* pViewFrame
)
278 Reference
<frame::XFrame
> xFrame
= pViewFrame
->GetFrame().GetFrameInterface();
279 Reference
<css::beans::XPropertySet
> xPropSet(xFrame
, UNO_QUERY
);
280 Reference
<css::frame::XLayoutManager
> xLayoutManager
;
287 Any aValue
= xPropSet
->getPropertyValue("LayoutManager");
288 aValue
>>= xLayoutManager
;
289 xLayoutManager
->createElement("private:resource/toolbar/redactionbar");
290 xLayoutManager
->showElement("private:resource/toolbar/redactionbar");
292 catch (const css::uno::RuntimeException
&)
296 catch (css::uno::Exception
&)
298 TOOLS_WARN_EXCEPTION("sfx.doc", "Exception while trying to show the Redaction Toolbar!");
303 SfxRedactionHelper::getPageMarginsForWriter(const css::uno::Reference
<css::frame::XModel
>& xModel
)
305 PageMargins aPageMargins
= { -1, -1, -1, -1 };
307 Reference
<text::XTextViewCursorSupplier
> xTextViewCursorSupplier(xModel
->getCurrentController(),
309 if (!xTextViewCursorSupplier
.is())
311 SAL_WARN("sfx.doc", "Ref to xTextViewCursorSupplier is null in setPageMargins().");
315 Reference
<text::XPageCursor
> xCursor(xTextViewCursorSupplier
->getViewCursor(), UNO_QUERY
);
317 uno::Reference
<beans::XPropertySet
> xPageProperySet(xCursor
, UNO_QUERY
);
318 OUString sPageStyleName
;
319 Any aValue
= xPageProperySet
->getPropertyValue("PageStyleName");
320 aValue
>>= sPageStyleName
;
322 Reference
<css::style::XStyleFamiliesSupplier
> xStyleFamiliesSupplier(xModel
, UNO_QUERY
);
323 if (!xStyleFamiliesSupplier
.is())
325 SAL_WARN("sfx.doc", "Ref to xStyleFamiliesSupplier is null in setPageMargins().");
328 uno::Reference
<container::XNameAccess
> xStyleFamilies
329 = xStyleFamiliesSupplier
->getStyleFamilies();
331 if (!xStyleFamilies
.is())
334 uno::Reference
<container::XNameAccess
> xPageStyles(xStyleFamilies
->getByName("PageStyles"),
337 if (!xPageStyles
.is())
340 uno::Reference
<css::style::XStyle
> xPageStyle(xPageStyles
->getByName(sPageStyleName
),
343 if (!xPageStyle
.is())
346 uno::Reference
<beans::XPropertySet
> xPageProperties(xPageStyle
, uno::UNO_QUERY
);
348 if (!xPageProperties
.is())
351 xPageProperties
->getPropertyValue("LeftMargin") >>= aPageMargins
.nLeft
;
352 xPageProperties
->getPropertyValue("RightMargin") >>= aPageMargins
.nRight
;
353 xPageProperties
->getPropertyValue("TopMargin") >>= aPageMargins
.nTop
;
354 xPageProperties
->getPropertyValue("BottomMargin") >>= aPageMargins
.nBottom
;
360 SfxRedactionHelper::getPageMarginsForCalc(const css::uno::Reference
<css::frame::XModel
>& xModel
)
362 PageMargins aPageMargins
= { -1, -1, -1, -1 };
363 OUString
sPageStyleName("Default");
365 css::uno::Reference
<css::sheet::XSpreadsheetView
> xSpreadsheetView(
366 xModel
->getCurrentController(), UNO_QUERY
);
368 if (!xSpreadsheetView
.is())
370 SAL_WARN("sfx.doc", "Ref to xSpreadsheetView is null in getPageMarginsForCalc().");
374 uno::Reference
<beans::XPropertySet
> xSheetProperties(xSpreadsheetView
->getActiveSheet(),
377 xSheetProperties
->getPropertyValue("PageStyle") >>= sPageStyleName
;
379 Reference
<css::style::XStyleFamiliesSupplier
> xStyleFamiliesSupplier(xModel
, UNO_QUERY
);
380 if (!xStyleFamiliesSupplier
.is())
382 SAL_WARN("sfx.doc", "Ref to xStyleFamiliesSupplier is null in getPageMarginsForCalc().");
385 uno::Reference
<container::XNameAccess
> xStyleFamilies
386 = xStyleFamiliesSupplier
->getStyleFamilies();
388 if (!xStyleFamilies
.is())
391 uno::Reference
<container::XNameAccess
> xPageStyles(xStyleFamilies
->getByName("PageStyles"),
394 if (!xPageStyles
.is())
397 uno::Reference
<css::style::XStyle
> xPageStyle(xPageStyles
->getByName(sPageStyleName
),
400 if (!xPageStyle
.is())
403 uno::Reference
<beans::XPropertySet
> xPageProperties(xPageStyle
, uno::UNO_QUERY
);
405 if (!xPageProperties
.is())
408 xPageProperties
->getPropertyValue("LeftMargin") >>= aPageMargins
.nLeft
;
409 xPageProperties
->getPropertyValue("RightMargin") >>= aPageMargins
.nRight
;
410 xPageProperties
->getPropertyValue("TopMargin") >>= aPageMargins
.nTop
;
411 xPageProperties
->getPropertyValue("BottomMargin") >>= aPageMargins
.nBottom
;
416 void SfxRedactionHelper::searchInMetaFile(const RedactionTarget
* pRedactionTarget
,
417 const GDIMetaFile
& rMtf
,
418 std::vector
<::tools::Rectangle
>& aRedactionRectangles
,
419 const uno::Reference
<XComponent
>& xComponent
)
422 i18nutil::SearchOptions2 aSearchOptions
;
423 fillSearchOptions(aSearchOptions
, pRedactionTarget
);
425 utl::TextSearch
textSearch(aSearchOptions
);
426 static tools::Long aLastFontHeight
= 0;
428 MetaAction
* pCurrAct
;
430 for (pCurrAct
= const_cast<GDIMetaFile
&>(rMtf
).FirstAction(); pCurrAct
;
431 pCurrAct
= const_cast<GDIMetaFile
&>(rMtf
).NextAction())
433 // Watch for TEXTARRAY actions.
434 // They contain the text of paragraphs.
435 if (pCurrAct
->GetType() == MetaActionType::TEXTARRAY
)
437 MetaTextArrayAction
* pMetaTextArrayAction
= static_cast<MetaTextArrayAction
*>(pCurrAct
);
439 // Search operation takes place here
440 OUString sText
= pMetaTextArrayAction
->GetText();
441 sal_Int32 nStart
= 0;
442 sal_Int32 nEnd
= sText
.getLength();
444 bool bFound
= textSearch
.SearchForward(sText
, &nStart
, &nEnd
);
446 // If found the string, add the corresponding rectangle to the collection
449 OutputDevice
* pOutputDevice
450 = SfxObjectShell::GetShellFromComponent(xComponent
)->GetDocumentRefDev();
451 tools::Rectangle
aNewRect(
452 ImplCalcActionBounds(*pMetaTextArrayAction
, *pOutputDevice
, nStart
, nEnd
));
454 if (!aNewRect
.IsEmpty())
456 // Calculate the difference between current wrong value and value should it be.
457 // Add the difference to current value.
458 // Then increase 10% of the new value to make it look better.
459 aNewRect
.SetTop(aNewRect
.getY() + (aNewRect
.getHeight() - aLastFontHeight
)
460 - aLastFontHeight
/ 10);
461 aRedactionRectangles
.push_back(aNewRect
);
464 // Search for the next occurrence
466 nEnd
= sText
.getLength();
467 bFound
= textSearch
.SearchForward(sText
, &nStart
, &nEnd
);
470 else if (pCurrAct
->GetType() == MetaActionType::FONT
)
472 const MetaFontAction
* pFontAct
= static_cast<const MetaFontAction
*>(pCurrAct
);
473 aLastFontHeight
= pFontAct
->GetFont().GetFontSize().getHeight();
478 void SfxRedactionHelper::addRedactionRectToPage(
479 const uno::Reference
<XComponent
>& xComponent
, const uno::Reference
<drawing::XDrawPage
>& xPage
,
480 const std::vector
<::tools::Rectangle
>& aNewRectangles
)
482 if (!xComponent
.is() || !xPage
.is())
485 if (aNewRectangles
.empty())
488 uno::Reference
<css::lang::XMultiServiceFactory
> xFactory(xComponent
, uno::UNO_QUERY
);
490 for (auto const& aNewRectangle
: aNewRectangles
)
492 uno::Reference
<drawing::XShape
> xRectShape(
493 xFactory
->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY
);
494 uno::Reference
<beans::XPropertySet
> xRectShapeProperySet(xRectShape
, uno::UNO_QUERY
);
496 xRectShapeProperySet
->setPropertyValue("Name",
497 uno::Any(OUString("RectangleRedactionShape")));
498 xRectShapeProperySet
->setPropertyValue("FillTransparence",
499 css::uno::makeAny(static_cast<sal_Int16
>(50)));
500 xRectShapeProperySet
->setPropertyValue("FillColor", css::uno::makeAny(COL_GRAY7
));
501 xRectShapeProperySet
->setPropertyValue(
502 "LineStyle", css::uno::makeAny(css::drawing::LineStyle::LineStyle_NONE
));
504 xRectShape
->setSize(awt::Size(aNewRectangle
.GetWidth(), aNewRectangle
.GetHeight()));
505 xRectShape
->setPosition(awt::Point(aNewRectangle
.getX(), aNewRectangle
.getY()));
507 xPage
->add(xRectShape
);
511 void SfxRedactionHelper::autoRedactPage(const RedactionTarget
* pRedactionTarget
,
512 const GDIMetaFile
& rGDIMetaFile
,
513 const uno::Reference
<drawing::XDrawPage
>& xPage
,
514 const uno::Reference
<XComponent
>& xComponent
)
516 if (pRedactionTarget
== nullptr || pRedactionTarget
->sContent
.isEmpty())
519 // Search for the redaction strings, and get the rectangle coordinates
520 std::vector
<::tools::Rectangle
> aRedactionRectangles
;
521 searchInMetaFile(pRedactionTarget
, rGDIMetaFile
, aRedactionRectangles
, xComponent
);
523 // Add the redaction rectangles to the page
524 addRedactionRectToPage(xComponent
, xPage
, aRedactionRectangles
);
529 const LanguageTag
& GetAppLanguageTag() { return Application::GetSettings().GetLanguageTag(); }
532 void SfxRedactionHelper::fillSearchOptions(i18nutil::SearchOptions2
& rSearchOpt
,
533 const RedactionTarget
* pTarget
)
538 "pTarget (pointer to Redactiontarget) is null. This should never happen.");
542 if (pTarget
->sType
== RedactionTargetType::REDACTION_TARGET_REGEX
543 || pTarget
->sType
== RedactionTargetType::REDACTION_TARGET_PREDEFINED
)
545 rSearchOpt
.algorithmType
= util::SearchAlgorithms_REGEXP
;
546 rSearchOpt
.AlgorithmType2
= util::SearchAlgorithms2::REGEXP
;
550 rSearchOpt
.algorithmType
= util::SearchAlgorithms_ABSOLUTE
;
551 rSearchOpt
.AlgorithmType2
= util::SearchAlgorithms2::ABSOLUTE
;
554 rSearchOpt
.Locale
= GetAppLanguageTag().getLocale();
555 if (pTarget
->sType
== RedactionTargetType::REDACTION_TARGET_PREDEFINED
)
557 auto nPredefIndex
= pTarget
->sContent
.getToken(0, ';').toUInt32();
558 rSearchOpt
.searchString
= m_aPredefinedTargets
[nPredefIndex
];
561 rSearchOpt
.searchString
= pTarget
->sContent
;
563 rSearchOpt
.replaceString
.clear();
565 if (!pTarget
->bCaseSensitive
&& pTarget
->sType
!= RedactionTargetType::REDACTION_TARGET_REGEX
566 && pTarget
->sType
!= RedactionTargetType::REDACTION_TARGET_PREDEFINED
)
567 rSearchOpt
.transliterateFlags
|= TransliterationFlags::IGNORE_CASE
;
568 if (pTarget
->bWholeWords
)
569 rSearchOpt
.searchFlag
|= util::SearchFlags::NORM_WORD_ONLY
;
572 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */