LibreOffice Module sw (master) 1
justify.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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
10#include <vector>
11#include <sal/types.h>
12#include <vcl/kernarray.hxx>
13#include <swfont.hxx>
14#include "justify.hxx"
15
16namespace
17{
18enum class IdeographicPunctuationClass
19{
20 NONE,
21 OPEN_BRACKET,
22 CLOSE_BRACKET,
23 COMMA_OR_FULLSTOP
24};
25
26IdeographicPunctuationClass lcl_WhichPunctuationClass(sal_Unicode cChar)
27{
28 if ((cChar < 0x3001 || cChar > 0x3002) && (cChar < 0x3008 || cChar > 0x3011)
29 && (cChar < 0x3014 || cChar > 0x301F) && 0xFF62 != cChar && 0xFF63 != cChar)
31 else if (0x3001 == cChar || 0x3002 == cChar)
32 return IdeographicPunctuationClass::COMMA_OR_FULLSTOP;
33 else if (0x3009 == cChar || 0x300B == cChar || 0x300D == cChar || 0x300F == cChar
34 || 0x3011 == cChar || 0x3015 == cChar || 0x3017 == cChar || 0x3019 == cChar
35 || 0x301B == cChar || 0x301E == cChar || 0x301F == cChar || 0xFF63 == cChar)
36 // right punctuation
37 return IdeographicPunctuationClass::CLOSE_BRACKET;
38
39 return IdeographicPunctuationClass::OPEN_BRACKET;
40}
41
42tools::Long lcl_MinGridWidth(tools::Long nGridWidth, tools::Long nCharWidth)
43{
44 tools::Long nCount = nCharWidth > nGridWidth ? (nCharWidth - 1) / nGridWidth + 1 : 1;
45 return nCount * nGridWidth;
46}
47
48tools::Long lcl_OffsetFromGridEdge(tools::Long nMinWidth, tools::Long nCharWidth, sal_Unicode cChar,
49 bool bForceLeft)
50{
51 if (bForceLeft)
52 return 0;
53
54 tools::Long nOffset = 0;
55
56 switch (lcl_WhichPunctuationClass(cChar))
57 {
59 // Centered
60 nOffset = (nMinWidth - nCharWidth) / 2;
61 break;
62 case IdeographicPunctuationClass::OPEN_BRACKET:
63 // Align to next edge, closer to next ideograph
64 nOffset = nMinWidth - nCharWidth;
65 break;
66 default:
67 // CLOSE_BRACKET or COMMA_OR_FULLSTOP:
68 // Align to previous edge, closer to previous ideograph.
69 break;
70 }
71 return nOffset;
72}
73}
74
75namespace sw::Justify
76{
77sal_Int32 GetModelPosition(const KernArray& rKernArray, sal_Int32 nLen, tools::Long nX)
78{
79 tools::Long nLeft = 0, nRight = 0;
80 sal_Int32 nLast = 0, nIdx = 0;
81
82 do
83 {
84 nRight = rKernArray[nLast];
85 ++nIdx;
86 while (nIdx < nLen && rKernArray[nIdx] == rKernArray[nLast])
87 ++nIdx;
88
89 if (nIdx < nLen)
90 {
91 if (nX < nRight)
92 return (nX - nLeft < nRight - nX) ? nLast : nIdx;
93
94 nLeft = nRight;
95 nLast = nIdx;
96 }
97 } while (nIdx < nLen);
98 return nIdx;
99}
100
101void SpaceDistribution(KernArray& rKernArray, std::u16string_view aText, sal_Int32 nStt,
102 sal_Int32 nLen, tools::Long nSpaceAdd, tools::Long nKern, bool bNoHalfSpace)
103{
104 assert(nStt + nLen <= sal_Int32(aText.size()));
105 assert(nLen <= sal_Int32(rKernArray.size()));
106 // nSpaceSum contains the sum of the intermediate space distributed
107 // among Spaces by the Justification.
108 // The Spaces themselves will be positioned in the middle of the
109 // intermediate space, hence the nSpace/2.
110 // In case of word-by-word underlining they have to be positioned
111 // at the beginning of the intermediate space, so that the space
112 // is not underlined.
113 // A Space at the beginning or end of the text must be positioned
114 // before (resp. after) the whole intermediate space, otherwise
115 // the underline/strike-through would have gaps.
116 tools::Long nSpaceSum = 0;
117 // in word line mode and for Arabic, we disable the half space trick:
118 const tools::Long nHalfSpace = bNoHalfSpace ? 0 : nSpaceAdd / 2;
119 const tools::Long nOtherHalf = nSpaceAdd - nHalfSpace;
120 tools::Long nKernSum = nKern;
121 sal_Unicode cChPrev = aText[nStt];
122
123 if (nSpaceAdd && (cChPrev == CH_BLANK))
124 nSpaceSum = nHalfSpace;
125
126 sal_Int32 nPrevIdx = 0;
127
128 for (sal_Int32 i = 1; i < nLen; ++i, nKernSum += nKern)
129 {
130 // Find the beginning of the next cluster that has a different kern value.
131 while (i < nLen && rKernArray[i] == rKernArray[nPrevIdx])
132 ++i;
133
134 if (i == nLen)
135 break;
136
137 sal_Unicode nCh = aText[nStt + i];
138
139 // Apply SpaceSum
140 if (cChPrev == CH_BLANK)
141 {
142 // no Pixel is lost:
143 nSpaceSum += nOtherHalf;
144 }
145
146 if (nCh == CH_BLANK)
147 {
148 if (i + 1 == nLen)
149 nSpaceSum += nSpaceAdd;
150 else
151 nSpaceSum += nHalfSpace;
152 }
153
154 cChPrev = nCh;
155 rKernArray.adjust(nPrevIdx, nKernSum + nSpaceSum);
156 // In word line mode and for Arabic, we disabled the half space trick. If a portion
157 // ends with a blank, the full nSpaceAdd value has been added to the character in
158 // front of the blank. This leads to painting artifacts, therefore we remove the
159 // nSpaceAdd value again:
160 if (bNoHalfSpace && i + 1 == nLen && nCh == CH_BLANK)
161 rKernArray.adjust(nPrevIdx, -nSpaceAdd);
162
163 // Advance nPrevIdx and assign kern values to previous cluster.
164 for (tools::Long nValue = rKernArray[nPrevIdx++]; nPrevIdx < i; ++nPrevIdx)
165 rKernArray.set(nPrevIdx, nValue);
166 }
167
168 // the layout engine requires the total width of the output
169 while (nPrevIdx < nLen)
170 {
171 rKernArray.adjust(nPrevIdx, nKernSum + nSpaceSum);
172 ++nPrevIdx;
173 }
174}
175
176tools::Long SnapToGrid(KernArray& rKernArray, std::u16string_view aText, sal_Int32 nStt,
177 sal_Int32 nLen, tools::Long nGridWidth, bool bForceLeft)
178{
179 assert(nStt + nLen <= sal_Int32(aText.size()));
180 assert(nLen <= sal_Int32(rKernArray.size()));
181
182 tools::Long nCharWidth = rKernArray[0];
183 tools::Long nMinWidth = lcl_MinGridWidth(nGridWidth, nCharWidth);
184 tools::Long nDelta = lcl_OffsetFromGridEdge(nMinWidth, nCharWidth, aText[nStt], bForceLeft);
185 tools::Long nEdge = nMinWidth - nDelta;
186
187 sal_Int32 nLast = 0;
188
189 for (sal_Int32 i = 1; i < nLen; ++i)
190 {
191 if (rKernArray[i] == rKernArray[nLast])
192 continue;
193
194 nCharWidth = rKernArray[i] - rKernArray[nLast];
195 nMinWidth = lcl_MinGridWidth(nGridWidth, nCharWidth);
196 tools::Long nX
197 = nEdge + lcl_OffsetFromGridEdge(nMinWidth, nCharWidth, aText[nStt + i], bForceLeft);
198 nEdge += nMinWidth;
199
200 while (nLast < i)
201 {
202 rKernArray.set(nLast, nX);
203 ++nLast;
204 }
205 }
206
207 while (nLast < nLen)
208 {
209 rKernArray.set(nLast, nEdge);
210 ++nLast;
211 }
212
213 return nDelta;
214}
215
216void SnapToGridEdge(KernArray& rKernArray, sal_Int32 nLen, tools::Long nGridWidth,
217 tools::Long nSpace, tools::Long nKern)
218{
219 assert(nLen <= sal_Int32(rKernArray.size()));
220
221 tools::Long nCharWidth = rKernArray[0];
222 tools::Long nEdge = lcl_MinGridWidth(nGridWidth, nCharWidth + nKern) + nSpace;
223
224 sal_Int32 nLast = 0;
225
226 for (sal_Int32 i = 1; i < nLen; ++i)
227 {
228 if (rKernArray[i] == rKernArray[nLast])
229 continue;
230
231 nCharWidth = rKernArray[i] - rKernArray[nLast];
232 tools::Long nMinWidth = lcl_MinGridWidth(nGridWidth, nCharWidth + nKern);
233 while (nLast < i)
234 {
235 rKernArray.set(nLast, nEdge);
236 ++nLast;
237 }
238
239 nEdge += nMinWidth + nSpace;
240 }
241
242 while (nLast < nLen)
243 {
244 rKernArray.set(nLast, nEdge);
245 ++nLast;
246 }
247}
248}
249
250/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
size_t size() const
void set(size_t nIndex, sal_Int32 nValue)
void adjust(size_t nIndex, sal_Int32 nDiff)
int nCount
sal_Int16 nValue
int i
tools::Long SnapToGrid(KernArray &rKernArray, std::u16string_view aText, sal_Int32 nStt, sal_Int32 nLen, tools::Long nGridWidth, bool bForceLeft)
Snap ideographs to text grids: a) Ideographic open brackets are aligned to the rightmost edge of span...
Definition: justify.cxx:176
void SnapToGridEdge(KernArray &rKernArray, sal_Int32 nLen, tools::Long nGridWidth, tools::Long nSpace, tools::Long nKern)
Snap ideographs to text grids edge ( used when snap to char is off ): space will be distributed ( in ...
Definition: justify.cxx:216
void SpaceDistribution(KernArray &rKernArray, std::u16string_view aText, sal_Int32 nStt, sal_Int32 nLen, tools::Long nSpaceAdd, tools::Long nKern, bool bNoHalfSpace)
Distribute space between words and letters.
Definition: justify.cxx:101
sal_Int32 GetModelPosition(const KernArray &rKernArray, sal_Int32 nLen, tools::Long nX)
Get model position base on given kern array.
Definition: justify.cxx:77
long Long
const sal_Unicode CH_BLANK
Definition: swfont.hxx:42
sal_uInt16 sal_Unicode