summaryrefslogtreecommitdiffstats
path: root/sw/source/core/txtnode/justify.cxx
blob: 40ded5663f17f47c9c7a378a04623cab9a536038 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <vector>
#include <sal/types.h>
#include <vcl/kernarray.hxx>
#include <swfont.hxx>
#include "justify.hxx"

namespace
{
enum class IdeographicPunctuationClass
{
    NONE,
    OPEN_BRACKET,
    CLOSE_BRACKET,
    COMMA_OR_FULLSTOP
};

IdeographicPunctuationClass lcl_WhichPunctuationClass(sal_Unicode cChar)
{
    if ((cChar < 0x3001 || cChar > 0x3002) && (cChar < 0x3008 || cChar > 0x3011)
        && (cChar < 0x3014 || cChar > 0x301F) && 0xFF62 != cChar && 0xFF63 != cChar)
        return IdeographicPunctuationClass::NONE;
    else if (0x3001 == cChar || 0x3002 == cChar)
        return IdeographicPunctuationClass::COMMA_OR_FULLSTOP;
    else if (0x3009 == cChar || 0x300B == cChar || 0x300D == cChar || 0x300F == cChar
             || 0x3011 == cChar || 0x3015 == cChar || 0x3017 == cChar || 0x3019 == cChar
             || 0x301B == cChar || 0x301E == cChar || 0x301F == cChar || 0xFF63 == cChar)
        // right punctuation
        return IdeographicPunctuationClass::CLOSE_BRACKET;

    return IdeographicPunctuationClass::OPEN_BRACKET;
}

tools::Long lcl_MinGridWidth(tools::Long nGridWidth, tools::Long nCharWidth)
{
    tools::Long nCount = nCharWidth > nGridWidth ? (nCharWidth - 1) / nGridWidth + 1 : 1;
    return nCount * nGridWidth;
}

tools::Long lcl_OffsetFromGridEdge(tools::Long nMinWidth, tools::Long nCharWidth, sal_Unicode cChar,
                                   bool bForceLeft)
{
    if (bForceLeft)
        return 0;

    tools::Long nOffset = 0;

    switch (lcl_WhichPunctuationClass(cChar))
    {
        case IdeographicPunctuationClass::NONE:
            // Centered
            nOffset = (nMinWidth - nCharWidth) / 2;
            break;
        case IdeographicPunctuationClass::OPEN_BRACKET:
            // Align to next edge, closer to next ideograph
            nOffset = nMinWidth - nCharWidth;
            break;
        default:
            // CLOSE_BRACKET or COMMA_OR_FULLSTOP:
            // Align to previous edge, closer to previous ideograph.
            break;
    }
    return nOffset;
}
}

namespace sw::Justify
{
sal_Int32 GetModelPosition(const KernArray& rKernArray, sal_Int32 nLen, tools::Long nX)
{
    tools::Long nLeft = 0, nRight = 0;
    sal_Int32 nLast = 0, nIdx = 0;

    do
    {
        nRight = rKernArray[nLast];
        ++nIdx;
        while (nIdx < nLen && rKernArray[nIdx] == rKernArray[nLast])
            ++nIdx;

        if (nIdx < nLen)
        {
            if (nX < nRight)
                return (nX - nLeft < nRight - nX) ? nLast : nIdx;

            nLeft = nRight;
            nLast = nIdx;
        }
    } while (nIdx < nLen);
    return nIdx;
}

void SpaceDistribution(KernArray& rKernArray, std::u16string_view aText, sal_Int32 nStt,
                       sal_Int32 nLen, tools::Long nSpaceAdd, tools::Long nKern, bool bNoHalfSpace)
{
    assert(nStt + nLen <= sal_Int32(aText.size()));
    assert(nLen <= sal_Int32(rKernArray.size()));
    // nSpaceSum contains the sum of the intermediate space distributed
    // among Spaces by the Justification.
    // The Spaces themselves will be positioned in the middle of the
    // intermediate space, hence the nSpace/2.
    // In case of word-by-word underlining they have to be positioned
    // at the beginning of the intermediate space, so that the space
    // is not underlined.
    // A Space at the beginning or end of the text must be positioned
    // before (resp. after) the whole intermediate space, otherwise
    // the underline/strike-through would have gaps.
    tools::Long nSpaceSum = 0;
    // in word line mode and for Arabic, we disable the half space trick:
    const tools::Long nHalfSpace = bNoHalfSpace ? 0 : nSpaceAdd / 2;
    const tools::Long nOtherHalf = nSpaceAdd - nHalfSpace;
    tools::Long nKernSum = nKern;
    sal_Unicode cChPrev = aText[nStt];

    if (nSpaceAdd && (cChPrev == CH_BLANK))
        nSpaceSum = nHalfSpace;

    sal_Int32 nPrevIdx = 0;

    for (sal_Int32 i = 1; i < nLen; ++i, nKernSum += nKern)
    {
        // Find the beginning of the next cluster that has a different kern value.
        while (i < nLen && rKernArray[i] == rKernArray[nPrevIdx])
            ++i;

        if (i == nLen)
            break;

        sal_Unicode nCh = aText[nStt + i];

        // Apply SpaceSum
        if (cChPrev == CH_BLANK)
        {
            // no Pixel is lost:
            nSpaceSum += nOtherHalf;
        }

        if (nCh == CH_BLANK)
        {
            if (i + 1 == nLen)
                nSpaceSum += nSpaceAdd;
            else
                nSpaceSum += nHalfSpace;
        }

        cChPrev = nCh;
        rKernArray.adjust(nPrevIdx, nKernSum + nSpaceSum);
        // In word line mode and for Arabic, we disabled the half space trick. If a portion
        // ends with a blank, the full nSpaceAdd value has been added to the character in
        // front of the blank. This leads to painting artifacts, therefore we remove the
        // nSpaceAdd value again:
        if (bNoHalfSpace && i + 1 == nLen && nCh == CH_BLANK)
            rKernArray.adjust(nPrevIdx, -nSpaceAdd);

        // Advance nPrevIdx and assign kern values to previous cluster.
        for (tools::Long nValue = rKernArray[nPrevIdx++]; nPrevIdx < i; ++nPrevIdx)
            rKernArray.set(nPrevIdx, nValue);
    }

    // the layout engine requires the total width of the output
    while (nPrevIdx < nLen)
    {
        rKernArray.adjust(nPrevIdx, nKernSum + nSpaceSum);
        ++nPrevIdx;
    }
}

tools::Long SnapToGrid(KernArray& rKernArray, std::u16string_view aText, sal_Int32 nStt,
                       sal_Int32 nLen, tools::Long nGridWidth, bool bForceLeft)
{
    assert(nStt + nLen <= sal_Int32(aText.size()));
    assert(nLen <= sal_Int32(rKernArray.size()));

    tools::Long nCharWidth = rKernArray[0];
    tools::Long nMinWidth = lcl_MinGridWidth(nGridWidth, nCharWidth);
    tools::Long nDelta = lcl_OffsetFromGridEdge(nMinWidth, nCharWidth, aText[nStt], bForceLeft);
    tools::Long nEdge = nMinWidth - nDelta;

    sal_Int32 nLast = 0;

    for (sal_Int32 i = 1; i < nLen; ++i)
    {
        if (rKernArray[i] == rKernArray[nLast])
            continue;

        nCharWidth = rKernArray[i] - rKernArray[nLast];
        nMinWidth = lcl_MinGridWidth(nGridWidth, nCharWidth);
        tools::Long nX
            = nEdge + lcl_OffsetFromGridEdge(nMinWidth, nCharWidth, aText[nStt + i], bForceLeft);
        nEdge += nMinWidth;

        while (nLast < i)
        {
            rKernArray.set(nLast, nX);
            ++nLast;
        }
    }

    while (nLast < nLen)
    {
        rKernArray.set(nLast, nEdge);
        ++nLast;
    }

    return nDelta;
}

void SnapToGridEdge(KernArray& rKernArray, sal_Int32 nLen, tools::Long nGridWidth,
                    tools::Long nSpace, tools::Long nKern)
{
    assert(nLen <= sal_Int32(rKernArray.size()));

    tools::Long nCharWidth = rKernArray[0];
    tools::Long nEdge = lcl_MinGridWidth(nGridWidth, nCharWidth + nKern) + nSpace;

    sal_Int32 nLast = 0;

    for (sal_Int32 i = 1; i < nLen; ++i)
    {
        if (rKernArray[i] == rKernArray[nLast])
            continue;

        nCharWidth = rKernArray[i] - rKernArray[nLast];
        tools::Long nMinWidth = lcl_MinGridWidth(nGridWidth, nCharWidth + nKern);
        while (nLast < i)
        {
            rKernArray.set(nLast, nEdge);
            ++nLast;
        }

        nEdge += nMinWidth + nSpace;
    }

    while (nLast < nLen)
    {
        rKernArray.set(nLast, nEdge);
        ++nLast;
    }
}
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */