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 <com/sun/star/embed/XEmbedPersist.hpp>
22 #include <com/sun/star/embed/Aspects.hpp>
23 #include <com/sun/star/embed/ElementModes.hpp>
24 #include <sal/log.hxx>
25 #include <vcl/graphicfilter.hxx>
26 #include <vcl/gdimtf.hxx>
27 #include <svl/itemiter.hxx>
28 #include <tools/UnitConversion.hxx>
30 #include <svtools/embedhlp.hxx>
32 #include <hintids.hxx>
33 #include <editeng/boxitem.hxx>
34 #include <editeng/shaditem.hxx>
35 #include <filter/msfilter/msoleexp.hxx>
36 #include <editeng/fhgtitem.hxx>
37 #include <svx/svdoole2.hxx>
39 #include <unotools/ucbstreamhelper.hxx>
40 #include <fmtanchr.hxx>
45 #include <fmtfsize.hxx>
46 #include <fmtornt.hxx>
48 #include "sprmids.hxx"
51 #include "writerhelper.hxx"
52 #include "writerwordglue.hxx"
53 #include "ww8struc.hxx"
58 #include "ww8attributeoutput.hxx"
61 #include <IDocumentDrawModelAccess.hxx>
62 #include <drawdoc.hxx>
63 #include <o3tl/string_view.hxx>
65 using namespace ::com::sun::star
;
68 // 5. convert the MapModes that Widows can't handle
70 // OutGrf () is called for every GrfNode in the document. A PicLocFc-Sprm
71 // will be inserted, which contains a magic number instead of an address.
72 // The GrfNode-Ptr is saved in Graf-Class (used later for output of
73 // the graphic and patching of the PicLocFc attributes)
75 void WW8Export::OutputGrfNode( const SwGrfNode
& /*rNode*/ )
77 SAL_INFO("sw", "WW8Export::OutputGrfNode( const SwGrfNode& )" );
78 OSL_ENSURE( m_pParentFrame
, "frame not set!" );
81 OutGrf( *m_pParentFrame
);
82 m_pFib
->m_fHasPic
= true;
86 bool WW8Export::TestOleNeedsGraphic(const SwAttrSet
& rSet
, rtl::Reference
<SotStorage
> const& xOleStg
,
87 const rtl::Reference
<SotStorage
>& xObjStg
,
88 OUString
const& rStorageName
, SwOLENode
& rOLENd
)
90 bool bGraphicNeeded
= false;
91 SfxItemIter
aIter( rSet
);
92 for (auto pItem
= aIter
.GetCurItem(); !bGraphicNeeded
&& pItem
; pItem
= aIter
.NextItem())
94 switch (pItem
->Which())
97 For an inline object these properties are irrelevant because they
98 will be the same as the defaults that msword applies in their
99 absence, so if that is all that there is for these inline objects
100 then if there turns out to be enough information in the object
101 itself to regenerate the correct size and preview of the object
102 then we will not need to provide an additional graphics preview in
103 the data stream, which can save a lot of disk space.
107 case RES_VERT_ORIENT
:
111 bGraphicNeeded
= true;
116 Now we must see if the object contains a preview itself which is equal to
117 the preview that we are currently using. If the graphics are equal then we
118 don't need to store another preview
121 tools::Long nX
=0,nY
=0;
122 if (!bGraphicNeeded
&& SwWW8ImplReader::ImportOleWMF(xOleStg
,aWMF
,nX
,nY
))
124 // bGraphicNeeded set to true is right / fixes #i51670#.
125 bGraphicNeeded
= true;
126 tools::Rectangle
aRect( Point(), Size( nX
, nY
) );
127 Graphic
aGraph(aWMF
);
129 ErrCode nErr
= ERRCODE_NONE
;
130 sal_Int64 nAspect
= rOLENd
.GetAspect();
131 rtl::Reference
<SdrOle2Obj
> pRet
= SvxMSDffManager::CreateSdrOLEFromStorage(
132 m_rDoc
.getIDocumentDrawModelAccess().GetOrCreateDrawModel(),
135 m_rDoc
.GetDocStorage(),
143 m_pWriter
->GetBaseURL());
147 uno::Reference
< embed::XEmbeddedObject
> xObj
= rOLENd
.GetOLEObj().GetOleRef();
150 std::unique_ptr
<SvStream
> pGraphicStream
;
151 comphelper::EmbeddedObjectContainer
aCnt( m_rDoc
.GetDocStorage() );
154 uno::Reference
< embed::XEmbedPersist
> xPersist(xObj
, uno::UNO_QUERY
);
157 // it makes no sense to search the object in the container by reference since the object was created
158 // outside of the container and was not inserted there, only the name makes sense
160 ::utl::UcbStreamHelper::CreateStream( aCnt
.GetGraphicStream( xPersist
->getEntryName() ) );
163 catch( const uno::Exception
& )
166 OSL_ENSURE( pGraphicStream
&& !pGraphicStream
->GetError(), "No graphic stream available!" );
167 if ( pGraphicStream
&& !pGraphicStream
->GetError() )
170 GraphicFilter
& rGF
= GraphicFilter::GetGraphicFilter();
171 if( rGF
.ImportGraphic( aGr1
, u
"", *pGraphicStream
) == ERRCODE_NONE
)
175 ::utl::UcbStreamHelper::CreateStream( aCnt
.GetGraphicStream( pRet
->GetObjRef() ) );
176 if( pGraphicStream
&& rGF
.ImportGraphic( aGr2
, u
"", *pGraphicStream
) == ERRCODE_NONE
)
179 bGraphicNeeded
= false;
187 bGraphicNeeded
= true;
188 return bGraphicNeeded
;
191 void WW8Export::OutputOLENode( const SwOLENode
& rOLENode
)
193 SAL_INFO("sw", "WW8Export::OutputOLENode( const SwOLENode& rOLENode )" );
197 static sal_uInt8 aSpecOLE_WW8
[] = {
198 0x03, 0x6a, 0, 0, 0, 0, // sprmCPicLocation
199 0x0a, 0x08, 1, // sprmCFOLE2
200 0x56, 0x08, 1 // sprmCFObj
203 pSpecOLE
= aSpecOLE_WW8
;
204 nSize
= sizeof( aSpecOLE_WW8
);
205 pDataAdr
= pSpecOLE
+ 2; //WW6 sprm is 1 but has 1 byte len as well.
207 rtl::Reference
<SotStorage
> xObjStg
= GetWriter().GetStorage().OpenSotStorage(SL::aObjectPool
);
212 uno::Reference
< embed::XEmbeddedObject
> xObj(const_cast<SwOLENode
&>(rOLENode
).GetOLEObj().GetOleRef());
216 const embed::XEmbeddedObject
*pObj
= xObj
.get();
217 //Don't want to use pointer ids, as is traditional, because we need
218 //to put this into a 32bit value, and on 64bit the bottom bits
219 //might collide and two unrelated ole objects end up considered the
220 //same. Don't want to simply start at 0 which is a special value
221 sal_Int32 nPictureId
= SAL_MAX_INT32
- m_aOleMap
.size();
222 WW8OleMap::value_type entry
= std::make_pair(pObj
, nPictureId
);
223 std::pair
<WW8OleMap::iterator
, bool> aRes
= m_aOleMap
.insert(entry
);
224 bool bIsNotDuplicate
= aRes
.second
; //.second is false when element already existed
225 nPictureId
= aRes
.first
->second
;
226 Set_UInt32(pDataAdr
, nPictureId
);
227 OUString sStorageName
= "_" + OUString::number( nPictureId
);
228 rtl::Reference
<SotStorage
> xOleStg
= xObjStg
->OpenSotStorage(sStorageName
);
233 If this object storage has been written already don't
234 waste time rewriting it
238 sal_Int64 nAspect
= rOLENode
.GetAspect();
239 svt::EmbeddedObjectRef
aObjRef( xObj
, nAspect
);
240 m_oOLEExp
->ExportOLEObject( aObjRef
, *xOleStg
);
241 if ( nAspect
== embed::Aspects::MSOLE_ICON
)
243 OUString
aObjInfo( u
"\3ObjInfo"_ustr
);
244 if ( !xOleStg
->IsStream( aObjInfo
) )
246 const sal_uInt8 pObjInfoData
[] = { 0x40, 0x00, 0x03, 0x00 };
247 rtl::Reference
<SotStorageStream
> rObjInfoStream
= xOleStg
->OpenSotStream(aObjInfo
);
248 if ( rObjInfoStream
.is() && !rObjInfoStream
->GetError() )
250 rObjInfoStream
->WriteBytes(pObjInfoData
, sizeof(pObjInfoData
));
257 // write as embedded field - the other things will be done
258 // in the escher export
259 OUString sServer
= FieldString(ww::eEMBED
) + xOleStg
->GetUserName() + " ";
261 OutputField(nullptr, ww::eEMBED
, sServer
, FieldFlags::Start
|
262 FieldFlags::CmdStart
| FieldFlags::CmdEnd
);
264 m_pChpPlc
->AppendFkpEntry( Strm().Tell(),
269 In the word filter we only need a preview image for
270 floating images, and then only (the usual case) if the
271 object doesn't contain enough information to reconstruct
274 We don't need a graphic for inline objects, so we don't
275 even need the overhead of a graphic in that case.
277 bool bGraphicNeeded
= false;
281 bGraphicNeeded
= true;
283 if (m_pParentFrame
->IsInline())
285 const SwAttrSet
& rSet
=
286 m_pParentFrame
->GetFrameFormat().GetAttrSet();
288 bGraphicNeeded
= TestOleNeedsGraphic(rSet
,
289 xOleStg
, xObjStg
, sStorageName
, const_cast<SwOLENode
&>(rOLENode
));
299 We need to insert the graphic representation of
300 this object for the inline case, otherwise word
301 has no place to find the dimensions of the ole
302 object, and will not be able to draw it
304 OutGrf(*m_pParentFrame
);
307 OutputField(nullptr, ww::eEMBED
, OUString(),
308 FieldFlags::End
| FieldFlags::Close
);
310 if (bEndCR
) //No newline in inline case
314 void WW8Export::OutputLinkedOLE( const OUString
& rOleId
)
316 uno::Reference
< embed::XStorage
> xDocStg
= m_rDoc
.GetDocStorage();
317 uno::Reference
< embed::XStorage
> xOleStg
= xDocStg
->openStorageElement( u
"OLELinks"_ustr
, embed::ElementModes::READ
);
318 rtl::Reference
<SotStorage
> xObjSrc
= SotStorage::OpenOLEStorage( xOleStg
, rOleId
, StreamMode::READ
);
320 rtl::Reference
<SotStorage
> xObjStg
= GetWriter().GetStorage().OpenSotStorage(SL::aObjectPool
);
322 if( !(xObjStg
.is() && xObjSrc
.is()) )
325 rtl::Reference
<SotStorage
> xOleDst
= xObjStg
->OpenSotStorage(rOleId
);
327 xObjSrc
->CopyTo( xOleDst
.get() );
329 if ( xOleDst
->GetError( ) )
334 // Output the cPicLocation attribute
335 std::unique_ptr
<ww::bytes
> pBuf( new ww::bytes
);
336 SwWW8Writer::InsUInt16( *pBuf
, NS_sprm::CPicLocation::val
);
337 SwWW8Writer::InsUInt32( *pBuf
, o3tl::toInt32(rOleId
.subView( 1 )) );
339 SwWW8Writer::InsUInt16( *pBuf
, NS_sprm::CFOle2::val
);
340 pBuf
->push_back( 1 );
342 SwWW8Writer::InsUInt16( *pBuf
, NS_sprm::CFSpec::val
);
343 pBuf
->push_back( 1 );
345 SwWW8Writer::InsUInt16( *pBuf
, NS_sprm::CFObj::val
);
346 pBuf
->push_back( 1 );
348 m_pChpPlc
->AppendFkpEntry( Strm().Tell(), pBuf
->size(), pBuf
->data() );
351 void WW8Export::OutGrf(const ww8::Frame
&rFrame
)
353 //Added for i120568,the hyperlink info within a graphic whose anchor type is "As character"
354 //will be exported to ensure the fidelity
355 const SwFormatURL
& rURL
= rFrame
.GetFrameFormat().GetAttrSet().GetURL();
356 bool bURLStarted
= false;
357 if( !rURL
.GetURL().isEmpty() && rFrame
.GetWriterType() == ww8::Frame::eGraphic
)
360 m_pAttrOutput
->StartURL( rURL
.GetURL(), rURL
.GetTargetFrameName() );
363 // Store the graphic settings in GrfNode so they may be written-out later
364 m_pGrf
->Insert(rFrame
);
366 m_pChpPlc
->AppendFkpEntry( Strm().Tell(), m_pO
->size(), m_pO
->data() );
370 // linked, as-character anchored graphics have to be exported as fields.
371 const SwGrfNode
* pGrfNd
= rFrame
.IsInline() && rFrame
.GetContent()
372 ? rFrame
.GetContent()->GetGrfNode() : nullptr;
373 if ( pGrfNd
&& pGrfNd
->IsLinkedFile() )
376 pGrfNd
->GetFileFilterNms(&sStr
, nullptr);
377 sStr
= FieldString(ww::eINCLUDEPICTURE
) + " \"" + sStr
+ "\" \\d";
379 OutputField( nullptr, ww::eINCLUDEPICTURE
, sStr
,
380 FieldFlags::Start
| FieldFlags::CmdStart
| FieldFlags::CmdEnd
);
383 WriteChar( char(1) ); // paste graphic symbols in the main text
385 sal_uInt8 aArr
[ 18 ];
386 sal_uInt8
* pArr
= aArr
;
388 const SwFrameFormat
&rFlyFormat
= rFrame
.GetFrameFormat();
389 const RndStdIds eAn
= rFlyFormat
.GetAttrSet().GetAnchor(false).GetAnchorId();
390 if (eAn
== RndStdIds::FLY_AS_CHAR
)
392 sal_Int16 eVert
= rFlyFormat
.GetVertOrient().GetVertOrient();
393 if ((eVert
== text::VertOrientation::CHAR_CENTER
) || (eVert
== text::VertOrientation::LINE_CENTER
))
396 //The default for word in vertical text mode is to center,
397 //otherwise a sub/super script hack is employed
398 if (auto pTextNd
= dynamic_cast< const SwContentNode
*>( m_pOutFormatNode
) )
400 SwPosition
aPos(*pTextNd
);
401 bVert
= m_rDoc
.IsInVerticalText(aPos
);
405 SwTwips nHeight
= rFlyFormat
.GetFrameSize().GetHeight();
406 nHeight
/=20; //nHeight was in twips, want it in half points, but
407 //then half of total height.
408 tools::Long nFontHeight
= GetItem(RES_CHRATR_FONTSIZE
).GetHeight();
409 nHeight
-=nFontHeight
/20;
411 Set_UInt16( pArr
, NS_sprm::CHpsPos::val
);
412 Set_UInt16( pArr
, - static_cast<sal_Int16
>(nHeight
));
418 Set_UInt16( pArr
, 0x855 );
419 Set_UInt8( pArr
, 1 );
422 Set_UInt16( pArr
, NS_sprm::CPicLocation::val
);
423 Set_UInt32( pArr
, GRF_MAGIC_321
);
425 // vary Magic, so that different graphic attributes will not be merged
426 static sal_uInt8 nAttrMagicIdx
= 0;
428 Set_UInt8( pArr
, nAttrMagicIdx
++ );
429 m_pChpPlc
->AppendFkpEntry( Strm().Tell(), static_cast< short >(pArr
- aArr
), aArr
);
432 // Check, if graphic isn't exported as-character anchored.
433 // Otherwise, an additional paragraph is exported for a graphic, which is
434 // forced to be treated as inline, because it's anchored inside another frame.
435 if ( !rFrame
.IsInline() &&
436 ( (eAn
== RndStdIds::FLY_AT_PARA
) ||
437 (eAn
== RndStdIds::FLY_AT_PAGE
) ) )
439 WriteChar( char(0x0d) ); // close the surrounding frame with CR
441 static sal_uInt8 nSty
[2] = { 0, 0 };
442 m_pO
->insert( m_pO
->end(), nSty
, nSty
+2 ); // Style #0
443 bool bOldGrf
= m_bOutGrf
;
446 OutputFormat( rFrame
.GetFrameFormat(), false, false, true ); // Fly-Attrs
449 m_pPapPlc
->AppendFkpEntry( Strm().Tell(), m_pO
->size(), m_pO
->data() );
453 // linked, as-character anchored graphics have to be exported as fields.
454 else if ( pGrfNd
&& pGrfNd
->IsLinkedFile() )
456 OutputField( nullptr, ww::eINCLUDEPICTURE
, OUString(), FieldFlags::Close
);
458 //Added for i120568,the hyperlink info within a graphic whose anchor type is
459 //"As character" will be exported to ensure the fidelity
461 m_pAttrOutput
->EndURL(false);
464 void SwWW8WrGrf::Insert(const ww8::Frame
&rFly
)
466 const Size
aSize( rFly
.GetLayoutSize() );
467 const sal_uInt16 nWidth
= static_cast< sal_uInt16
>(aSize
.Width());
468 const sal_uInt16 nHeight
= static_cast< sal_uInt16
>(aSize
.Height());
469 maDetails
.emplace_back(rFly
, nWidth
, nHeight
);
472 void SwWW8WrGrf::WritePICFHeader(SvStream
& rStrm
, const ww8::Frame
&rFly
,
473 sal_uInt16 mm
, sal_uInt16 nWidth
, sal_uInt16 nHeight
, const SwAttrSet
* pAttrSet
)
475 sal_Int16 nXSizeAdd
= 0, nYSizeAdd
= 0;
476 sal_Int16 nCropL
= 0, nCropR
= 0, nCropT
= 0, nCropB
= 0;
478 // write Crop-Attribute content in Header ( if available )
479 const SwCropGrf
* pCropItem
;
480 if (pAttrSet
&& (pCropItem
481 = pAttrSet
->GetItemIfSet(RES_GRFATR_CROPGRF
, false)))
483 nCropL
= static_cast<sal_Int16
>(pCropItem
->GetLeft());
484 nCropR
= static_cast<sal_Int16
>(pCropItem
->GetRight());
485 nCropT
= static_cast<sal_Int16
>(pCropItem
->GetTop());
486 nCropB
= static_cast<sal_Int16
>(pCropItem
->GetBottom());
487 nXSizeAdd
= nXSizeAdd
- static_cast<sal_Int16
>( pCropItem
->GetLeft() + pCropItem
->GetRight() );
488 nYSizeAdd
= nYSizeAdd
- static_cast<sal_Int16
>( pCropItem
->GetTop() + pCropItem
->GetBottom() );
491 Size
aGrTwipSz(rFly
.GetSize());
492 sal_uInt16 nHdrLen
= 0x44;
494 sal_uInt8 aArr
[ 0x44 ] = { 0 };
496 sal_uInt8
* pArr
= aArr
+ 0x2E; // Do borders first
498 const SwAttrSet
& rAttrSet
= rFly
.GetFrameFormat().GetAttrSet();
499 if (const SvxBoxItem
* pBox
= rAttrSet
.GetItemIfSet(RES_BOX
, false))
501 bool bShadow
= false; // Shadow ?
502 if (const SvxShadowItem
* pSI
= rAttrSet
.GetItem
<SvxShadowItem
>(RES_SHADOW
))
504 bShadow
= (pSI
->GetLocation() != SvxShadowLocation::NONE
) &&
505 (pSI
->GetWidth() != 0);
508 static const SvxBoxItemLine aLnArr
[4] = { SvxBoxItemLine::TOP
, SvxBoxItemLine::LEFT
,
509 SvxBoxItemLine::BOTTOM
, SvxBoxItemLine::RIGHT
};
510 for(const SvxBoxItemLine
& i
: aLnArr
)
512 const ::editeng::SvxBorderLine
* pLn
= pBox
->GetLine( i
);
516 WW8_BRCVer9 aBrc90
= WW8Export::TranslateBorderLine( *pLn
,
517 pBox
->GetDistance( i
), bShadow
);
518 sal_uInt8 ico
= msfilter::util::TransColToIco(msfilter::util::BGRToRGB(
520 aBrc
= WW8_BRC(aBrc90
.dptLineWidth(), aBrc90
.brcType(), ico
,
521 aBrc90
.dptSpace(), aBrc90
.fShadow(), aBrc90
.fFrame());
524 // use importer logic to determine how large the exported
525 // border will really be in word and adjust accordingly
527 short nThick
= aBrc
.DetermineBorderProperties(&nSpacing
);
530 case SvxBoxItemLine::TOP
:
531 case SvxBoxItemLine::BOTTOM
:
532 nHeight
-= bShadow
? nThick
*2 : nThick
;
533 nHeight
= nHeight
- nSpacing
;
535 case SvxBoxItemLine::LEFT
:
536 case SvxBoxItemLine::RIGHT
:
538 nWidth
-= bShadow
? nThick
*2 : nThick
;
539 nWidth
= nWidth
- nSpacing
;
542 memcpy( pArr
, &aBrc
.aBits1
, 2);
545 memcpy( pArr
, &aBrc
.aBits2
, 2);
550 pArr
= aArr
+ 4; // skip lcb
551 Set_UInt16( pArr
, nHdrLen
); // set cbHeader
553 Set_UInt16( pArr
, mm
); // set mm
556 Just in case our original size is too big to fit inside a ushort we can
557 substitute the final size and lose on retaining the scaling factor but
558 still keep the correct display size anyway.
560 const bool bIsSubstitutedSize
= (aGrTwipSz
.Width() > SHRT_MAX
) || (aGrTwipSz
.Height() > SHRT_MAX
) ||
562 if ( bIsSubstitutedSize
)
564 aGrTwipSz
.setWidth( nWidth
);
565 aGrTwipSz
.setHeight( nHeight
);
567 using namespace sw::types
;
569 Set_UInt16(pArr
, msword_cast
<sal_uInt16
>(convertTwipToMm100(aGrTwipSz
.Width())));
570 Set_UInt16(pArr
, msword_cast
<sal_uInt16
>(convertTwipToMm100(aGrTwipSz
.Height())));
572 // skip hMF & rcWinMF
573 // set dxaGoal & dyaGoal
574 Set_UInt16(pArr
, msword_cast
<sal_uInt16
>(aGrTwipSz
.Width()));
575 Set_UInt16(pArr
, msword_cast
<sal_uInt16
>(aGrTwipSz
.Height()));
577 if ( aGrTwipSz
.Width() + nXSizeAdd
) // set mx
579 if ( !bIsSubstitutedSize
)
581 const double fVal
= nWidth
* 1000.0 / (aGrTwipSz
.Width() + nXSizeAdd
);
582 Set_UInt16( pArr
, o3tl::narrowing
<sal_uInt16
>(::rtl::math::round(fVal
)) );
586 Set_UInt16( pArr
, 1000 );
594 if ( aGrTwipSz
.Height() + nYSizeAdd
) // set my
596 if ( !bIsSubstitutedSize
)
598 const double fVal
= nHeight
* 1000.0 / (aGrTwipSz
.Height() + nYSizeAdd
);
599 Set_UInt16( pArr
, o3tl::narrowing
<sal_uInt16
>(::rtl::math::round(fVal
)) );
603 Set_UInt16( pArr
, 1000 );
611 if ( !bIsSubstitutedSize
)
613 Set_UInt16( pArr
, nCropL
); // set dxaCropLeft
614 Set_UInt16( pArr
, nCropT
); // set dyaCropTop
615 Set_UInt16( pArr
, nCropR
); // set dxaCropRight
616 Set_UInt16( pArr
, nCropB
); // set dyaCropBottom
619 rStrm
.WriteBytes(aArr
, nHdrLen
);
622 void SwWW8WrGrf::WriteGrfFromGrfNode(SvStream
& rStrm
, const SwGrfNode
&rGrfNd
,
623 const ww8::Frame
&rFly
, sal_uInt16 nWidth
, sal_uInt16 nHeight
)
625 if (rGrfNd
.IsLinkedFile()) // Linked File
628 rGrfNd
.GetFileFilterNms( &aFileN
, nullptr );
630 sal_uInt16
const mm
= 94; // 94 = BMP, GIF
632 WritePICFHeader(rStrm
, rFly
, mm
, nWidth
, nHeight
,
633 rGrfNd
.GetpSwAttrSet());
634 rStrm
.WriteUChar( aFileN
.getLength() ); // write Pascal-String
635 SwWW8Writer::WriteString8(rStrm
, aFileN
, false,
636 RTL_TEXTENCODING_MS_1252
);
638 else // Embedded File or DDE or something like that
640 WritePICFHeader(rStrm
, rFly
, 0x64, nWidth
, nHeight
,
641 rGrfNd
.GetpSwAttrSet());
642 SwBasicEscherEx
aInlineEscher(&rStrm
, m_rWrt
);
643 aInlineEscher
.WriteGrfFlyFrame(rFly
.GetFrameFormat(), 0x401);
644 aInlineEscher
.WritePictures();
647 //For i120928,export graphic info of bullet
648 void SwWW8WrGrf::WritePICBulletFHeader(SvStream
& rStrm
, const Graphic
&rGrf
,
649 sal_uInt16 mm
, sal_uInt16 nWidth
, sal_uInt16 nHeight
)
651 sal_Int16 nXSizeAdd
= 0, nYSizeAdd
= 0;
653 Size
aGrTwipSz(rGrf
.GetPrefSize());
654 sal_uInt16 nHdrLen
= 0x44;
656 sal_uInt8 aArr
[ 0x44 ] = { 0 };
658 sal_uInt8
* pArr
= aArr
+ 0x2E; //Do borders first
660 static const SvxBoxItemLine aLnArr
[4] = { SvxBoxItemLine::TOP
, SvxBoxItemLine::LEFT
,
661 SvxBoxItemLine::BOTTOM
, SvxBoxItemLine::RIGHT
};
662 for(const SvxBoxItemLine
& i
: aLnArr
)
667 short nThick
= aBrc
.DetermineBorderProperties(&nSpacing
);
670 case SvxBoxItemLine::TOP
:
671 case SvxBoxItemLine::BOTTOM
:
673 nHeight
= nHeight
- nSpacing
;
675 case SvxBoxItemLine::LEFT
:
676 case SvxBoxItemLine::RIGHT
:
679 nWidth
= nWidth
- nSpacing
;
682 memcpy( pArr
, &aBrc
.aBits1
, 2);
685 memcpy(pArr
, &aBrc
.aBits2
, 2);
689 pArr
= aArr
+ 4; //skip lcb
690 Set_UInt16( pArr
, nHdrLen
); // set cbHeader
692 Set_UInt16( pArr
, mm
); // set mm
694 if ( (convertTwipToMm100(aGrTwipSz
.Width()) > USHRT_MAX
) || ( convertTwipToMm100(aGrTwipSz
.Height()) > USHRT_MAX
)
695 || aGrTwipSz
.IsEmpty() )
697 aGrTwipSz
.setWidth( nWidth
);
698 aGrTwipSz
.setHeight( nHeight
);
700 using namespace sw::types
;
702 Set_UInt16(pArr
, msword_cast
<sal_uInt16
>(convertTwipToMm100(aGrTwipSz
.Width())));
703 Set_UInt16(pArr
, msword_cast
<sal_uInt16
>(convertTwipToMm100(aGrTwipSz
.Height())));
705 // skip hMF & rcWinMF
706 // set dxaGoal & dyaGoal
707 Set_UInt16(pArr
, msword_cast
<sal_uInt16
>(aGrTwipSz
.Width()));
708 Set_UInt16(pArr
, msword_cast
<sal_uInt16
>(aGrTwipSz
.Height()));
710 if( aGrTwipSz
.Width() + nXSizeAdd
) // set mx
712 double fVal
= nWidth
* 1000.0 / (aGrTwipSz
.Width() + nXSizeAdd
);
713 Set_UInt16( pArr
, o3tl::narrowing
<sal_uInt16
>(::rtl::math::round(fVal
)) );
718 if( aGrTwipSz
.Height() + nYSizeAdd
) // set my
720 double fVal
= nHeight
* 1000.0 / (aGrTwipSz
.Height() + nYSizeAdd
);
721 Set_UInt16( pArr
, o3tl::narrowing
<sal_uInt16
>(::rtl::math::round(fVal
)) );
726 Set_UInt16( pArr
, 0 ); // set dxaCropLeft
727 Set_UInt16( pArr
, 0 ); // set dyaCropTop
728 Set_UInt16( pArr
, 0 ); // set dxaCropRight
729 Set_UInt16( pArr
, 0 ); // set dyaCropBottom
731 rStrm
.WriteBytes(aArr
, nHdrLen
);
734 void SwWW8WrGrf::WriteGrfForBullet(SvStream
& rStrm
, const Graphic
&rGrf
, sal_uInt16 nWidth
, sal_uInt16 nHeight
)
736 WritePICBulletFHeader(rStrm
,rGrf
, 0x64,nWidth
,nHeight
);
737 SwBasicEscherEx
aInlineEscher(&rStrm
, m_rWrt
);
738 aInlineEscher
.WriteGrfBullet(rGrf
);
739 aInlineEscher
.WritePictures();
742 void SwWW8WrGrf::WriteGraphicNode(SvStream
& rStrm
, const GraphicDetails
&rItem
)
744 sal_uInt16 nWidth
= rItem
.mnWid
;
745 sal_uInt16 nHeight
= rItem
.mnHei
;
746 sal_uInt64 nPos
= rStrm
.Tell(); // store start of graphic
748 const ww8::Frame
&rFly
= rItem
.maFly
;
749 switch (rFly
.GetWriterType())
751 case ww8::Frame::eGraphic
:
753 const SwNode
*pNode
= rItem
.maFly
.GetContent();
754 const SwGrfNode
*pNd
= pNode
? pNode
->GetGrfNode() : nullptr;
755 OSL_ENSURE(pNd
, "Impossible");
757 WriteGrfFromGrfNode(rStrm
, *pNd
, rItem
.maFly
, nWidth
, nHeight
);
760 //For i120928,add branch to export graphic of bullet
761 case ww8::Frame::eBulletGrf
:
763 if (rItem
.maFly
.HasGraphic())
765 const Graphic
& rGrf
= rItem
.maFly
.GetGraphic();
766 WriteGrfForBullet(rStrm
, rGrf
, nWidth
, nHeight
);
771 case ww8::Frame::eOle
:
773 const SwNode
*pNode
= rItem
.maFly
.GetContent();
774 const SwOLENode
*pNd
= pNode
? pNode
->GetOLENode() : nullptr;
775 OSL_ENSURE(pNd
, "Impossible");
778 #ifdef OLE_PREVIEW_AS_EMF
779 //Convert this ole2 preview in ww8+ to an EMF for better unicode
780 //support (note that at this moment this breaks StarSymbol
781 //using graphics because I need to embed starsymbol in exported
783 WritePICFHeader(rStrm
, rFly
, 0x64, nWidth
, nHeight
,
784 pNd
->GetpSwAttrSet());
785 SwBasicEscherEx
aInlineEscher(&rStrm
, m_rWrt
);
786 aInlineEscher
.WriteOLEFlyFrame(rFly
.GetFrameFormat(), 0x401);
787 aInlineEscher
.WritePictures();
790 SwOLENode
*pOleNd
= const_cast<SwOLENode
*>(pNd
);
791 SwOLEObj
& rSObj
= pOleNd
->GetOLEObj();
793 // TODO/LATER: do we need to load object?
794 Graphic
* pGr
= SdrOle2Obj::GetGraphicFromObject( pOleNd
->GetDoc()->GetDocStorage(), rObj
);
796 //TODO/LATER: do we really want to use GDIMetafile?!
799 aMtf
= pGr
->GetGDIMetaFile();
801 Size
aS(aMtf
.GetPrefSize());
803 aMtf
.Play(Application::GetDefaultDevice(), Point(0, 0),
806 WritePICFHeader(rStrm
, rFly
, 8, nWidth
, nHeight
,
807 pNd
->GetpSwAttrSet());
808 WriteWindowMetafileBits(rStrm
, aMtf
);
814 case ww8::Frame::eDrawing
:
815 case ww8::Frame::eTextBox
:
816 case ww8::Frame::eFormControl
:
818 #i3958# We only export an empty dummy picture frame here, this is
819 what word does the escher export should contain an anchored to
820 character element which is drawn over this dummy and the whole
821 shebang surrounded with a SHAPE field. This isn't *my* hack :-),
825 WritePICFHeader(rStrm
, rFly
, 0x64, nWidth
, nHeight
);
826 SwBasicEscherEx
aInlineEscher(&rStrm
, m_rWrt
);
827 aInlineEscher
.WriteEmptyFlyFrame(rFly
.GetFrameFormat(), 0x401);
831 OSL_ENSURE(false, "Some inline export not implemented");
835 sal_uInt64 nPos2
= rStrm
.Tell(); // store the end
837 rStrm
.WriteUInt32(nPos2
- nPos
); // patch graphic length in the header
838 rStrm
.Seek( nPos2
); // restore Pos
841 // SwWW8WrGrf::Write() is called after the text.
842 // It writes out all the graphics and remembers the file locations of the graphics,
843 // so when writing the attributes of the items it can be patched into PicLocFc-SPRMs.
844 // The search in the attributes for the Magic sal_uLong and patching
845 // happens when writing the attributes. Class SwWW8WrGrf provides with
846 // GetFPos() sequentially the positions
847 void SwWW8WrGrf::Write()
849 SvStream
& rStrm
= *m_rWrt
.m_pDataStrm
;
850 auto aEnd
= maDetails
.end();
851 for (auto aIter
= maDetails
.begin(); aIter
!= aEnd
; ++aIter
)
853 sal_uInt64 nPos
= rStrm
.Tell(); // align to 4 Bytes
855 SwWW8Writer::FillCount( rStrm
, 4 - ( nPos
& 0x3 ) );
857 auto aIter2
= std::find(maDetails
.begin(), aIter
, *aIter
);
860 aIter
->mnPos
= aIter2
->mnPos
;
864 aIter
->mnPos
= rStrm
.Tell();
865 WriteGraphicNode(rStrm
, *aIter
);
870 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */