Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / svx / source / table / viewcontactoftableobj.cxx
blob5b9418ab06bf7f1c2f42e026c7170dc4c1e46cd9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "viewcontactoftableobj.hxx"
22 #include <svx/svdotable.hxx>
23 #include <com/sun/star/table/XTable.hpp>
24 #include <basegfx/polygon/b2dpolygontools.hxx>
25 #include <sdr/primitive2d/sdrattributecreator.hxx>
26 #include <sdr/primitive2d/sdrdecompositiontools.hxx>
27 #include <basegfx/matrix/b2dhommatrix.hxx>
28 #include <sdr/attribute/sdrtextattribute.hxx>
29 #include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
30 #include <editeng/borderline.hxx>
31 #include <sdr/attribute/sdrfilltextattribute.hxx>
32 #include <drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx>
33 #include <drawinglayer/attribute/sdrlineattribute.hxx>
34 #include <drawinglayer/attribute/sdrshadowattribute.hxx>
35 #include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx>
36 #include <drawinglayer/primitive2d/structuretagprimitive2d.hxx>
37 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
38 #include <basegfx/matrix/b2dhommatrixtools.hxx>
39 #include <svx/sdr/contact/viewobjectcontactofsdrobj.hxx>
40 #include <svx/sdr/contact/objectcontact.hxx>
41 #include <svx/svdpage.hxx>
42 #include <svx/framelink.hxx>
43 #include <svx/framelinkarray.hxx>
44 #include <svx/sdooitm.hxx>
45 #include <utility>
46 #include <vcl/canvastools.hxx>
47 #include <o3tl/unit_conversion.hxx>
48 #include <svx/xfltrit.hxx>
50 #include <cell.hxx>
51 #include "tablelayouter.hxx"
54 using editeng::SvxBorderLine;
55 using namespace com::sun::star;
58 namespace drawinglayer::primitive2d
60 namespace {
62 class SdrCellPrimitive2D : public BufferedDecompositionPrimitive2D
64 private:
65 basegfx::B2DHomMatrix maTransform;
66 attribute::SdrFillTextAttribute maSdrFTAttribute;
68 protected:
69 // local decomposition.
70 virtual void create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const override;
72 public:
73 SdrCellPrimitive2D(
74 basegfx::B2DHomMatrix aTransform,
75 const attribute::SdrFillTextAttribute& rSdrFTAttribute)
76 : maTransform(std::move(aTransform)),
77 maSdrFTAttribute(rSdrFTAttribute)
81 // data access
82 const basegfx::B2DHomMatrix& getTransform() const { return maTransform; }
83 const attribute::SdrFillTextAttribute& getSdrFTAttribute() const { return maSdrFTAttribute; }
85 // compare operator
86 virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
88 // provide unique ID
89 virtual sal_uInt32 getPrimitive2DID() const override;
94 void SdrCellPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const
96 // prepare unit polygon
97 const basegfx::B2DPolyPolygon aUnitPolyPolygon(basegfx::utils::createUnitPolygon());
99 // add fill
100 if(!getSdrFTAttribute().getFill().isDefault())
102 basegfx::B2DPolyPolygon aTransformed(aUnitPolyPolygon);
104 aTransformed.transform(getTransform());
105 rContainer.push_back(
106 createPolyPolygonFillPrimitive(
107 aTransformed,
108 getSdrFTAttribute().getFill(),
109 getSdrFTAttribute().getFillFloatTransGradient()));
111 else
113 // if no fill create one for HitTest and BoundRect fallback
114 rContainer.push_back(
115 createHiddenGeometryPrimitives2D(
116 true,
117 aUnitPolyPolygon,
118 getTransform()));
121 // add text
122 if(!getSdrFTAttribute().getText().isDefault())
124 rContainer.push_back(
125 createTextPrimitive(
126 aUnitPolyPolygon,
127 getTransform(),
128 getSdrFTAttribute().getText(),
129 attribute::SdrLineAttribute(),
130 true,
131 false));
135 bool SdrCellPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
137 if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
139 const SdrCellPrimitive2D& rCompare = static_cast<const SdrCellPrimitive2D&>(rPrimitive);
141 return (getTransform() == rCompare.getTransform()
142 && getSdrFTAttribute() == rCompare.getSdrFTAttribute());
145 return false;
148 // provide unique ID
149 sal_uInt32 SdrCellPrimitive2D::getPrimitive2DID() const
151 return PRIMITIVE2D_ID_SDRCELLPRIMITIVE2D;
154 } // end of namespace
156 namespace sdr::contact
159 namespace {
160 class ViewObjectContactOfTableObj : public ViewObjectContactOfSdrObj
162 public:
163 ViewObjectContactOfTableObj(ObjectContact& rObjectContact, ViewContact& rViewContact)
164 : ViewObjectContactOfSdrObj(rObjectContact, rViewContact)
168 protected:
169 virtual void createPrimitive2DSequence(DisplayInfo const& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const override;
171 } // namespace
173 static svx::frame::Style impGetLineStyle(
174 const sdr::table::TableLayouter& rLayouter,
175 sal_Int32 nX,
176 sal_Int32 nY,
177 bool bHorizontal,
178 sal_Int32 nColCount,
179 sal_Int32 nRowCount,
180 bool bIsRTL)
182 if(nX >= 0 && nX <= nColCount && nY >= 0 && nY <= nRowCount)
184 const SvxBorderLine* pLine = rLayouter.getBorderLine(nX, nY, bHorizontal);
186 if(pLine)
188 // copy line content
189 SvxBorderLine aLine(*pLine);
191 // check for mirroring. This shall always be done when it is
192 // not a top- or rightmost line
193 bool bMirror(aLine.isDouble());
195 if(bMirror)
197 if(bHorizontal)
199 // mirror all bottom lines
200 bMirror = (0 != nY);
202 else
204 // mirror all left lines
205 bMirror = (bIsRTL ? 0 != nX : nX != nColCount);
209 if(bMirror)
211 aLine.SetMirrorWidths( );
214 constexpr double fTwipsToMM(
215 o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::mm100));
216 return svx::frame::Style(&aLine, fTwipsToMM);
220 // no success, copy empty line
221 return svx::frame::Style();
224 static void createPrimitive2DSequenceImpl(
225 sdr::table::SdrTableObj const& rTableObj,
226 bool const isTaggedPDF,
227 drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor)
229 const uno::Reference< css::table::XTable > xTable = rTableObj.getTable();
231 if(xTable.is())
233 // create primitive representation for table. Cell info goes
234 // directly to aRetval, Border info to aBorderSequence and added
235 // later to get the correct overlapping
236 drawinglayer::primitive2d::Primitive2DContainer aRetval;
237 drawinglayer::primitive2d::Primitive2DContainer aRetvalForShadow;
238 const sal_Int32 nRowCount(xTable->getRowCount());
239 const sal_Int32 nColCount(xTable->getColumnCount());
240 const sal_Int32 nAllCount(nRowCount * nColCount);
241 SdrPage const*const pPage(rTableObj.getSdrPageFromSdrObject());
243 if(nAllCount)
245 const sdr::table::TableLayouter& rTableLayouter(rTableObj.getTableLayouter());
246 const bool bIsRTL(css::text::WritingMode_RL_TB == rTableObj.GetWritingMode());
247 sdr::table::CellPos aCellPos;
248 sdr::table::CellRef xCurrentCell;
249 basegfx::B2IRectangle aCellArea;
251 // create range using the model data directly. This is in SdrTextObj::aRect which i will access using
252 // GetGeoRect() to not trigger any calculations. It's the unrotated geometry.
253 const basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(rTableObj.GetGeoRect());
255 // To create the CellBorderPrimitives, use the tooling from svx::frame::Array
256 // which is capable of creating the needed visualization. Fill it during the
257 // anyways needed run over the table.
258 svx::frame::Array aArray;
260 // initialize CellBorderArray for primitive creation
261 aArray.Initialize(nColCount, nRowCount);
263 // create single primitives per cell
264 for(aCellPos.mnRow = 0; aCellPos.mnRow < nRowCount; aCellPos.mnRow++)
266 drawinglayer::primitive2d::Primitive2DContainer row;
267 // add RowHeight to CellBorderArray for primitive creation
268 aArray.SetRowHeight(aCellPos.mnRow, rTableLayouter.getRowHeight(aCellPos.mnRow));
270 for(aCellPos.mnCol = 0; aCellPos.mnCol < nColCount; aCellPos.mnCol++)
272 drawinglayer::primitive2d::Primitive2DContainer cell;
273 // add ColWidth to CellBorderArray for primitive creation, only
274 // needs to be done in the 1st run
275 if(0 == aCellPos.mnRow)
277 aArray.SetColWidth(aCellPos.mnCol, rTableLayouter.getColumnWidth(aCellPos.mnCol));
280 // access the cell
281 xCurrentCell.set(dynamic_cast< sdr::table::Cell* >(xTable->getCellByPosition(aCellPos.mnCol, aCellPos.mnRow).get()));
283 if(xCurrentCell.is())
285 // copy styles for current cell to CellBorderArray for primitive creation
286 aArray.SetCellStyleLeft(aCellPos.mnCol, aCellPos.mnRow, impGetLineStyle(rTableLayouter, aCellPos.mnCol, aCellPos.mnRow, false, nColCount, nRowCount, bIsRTL));
287 aArray.SetCellStyleRight(aCellPos.mnCol, aCellPos.mnRow, impGetLineStyle(rTableLayouter, aCellPos.mnCol + 1, aCellPos.mnRow, false, nColCount, nRowCount, bIsRTL));
288 aArray.SetCellStyleTop(aCellPos.mnCol, aCellPos.mnRow, impGetLineStyle(rTableLayouter, aCellPos.mnCol, aCellPos.mnRow, true, nColCount, nRowCount, bIsRTL));
289 aArray.SetCellStyleBottom(aCellPos.mnCol, aCellPos.mnRow, impGetLineStyle(rTableLayouter, aCellPos.mnCol, aCellPos.mnRow + 1, true, nColCount, nRowCount, bIsRTL));
291 // ignore merged cells (all except the top-left of a merged cell)
292 if(!xCurrentCell->isMerged())
294 // check if we are the top-left of a merged cell
295 const sal_Int32 nXSpan(xCurrentCell->getColumnSpan());
296 const sal_Int32 nYSpan(xCurrentCell->getRowSpan());
298 if(nXSpan > 1 || nYSpan > 1)
300 // if merged, set so at CellBorderArray for primitive creation
301 aArray.SetMergedRange(aCellPos.mnCol, aCellPos.mnRow, aCellPos.mnCol + nXSpan - 1, aCellPos.mnRow + nYSpan - 1);
306 if(xCurrentCell.is() && !xCurrentCell->isMerged())
308 if(rTableLayouter.getCellArea(xCurrentCell, aCellPos, aCellArea))
310 // create cell transformation matrix
311 basegfx::B2DHomMatrix aCellMatrix;
312 aCellMatrix.set(0, 0, static_cast<double>(aCellArea.getWidth()));
313 aCellMatrix.set(1, 1, static_cast<double>(aCellArea.getHeight()));
314 aCellMatrix.set(0, 2, static_cast<double>(aCellArea.getMinX()) + aObjectRange.getMinX());
315 aCellMatrix.set(1, 2, static_cast<double>(aCellArea.getMinY()) + aObjectRange.getMinY());
317 // handle cell fillings and text
318 const SfxItemSet& rCellItemSet = xCurrentCell->GetItemSet();
319 const sal_uInt32 nTextIndex(nColCount * aCellPos.mnRow + aCellPos.mnCol);
320 const SdrText* pSdrText = rTableObj.getText(nTextIndex);
321 drawinglayer::attribute::SdrFillTextAttribute aAttribute;
323 if(pSdrText)
325 // #i101508# take cell's local text frame distances into account
326 const sal_Int32 nLeft(xCurrentCell->GetTextLeftDistance());
327 const sal_Int32 nRight(xCurrentCell->GetTextRightDistance());
328 const sal_Int32 nUpper(xCurrentCell->GetTextUpperDistance());
329 const sal_Int32 nLower(xCurrentCell->GetTextLowerDistance());
331 aAttribute = drawinglayer::primitive2d::createNewSdrFillTextAttribute(
332 rCellItemSet,
333 pSdrText,
334 &nLeft,
335 &nUpper,
336 &nRight,
337 &nLower);
339 else
341 aAttribute = drawinglayer::primitive2d::createNewSdrFillTextAttribute(
342 rCellItemSet,
343 pSdrText);
346 // always create cell primitives for BoundRect and HitTest
348 const drawinglayer::primitive2d::Primitive2DReference xCellReference(
349 new drawinglayer::primitive2d::SdrCellPrimitive2D(
350 aCellMatrix, aAttribute));
351 cell.append(xCellReference);
354 // Create cell primitive without text.
355 aAttribute
356 = drawinglayer::primitive2d::createNewSdrFillTextAttribute(
357 rCellItemSet, nullptr);
358 rtl::Reference pCellReference
359 = new drawinglayer::primitive2d::SdrCellPrimitive2D(
360 aCellMatrix, aAttribute);
362 sal_uInt16 nTransparence(
363 rCellItemSet.Get(XATTR_FILLTRANSPARENCE).GetValue());
364 if (nTransparence != 0)
366 pCellReference->setTransparenceForShadow(nTransparence);
369 const drawinglayer::primitive2d::Primitive2DReference
370 xCellReference(pCellReference);
371 aRetvalForShadow.append(xCellReference);
374 if (isTaggedPDF && pPage)
376 // heuristic: if there's a special formatting on
377 // first row, assume that it's a header row
378 auto const eType(
379 aCellPos.mnRow == 0 && rTableObj.getTableStyleSettings().mbUseFirstRow
380 ? vcl::PDFWriter::TableHeader
381 : vcl::PDFWriter::TableData);
382 cell = drawinglayer::primitive2d::Primitive2DContainer {
383 new drawinglayer::primitive2d::StructureTagPrimitive2D(
384 eType,
385 pPage->IsMasterPage(),
386 false,
387 std::move(cell)) };
389 row.append(cell);
392 if (isTaggedPDF && pPage)
394 row = drawinglayer::primitive2d::Primitive2DContainer {
395 new drawinglayer::primitive2d::StructureTagPrimitive2D(
396 vcl::PDFWriter::TableRow,
397 pPage->IsMasterPage(),
398 false,
399 std::move(row)) };
401 aRetval.append(row);
404 // now create all CellBorderPrimitives
405 drawinglayer::primitive2d::Primitive2DContainer aCellBorderPrimitives(aArray.CreateB2DPrimitiveArray());
407 if(!aCellBorderPrimitives.empty())
409 // this is already scaled (due to Table in non-uniform coordinates), so
410 // first transform removing scale
411 basegfx::B2DHomMatrix aTransform(
412 basegfx::utils::createScaleB2DHomMatrix(
413 1.0 / aObjectRange.getWidth(),
414 1.0 / aObjectRange.getHeight()));
416 // If RTL, mirror the whole unified table in X and move right.
417 // This is much easier than taking this into account for the whole
418 // index calculations
419 if(bIsRTL)
421 aTransform.scale(-1.0, 1.0);
422 aTransform.translate(1.0, 0.0);
425 // create object matrix
426 const GeoStat& rGeoStat(rTableObj.GetGeoStat());
427 const double fShearX(-rGeoStat.mfTanShearAngle);
428 const double fRotate(rGeoStat.nRotationAngle ? toRadians(36000_deg100 - rGeoStat.nRotationAngle) : 0.0);
429 const basegfx::B2DHomMatrix aObjectMatrix(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
430 aObjectRange.getWidth(), aObjectRange.getHeight(), fShearX, fRotate,
431 aObjectRange.getMinX(), aObjectRange.getMinY()));
433 // add object matrix to transform. By doing so theoretically
434 // CellBorders could be also rotated/sheared for the first time ever.
435 // To completely make that work, the primitives already created in
436 // aRetval would also have to be based on ObjectMatrix, not only on
437 // ObjectRange as it currently is.
438 aTransform *= aObjectMatrix;
440 // create a transform primitive with this and embed CellBorders
441 // and append to retval
442 aRetval.append(
443 new drawinglayer::primitive2d::TransformPrimitive2D(
444 aTransform,
445 drawinglayer::primitive2d::Primitive2DContainer(aCellBorderPrimitives)));
447 // Borders are always the same for shadow as well.
448 aRetvalForShadow.append(new drawinglayer::primitive2d::TransformPrimitive2D(
449 aTransform, std::move(aCellBorderPrimitives)));
452 if(!aRetval.empty())
454 // check and create evtl. shadow for created content
455 const SfxItemSet& rObjectItemSet = rTableObj.GetMergedItemSet();
456 const drawinglayer::attribute::SdrShadowAttribute aNewShadowAttribute(
457 drawinglayer::primitive2d::createNewSdrShadowAttribute(rObjectItemSet));
459 if(!aNewShadowAttribute.isDefault())
461 // pass in table's transform and scale matrix, to
462 // correctly scale and align shadows
463 const basegfx::B2DHomMatrix aTransformScaleMatrix
464 = basegfx::utils::createScaleTranslateB2DHomMatrix(
465 aObjectRange.getRange(), aObjectRange.getMinimum());
467 bool bDirectShadow
468 = rObjectItemSet.Get(SDRATTR_SHADOW, /*bSrchInParent=*/false)
469 .GetValue();
470 if (bDirectShadow)
472 // Shadow as direct formatting: no shadow for text, to be compatible
473 // with PowerPoint.
474 aRetval = drawinglayer::primitive2d::createEmbeddedShadowPrimitive(
475 std::move(aRetval), aNewShadowAttribute, aTransformScaleMatrix,
476 &aRetvalForShadow);
478 else
480 // Shadow as style: shadow for text, to be backwards-compatible.
481 aRetval = drawinglayer::primitive2d::createEmbeddedShadowPrimitive(
482 std::move(aRetval), aNewShadowAttribute, aTransformScaleMatrix);
488 rVisitor.visit(std::move(aRetval));
490 else
492 // take unrotated snap rect (direct model data) for position and size
493 const basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(rTableObj.GetGeoRect());
495 // create object matrix
496 const GeoStat& rGeoStat(rTableObj.GetGeoStat());
497 const double fShearX(-rGeoStat.mfTanShearAngle);
498 const double fRotate(rGeoStat.nRotationAngle ? toRadians(36000_deg100 - rGeoStat.nRotationAngle) : 0.0);
499 const basegfx::B2DHomMatrix aObjectMatrix(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
500 aObjectRange.getWidth(), aObjectRange.getHeight(), fShearX, fRotate,
501 aObjectRange.getMinX(), aObjectRange.getMinY()));
503 // created an invisible outline for the cases where no visible content exists
504 const drawinglayer::primitive2d::Primitive2DReference xReference(
505 drawinglayer::primitive2d::createHiddenGeometryPrimitives2D(
506 aObjectMatrix));
508 rVisitor.visit(xReference);
512 void ViewObjectContactOfTableObj::createPrimitive2DSequence(
513 DisplayInfo const& rDisplayInfo,
514 drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
516 bool const isTaggedPDF(GetObjectContact().isExportTaggedPDF());
517 if (isTaggedPDF)
519 // this will be unbuffered and contain structure tags
520 const sdr::table::SdrTableObj& rTableObj =
521 static_cast<const sdr::table::SdrTableObj&>(*GetViewContact().TryToGetSdrObject());
522 return createPrimitive2DSequenceImpl(rTableObj, true, rVisitor);
524 else
526 // call it via the base class - this is supposed to be buffered
527 return sdr::contact::ViewObjectContactOfSdrObj::createPrimitive2DSequence(rDisplayInfo, rVisitor);
531 void ViewContactOfTableObj::createViewIndependentPrimitive2DSequence(
532 drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
534 const sdr::table::SdrTableObj& rTableObj =
535 static_cast<const sdr::table::SdrTableObj&>(GetSdrObject());
536 return createPrimitive2DSequenceImpl(rTableObj, false, rVisitor);
539 ViewObjectContact& ViewContactOfTableObj::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
541 return *new ViewObjectContactOfTableObj(rObjectContact, *this);
544 ViewContactOfTableObj::ViewContactOfTableObj(sdr::table::SdrTableObj& rTableObj)
545 : ViewContactOfSdrObj(rTableObj)
549 ViewContactOfTableObj::~ViewContactOfTableObj()
552 } // end of namespace
554 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */