Coverage Report

Created: 2026-04-09 11:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/treelist/imap2.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 <comphelper/string.hxx>
22
#include <string.h>
23
#include <o3tl/string_view.hxx>
24
#include <tools/mapunit.hxx>
25
#include <tools/stream.hxx>
26
#include <rtl/strbuf.hxx>
27
#include <vcl/outdev.hxx>
28
#include <vcl/svapp.hxx>
29
#include <tools/urlobj.hxx>
30
31
#include <svl/urihelper.hxx>
32
#include <vcl/imap.hxx>
33
#include <vcl/imapobj.hxx>
34
#include <vcl/imaprect.hxx>
35
#include <vcl/imapcirc.hxx>
36
#include <vcl/imappoly.hxx>
37
38
#include <math.h>
39
40
0
#define NOTEOL(c) ((c)!='\0')
41
42
void IMapObject::AppendCERNCoords(OStringBuffer& rBuf, const Point& rPoint100)
43
0
{
44
0
    const Point aPixPt( Application::GetDefaultDevice()->LogicToPixel( rPoint100, MapMode( MapUnit::Map100thMM ) ) );
45
46
0
    rBuf.append('(');
47
0
    rBuf.append(static_cast<sal_Int32>(aPixPt.X()));
48
0
    rBuf.append(',');
49
0
    rBuf.append(static_cast<sal_Int32>(aPixPt.Y()));
50
0
    rBuf.append(") ");
51
0
}
52
53
void IMapObject::AppendNCSACoords(OStringBuffer& rBuf, const Point& rPoint100)
54
0
{
55
0
    const Point aPixPt( Application::GetDefaultDevice()->LogicToPixel( rPoint100, MapMode( MapUnit::Map100thMM ) ) );
56
57
0
    rBuf.append(static_cast<sal_Int32>(aPixPt.X()));
58
0
    rBuf.append(',');
59
0
    rBuf.append(static_cast<sal_Int32>(aPixPt.Y()));
60
0
    rBuf.append(' ');
61
0
}
62
63
void IMapObject::AppendCERNURL(OStringBuffer& rBuf) const
64
0
{
65
0
    rBuf.append(OUStringToOString(URIHelper::simpleNormalizedMakeRelative(u""_ustr, aURL), osl_getThreadTextEncoding()));
66
0
}
67
68
void IMapObject::AppendNCSAURL(OStringBuffer& rBuf) const
69
0
{
70
0
    rBuf.append(OUStringToOString(URIHelper::simpleNormalizedMakeRelative(u""_ustr, aURL), osl_getThreadTextEncoding()));
71
0
    rBuf.append(' ');
72
0
}
73
74
void IMapRectangleObject::WriteCERN( SvStream& rOStm ) const
75
0
{
76
0
    OStringBuffer aStrBuf("rectangle ");
77
78
0
    AppendCERNCoords(aStrBuf, aRect.TopLeft());
79
0
    AppendCERNCoords(aStrBuf, aRect.BottomRight());
80
0
    AppendCERNURL(aStrBuf);
81
82
0
    rOStm.WriteLine(aStrBuf);
83
0
}
84
85
void IMapRectangleObject::WriteNCSA( SvStream& rOStm ) const
86
0
{
87
0
    OStringBuffer aStrBuf("rect ");
88
89
0
    AppendNCSAURL(aStrBuf);
90
0
    AppendNCSACoords(aStrBuf, aRect.TopLeft());
91
0
    AppendNCSACoords(aStrBuf, aRect.BottomRight());
92
93
0
    rOStm.WriteLine(aStrBuf);
94
0
}
95
96
void IMapCircleObject::WriteCERN( SvStream& rOStm ) const
97
0
{
98
0
    OStringBuffer aStrBuf("circle ");
99
100
0
    AppendCERNCoords(aStrBuf, aCenter);
101
0
    aStrBuf.append(OString::number(nRadius) + " ");
102
0
    AppendCERNURL(aStrBuf);
103
104
0
    rOStm.WriteLine(aStrBuf);
105
0
}
106
107
void IMapCircleObject::WriteNCSA( SvStream& rOStm ) const
108
0
{
109
0
    OStringBuffer aStrBuf("circle ");
110
111
0
    AppendNCSAURL(aStrBuf);
112
0
    AppendNCSACoords(aStrBuf, aCenter);
113
0
    AppendNCSACoords(aStrBuf, aCenter + Point(nRadius, 0));
114
115
0
    rOStm.WriteLine(aStrBuf);
116
0
}
117
118
void IMapPolygonObject::WriteCERN( SvStream& rOStm  ) const
119
0
{
120
0
    OStringBuffer aStrBuf("polygon ");
121
0
    const sal_uInt16 nCount = aPoly.GetSize();
122
123
0
    for (sal_uInt16 i = 0; i < nCount; ++i)
124
0
        AppendCERNCoords(aStrBuf, aPoly[i]);
125
126
0
    AppendCERNURL(aStrBuf);
127
128
0
    rOStm.WriteLine(aStrBuf);
129
0
}
130
131
void IMapPolygonObject::WriteNCSA( SvStream& rOStm  ) const
132
0
{
133
0
    OStringBuffer aStrBuf("poly ");
134
0
    const sal_uInt16 nCount = std::min( aPoly.GetSize(), sal_uInt16(100) );
135
136
0
    AppendNCSAURL(aStrBuf);
137
138
0
    for (sal_uInt16 i = 0; i < nCount; ++i)
139
0
        AppendNCSACoords(aStrBuf, aPoly[i]);
140
141
0
    rOStm.WriteLine(aStrBuf);
142
0
}
143
144
void ImageMap::Write( SvStream& rOStm, IMapFormat nFormat ) const
145
0
{
146
0
    switch( nFormat )
147
0
    {
148
0
        case IMapFormat::Binary : Write( rOStm ); break;
149
0
        case IMapFormat::CERN : ImpWriteCERN( rOStm ); break;
150
0
        case IMapFormat::NCSA : ImpWriteNCSA( rOStm ); break;
151
152
0
        default:
153
0
        break;
154
0
    }
155
0
}
156
157
void ImageMap::ImpWriteCERN( SvStream& rOStm ) const
158
0
{
159
0
    size_t      nCount = maList.size();
160
161
0
    for ( size_t i = 0; i < nCount; i++ )
162
0
    {
163
0
        IMapObject* pObj = maList[ i ].get();
164
165
0
        switch( pObj->GetType() )
166
0
        {
167
0
            case IMapObjectType::Rectangle:
168
0
                static_cast<IMapRectangleObject*>( pObj )->WriteCERN( rOStm );
169
0
            break;
170
171
0
            case IMapObjectType::Circle:
172
0
                static_cast<IMapCircleObject*>( pObj )->WriteCERN( rOStm );
173
0
            break;
174
175
0
            case IMapObjectType::Polygon:
176
0
                static_cast<IMapPolygonObject*>( pObj )->WriteCERN( rOStm );
177
0
            break;
178
179
0
            default:
180
0
            break;
181
0
        }
182
0
    }
183
0
}
184
185
void ImageMap::ImpWriteNCSA( SvStream& rOStm  ) const
186
0
{
187
0
    size_t      nCount = maList.size();
188
189
0
    for ( size_t i = 0; i < nCount; i++ )
190
0
    {
191
0
        IMapObject* pObj = maList[ i ].get();
192
193
0
        switch( pObj->GetType() )
194
0
        {
195
0
            case IMapObjectType::Rectangle:
196
0
                static_cast<IMapRectangleObject*>( pObj )->WriteNCSA( rOStm );
197
0
            break;
198
199
0
            case IMapObjectType::Circle:
200
0
                static_cast<IMapCircleObject*>( pObj )->WriteNCSA( rOStm );
201
0
            break;
202
203
0
            case IMapObjectType::Polygon:
204
0
                static_cast<IMapPolygonObject*>( pObj )->WriteNCSA( rOStm );
205
0
            break;
206
207
0
            default:
208
0
            break;
209
0
        }
210
0
    }
211
0
}
212
213
sal_uLong ImageMap::Read( SvStream& rIStm, IMapFormat nFormat  )
214
0
{
215
0
    sal_uLong nRet = IMAP_ERR_FORMAT;
216
217
0
    if ( nFormat == IMapFormat::Detect )
218
0
        nFormat = ImpDetectFormat( rIStm );
219
220
0
    switch ( nFormat )
221
0
    {
222
0
        case IMapFormat::Binary : Read( rIStm ); break;
223
0
        case IMapFormat::CERN   : ImpReadCERN( rIStm ); break;
224
0
        case IMapFormat::NCSA   : ImpReadNCSA( rIStm ); break;
225
226
0
        default:
227
0
        break;
228
0
    }
229
230
0
    if ( !rIStm.GetError() )
231
0
        nRet = IMAP_ERR_OK;
232
233
0
    return nRet;
234
0
}
235
236
void ImageMap::ImpReadCERN( SvStream& rIStm )
237
0
{
238
    // delete old content
239
0
    ClearImageMap();
240
241
0
    OStringBuffer aStr;
242
0
    while ( rIStm.ReadLine( aStr ) )
243
0
        ImpReadCERNLine( aStr );
244
0
}
245
246
void ImageMap::ImpReadCERNLine( std::string_view rLine  )
247
0
{
248
0
    OString aStr( comphelper::string::stripStart(rLine, ' ') );
249
0
    aStr = comphelper::string::stripStart(aStr, '\t');
250
0
    aStr = aStr.replaceAll(";"_ostr, ""_ostr);
251
0
    aStr = aStr.toAsciiLowerCase();
252
253
0
    const char* pStr = aStr.getStr();
254
0
    char        cChar = *pStr++;
255
256
    // find instruction
257
0
    OStringBuffer aBuf;
258
0
    while ((cChar >= 'a') && (cChar <= 'z'))
259
0
    {
260
0
        aBuf.append(cChar);
261
0
        cChar = *pStr++;
262
0
    }
263
0
    OString aToken = aBuf.makeStringAndClear();
264
265
0
    if ( !(NOTEOL( cChar )) )
266
0
        return;
267
268
0
    if ( ( aToken == "rectangle" ) || ( aToken == "rect" ) )
269
0
    {
270
0
        const Point     aTopLeft( ImpReadCERNCoords( &pStr ) );
271
0
        const Point     aBottomRight( ImpReadCERNCoords( &pStr ) );
272
0
        const OUString  aURL( ImpReadCERNURL( &pStr ) );
273
0
        const tools::Rectangle aRect( aTopLeft, aBottomRight );
274
275
0
        maList.emplace_back( new IMapRectangleObject( aRect, aURL, OUString(), OUString(), OUString(), OUString() ) );
276
0
    }
277
0
    else if ( ( aToken == "circle" ) || ( aToken == "circ" ) )
278
0
    {
279
0
        const Point     aCenter( ImpReadCERNCoords( &pStr ) );
280
0
        const tools::Long      nRadius = ImpReadCERNRadius( &pStr );
281
0
        const OUString  aURL( ImpReadCERNURL( &pStr ) );
282
283
0
        maList.emplace_back( new IMapCircleObject( aCenter, nRadius, aURL, OUString(), OUString(), OUString(), OUString() ) );
284
0
    }
285
0
    else if ( ( aToken == "polygon" ) || ( aToken == "poly" ) )
286
0
    {
287
0
        const sal_Int32 nTokenCount = comphelper::string::getTokenCount(aStr, '(');
288
0
        tools::Polygon aPoly;
289
0
        if (nTokenCount > 0)
290
0
        {
291
0
            const sal_uInt16 nCount = nTokenCount - 1;
292
0
            aPoly.SetSize(nCount);
293
294
0
            for (sal_uInt16 i = 0; i < nCount; ++i)
295
0
                aPoly[ i ] = ImpReadCERNCoords( &pStr );
296
0
        }
297
298
0
        const OUString aURL = ImpReadCERNURL( &pStr );
299
300
0
        maList.emplace_back( new IMapPolygonObject( aPoly, aURL, OUString(), OUString(), OUString(), OUString() ) );
301
0
    }
302
0
}
303
304
Point ImageMap::ImpReadCERNCoords( const char** ppStr )
305
0
{
306
0
    OUStringBuffer  aStrX;
307
0
    OUStringBuffer  aStrY;
308
0
    Point           aPt;
309
0
    char            cChar = *(*ppStr)++;
310
311
0
    while( NOTEOL( cChar ) && ( ( cChar < '0' ) || ( cChar > '9' ) ) )
312
0
        cChar = *(*ppStr)++;
313
314
0
    if ( NOTEOL( cChar ) )
315
0
    {
316
0
        while( NOTEOL( cChar ) && ( cChar >= '0' ) && ( cChar <= '9' ) )
317
0
        {
318
0
            aStrX.append( cChar );
319
0
            cChar = *(*ppStr)++;
320
0
        }
321
322
0
        if ( NOTEOL( cChar ) )
323
0
        {
324
0
            while( NOTEOL( cChar ) && ( ( cChar < '0' ) || ( cChar > '9' ) ) )
325
0
                cChar = *(*ppStr)++;
326
327
0
            while( NOTEOL( cChar ) && ( cChar >= '0' ) && ( cChar <= '9' ) )
328
0
            {
329
0
                aStrY.append( cChar );
330
0
                cChar = *(*ppStr)++;
331
0
            }
332
333
0
            if ( NOTEOL( cChar ) )
334
0
                while( NOTEOL( cChar ) && ( cChar != ')' ) )
335
0
                    cChar = *(*ppStr)++;
336
337
0
            aPt = Point( o3tl::toInt32(aStrX), o3tl::toInt32(aStrY) );
338
0
        }
339
0
    }
340
341
0
    return aPt;
342
0
}
343
344
tools::Long ImageMap::ImpReadCERNRadius( const char** ppStr )
345
0
{
346
0
    OUStringBuffer  aStr;
347
0
    char            cChar = *(*ppStr)++;
348
349
0
    while( NOTEOL( cChar ) && ( ( cChar < '0' ) || ( cChar > '9' ) ) )
350
0
        cChar = *(*ppStr)++;
351
352
0
    if ( NOTEOL( cChar ) )
353
0
    {
354
0
        while( NOTEOL( cChar ) && ( cChar >= '0' ) && ( cChar <= '9' ) )
355
0
        {
356
0
            aStr.append( cChar );
357
0
            cChar = *(*ppStr)++;
358
0
        }
359
0
    }
360
361
0
    return o3tl::toInt32(aStr);
362
0
}
363
364
OUString ImageMap::ImpReadCERNURL( const char** ppStr )
365
0
{
366
0
    OUString aStr(OUString::createFromAscii(*ppStr));
367
368
0
    aStr = comphelper::string::strip(aStr, ' ');
369
0
    aStr = comphelper::string::strip(aStr, '\t');
370
371
0
    return INetURLObject::GetAbsURL( u"", aStr );
372
0
}
373
374
void ImageMap::ImpReadNCSA( SvStream& rIStm )
375
0
{
376
    // delete old content
377
0
    ClearImageMap();
378
379
0
    OStringBuffer aStr;
380
0
    while ( rIStm.ReadLine( aStr ) )
381
0
        ImpReadNCSALine( aStr );
382
0
}
383
384
void ImageMap::ImpReadNCSALine( std::string_view rLine )
385
0
{
386
0
    OString aStr( comphelper::string::stripStart(rLine, ' ') );
387
0
    aStr = comphelper::string::stripStart(aStr, '\t');
388
0
    aStr = aStr.replaceAll(";"_ostr, ""_ostr);
389
0
    aStr = aStr.toAsciiLowerCase();
390
391
0
    const char* pStr = aStr.getStr();
392
0
    char        cChar = *pStr++;
393
394
    // find instruction
395
0
    OStringBuffer aBuf;
396
0
    while ((cChar >= 'a') && (cChar <= 'z'))
397
0
    {
398
0
        aBuf.append(cChar);
399
0
        cChar = *pStr++;
400
0
    }
401
0
    OString aToken = aBuf.makeStringAndClear();
402
403
0
    if ( !(NOTEOL( cChar )) )
404
0
        return;
405
406
0
    if ( aToken == "rect" )
407
0
    {
408
0
        const OUString  aURL( ImpReadNCSAURL( &pStr ) );
409
0
        const Point     aTopLeft( ImpReadNCSACoords( &pStr ) );
410
0
        const Point     aBottomRight( ImpReadNCSACoords( &pStr ) );
411
0
        const tools::Rectangle aRect( aTopLeft, aBottomRight );
412
413
0
        maList.emplace_back( new IMapRectangleObject( aRect, aURL, OUString(), OUString(), OUString(), OUString() ) );
414
0
    }
415
0
    else if ( aToken == "circle" )
416
0
    {
417
0
        const OUString  aURL( ImpReadNCSAURL( &pStr ) );
418
0
        const Point     aCenter( ImpReadNCSACoords( &pStr ) );
419
0
        const Point     aDX( aCenter - ImpReadNCSACoords( &pStr ) );
420
0
        tools::Long            nRadius = static_cast<tools::Long>(std::hypot( aDX.X(), aDX.Y()));
421
422
0
        maList.emplace_back( new IMapCircleObject( aCenter, nRadius, aURL, OUString(), OUString(), OUString(), OUString() ) );
423
0
    }
424
0
    else if ( aToken == "poly" )
425
0
    {
426
0
        const sal_Int32 nTokenCount = comphelper::string::getTokenCount(aStr, ',');
427
0
        const OUString aURL( ImpReadNCSAURL( &pStr ) );
428
0
        tools::Polygon aPoly;
429
0
        if (nTokenCount > 0)
430
0
        {
431
0
            const sal_uInt16 nCount = nTokenCount - 1;
432
0
            aPoly.SetSize(nCount);
433
434
0
            for (sal_uInt16 i = 0; i < nCount; ++i)
435
0
                aPoly[ i ] = ImpReadNCSACoords( &pStr );
436
0
        }
437
438
0
        maList.emplace_back( new IMapPolygonObject( aPoly, aURL, OUString(), OUString(), OUString(), OUString() ) );
439
0
    }
440
0
}
441
442
OUString ImageMap::ImpReadNCSAURL( const char** ppStr )
443
0
{
444
0
    OUStringBuffer  aStr;
445
0
    char            cChar = *(*ppStr)++;
446
447
0
    while( NOTEOL( cChar ) && ( ( cChar == ' ' ) || ( cChar == '\t' ) ) )
448
0
        cChar = *(*ppStr)++;
449
450
0
    if ( NOTEOL( cChar ) )
451
0
    {
452
0
        while( NOTEOL( cChar ) && ( cChar != ' ' ) && ( cChar != '\t' ) )
453
0
        {
454
0
            aStr.append( cChar );
455
0
            cChar = *(*ppStr)++;
456
0
        }
457
0
    }
458
459
0
    return INetURLObject::GetAbsURL( u"", aStr.makeStringAndClear() );
460
0
}
461
462
Point ImageMap::ImpReadNCSACoords( const char** ppStr )
463
0
{
464
0
    OUStringBuffer  aStrX;
465
0
    OUStringBuffer  aStrY;
466
0
    Point           aPt;
467
0
    char            cChar = *(*ppStr)++;
468
469
0
    while( NOTEOL( cChar ) && ( ( cChar < '0' ) || ( cChar > '9' ) ) )
470
0
        cChar = *(*ppStr)++;
471
472
0
    if ( NOTEOL( cChar ) )
473
0
    {
474
0
        while( NOTEOL( cChar ) && ( cChar >= '0' ) && ( cChar <= '9' ) )
475
0
        {
476
0
            aStrX.append( cChar );
477
0
            cChar = *(*ppStr)++;
478
0
        }
479
480
0
        if ( NOTEOL( cChar ) )
481
0
        {
482
0
            while( NOTEOL( cChar ) && ( ( cChar < '0' ) || ( cChar > '9' ) ) )
483
0
                cChar = *(*ppStr)++;
484
485
0
            while( NOTEOL( cChar ) && ( cChar >= '0' ) && ( cChar <= '9' ) )
486
0
            {
487
0
                aStrY.append( cChar );
488
0
                cChar = *(*ppStr)++;
489
0
            }
490
491
0
            aPt = Point( o3tl::toInt32(aStrX), o3tl::toInt32(aStrY) );
492
0
        }
493
0
    }
494
495
0
    return aPt;
496
0
}
497
498
IMapFormat ImageMap::ImpDetectFormat( SvStream& rIStm )
499
0
{
500
0
    sal_uInt64  nPos = rIStm.Tell();
501
0
    IMapFormat  nRet = IMapFormat::Binary;
502
0
    char    cMagic[6];
503
504
0
    rIStm.ReadBytes(cMagic, sizeof(cMagic));
505
506
    // if we do not have an internal formats
507
    // we check the format
508
0
    if ( memcmp( cMagic, IMAPMAGIC, sizeof( cMagic ) ) )
509
0
    {
510
0
        tools::Long        nCount = 128;
511
512
0
        rIStm.Seek( nPos );
513
0
        OString aStr;
514
0
        while ( rIStm.ReadLine( aStr ) && nCount-- )
515
0
        {
516
0
            aStr = aStr.toAsciiLowerCase();
517
518
0
            if ( (aStr.indexOf("rect") != -1) ||
519
0
                 (aStr.indexOf("circ") != -1) ||
520
0
                 (aStr.indexOf("poly") != -1) )
521
0
            {
522
0
                if ( ( aStr.indexOf('(') != -1 ) &&
523
0
                     ( aStr.indexOf(')') != -1 ) )
524
0
                {
525
0
                    nRet = IMapFormat::CERN;
526
0
                }
527
0
                else
528
0
                    nRet = IMapFormat::NCSA;
529
530
0
                break;
531
0
            }
532
0
        }
533
0
    }
534
535
0
    rIStm.Seek( nPos );
536
537
0
    return nRet;
538
0
}
539
540
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */