Coverage Report

Created: 2026-09-28 10:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sfx2/source/doc/docinf.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
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/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
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 .
18
 */
19
20
21
#include <sfx2/docinf.hxx>
22
#include <com/sun/star/beans/PropertyAttribute.hpp>
23
#include <com/sun/star/beans/XPropertySet.hpp>
24
#include <com/sun/star/beans/XPropertyContainer.hpp>
25
#include <com/sun/star/document/XDocumentProperties.hpp>
26
#include <com/sun/star/document/XCompatWriterDocProperties.hpp>
27
#include <com/sun/star/uno/Exception.hpp>
28
#include <rtl/ustring.hxx>
29
#include <sal/log.hxx>
30
#include <tools/debug.hxx>
31
#include <comphelper/string.hxx>
32
#include <sot/storage.hxx>
33
#include <vcl/bitmap.hxx>
34
#include <vcl/gdimtf.hxx>
35
#include <vcl/dibtools.hxx>
36
#include "oleprops.hxx"
37
38
39
// stream names
40
constexpr OUString STREAM_SUMMARYINFO = u"\005SummaryInformation"_ustr;
41
constexpr OUString STREAM_DOCSUMMARYINFO = u"\005DocumentSummaryInformation"_ustr;
42
43
// usings
44
using namespace ::com::sun::star;
45
46
47
namespace sfx2 {
48
49
ErrCode LoadOlePropertySet(
50
    const uno::Reference< document::XDocumentProperties>& i_xDocProps,
51
    SotStorage* i_pStorage )
52
30.4k
{
53
    // *** global properties from stream "005SummaryInformation" ***
54
55
    // load the property set
56
30.4k
    SfxOlePropertySet aGlobSet;
57
30.4k
    ErrCode nGlobError = aGlobSet.LoadPropertySet(i_pStorage,
58
30.4k
        STREAM_SUMMARYINFO );
59
60
    // global section
61
30.4k
    SfxOleSectionRef xGlobSect = aGlobSet.GetSection( SECTION_GLOBAL );
62
30.4k
    if( xGlobSect )
63
6.60k
    {
64
        // set supported properties
65
6.60k
        OUString aStrValue;
66
6.60k
        util::DateTime aDateTime;
67
68
6.60k
        if( xGlobSect->GetStringValue( aStrValue, PROPID_TITLE ) )
69
4.86k
            i_xDocProps->setTitle( aStrValue );
70
6.60k
        if( xGlobSect->GetStringValue( aStrValue, PROPID_SUBJECT ) )
71
2.10k
            i_xDocProps->setSubject( aStrValue );
72
6.60k
        if( xGlobSect->GetStringValue( aStrValue, PROPID_KEYWORDS ) ) {
73
2.09k
            i_xDocProps->setKeywords(
74
2.09k
                ::comphelper::string::convertCommaSeparated(aStrValue) );
75
2.09k
        }
76
6.60k
        if( xGlobSect->GetStringValue( aStrValue, PROPID_TEMPLATE ) )
77
3.33k
            i_xDocProps->setTemplateName( aStrValue );
78
6.60k
        if( xGlobSect->GetStringValue( aStrValue, PROPID_COMMENTS ) )
79
1.72k
            i_xDocProps->setDescription( aStrValue );
80
81
6.60k
        util::DateTime aInvalid;
82
6.60k
        if( xGlobSect->GetStringValue( aStrValue, PROPID_AUTHOR) )
83
5.37k
            i_xDocProps->setAuthor( aStrValue );
84
1.22k
        else
85
1.22k
            i_xDocProps->setAuthor( OUString() );
86
6.60k
        if( xGlobSect->GetFileTimeValue( aDateTime, PROPID_CREATED ) )
87
5.29k
            i_xDocProps->setCreationDate( aDateTime );
88
1.30k
        else
89
1.30k
            i_xDocProps->setCreationDate( aInvalid );
90
91
6.60k
        if( xGlobSect->GetStringValue( aStrValue, PROPID_LASTAUTHOR) )
92
5.43k
            i_xDocProps->setModifiedBy( aStrValue );
93
1.17k
        else
94
1.17k
            i_xDocProps->setModifiedBy( OUString() );
95
6.60k
        if( xGlobSect->GetFileTimeValue( aDateTime, PROPID_LASTSAVED ) )
96
5.46k
            i_xDocProps->setModificationDate( aDateTime );
97
1.13k
        else
98
1.13k
            i_xDocProps->setModificationDate( aInvalid );
99
100
6.60k
        i_xDocProps->setPrintedBy( OUString() );
101
6.60k
        if( xGlobSect->GetFileTimeValue( aDateTime, PROPID_LASTPRINTED ) )
102
3.05k
            i_xDocProps->setPrintDate( aDateTime );
103
3.54k
        else
104
3.54k
            i_xDocProps->setPrintDate( aInvalid );
105
106
6.60k
        if( xGlobSect->GetStringValue( aStrValue, PROPID_REVNUMBER ) )
107
5.42k
        {
108
5.42k
            sal_Int16 nRevision = static_cast< sal_Int16 >( aStrValue.toInt32() );
109
5.42k
            if ( nRevision > 0 )
110
5.07k
                i_xDocProps->setEditingCycles( nRevision );
111
5.42k
        }
112
113
6.60k
        if( xGlobSect->GetFileTimeValue( aDateTime, PROPID_EDITTIME )
114
5.06k
            && !(aDateTime.NanoSeconds == 0 && aDateTime.Seconds == 0
115
2.32k
                 && aDateTime.Minutes == 0 && aDateTime.Hours == 0
116
1.09k
                 && aDateTime.Day == 0 && aDateTime.Month == 0
117
1.07k
                 && aDateTime.Year == 0) )
118
3.99k
        {
119
3.99k
            assert(aDateTime.Day <= 31);
120
            // subtract offset 1601-01-01
121
3.99k
            aDateTime.Year  -= 1601;
122
3.99k
            aDateTime.Month -= 1;
123
3.99k
            aDateTime.Day   -= 1;
124
3.99k
            try
125
3.99k
            {
126
3.99k
                i_xDocProps->setEditingDuration(
127
3.99k
                    aDateTime.Day     * 60*60*24 +
128
3.99k
                    aDateTime.Hours   * 60*60    +
129
3.99k
                    aDateTime.Minutes * 60       +
130
3.99k
                    aDateTime.Seconds            );
131
3.99k
            }
132
3.99k
            catch (const lang::IllegalArgumentException &)
133
3.99k
            {
134
                // ignore
135
0
            }
136
3.99k
        }
137
6.60k
    }
138
139
    // *** custom properties from stream "005DocumentSummaryInformation" ***
140
141
    // load the property set
142
30.4k
    SfxOlePropertySet aDocSet;
143
30.4k
    ErrCode nDocError = aDocSet.LoadPropertySet(i_pStorage,
144
30.4k
        STREAM_DOCSUMMARYINFO );
145
146
    // custom properties
147
30.4k
    SfxOleSectionRef xCustomSect = aDocSet.GetSection( SECTION_CUSTOM );
148
30.4k
    if( xCustomSect )
149
2.31k
    {
150
2.31k
        uno::Reference < beans::XPropertyContainer > xUserDefined(
151
2.31k
            i_xDocProps->getUserDefinedProperties(), uno::UNO_SET_THROW);
152
2.31k
        ::std::vector< sal_Int32 > aPropIds;
153
2.31k
        xCustomSect->GetPropertyIds( aPropIds );
154
2.31k
        for( const auto& rPropId : aPropIds )
155
2.32k
        {
156
2.32k
            const OUString aPropName = xCustomSect->GetPropertyName( rPropId );
157
2.32k
            uno::Any aPropValue = xCustomSect->GetAnyValue( rPropId );
158
2.32k
            if( !aPropName.isEmpty() && aPropValue.hasValue() )
159
1.10k
            {
160
1.10k
                try
161
1.10k
                {
162
1.10k
                    xUserDefined->addProperty( aPropName,
163
1.10k
                        beans::PropertyAttribute::REMOVABLE, aPropValue );
164
1.10k
                }
165
1.10k
                catch (const uno::Exception&)
166
1.10k
                {
167
                    //ignore
168
0
                }
169
1.10k
            }
170
2.32k
        }
171
2.31k
    }
172
173
30.4k
    uno::Reference< document::XCompatWriterDocProperties > xWriterProps( i_xDocProps, uno::UNO_QUERY );
174
30.4k
    if ( xWriterProps.is() )
175
0
    {
176
0
        SfxOleSectionRef xBuiltin = aDocSet.GetSection( SECTION_BUILTIN );
177
0
        if ( xBuiltin )
178
0
        {
179
0
            try
180
0
            {
181
0
                OUString aStrValue;
182
0
                if ( xBuiltin->GetStringValue( aStrValue, PROPID_MANAGER ) )
183
0
                    xWriterProps->setManager( aStrValue );
184
0
                if ( xBuiltin->GetStringValue( aStrValue, PROPID_CATEGORY ) )
185
0
                    xWriterProps->setCategory( aStrValue );
186
0
                if ( xBuiltin->GetStringValue( aStrValue, PROPID_COMPANY ) )
187
0
                    xWriterProps->setCompany( aStrValue );
188
0
            }
189
0
            catch (const uno::Exception&)
190
0
            {
191
0
            }
192
0
        }
193
0
    }
194
195
    // return code
196
30.4k
    return (nGlobError != ERRCODE_NONE) ? nGlobError : nDocError;
197
30.4k
}
198
199
bool SaveOlePropertySet(
200
    const uno::Reference< document::XDocumentProperties>& i_xDocProps,
201
    SotStorage* i_pStorage,
202
    const uno::Sequence<sal_Int8> * i_pThumb,
203
    const uno::Sequence<sal_Int8> * i_pGuid,
204
    const uno::Sequence<sal_Int8> * i_pHyperlinks)
205
0
{
206
    // *** global properties into stream "005SummaryInformation" ***
207
208
0
    SfxOlePropertySet aGlobSet;
209
210
    // set supported properties
211
0
    SfxOleSection& rGlobSect = aGlobSet.AddSection( SECTION_GLOBAL );
212
0
    rGlobSect.SetStringValue( PROPID_TITLE,    i_xDocProps->getTitle() );
213
0
    rGlobSect.SetStringValue( PROPID_SUBJECT,  i_xDocProps->getSubject() );
214
0
    const OUString aStr = ::comphelper::string::convertCommaSeparated(
215
0
        i_xDocProps->getKeywords() );
216
0
    rGlobSect.SetStringValue( PROPID_KEYWORDS, aStr );
217
0
    rGlobSect.SetStringValue( PROPID_TEMPLATE, i_xDocProps->getTemplateName() );
218
0
    rGlobSect.SetStringValue( PROPID_COMMENTS, i_xDocProps->getDescription() );
219
0
    rGlobSect.SetStringValue( PROPID_AUTHOR,   i_xDocProps->getAuthor() );
220
0
    rGlobSect.SetFileTimeValue(PROPID_CREATED, i_xDocProps->getCreationDate());
221
0
    rGlobSect.SetStringValue( PROPID_LASTAUTHOR, i_xDocProps->getModifiedBy() );
222
0
    rGlobSect.SetFileTimeValue(PROPID_LASTSAVED,
223
0
                                i_xDocProps->getModificationDate() );
224
    // note: apparently PrintedBy is not supported in file format
225
0
    rGlobSect.SetFileTimeValue(PROPID_LASTPRINTED, i_xDocProps->getPrintDate());
226
227
0
    sal_Int32 dur = i_xDocProps->getEditingDuration();
228
0
    util::DateTime aEditTime;
229
    // add offset 1601-01-01
230
0
    aEditTime.Year    = 1601;
231
0
    aEditTime.Month   = 1;
232
0
    aEditTime.Day     = 1;
233
0
    aEditTime.Hours   = static_cast<sal_Int16>(dur / 3600);
234
0
    aEditTime.Minutes = static_cast<sal_Int16>((dur % 3600) / 60);
235
0
    aEditTime.Seconds = static_cast<sal_Int16>(dur % 60);
236
0
    rGlobSect.SetFileTimeValue( PROPID_EDITTIME, aEditTime );
237
238
0
    rGlobSect.SetStringValue( PROPID_REVNUMBER,
239
0
                OUString::number( i_xDocProps->getEditingCycles() ) );
240
0
    if ( i_pThumb && i_pThumb->hasElements() )
241
0
        rGlobSect.SetThumbnailValue( PROPID_THUMBNAIL, *i_pThumb );
242
243
    // save the property set
244
0
    ErrCode nGlobError = aGlobSet.SavePropertySet(i_pStorage,
245
0
        STREAM_SUMMARYINFO);
246
247
    // *** custom properties into stream "005DocumentSummaryInformation" ***
248
249
0
    SfxOlePropertySet aDocSet;
250
251
    // set builtin properties
252
0
    aDocSet.AddSection( SECTION_BUILTIN );
253
254
    // set custom properties
255
0
    SfxOleSection& rCustomSect = aDocSet.AddSection( SECTION_CUSTOM );
256
257
    // write GUID
258
0
    if (i_pGuid) {
259
0
        const sal_Int32 nPropId = rCustomSect.GetFreePropertyId();
260
0
        rCustomSect.SetBlobValue( nPropId, *i_pGuid );
261
0
        rCustomSect.SetPropertyName( nPropId,
262
0
            u"_PID_GUID"_ustr );
263
0
    }
264
265
    // write hyperlinks
266
0
    if (i_pHyperlinks) {
267
0
        const sal_Int32 nPropId = rCustomSect.GetFreePropertyId();
268
0
        rCustomSect.SetBlobValue( nPropId, *i_pHyperlinks );
269
0
        rCustomSect.SetPropertyName( nPropId,
270
0
            u"_PID_HLINKS"_ustr );
271
0
    }
272
273
0
    uno::Reference<beans::XPropertySet> xUserDefinedProps(
274
0
        i_xDocProps->getUserDefinedProperties(), uno::UNO_QUERY_THROW);
275
0
    uno::Reference<beans::XPropertySetInfo> xPropInfo =
276
0
        xUserDefinedProps->getPropertySetInfo();
277
0
    DBG_ASSERT(xPropInfo.is(), "UserDefinedProperties Info is null");
278
0
    const uno::Sequence<beans::Property> props = xPropInfo->getProperties();
279
0
    for (const auto& rProp : props)
280
0
    {
281
0
        try
282
0
        {
283
            // skip transient properties
284
0
            if (~rProp.Attributes & beans::PropertyAttribute::TRANSIENT)
285
0
            {
286
0
                const OUString name = rProp.Name;
287
0
                const sal_Int32 nPropId = rCustomSect.GetFreePropertyId();
288
0
                if (rCustomSect.SetAnyValue( nPropId,
289
0
                            xUserDefinedProps->getPropertyValue(name))) {
290
0
                    rCustomSect.SetPropertyName( nPropId, name );
291
0
                }
292
0
            }
293
0
        }
294
0
        catch (const uno::Exception &)
295
0
        {
296
            // may happen with concurrent modification...
297
0
            SAL_INFO("sfx", "SavePropertySet: exception");
298
0
        }
299
0
    }
300
301
    // save the property set
302
0
    ErrCode nDocError = aDocSet.SavePropertySet(i_pStorage,
303
0
        STREAM_DOCSUMMARYINFO );
304
305
    // return code
306
0
    return (nGlobError == ERRCODE_NONE) && (nDocError == ERRCODE_NONE);
307
0
}
308
309
uno::Sequence<sal_Int8> convertMetaFile(GDIMetaFile const * i_pThumb)
310
0
{
311
0
    if (i_pThumb) {
312
0
        Bitmap aBitmap;
313
0
        SvMemoryStream aStream;
314
0
        if (i_pThumb->CreateThumbnail(aBitmap))
315
0
        {
316
0
            WriteDIB(aBitmap, aStream, false, false);
317
0
            return uno::Sequence<sal_Int8>(static_cast< const sal_Int8* >( aStream.GetData() ), aStream.TellEnd());
318
0
        }
319
0
    }
320
0
    return uno::Sequence<sal_Int8>();
321
0
}
322
323
} // namespace sfx2
324
325
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */