1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <IDocumentContentOperations.hxx>
22 #include "writerhelper.hxx"
23 #include <com/sun/star/embed/XClassifiedObject.hpp>
24 #include <com/sun/star/embed/Aspects.hpp>
27 #include <osl/endian.h>
28 #include <sot/storage.hxx>
29 #include <com/sun/star/drawing/XShape.hpp>
30 #include <hintids.hxx>
31 #include <svx/svdoole2.hxx>
32 #include <filter/msfilter/msdffimp.hxx>
33 #include "sprmids.hxx"
34 #include <svx/unoapi.hxx>
35 #include <sal/log.hxx>
37 #include <sot/exchange.hxx>
38 #include <fmtanchr.hxx>
43 #include <fltshell.hxx>
44 #include <shellio.hxx>
46 #include <vcl/wmf.hxx>
47 #include <vcl/gdimtf.hxx>
49 #include "ww8scan.hxx"
51 #include "ww8par2.hxx"
57 sal_Int16 mm
; // 0x6 int
58 sal_Int16 xExt
; // 0x8 int in 1/100 mm
59 sal_Int16 yExt
; // 0xa int in 1/100 mm
60 sal_Int16 hMF
; // 0xc int
65 using namespace ::com::sun::star
;
67 static bool SwWw8ReadScaling(tools::Long
& rX
, tools::Long
& rY
, tools::SvRef
<SotStorage
> const & rSrc1
)
69 // Getting the scaling factor:
70 // Information in the PIC-stream (by trying out)
72 // 0x08 .. 0x0a Flags ??
73 // 0x08 contains: 1 / 0
74 // 0x09 contains: 0,8,0x18
75 // 0x0a contains: always 8, MAP_ANISOTROPIC ???
76 // 0x0b contains: always 0
77 // 0x0c, 0x10 original size x,y in 1/100 mm
78 // 0x14, 0x16 original size x,y in tw
79 // 0x2c, 0x30 scaling x,y in per thousand
80 // 0x34, 0x38, 0x3c, 0x40 Crop Left, Top, Right, Bot in tw
82 tools::SvRef
<SotStorageStream
> xSrc3
= rSrc1
->OpenSotStream( "\3PIC",
83 StreamMode::STD_READ
);
84 SotStorageStream
* pS
= xSrc3
.get();
85 pS
->SetEndian( SvStreamEndian::LITTLE
);
86 pS
->Seek( STREAM_SEEK_TO_END
);
88 OSL_ENSURE( pS
->Tell() >= 76, "+OLE-PIC-Stream is shorter than 76 Byte" );
99 pS
->ReadInt32( nOrgWidth
) // Original Size in 1/100 mm
100 .ReadInt32( nOrgHeight
);
102 pS
->ReadInt32( nScaleX
) // Scaling in Promille
103 .ReadInt32( nScaleY
)
104 .ReadInt32( nCropLeft
) // Cropping in 1/100 mm
105 .ReadInt32( nCropTop
)
106 .ReadInt32( nCropRight
)
107 .ReadInt32( nCropBottom
);
109 rX
= nOrgWidth
- nCropLeft
- nCropRight
;
110 rY
= nOrgHeight
- nCropTop
- nCropBottom
;
111 if (10 > nScaleX
|| 65536 < nScaleX
|| 10 > nScaleY
|| 65536 < nScaleY
)
113 OSL_ENSURE( !pS
, "+OLE-scaling information in PIC-stream wrong" );
118 rX
= (rX
* nScaleX
) / 1000;
119 rY
= (rY
* nScaleY
) / 1000;
124 static bool SwWw6ReadMetaStream(GDIMetaFile
& rWMF
, OLE_MFP
* pMfp
,
125 tools::SvRef
<SotStorage
> const & rSrc1
)
127 tools::SvRef
<SotStorageStream
> xSrc2
= rSrc1
->OpenSotStream( "\3META",
128 StreamMode::STD_READ
);
129 SotStorageStream
* pSt
= xSrc2
.get();
130 pSt
->SetEndian( SvStreamEndian::LITTLE
);
131 size_t const nRead
= pSt
->ReadBytes(pMfp
, sizeof(*pMfp
));
132 // read mini-placable-header
133 if (nRead
!= sizeof(*pMfp
))
136 #if defined OSL_BIGENDIAN
137 pMfp
->mm
= OSL_SWAPWORD( pMfp
->mm
);
138 pMfp
->xExt
= OSL_SWAPWORD( pMfp
->xExt
);
139 pMfp
->yExt
= OSL_SWAPWORD( pMfp
->yExt
);
140 #endif // OSL_BIGENDIAN
142 if( pMfp
->mm
== 94 || pMfp
->mm
== 99 )
144 SAL_WARN("sw.ww8", "+OLE: wrong metafile type");
149 SAL_WARN("sw.ww8", "OLE: wrong mMetafile type (not anisotropic)");
151 if( !pMfp
->xExt
|| !pMfp
->yExt
)
153 SAL_WARN("sw.ww8", "+OLE: size of 0?");
156 bool bOk
= ReadWindowMetafile( *pSt
, rWMF
); // read WMF
157 // *pSt >> aWMF doesn't work without the placable header
158 if (!bOk
|| pSt
->GetError() || rWMF
.GetActionSize() == 0)
160 SAL_WARN("sw.ww8", "+OLE: could not read the metafile");
164 rWMF
.SetPrefMapMode( MapMode( MapUnit::Map100thMM
) );
166 // Scale MetaFile to new size and save new size to MetaFile
167 Size
aOldSiz( rWMF
.GetPrefSize() );
168 Size
aNewSiz( pMfp
->xExt
, pMfp
->yExt
);
169 Fraction
aFracX( aNewSiz
.Width(), aOldSiz
.Width() );
170 Fraction
aFracY( aNewSiz
.Height(), aOldSiz
.Height() );
172 rWMF
.Scale( aFracX
, aFracY
);
173 rWMF
.SetPrefSize( aNewSiz
);
178 static bool SwWw6ReadMacPICTStream(Graphic
& rGraph
, tools::SvRef
<SotStorage
> const & rSrc1
)
180 // 03-META-stream does not exist. Maybe a 03-PICT?
181 tools::SvRef
<SotStorageStream
> xSrc4
= rSrc1
->OpenSotStream("\3PICT");
182 SotStorageStream
* pStp
= xSrc4
.get();
183 pStp
->SetEndian( SvStreamEndian::LITTLE
);
184 sal_uInt8 aTestA
[10]; // Does the 01Ole-stream even exist?
185 size_t const nReadTst
= pStp
->ReadBytes(aTestA
, sizeof(aTestA
));
186 if (nReadTst
!= sizeof(aTestA
))
189 pStp
->Seek( STREAM_SEEK_TO_BEGIN
);
191 // Mac-Pict is in the 03PICT-StorageStream but without the first 512 Bytes
192 // which are not relevant in a MAC-PICT (they are not evaluated)
193 return SwWW8ImplReader::GetPictGrafFromStream(rGraph
, *pStp
);
196 SwFlyFrameFormat
* SwWW8ImplReader::InsertOle(SdrOle2Obj
&rObject
,
197 const SfxItemSet
&rFlySet
, const SfxItemSet
*rGrfSet
)
199 SfxObjectShell
*pPersist
= m_rDoc
.GetPersist();
200 OSL_ENSURE(pPersist
, "No persist, cannot insert objects correctly");
204 SwFlyFrameFormat
*pRet
= nullptr;
206 std::unique_ptr
<SfxItemSet
> pMathFlySet
;
207 uno::Reference
< embed::XClassifiedObject
> xClass
= rObject
.GetObjRef();
210 SvGlobalName
aClassName( xClass
->getClassID() );
211 if (SotExchange::IsMath(aClassName
))
213 // StarMath sets it own fixed size, so its counter productive to use
214 // the size Word says it is. i.e. Don't attempt to override its size.
215 pMathFlySet
.reset(new SfxItemSet(rFlySet
));
216 pMathFlySet
->ClearItem(RES_FRM_SIZE
);
221 Take complete responsibility of the object away from SdrOle2Obj and to
222 me here locally. This utility class now owns the object.
225 // TODO/MBA: is the object inserted multiple times here? Testing!
226 // And is it a problem that we now use the same naming scheme as in the other apps?
227 sw::hack::DrawingOLEAdaptor
aOLEObj(rObject
, *pPersist
);
229 bool bSuccess
= aOLEObj
.TransferToDoc(sNewName
);
231 OSL_ENSURE(bSuccess
, "Insert OLE failed");
234 const SfxItemSet
*pFlySet
= pMathFlySet
? pMathFlySet
.get() : &rFlySet
;
235 pRet
= m_rDoc
.getIDocumentContentOperations().InsertOLE(*m_pPaM
, sNewName
, rObject
.GetAspect(), pFlySet
, rGrfSet
);
240 SwFrameFormat
* SwWW8ImplReader::ImportOle(const Graphic
* pGrf
,
241 const SfxItemSet
* pFlySet
, const SfxItemSet
*pGrfSet
, const tools::Rectangle
& aVisArea
)
243 ::SetProgressState(m_nProgress
, m_pDocShell
); // Update
244 SwFrameFormat
* pFormat
= nullptr;
249 SdrObject
* pRet
= ImportOleBase(aGraph
, pGrf
, pFlySet
, aVisArea
);
252 std::unique_ptr
<SfxItemSet
> pTempSet
;
255 pTempSet
.reset( new SfxItemSet( m_rDoc
.GetAttrPool(), svl::Items
<RES_FRMATR_BEGIN
,
256 RES_FRMATR_END
-1>{}) );
258 pFlySet
= pTempSet
.get();
260 // Remove distance/borders
261 Reader::ResetFrameFormatAttrs( *pTempSet
);
263 SwFormatAnchor
aAnchor( RndStdIds::FLY_AS_CHAR
);
264 aAnchor
.SetAnchor( m_pPaM
->GetPoint() );
265 pTempSet
->Put( aAnchor
);
267 const Size aSizeTwip
= OutputDevice::LogicToLogic(
268 aGraph
.GetPrefSize(), aGraph
.GetPrefMapMode(), MapMode(MapUnit::MapTwip
));
270 pTempSet
->Put( SwFormatFrameSize( SwFrameSize::Fixed
, aSizeTwip
.Width(),
271 aSizeTwip
.Height() ) );
272 pTempSet
->Put( SwFormatVertOrient( 0, text::VertOrientation::TOP
, text::RelOrientation::FRAME
));
276 // Resize the frame to the picture size if there is an OLE object
277 // in the frame (only if auto-width)
278 m_xSFlyPara
->BoxUpWidth(aSizeTwip
.Width());
282 if (pRet
) // OLE object was inserted
284 if (SdrOle2Obj
*pOleObj
= dynamic_cast<SdrOle2Obj
*>(pRet
))
286 pFormat
= InsertOle(*pOleObj
, *pFlySet
, pGrfSet
);
287 SdrObject::Free(pRet
); // we don't need this anymore
290 pFormat
= m_rDoc
.getIDocumentContentOperations().InsertDrawObj(*m_pPaM
, *pRet
, *pFlySet
);
293 GraphicType::GdiMetafile
== aGraph
.GetType() ||
294 GraphicType::Bitmap
== aGraph
.GetType()
297 pFormat
= m_rDoc
.getIDocumentContentOperations().InsertGraphic(
298 *m_pPaM
, OUString(), OUString(), &aGraph
, pFlySet
,
304 bool SwWW8ImplReader::ImportOleWMF(const tools::SvRef
<SotStorage
>& xSrc1
, GDIMetaFile
& rWMF
,
305 tools::Long
& rX
, tools::Long
& rY
)
309 if( SwWw6ReadMetaStream( rWMF
, &aMfp
, xSrc1
) )
311 // take scaling factor as found in PIC and apply it to graphic.
312 SwWw8ReadScaling( rX
, rY
, xSrc1
);
313 Size aFinalSize
, aOrigSize
;
314 aFinalSize
.setWidth( rX
);
315 aFinalSize
.setHeight( rY
);
316 aFinalSize
= OutputDevice::LogicToLogic(
317 aFinalSize
, MapMode(MapUnit::MapTwip
), rWMF
.GetPrefMapMode() );
318 aOrigSize
= rWMF
.GetPrefSize();
319 Fraction
aScaleX(aFinalSize
.Width(),aOrigSize
.Width());
320 Fraction
aScaleY(aFinalSize
.Height(),aOrigSize
.Height());
321 rWMF
.Scale( aScaleX
, aScaleY
);
327 SdrObject
* SwWW8ImplReader::ImportOleBase( Graphic
& rGraph
,
328 const Graphic
* pGrf
, const SfxItemSet
* pFlySet
, const tools::Rectangle
& aVisArea
)
332 SAL_WARN("sw.ww8", "no storage for ole objects");
336 ::SetProgressState( m_nProgress
, m_rDoc
.GetDocShell() ); // Update
338 tools::Long nX
=0, nY
=0; // nX, nY is graphic size
341 // results in the name "_4711"
342 OUString aSrcStgName
= "_" + OUString::number( m_nObjLocFc
);
344 tools::SvRef
<SotStorage
> xSrc0
= m_pStg
->OpenSotStorage(SL::aObjectPool
);
345 tools::SvRef
<SotStorage
> xSrc1
= xSrc0
->OpenSotStorage( aSrcStgName
);
350 const Size aSizeTwip
= OutputDevice::LogicToLogic(
351 rGraph
.GetPrefSize(), rGraph
.GetPrefMapMode(), MapMode(MapUnit::MapTwip
));
352 nX
= aSizeTwip
.Width();
353 nY
= aSizeTwip
.Height();
359 if (ImportOleWMF(xSrc1
,aWMF
,nX
,nY
))
360 rGraph
= Graphic( aWMF
);
361 else if( SwWw6ReadMacPICTStream( rGraph
, xSrc1
) )
363 // 03-META stream is not available. Maybe it's a 03-PICT?
364 const Size aSizeTwip
= OutputDevice::LogicToLogic(
365 rGraph
.GetPrefSize(), rGraph
.GetPrefMapMode(), MapMode(MapUnit::MapTwip
));
366 nX
= aSizeTwip
.Width();
367 nY
= aSizeTwip
.Height();
368 // PICT: no WMF available -> Graphic instead of OLE
371 } // StorageStreams closed again
373 tools::Rectangle
aRect(0, 0, nX
, nY
);
377 if (const SwFormatFrameSize
* pSize
= pFlySet
->GetItem
<SwFormatFrameSize
>(RES_FRM_SIZE
, false))
379 aRect
.SetSize(pSize
->GetSize());
383 SdrObject
* pRet
= nullptr;
385 if (!(m_bIsHeader
|| m_bIsFooter
))
387 //Can't put them in headers/footers :-(
388 uno::Reference
< drawing::XShape
> xRef
;
389 OSL_ENSURE(m_xFormImpl
, "Impossible");
390 if (m_xFormImpl
&& m_xFormImpl
->ReadOCXStream(xSrc1
, &xRef
))
392 pRet
= GetSdrObjectFromXShape(xRef
);
393 OSL_ENSURE(pRet
, "Impossible");
395 pRet
->SetLogicRect(aRect
);
400 if (GraphicType::GdiMetafile
== rGraph
.GetType() ||
401 GraphicType::Bitmap
== rGraph
.GetType())
403 ::SetProgressState(m_nProgress
, m_pDocShell
); // Update
407 sal_uLong nOldPos
= m_pDataStream
->Tell();
408 m_pDataStream
->Seek(STREAM_SEEK_TO_END
);
409 SvStream
*pTmpData
= nullptr;
410 if (m_nObjLocFc
< m_pDataStream
->Tell())
412 pTmpData
= m_pDataStream
;
413 pTmpData
->Seek( m_nObjLocFc
);
416 sal_Int64 nAspect
= embed::Aspects::MSOLE_CONTENT
;
419 tools::SvRef
<SotStorageStream
> xObjInfoSrc
= xSrc1
->OpenSotStream("\3ObjInfo",
420 StreamMode::STD_READ
);
421 if ( xObjInfoSrc
.is() && !xObjInfoSrc
->GetError() )
424 xObjInfoSrc
->ReadUChar( nByte
);
425 if ( ( nByte
>> 4 ) & embed::Aspects::MSOLE_ICON
)
426 nAspect
= embed::Aspects::MSOLE_ICON
;
430 ErrCode nError
= ERRCODE_NONE
;
433 pRet
= SvxMSDffManager::CreateSdrOLEFromStorage(
437 m_pDocShell
->GetStorage(),
443 SwMSDffManager::GetFilterFlags(),
446 m_pDataStream
->Seek( nOldPos
);
452 void SwWW8ImplReader::ReadRevMarkAuthorStrTabl( SvStream
& rStrm
,
453 sal_Int32 nTablePos
, sal_Int32 nTableSiz
, SwDoc
& rDocOut
)
455 std::vector
<OUString
> aAuthorNames
;
456 WW8ReadSTTBF( !m_bVer67
, rStrm
, nTablePos
, nTableSiz
, m_bVer67
? 2 : 0,
457 m_eStructCharSet
, aAuthorNames
);
459 sal_uInt16 nCount
= static_cast< sal_uInt16
>(aAuthorNames
.size());
460 for( sal_uInt16 nAuthor
= 0; nAuthor
< nCount
; ++nAuthor
)
462 // Store author in doc
463 std::size_t nSWId
= rDocOut
.getIDocumentRedlineAccess().InsertRedlineAuthor(aAuthorNames
[nAuthor
]);
465 m_aAuthorInfos
[nAuthor
] = nSWId
;
470 Revision Marks ( == Redlining )
472 // insert or delete content (change char attributes resp.)
473 void SwWW8ImplReader::Read_CRevisionMark(RedlineType eType
,
474 const sal_uInt8
* pData
, short nLen
)
476 // there *must* be a SprmCIbstRMark[Del] and a SprmCDttmRMark[Del]
477 // pointing to the very same char position as our SprmCFRMark[Del]
480 const sal_uInt8
* pSprmCIbstRMark
;
481 const sal_uInt8
* pSprmCDttmRMark
;
482 if( RedlineType::Format
== eType
)
484 pSprmCIbstRMark
= nLen
>= 3 ? pData
+1 : nullptr;
485 pSprmCDttmRMark
= nLen
>= 7 ? pData
+3 : nullptr;
489 /* It is possible to have a number of date stamps for the created time
490 * of the change, (possibly a word bug) so we must use the "get a full
491 * list" variant of HasCharSprm and take the last one as the true one.
493 std::vector
<SprmResult
> aResult
;
494 bool bIns
= (RedlineType::Insert
== eType
);
497 m_xPlcxMan
->HasCharSprm(69, aResult
);
498 pSprmCIbstRMark
= (aResult
.empty() || aResult
.back().nRemainingData
< 2) ? nullptr : aResult
.back().pSprm
;
500 m_xPlcxMan
->HasCharSprm(70, aResult
);
501 pSprmCDttmRMark
= (aResult
.empty() || aResult
.back().nRemainingData
< 4) ? nullptr : aResult
.back().pSprm
;
505 m_xPlcxMan
->HasCharSprm( bIns
? 0x4804 : 0x4863, aResult
);
506 pSprmCIbstRMark
= (aResult
.empty() || aResult
.back().nRemainingData
< 2) ? nullptr : aResult
.back().pSprm
;
508 m_xPlcxMan
->HasCharSprm( bIns
? 0x6805 : NS_sprm::CDttmRMarkDel::val
, aResult
);
509 pSprmCDttmRMark
= (aResult
.empty() || aResult
.back().nRemainingData
< 4) ? nullptr : aResult
.back().pSprm
;
514 m_xRedlineStack
->close(*m_pPaM
->GetPoint(), eType
, m_xTableDesc
.get());
517 // start of new revision mark, if not there default to first entry
518 sal_uInt16 nWWAutNo
= pSprmCIbstRMark
? SVBT16ToUInt16(pSprmCIbstRMark
) : 0;
519 sal_uInt32 nWWDate
= pSprmCDttmRMark
? SVBT32ToUInt32(pSprmCDttmRMark
): 0;
520 DateTime
aStamp(msfilter::util::DTTM2DateTime(nWWDate
));
521 std::size_t nAuthorNo
= m_aAuthorInfos
[nWWAutNo
];
522 SwFltRedline
aNewAttr(eType
, nAuthorNo
, aStamp
);
527 // insert new content
528 void SwWW8ImplReader::Read_CFRMark(sal_uInt16
, const sal_uInt8
* pData
, short nLen
)
530 Read_CRevisionMark( RedlineType::Insert
, pData
, nLen
);
533 // delete old content
534 void SwWW8ImplReader::Read_CFRMarkDel(sal_uInt16
, const sal_uInt8
* pData
, short nLen
)
536 Read_CRevisionMark( RedlineType::Delete
, pData
, nLen
);
539 // change properties of content ( == char formatting)
540 void SwWW8ImplReader::Read_CPropRMark(sal_uInt16
, const sal_uInt8
* pData
, short nLen
)
542 // complex (len is always 7)
543 // 1 byte - chp.fPropRMark
544 // 2 bytes - chp.ibstPropRMark
545 // 4 bytes - chp.dttmPropRMark;
546 Read_CRevisionMark( RedlineType::Format
, pData
, nLen
);
549 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */