Coverage Report

Created: 2025-07-07 10:01

/src/libreoffice/oox/source/vml/vmltextbox.cxx
Line
Count
Source (jump to first uncovered line)
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
#include <oox/vml/vmltextbox.hxx>
21
22
#include <rtl/ustrbuf.hxx>
23
#include <comphelper/diagnose_ex.hxx>
24
#include <com/sun/star/awt/FontWeight.hpp>
25
#include <com/sun/star/beans/XPropertySet.hpp>
26
#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
27
#include <com/sun/star/drawing/XShape.hpp>
28
#include <com/sun/star/text/XTextAppend.hpp>
29
#include <com/sun/star/text/WritingMode.hpp>
30
#include <com/sun/star/style/ParagraphAdjust.hpp>
31
#include <comphelper/sequence.hxx>
32
#include <comphelper/sequenceashashmap.hxx>
33
#include <utility>
34
35
namespace oox::vml {
36
37
using namespace com::sun::star;
38
39
TextFontModel::TextFontModel()
40
11.7k
{
41
11.7k
}
42
43
TextPortionModel::TextPortionModel( TextParagraphModel aParagraph, TextFontModel aFont, OUString aText ) :
44
841
    maParagraph(std::move( aParagraph )),
45
841
    maFont(std::move( aFont )),
46
841
    maText(std::move( aText ))
47
841
{
48
841
}
49
50
TextBox::TextBox(ShapeTypeModel& rTypeModel)
51
7.72k
    : mrTypeModel(rTypeModel)
52
7.72k
    , borderDistanceSet( false )
53
7.72k
    , borderDistanceLeft(0)
54
7.72k
    , borderDistanceTop(0)
55
7.72k
    , borderDistanceRight(0)
56
7.72k
    , borderDistanceBottom(0)
57
7.72k
{
58
7.72k
}
59
60
void TextBox::appendPortion( const TextParagraphModel& rParagraph, const TextFontModel& rFont, const OUString& rText )
61
841
{
62
841
    maPortions.emplace_back( rParagraph, rFont, rText );
63
841
}
64
65
const TextFontModel* TextBox::getFirstFont() const
66
4
{
67
4
    return maPortions.empty() ? nullptr : &maPortions.front().maFont;
68
4
}
69
70
OUString TextBox::getText() const
71
4
{
72
4
    OUStringBuffer aBuffer;
73
4
    for (auto const& portion : maPortions)
74
4
        aBuffer.append( portion.maText );
75
4
    return aBuffer.makeStringAndClear();
76
4
}
77
78
void TextBox::convert(const uno::Reference<drawing::XShape>& xShape) const
79
872
{
80
872
    uno::Reference<text::XTextAppend> xTextAppend(xShape, uno::UNO_QUERY);
81
872
    OUString sParaStyle;
82
872
    bool bAmbiguousStyle = true;
83
84
872
    for (auto const& portion : maPortions)
85
837
    {
86
837
        beans::PropertyValue aPropertyValue;
87
837
        std::vector<beans::PropertyValue> aPropVec;
88
837
        const TextParagraphModel& rParagraph = portion.maParagraph;
89
837
        const TextFontModel& rFont = portion.maFont;
90
837
        if (rFont.moName.has_value())
91
0
        {
92
0
            aPropertyValue.Name = "CharFontName";
93
0
            aPropertyValue.Value <<= rFont.moName.value();
94
0
            aPropVec.push_back(aPropertyValue);
95
96
0
            aPropertyValue.Name = "CharFontNameAsian";
97
0
            aPropertyValue.Value <<= rFont.moNameAsian.value_or("");
98
0
            aPropVec.push_back(aPropertyValue);
99
100
0
            aPropertyValue.Name = "CharFontNameComplex";
101
0
            aPropertyValue.Value <<= rFont.moNameComplex.value_or("");
102
0
            aPropVec.push_back(aPropertyValue);
103
0
        }
104
837
        if (rFont.mobBold.has_value())
105
9
        {
106
9
            aPropertyValue.Name = "CharWeight";
107
9
            aPropertyValue.Value <<= rFont.mobBold.value() ? awt::FontWeight::BOLD : awt::FontWeight::NORMAL;
108
9
            aPropVec.push_back(aPropertyValue);
109
9
        }
110
837
        if (rFont.monSize.has_value())
111
9
        {
112
9
            aPropertyValue.Name = "CharHeight";
113
9
            aPropertyValue.Value <<= double(rFont.monSize.value()) / 2.;
114
9
            aPropVec.push_back(aPropertyValue);
115
9
        }
116
837
        if (rFont.monSpacing.has_value())
117
0
        {
118
0
            aPropertyValue.Name = "CharKerning";
119
            // Value is not converted to mm100: SvxKerningItem::PutValue() gets
120
            // called with nMemberId = 0, so no mm100 -> twips conversion will
121
            // be done there.
122
0
            aPropertyValue.Value <<= sal_Int16(rFont.monSpacing.value());
123
0
            aPropVec.push_back(aPropertyValue);
124
0
        }
125
837
        if (rParagraph.moParaAdjust.has_value())
126
18
        {
127
18
            style::ParagraphAdjust eAdjust = style::ParagraphAdjust_LEFT;
128
18
            if (rParagraph.moParaAdjust.value() == "center")
129
18
                eAdjust = style::ParagraphAdjust_CENTER;
130
0
            else if (rParagraph.moParaAdjust.value() == "right")
131
0
                eAdjust = style::ParagraphAdjust_RIGHT;
132
133
18
            aPropertyValue.Name = "ParaAdjust";
134
18
            aPropertyValue.Value <<= eAdjust;
135
18
            aPropVec.push_back(aPropertyValue);
136
18
        }
137
138
        // All paragraphs should be either undefined (default) or the same style,
139
        // because it will only  be applied to the entire shape, and not per-paragraph.
140
837
        if (sParaStyle.isEmpty() )
141
828
        {
142
828
            if ( rParagraph.moParaStyleName.has_value() )
143
9
                sParaStyle = rParagraph.moParaStyleName.value();
144
828
            if ( bAmbiguousStyle )
145
828
                bAmbiguousStyle = false; // both empty parastyle and ambiguous can only be true at the first paragraph
146
0
            else
147
0
                bAmbiguousStyle = rParagraph.moParaStyleName.has_value(); // ambiguous if both default and specified style used.
148
828
        }
149
9
        else if ( !bAmbiguousStyle )
150
9
        {
151
9
            if ( !rParagraph.moParaStyleName.has_value() )
152
0
                bAmbiguousStyle = true; // ambiguous if both specified and default style used.
153
9
            else if ( rParagraph.moParaStyleName.value() != sParaStyle )
154
0
                bAmbiguousStyle = true; // ambiguous if two different styles specified.
155
9
        }
156
837
        if (rFont.moColor.has_value())
157
9
        {
158
9
            aPropertyValue.Name = "CharColor";
159
9
            aPropertyValue.Value <<= rFont.moColor.value().toUInt32(16);
160
9
            aPropVec.push_back(aPropertyValue);
161
9
        }
162
837
        xTextAppend->appendTextPortion(portion.maText, comphelper::containerToSequence(aPropVec));
163
837
    }
164
165
872
    try
166
872
    {
167
        // Track the style in a grabBag for use later when style details are known.
168
872
        comphelper::SequenceAsHashMap aGrabBag;
169
872
        uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY_THROW);
170
872
        aGrabBag.update( xPropertySet->getPropertyValue(u"CharInteropGrabBag"_ustr) );
171
872
        aGrabBag[u"mso-pStyle"_ustr] <<= sParaStyle;
172
872
        xPropertySet->setPropertyValue(u"CharInteropGrabBag"_ustr, uno::Any(aGrabBag.getAsConstPropertyValueList()));
173
872
    }
174
872
    catch (const uno::Exception&)
175
872
    {
176
0
        TOOLS_WARN_EXCEPTION( "oox.vml","convert() grabbag exception" );
177
0
    }
178
179
    // Remove the last character of the shape text, if it would be a newline.
180
872
    uno::Reference< text::XTextCursor > xCursor = xTextAppend->createTextCursor();
181
872
    xCursor->gotoEnd(false);
182
872
    xCursor->goLeft(1, true);
183
872
    if (xCursor->getString() == "\n")
184
828
        xCursor->setString(u""_ustr);
185
186
872
    if ( maLayoutFlow != "vertical" )
187
872
        return;
188
189
0
    uno::Reference<beans::XPropertySet> xProperties(xShape, uno::UNO_QUERY);
190
191
    // VML has the text horizontally aligned to left (all the time),
192
    // v-text-anchor for vertical alignment, and vertical mode to swap the
193
    // two.  drawinglayer supports both horizontal and vertical alignment,
194
    // but no vertical mode: we use T->B, R->L instead.
195
    // As a result, we need to set horizontal adjustment here to 'right',
196
    // that will result in vertical 'top' after writing mode is applied,
197
    // which matches the VML behavior.
198
0
    xProperties->setPropertyValue(u"TextHorizontalAdjust"_ustr, uno::Any(drawing::TextHorizontalAdjust_RIGHT));
199
200
0
    xProperties->setPropertyValue( u"TextWritingMode"_ustr, uno::Any( text::WritingMode_TB_RL ) );
201
0
}
202
203
} // namespace oox::vml
204
205
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */