1*9f62ea84SAndrew Rist /************************************************************** 2cdf0e10cSrcweir * 3*9f62ea84SAndrew Rist * Licensed to the Apache Software Foundation (ASF) under one 4*9f62ea84SAndrew Rist * or more contributor license agreements. See the NOTICE file 5*9f62ea84SAndrew Rist * distributed with this work for additional information 6*9f62ea84SAndrew Rist * regarding copyright ownership. The ASF licenses this file 7*9f62ea84SAndrew Rist * to you under the Apache License, Version 2.0 (the 8*9f62ea84SAndrew Rist * "License"); you may not use this file except in compliance 9*9f62ea84SAndrew Rist * with the License. You may obtain a copy of the License at 10*9f62ea84SAndrew Rist * 11*9f62ea84SAndrew Rist * http://www.apache.org/licenses/LICENSE-2.0 12*9f62ea84SAndrew Rist * 13*9f62ea84SAndrew Rist * Unless required by applicable law or agreed to in writing, 14*9f62ea84SAndrew Rist * software distributed under the License is distributed on an 15*9f62ea84SAndrew Rist * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16*9f62ea84SAndrew Rist * KIND, either express or implied. See the License for the 17*9f62ea84SAndrew Rist * specific language governing permissions and limitations 18*9f62ea84SAndrew Rist * under the License. 19*9f62ea84SAndrew Rist * 20*9f62ea84SAndrew Rist *************************************************************/ 21*9f62ea84SAndrew Rist 22*9f62ea84SAndrew Rist 23cdf0e10cSrcweir 24cdf0e10cSrcweir // Description: An implementation of the SalLayout interface that uses the 25cdf0e10cSrcweir // Graphite engine. 26cdf0e10cSrcweir 27cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove 28cdf0e10cSrcweir #include "precompiled_vcl.hxx" 29cdf0e10cSrcweir 30cdf0e10cSrcweir // We need this to enable namespace support in libgrengine headers. 31cdf0e10cSrcweir #define GR_NAMESPACE 32cdf0e10cSrcweir 33cdf0e10cSrcweir // Enable lots of debug info 34cdf0e10cSrcweir #ifdef DEBUG 35cdf0e10cSrcweir //#define GRLAYOUT_DEBUG 1 36cdf0e10cSrcweir //#undef NDEBUG 37cdf0e10cSrcweir #endif 38cdf0e10cSrcweir 39cdf0e10cSrcweir // Header files 40cdf0e10cSrcweir // 41cdf0e10cSrcweir // Standard Library 42cdf0e10cSrcweir #include <algorithm> 43cdf0e10cSrcweir #include <cassert> 44cdf0e10cSrcweir #include <functional> 45cdf0e10cSrcweir #include <limits> 46cdf0e10cSrcweir #include <numeric> 47cdf0e10cSrcweir #include <deque> 48cdf0e10cSrcweir 49cdf0e10cSrcweir // Platform 50cdf0e10cSrcweir #ifdef WNT 51cdf0e10cSrcweir #include <tools/svwin.h> 52cdf0e10cSrcweir #include <svsys.h> 53cdf0e10cSrcweir #endif 54cdf0e10cSrcweir 55cdf0e10cSrcweir #ifdef UNX 56cdf0e10cSrcweir #include <graphite_adaptors.hxx> 57cdf0e10cSrcweir #endif 58cdf0e10cSrcweir 59cdf0e10cSrcweir #include <salgdi.hxx> 60cdf0e10cSrcweir 61cdf0e10cSrcweir #include <unicode/uchar.h> 62cdf0e10cSrcweir #include <unicode/ubidi.h> 63cdf0e10cSrcweir #include <unicode/uscript.h> 64cdf0e10cSrcweir 65cdf0e10cSrcweir // Graphite Libraries (must be after vcl headers on windows) 66cdf0e10cSrcweir #include <preextstl.h> 67cdf0e10cSrcweir #include <graphite/GrClient.h> 68cdf0e10cSrcweir #include <graphite/Font.h> 69cdf0e10cSrcweir #include <graphite/ITextSource.h> 70cdf0e10cSrcweir #include <graphite/Segment.h> 71cdf0e10cSrcweir #include <graphite/SegmentPainter.h> 72cdf0e10cSrcweir #include <postextstl.h> 73cdf0e10cSrcweir 74cdf0e10cSrcweir #include <graphite_layout.hxx> 75cdf0e10cSrcweir #include <graphite_features.hxx> 76cdf0e10cSrcweir #include "graphite_textsrc.hxx" 77cdf0e10cSrcweir 78cdf0e10cSrcweir 79cdf0e10cSrcweir // Module private type definitions and forward declarations. 80cdf0e10cSrcweir // 81cdf0e10cSrcweir // Module private names. 82cdf0e10cSrcweir // 83cdf0e10cSrcweir 84cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 85cdf0e10cSrcweir FILE * grLogFile = NULL; 86cdf0e10cSrcweir FILE * grLog() 87cdf0e10cSrcweir { 88cdf0e10cSrcweir #ifdef WNT 89cdf0e10cSrcweir std::string logFileName(getenv("TEMP")); 90cdf0e10cSrcweir logFileName.append("\\graphitelayout.log"); 91cdf0e10cSrcweir if (grLogFile == NULL) grLogFile = fopen(logFileName.c_str(),"w"); 92cdf0e10cSrcweir else fflush(grLogFile); 93cdf0e10cSrcweir return grLogFile; 94cdf0e10cSrcweir #else 95cdf0e10cSrcweir return stdout; 96cdf0e10cSrcweir #endif 97cdf0e10cSrcweir } 98cdf0e10cSrcweir #endif 99cdf0e10cSrcweir 100cdf0e10cSrcweir #ifdef GRCACHE 101cdf0e10cSrcweir #include <graphite_cache.hxx> 102cdf0e10cSrcweir #endif 103cdf0e10cSrcweir 104cdf0e10cSrcweir 105cdf0e10cSrcweir namespace 106cdf0e10cSrcweir { 107cdf0e10cSrcweir typedef ext_std::pair<gr::GlyphIterator, gr::GlyphIterator> glyph_range_t; 108cdf0e10cSrcweir typedef ext_std::pair<gr::GlyphSetIterator, gr::GlyphSetIterator> glyph_set_range_t; 109cdf0e10cSrcweir 110cdf0e10cSrcweir inline long round(const float n) { 111cdf0e10cSrcweir return long(n + (n < 0 ? -0.5 : 0.5)); 112cdf0e10cSrcweir } 113cdf0e10cSrcweir 114cdf0e10cSrcweir 115cdf0e10cSrcweir template<typename T> 116cdf0e10cSrcweir inline bool in_range(const T i, const T b, const T e) { 117cdf0e10cSrcweir return !(b > i) && i < e; 118cdf0e10cSrcweir } 119cdf0e10cSrcweir 120cdf0e10cSrcweir 121cdf0e10cSrcweir template<typename T> 122cdf0e10cSrcweir inline bool is_subrange(const T sb, const T se, const T b, const T e) { 123cdf0e10cSrcweir return !(b > sb || se > e); 124cdf0e10cSrcweir } 125cdf0e10cSrcweir 126cdf0e10cSrcweir 127cdf0e10cSrcweir template<typename T> 128cdf0e10cSrcweir inline bool is_subrange(const std::pair<T, T> &s, const T b, const T e) { 129cdf0e10cSrcweir return is_subrange(s.first, s.second, b, e); 130cdf0e10cSrcweir } 131cdf0e10cSrcweir 132cdf0e10cSrcweir int findSameDirLimit(const xub_Unicode* buffer, int charCount, bool rtl) 133cdf0e10cSrcweir { 134cdf0e10cSrcweir UErrorCode status = U_ZERO_ERROR; 135cdf0e10cSrcweir UBiDi *ubidi = ubidi_openSized(charCount, 0, &status); 136cdf0e10cSrcweir int limit = 0; 137cdf0e10cSrcweir ubidi_setPara(ubidi, reinterpret_cast<const UChar *>(buffer), charCount, 138cdf0e10cSrcweir (rtl)?UBIDI_DEFAULT_RTL:UBIDI_DEFAULT_LTR, NULL, &status); 139cdf0e10cSrcweir UBiDiLevel level = 0; 140cdf0e10cSrcweir ubidi_getLogicalRun(ubidi, 0, &limit, &level); 141cdf0e10cSrcweir ubidi_close(ubidi); 142cdf0e10cSrcweir if ((rtl && !(level & 1)) || (!rtl && (level & 1))) 143cdf0e10cSrcweir { 144cdf0e10cSrcweir limit = 0; 145cdf0e10cSrcweir } 146cdf0e10cSrcweir return limit; 147cdf0e10cSrcweir } 148cdf0e10cSrcweir 149cdf0e10cSrcweir } // namespace 150cdf0e10cSrcweir 151cdf0e10cSrcweir 152cdf0e10cSrcweir 153cdf0e10cSrcweir // Impementation of the GraphiteLayout::Glyphs container class. 154cdf0e10cSrcweir // This is an extended vector class with methods added to enable 155cdf0e10cSrcweir // o Correctly filling with glyphs. 156cdf0e10cSrcweir // o Querying clustering relationships. 157cdf0e10cSrcweir // o manipulations that affect neighouring glyphs. 158cdf0e10cSrcweir 159cdf0e10cSrcweir const int GraphiteLayout::EXTRA_CONTEXT_LENGTH = 10; 160cdf0e10cSrcweir #ifdef GRCACHE 161cdf0e10cSrcweir GraphiteCacheHandler GraphiteCacheHandler::instance; 162cdf0e10cSrcweir #endif 163cdf0e10cSrcweir 164cdf0e10cSrcweir // The Graphite glyph stream is really a sequence of glyph attachment trees 165cdf0e10cSrcweir // each rooted at a non-attached base glyph. fill_from walks the glyph stream 166cdf0e10cSrcweir // find each non-attached base glyph and calls append to record them as a 167cdf0e10cSrcweir // sequence of clusters. 168cdf0e10cSrcweir void 169cdf0e10cSrcweir GraphiteLayout::Glyphs::fill_from(gr::Segment & rSegment, ImplLayoutArgs &rArgs, 170cdf0e10cSrcweir bool bRtl, long &rWidth, float fScaling, std::vector<int> & rChar2Base, std::vector<int> & rGlyph2Char, std::vector<int> & rCharDxs) 171cdf0e10cSrcweir { 172cdf0e10cSrcweir // Create a glyph item for each of the glyph and append it to the base class glyph list. 173cdf0e10cSrcweir typedef ext_std::pair< gr::GlyphSetIterator, gr::GlyphSetIterator > GrGlyphSet; 174cdf0e10cSrcweir int nChar = rArgs.mnEndCharPos - rArgs.mnMinCharPos; 175cdf0e10cSrcweir glyph_range_t iGlyphs = rSegment.glyphs(); 176cdf0e10cSrcweir int nGlyphs = iGlyphs.second - iGlyphs.first; 177cdf0e10cSrcweir gr::GlyphIterator prevBase = iGlyphs.second; 178cdf0e10cSrcweir float fSegmentAdvance = rSegment.advanceWidth(); 179cdf0e10cSrcweir float fMinX = fSegmentAdvance; 180cdf0e10cSrcweir float fMaxX = 0.0f; 181cdf0e10cSrcweir rGlyph2Char.assign(nGlyphs, -1); 182cdf0e10cSrcweir long nDxOffset = 0; 183cdf0e10cSrcweir int nGlyphIndex = (bRtl)? (nGlyphs - 1) : 0; 184cdf0e10cSrcweir // OOo always expects the glyphs in ltr order 185cdf0e10cSrcweir int nDelta = (bRtl)? -1 : 1; 186cdf0e10cSrcweir 187cdf0e10cSrcweir int nLastGlyph = (bRtl)? nGlyphs - 1: 0; 188cdf0e10cSrcweir int nNextChar = (bRtl)? (rSegment.stopCharacter() - 1) : rSegment.startCharacter();//rArgs.mnMinCharPos; 189cdf0e10cSrcweir // current glyph number (Graphite glyphs) 190cdf0e10cSrcweir //int currGlyph = 0; 191cdf0e10cSrcweir int nFirstCharInCluster = nNextChar; 192cdf0e10cSrcweir int nFirstGlyphInCluster = nLastGlyph; 193cdf0e10cSrcweir 194cdf0e10cSrcweir // ltr first char in cluster is lowest, same is true for rtl 195cdf0e10cSrcweir // ltr first glyph in cluster is lowest, rtl first glyph is highest 196cdf0e10cSrcweir 197cdf0e10cSrcweir // loop over the glyphs determining which characters are linked to them 198cdf0e10cSrcweir gr::GlyphIterator gi; 199cdf0e10cSrcweir for (gi = iGlyphs.first + nGlyphIndex; 200cdf0e10cSrcweir nGlyphIndex >= 0 && nGlyphIndex < nGlyphs; 201cdf0e10cSrcweir nGlyphIndex+= nDelta, gi = iGlyphs.first + nGlyphIndex) 202cdf0e10cSrcweir { 203cdf0e10cSrcweir gr::GlyphInfo info = (*gi); 204cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 205cdf0e10cSrcweir fprintf(grLog(),"Glyph %d %f,%f\n", (int)info.logicalIndex(), info.origin(), info.yOffset()); 206cdf0e10cSrcweir #endif 207cdf0e10cSrcweir // the last character associated with this glyph is after 208cdf0e10cSrcweir // our current cluster buffer position 209cdf0e10cSrcweir if ((bRtl && ((signed)info.firstChar() <= nNextChar)) || 210cdf0e10cSrcweir (!bRtl && ((signed)info.lastChar() >= nNextChar))) 211cdf0e10cSrcweir { 212cdf0e10cSrcweir if ((bRtl && nGlyphIndex < nLastGlyph) || 213cdf0e10cSrcweir (!bRtl && nGlyphIndex > nLastGlyph)) 214cdf0e10cSrcweir { 215cdf0e10cSrcweir // this glyph is after the previous one left->right 216cdf0e10cSrcweir // if insertion is allowed before it then we are in a 217cdf0e10cSrcweir // new cluster 218cdf0e10cSrcweir int nAttachedBase = (*(info.attachedClusterBase())).logicalIndex(); 219cdf0e10cSrcweir if (!info.isAttached() || 220cdf0e10cSrcweir !in_range(nAttachedBase, nFirstGlyphInCluster, nGlyphIndex)) 221cdf0e10cSrcweir { 222cdf0e10cSrcweir if (in_range(nFirstCharInCluster, rArgs.mnMinCharPos, rArgs.mnEndCharPos) && 223cdf0e10cSrcweir nFirstGlyphInCluster != nGlyphIndex) 224cdf0e10cSrcweir { 225cdf0e10cSrcweir std::pair <float,float> aBounds = 226cdf0e10cSrcweir appendCluster(rSegment, rArgs, bRtl, 227cdf0e10cSrcweir fSegmentAdvance, nFirstCharInCluster, 228cdf0e10cSrcweir nNextChar, nFirstGlyphInCluster, nGlyphIndex, fScaling, 229cdf0e10cSrcweir rChar2Base, rGlyph2Char, rCharDxs, nDxOffset); 230cdf0e10cSrcweir fMinX = std::min(aBounds.first, fMinX); 231cdf0e10cSrcweir fMaxX = std::max(aBounds.second, fMaxX); 232cdf0e10cSrcweir } 233cdf0e10cSrcweir nFirstCharInCluster = (bRtl)? info.lastChar() : info.firstChar(); 234cdf0e10cSrcweir nFirstGlyphInCluster = nGlyphIndex; 235cdf0e10cSrcweir } 236cdf0e10cSrcweir nLastGlyph = (bRtl)? std::min(nGlyphIndex, nAttachedBase) : 237cdf0e10cSrcweir std::max(nGlyphIndex, nAttachedBase); 238cdf0e10cSrcweir } 239cdf0e10cSrcweir // loop over chacters associated with this glyph and characters 240cdf0e10cSrcweir // between nextChar and the last character associated with this glyph 241cdf0e10cSrcweir // giving them the current cluster id. This allows for character /glyph 242cdf0e10cSrcweir // order reversal. 243cdf0e10cSrcweir // For each character we do a reverse glyph id look up 244cdf0e10cSrcweir // and store the glyph id with the highest logical index in nLastGlyph 245cdf0e10cSrcweir while ((bRtl && ((signed)info.firstChar() <= nNextChar)) || 246cdf0e10cSrcweir (!bRtl && (signed)info.lastChar() >= nNextChar)) 247cdf0e10cSrcweir { 248cdf0e10cSrcweir GrGlyphSet charGlyphs = rSegment.charToGlyphs(nNextChar); 249cdf0e10cSrcweir nNextChar += nDelta; 250cdf0e10cSrcweir gr::GlyphSetIterator gj = charGlyphs.first; 251cdf0e10cSrcweir while (gj != charGlyphs.second) 252cdf0e10cSrcweir { 253cdf0e10cSrcweir nLastGlyph = (bRtl)? min(nLastGlyph, (signed)(*gj).logicalIndex()) : max(nLastGlyph, (signed)(*gj).logicalIndex()); 254cdf0e10cSrcweir ++gj; 255cdf0e10cSrcweir } 256cdf0e10cSrcweir } 257cdf0e10cSrcweir // Loop over attached glyphs and make sure they are all in the cluster since you 258cdf0e10cSrcweir // can have glyphs attached with another base glyph in between 259cdf0e10cSrcweir glyph_set_range_t iAttached = info.attachedClusterGlyphs(); 260cdf0e10cSrcweir for (gr::GlyphSetIterator agi = iAttached.first; agi != iAttached.second; ++agi) 261cdf0e10cSrcweir { 262cdf0e10cSrcweir nLastGlyph = (bRtl)? min(nLastGlyph, (signed)(*agi).logicalIndex()) : max(nLastGlyph, (signed)(*agi).logicalIndex()); 263cdf0e10cSrcweir } 264cdf0e10cSrcweir 265cdf0e10cSrcweir // if this is a rtl attached glyph, then we need to include its 266cdf0e10cSrcweir // base in the cluster, which will have a lower graphite index 267cdf0e10cSrcweir if (bRtl) 268cdf0e10cSrcweir { 269cdf0e10cSrcweir if ((signed)info.attachedClusterBase()->logicalIndex() < nLastGlyph) 270cdf0e10cSrcweir { 271cdf0e10cSrcweir nLastGlyph = info.attachedClusterBase()->logicalIndex(); 272cdf0e10cSrcweir } 273cdf0e10cSrcweir } 274cdf0e10cSrcweir } 275cdf0e10cSrcweir 276cdf0e10cSrcweir // it is possible for the lastChar to be after nextChar and 277cdf0e10cSrcweir // firstChar to be before the nFirstCharInCluster in rare 278cdf0e10cSrcweir // circumstances e.g. Myanmar word for cemetery 279cdf0e10cSrcweir if ((bRtl && ((signed)info.lastChar() > nFirstCharInCluster)) || 280cdf0e10cSrcweir (!bRtl && ((signed)info.firstChar() < nFirstCharInCluster))) 281cdf0e10cSrcweir { 282cdf0e10cSrcweir nFirstCharInCluster = info.firstChar(); 283cdf0e10cSrcweir } 284cdf0e10cSrcweir } 285cdf0e10cSrcweir // process last cluster 286cdf0e10cSrcweir if (in_range(nFirstCharInCluster, rArgs.mnMinCharPos, rArgs.mnEndCharPos) && 287cdf0e10cSrcweir nFirstGlyphInCluster != nGlyphIndex) 288cdf0e10cSrcweir { 289cdf0e10cSrcweir std::pair <float,float> aBounds = 290cdf0e10cSrcweir appendCluster(rSegment, rArgs, bRtl, fSegmentAdvance, 291cdf0e10cSrcweir nFirstCharInCluster, nNextChar, 292cdf0e10cSrcweir nFirstGlyphInCluster, nGlyphIndex, fScaling, 293cdf0e10cSrcweir rChar2Base, rGlyph2Char, rCharDxs, nDxOffset); 294cdf0e10cSrcweir fMinX = std::min(aBounds.first, fMinX); 295cdf0e10cSrcweir fMaxX = std::max(aBounds.second, fMaxX); 296cdf0e10cSrcweir } 297cdf0e10cSrcweir long nXOffset = round(fMinX * fScaling); 298cdf0e10cSrcweir rWidth = round(fMaxX * fScaling) - nXOffset + nDxOffset; 299cdf0e10cSrcweir if (rWidth < 0) 300cdf0e10cSrcweir { 301cdf0e10cSrcweir // This can happen when there was no base inside the range 302cdf0e10cSrcweir rWidth = 0; 303cdf0e10cSrcweir } 304cdf0e10cSrcweir // fill up non-base char dx with cluster widths from previous base glyph 305cdf0e10cSrcweir if (bRtl) 306cdf0e10cSrcweir { 307cdf0e10cSrcweir if (rCharDxs[nChar-1] == -1) 308cdf0e10cSrcweir rCharDxs[nChar-1] = 0; 309cdf0e10cSrcweir else 310cdf0e10cSrcweir rCharDxs[nChar-1] -= nXOffset; 311cdf0e10cSrcweir for (int i = nChar - 2; i >= 0; i--) 312cdf0e10cSrcweir { 313cdf0e10cSrcweir if (rCharDxs[i] == -1) rCharDxs[i] = rCharDxs[i+1]; 314cdf0e10cSrcweir else rCharDxs[i] -= nXOffset; 315cdf0e10cSrcweir } 316cdf0e10cSrcweir } 317cdf0e10cSrcweir else 318cdf0e10cSrcweir { 319cdf0e10cSrcweir if (rCharDxs[0] == -1) 320cdf0e10cSrcweir rCharDxs[0] = 0; 321cdf0e10cSrcweir else 322cdf0e10cSrcweir rCharDxs[0] -= nXOffset; 323cdf0e10cSrcweir for (int i = 1; i < nChar; i++) 324cdf0e10cSrcweir { 325cdf0e10cSrcweir if (rCharDxs[i] == -1) rCharDxs[i] = rCharDxs[i-1]; 326cdf0e10cSrcweir else rCharDxs[i] -= nXOffset; 327cdf0e10cSrcweir } 328cdf0e10cSrcweir } 329cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 330cdf0e10cSrcweir fprintf(grLog(),"Glyphs xOff%ld dropDx%ld w%ld\n", nXOffset, nDxOffset, rWidth); 331cdf0e10cSrcweir #endif 332cdf0e10cSrcweir // remove offset due to context if there is one 333cdf0e10cSrcweir if (nXOffset != 0) 334cdf0e10cSrcweir { 335cdf0e10cSrcweir for (size_t i = 0; i < size(); i++) 336cdf0e10cSrcweir (*this)[i].maLinearPos.X() -= nXOffset; 337cdf0e10cSrcweir } 338cdf0e10cSrcweir } 339cdf0e10cSrcweir 340cdf0e10cSrcweir std::pair<float,float> GraphiteLayout::Glyphs::appendCluster(gr::Segment& rSeg, 341cdf0e10cSrcweir ImplLayoutArgs & rArgs, bool bRtl,float fSegmentAdvance, 342cdf0e10cSrcweir int nFirstCharInCluster, int nNextChar, int nFirstGlyphInCluster, 343cdf0e10cSrcweir int nNextGlyph, float fScaling, std::vector<int> & rChar2Base, 344cdf0e10cSrcweir std::vector<int> & rGlyph2Char, std::vector<int> & rCharDxs, long & rDXOffset) 345cdf0e10cSrcweir { 346cdf0e10cSrcweir glyph_range_t iGlyphs = rSeg.glyphs(); 347cdf0e10cSrcweir int nGlyphs = iGlyphs.second - iGlyphs.first; 348cdf0e10cSrcweir int nDelta = (bRtl)? -1 : 1; 349cdf0e10cSrcweir gr::GlyphInfo aFirstGlyph = *(iGlyphs.first + nFirstGlyphInCluster); 350cdf0e10cSrcweir std::pair <float, float> aBounds; 351cdf0e10cSrcweir aBounds.first = aFirstGlyph.origin(); 352cdf0e10cSrcweir aBounds.second = aFirstGlyph.origin(); 353cdf0e10cSrcweir // before we add the glyphs to this vector, we record the 354cdf0e10cSrcweir // glyph's index in the vector (which is not the same as 355cdf0e10cSrcweir // the Segment's glyph index!) 356cdf0e10cSrcweir assert(size() < rGlyph2Char.size()); 357cdf0e10cSrcweir rChar2Base[nFirstCharInCluster-rArgs.mnMinCharPos] = size(); 358cdf0e10cSrcweir rGlyph2Char[size()] = nFirstCharInCluster; 359cdf0e10cSrcweir 360cdf0e10cSrcweir // can we break before this cluster? 361cdf0e10cSrcweir // Glyphs may have either a positive or negative breakWeight refering to 362cdf0e10cSrcweir // the position after or before the glyph respectively 363cdf0e10cSrcweir int nPrevBreakWeight = 0; 364cdf0e10cSrcweir if (nFirstGlyphInCluster > 0) 365cdf0e10cSrcweir { 366cdf0e10cSrcweir nPrevBreakWeight = (iGlyphs.first + (nFirstGlyphInCluster - 1))->breakweight(); 367cdf0e10cSrcweir } 368cdf0e10cSrcweir int nBreakWeight = aFirstGlyph.breakweight(); 369cdf0e10cSrcweir if (nBreakWeight < 0) 370cdf0e10cSrcweir { 371cdf0e10cSrcweir // negative means it applies to the position before the glyph's character 372cdf0e10cSrcweir nBreakWeight *= -1; 373cdf0e10cSrcweir if (nPrevBreakWeight > 0 && nPrevBreakWeight < nBreakWeight) 374cdf0e10cSrcweir { 375cdf0e10cSrcweir // prevBreakWeight wins 376cdf0e10cSrcweir nBreakWeight = nPrevBreakWeight; 377cdf0e10cSrcweir } 378cdf0e10cSrcweir } 379cdf0e10cSrcweir else 380cdf0e10cSrcweir { 381cdf0e10cSrcweir nBreakWeight = 0; 382cdf0e10cSrcweir // positive means break after 383cdf0e10cSrcweir if (nPrevBreakWeight > 0) 384cdf0e10cSrcweir nBreakWeight = nPrevBreakWeight; 385cdf0e10cSrcweir } 386cdf0e10cSrcweir if (nBreakWeight > gr::klbNoBreak/*0*/ && 387cdf0e10cSrcweir // nBreakWeight <= gr::klbHyphenBreak) // uses Graphite hyphenation 388cdf0e10cSrcweir nBreakWeight <= gr::klbLetterBreak) // Needed for issue 111272 389cdf0e10cSrcweir { 390cdf0e10cSrcweir if (nBreakWeight < gr::klbHyphenBreak) 391cdf0e10cSrcweir rChar2Base[nFirstCharInCluster-rArgs.mnMinCharPos] |= WORD_BREAK_BEFORE; 392cdf0e10cSrcweir else 393cdf0e10cSrcweir rChar2Base[nFirstCharInCluster-rArgs.mnMinCharPos] |= HYPHEN_BREAK_BEFORE; 394cdf0e10cSrcweir } 395cdf0e10cSrcweir // always allow a break before a space even if graphite doesn't 396cdf0e10cSrcweir if (rArgs.mpStr[nFirstCharInCluster] == 0x20) 397cdf0e10cSrcweir rChar2Base[nFirstCharInCluster-rArgs.mnMinCharPos] |= WORD_BREAK_BEFORE; 398cdf0e10cSrcweir 399cdf0e10cSrcweir bool bBaseGlyph = true; 400cdf0e10cSrcweir for (int j = nFirstGlyphInCluster; 401cdf0e10cSrcweir j != nNextGlyph; j += nDelta) 402cdf0e10cSrcweir { 403cdf0e10cSrcweir long nNextOrigin; 404cdf0e10cSrcweir float fNextOrigin; 405cdf0e10cSrcweir gr::GlyphInfo aGlyph = *(iGlyphs.first + j); 406cdf0e10cSrcweir if (j + nDelta >= nGlyphs || j + nDelta < 0) // at rhs ltr,rtl 407cdf0e10cSrcweir { 408cdf0e10cSrcweir fNextOrigin = fSegmentAdvance; 409cdf0e10cSrcweir nNextOrigin = round(fSegmentAdvance * fScaling + rDXOffset); 410cdf0e10cSrcweir aBounds.second = std::max(fSegmentAdvance, aBounds.second); 411cdf0e10cSrcweir } 412cdf0e10cSrcweir else 413cdf0e10cSrcweir { 414cdf0e10cSrcweir gr::GlyphInfo aNextGlyph = *(iGlyphs.first + j + nDelta); 415cdf0e10cSrcweir fNextOrigin = std::max(aNextGlyph.attachedClusterBase()->origin(), aNextGlyph.origin()); 416cdf0e10cSrcweir aBounds.second = std::max(fNextOrigin, aBounds.second); 417cdf0e10cSrcweir nNextOrigin = round(fNextOrigin * fScaling + rDXOffset); 418cdf0e10cSrcweir } 419cdf0e10cSrcweir aBounds.first = std::min(aGlyph.origin(), aBounds.first); 420cdf0e10cSrcweir if ((signed)aGlyph.firstChar() < rArgs.mnEndCharPos && 421cdf0e10cSrcweir (signed)aGlyph.firstChar() >= rArgs.mnMinCharPos) 422cdf0e10cSrcweir { 423cdf0e10cSrcweir rCharDxs[aGlyph.firstChar()-rArgs.mnMinCharPos] = nNextOrigin; 424cdf0e10cSrcweir } 425cdf0e10cSrcweir if ((signed)aGlyph.attachedClusterBase()->logicalIndex() == j) 426cdf0e10cSrcweir { 427cdf0e10cSrcweir append(rSeg, rArgs, aGlyph, fNextOrigin, fScaling, rChar2Base, rGlyph2Char, rCharDxs, rDXOffset, bBaseGlyph); 428cdf0e10cSrcweir bBaseGlyph = false; 429cdf0e10cSrcweir } 430cdf0e10cSrcweir } 431cdf0e10cSrcweir // from the point of view of the dx array, the xpos is 432cdf0e10cSrcweir // the origin of the first glyph of the next cluster ltr 433cdf0e10cSrcweir // rtl it is the origin of the 1st glyph of the cluster 434cdf0e10cSrcweir long nXPos = (bRtl)? 435cdf0e10cSrcweir round(aFirstGlyph.attachedClusterBase()->origin() * fScaling) + rDXOffset : 436cdf0e10cSrcweir round(aBounds.second * fScaling) + rDXOffset; 437cdf0e10cSrcweir // force the last char in range to have the width of the cluster 438cdf0e10cSrcweir if (bRtl) 439cdf0e10cSrcweir { 440cdf0e10cSrcweir for (int n = nNextChar + 1; n <= nFirstCharInCluster; n++) 441cdf0e10cSrcweir { 442cdf0e10cSrcweir if ((n < rArgs.mnEndCharPos) && (n >= rArgs.mnMinCharPos)) 443cdf0e10cSrcweir rCharDxs[n-rArgs.mnMinCharPos] = nXPos; 444cdf0e10cSrcweir } 445cdf0e10cSrcweir } 446cdf0e10cSrcweir else 447cdf0e10cSrcweir { 448cdf0e10cSrcweir for (int n = nNextChar - 1; n >= nFirstCharInCluster; n--) 449cdf0e10cSrcweir { 450cdf0e10cSrcweir if (n < rArgs.mnEndCharPos && n >= rArgs.mnMinCharPos) 451cdf0e10cSrcweir rCharDxs[n-rArgs.mnMinCharPos] = nXPos; 452cdf0e10cSrcweir } 453cdf0e10cSrcweir } 454cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 455cdf0e10cSrcweir fprintf(grLog(),"Cluster g[%d-%d) c[%d-%d)%x x%ld y%f bw%d\n", nFirstGlyphInCluster, nNextGlyph, nFirstCharInCluster, nNextChar, rArgs.mpStr[nFirstCharInCluster], nXPos, aFirstGlyph.yOffset(), nBreakWeight); 456cdf0e10cSrcweir #endif 457cdf0e10cSrcweir return aBounds; 458cdf0e10cSrcweir } 459cdf0e10cSrcweir 460cdf0e10cSrcweir // append walks an attachment tree, flattening it, and converting it into a 461cdf0e10cSrcweir // sequence of GlyphItem objects which we can later manipulate. 462cdf0e10cSrcweir void 463cdf0e10cSrcweir GraphiteLayout::Glyphs::append(gr::Segment &segment, ImplLayoutArgs &args, gr::GlyphInfo & gi, float nextGlyphOrigin, float scaling, std::vector<int> & rChar2Base, std::vector<int> & rGlyph2Char, std::vector<int> & rCharDxs, long & rDXOffset, bool bIsBase) 464cdf0e10cSrcweir { 465cdf0e10cSrcweir float nextOrigin = nextGlyphOrigin; 466cdf0e10cSrcweir int firstChar = std::min(gi.firstChar(), gi.lastChar()); 467cdf0e10cSrcweir assert(size() < rGlyph2Char.size()); 468cdf0e10cSrcweir if (!bIsBase) rGlyph2Char[size()] = firstChar; 469cdf0e10cSrcweir // is the next glyph attached or in the next cluster? 470cdf0e10cSrcweir glyph_set_range_t iAttached = gi.attachedClusterGlyphs(); 471cdf0e10cSrcweir if (iAttached.first != iAttached.second) 472cdf0e10cSrcweir { 473cdf0e10cSrcweir nextOrigin = iAttached.first->origin(); 474cdf0e10cSrcweir } 475cdf0e10cSrcweir long glyphId = gi.glyphID(); 476cdf0e10cSrcweir long deltaOffset = 0; 477cdf0e10cSrcweir int glyphWidth = round(nextOrigin * scaling) - round(gi.origin() * scaling); 478cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 479cdf0e10cSrcweir fprintf(grLog(),"c%d g%d gWidth%d x%f ", firstChar, (int)gi.logicalIndex(), glyphWidth, nextOrigin); 480cdf0e10cSrcweir #endif 481cdf0e10cSrcweir if (glyphId == 0) 482cdf0e10cSrcweir { 483cdf0e10cSrcweir args.NeedFallback( 484cdf0e10cSrcweir firstChar, 485cdf0e10cSrcweir gr::RightToLeftDir(gr::DirCode(gi.directionality()))); 486cdf0e10cSrcweir if( (SAL_LAYOUT_FOR_FALLBACK & args.mnFlags )) 487cdf0e10cSrcweir { 488cdf0e10cSrcweir glyphId = GF_DROPPED; 489cdf0e10cSrcweir deltaOffset -= glyphWidth; 490cdf0e10cSrcweir glyphWidth = 0; 491cdf0e10cSrcweir } 492cdf0e10cSrcweir } 493cdf0e10cSrcweir else if(args.mnFlags & SAL_LAYOUT_FOR_FALLBACK) 494cdf0e10cSrcweir { 495cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 496cdf0e10cSrcweir fprintf(grLog(),"fallback c%d %x in run %d\n", firstChar, args.mpStr[firstChar], 497cdf0e10cSrcweir args.maRuns.PosIsInAnyRun(firstChar)); 498cdf0e10cSrcweir #endif 499cdf0e10cSrcweir // glyphs that aren't requested for fallback will be taken from base 500cdf0e10cSrcweir // layout, so mark them as dropped (should this wait until Simplify(false) is called?) 501cdf0e10cSrcweir if (!args.maRuns.PosIsInAnyRun(firstChar) && 502cdf0e10cSrcweir in_range(firstChar, args.mnMinCharPos, args.mnEndCharPos)) 503cdf0e10cSrcweir { 504cdf0e10cSrcweir glyphId = GF_DROPPED; 505cdf0e10cSrcweir deltaOffset -= glyphWidth; 506cdf0e10cSrcweir glyphWidth = 0; 507cdf0e10cSrcweir } 508cdf0e10cSrcweir } 509cdf0e10cSrcweir // append this glyph. 510cdf0e10cSrcweir long nGlyphFlags = bIsBase ? 0 : GlyphItem::IS_IN_CLUSTER; 511cdf0e10cSrcweir // directionality seems to be unreliable 512cdf0e10cSrcweir //nGlyphFlags |= gr::RightToLeftDir(gr::DirCode(gi.attachedClusterBase()->directionality())) ? GlyphItem::IS_RTL_GLYPH : 0; 513cdf0e10cSrcweir nGlyphFlags |= (gi.directionLevel() & 0x1)? GlyphItem::IS_RTL_GLYPH : 0; 514cdf0e10cSrcweir GlyphItem aGlyphItem(size(),//gi.logicalIndex(), 515cdf0e10cSrcweir glyphId, 516cdf0e10cSrcweir Point(round(gi.origin() * scaling + rDXOffset), 517cdf0e10cSrcweir round((-gi.yOffset() * scaling) - segment.AscentOffset()* scaling)), 518cdf0e10cSrcweir nGlyphFlags, 519cdf0e10cSrcweir glyphWidth); 520cdf0e10cSrcweir aGlyphItem.mnOrigWidth = round(gi.advanceWidth() * scaling); 521cdf0e10cSrcweir push_back(aGlyphItem); 522cdf0e10cSrcweir 523cdf0e10cSrcweir // update the offset if this glyph was dropped 524cdf0e10cSrcweir rDXOffset += deltaOffset; 525cdf0e10cSrcweir 526cdf0e10cSrcweir // Recursively apply append all the attached glyphs. 527cdf0e10cSrcweir for (gr::GlyphSetIterator agi = iAttached.first; agi != iAttached.second; ++agi) 528cdf0e10cSrcweir { 529cdf0e10cSrcweir if (agi + 1 == iAttached.second) 530cdf0e10cSrcweir append(segment, args, *agi, nextGlyphOrigin, scaling, rChar2Base, rGlyph2Char,rCharDxs, rDXOffset, false); 531cdf0e10cSrcweir else 532cdf0e10cSrcweir append(segment, args, *agi, (agi + 1)->origin(), scaling, rChar2Base, rGlyph2Char, rCharDxs, rDXOffset, false); 533cdf0e10cSrcweir } 534cdf0e10cSrcweir } 535cdf0e10cSrcweir 536cdf0e10cSrcweir // 537cdf0e10cSrcweir // An implementation of the SalLayout interface to enable Graphite enabled fonts to be used. 538cdf0e10cSrcweir // 539cdf0e10cSrcweir GraphiteLayout::GraphiteLayout(const gr::Font & font, const grutils::GrFeatureParser * pFeatures) throw() 540cdf0e10cSrcweir : mpTextSrc(0), 541cdf0e10cSrcweir mrFont(font), 542cdf0e10cSrcweir mnWidth(0), 543cdf0e10cSrcweir mfScaling(1.0), 544cdf0e10cSrcweir mpFeatures(pFeatures) 545cdf0e10cSrcweir { 546cdf0e10cSrcweir // Line settings can have subtle affects on space handling 547cdf0e10cSrcweir // since we don't really know whether it is the end of a line or just a run 548cdf0e10cSrcweir // in the middle, it is hard to know what to set them to. 549cdf0e10cSrcweir // If true, it can cause end of line spaces to be hidden e.g. Doulos SIL 550cdf0e10cSrcweir maLayout.setStartOfLine(false); 551cdf0e10cSrcweir maLayout.setEndOfLine(false); 552cdf0e10cSrcweir maLayout.setDumbFallback(true); 553cdf0e10cSrcweir // trailing ws doesn't seem to always take affect if end of line is true 554cdf0e10cSrcweir maLayout.setTrailingWs(gr::ktwshAll); 555cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 556cdf0e10cSrcweir gr::ScriptDirCode aDirCode = font.getSupportedScriptDirections(); 557cdf0e10cSrcweir fprintf(grLog(),"GraphiteLayout scripts %x %lx\n", aDirCode, long(this)); 558cdf0e10cSrcweir #endif 559cdf0e10cSrcweir } 560cdf0e10cSrcweir 561cdf0e10cSrcweir 562cdf0e10cSrcweir GraphiteLayout::~GraphiteLayout() throw() 563cdf0e10cSrcweir { 564cdf0e10cSrcweir clear(); 565cdf0e10cSrcweir // the features are owned by the platform layers 566cdf0e10cSrcweir mpFeatures = NULL; 567cdf0e10cSrcweir } 568cdf0e10cSrcweir 569cdf0e10cSrcweir void GraphiteLayout::clear() 570cdf0e10cSrcweir { 571cdf0e10cSrcweir // Destroy the segment and text source from any previous invocation of 572cdf0e10cSrcweir // LayoutText 573cdf0e10cSrcweir mvGlyphs.clear(); 574cdf0e10cSrcweir mvCharDxs.clear(); 575cdf0e10cSrcweir mvChar2BaseGlyph.clear(); 576cdf0e10cSrcweir mvGlyph2Char.clear(); 577cdf0e10cSrcweir 578cdf0e10cSrcweir #ifndef GRCACHE 579cdf0e10cSrcweir delete mpTextSrc; 580cdf0e10cSrcweir #endif 581cdf0e10cSrcweir 582cdf0e10cSrcweir // Reset the state to the empty state. 583cdf0e10cSrcweir mpTextSrc=0; 584cdf0e10cSrcweir mnWidth = 0; 585cdf0e10cSrcweir // Don't reset the scaling, because it is set before LayoutText 586cdf0e10cSrcweir } 587cdf0e10cSrcweir 588cdf0e10cSrcweir // This method shouldn't be called on windows, since it needs the dc reset 589cdf0e10cSrcweir bool GraphiteLayout::LayoutText(ImplLayoutArgs & rArgs) 590cdf0e10cSrcweir { 591cdf0e10cSrcweir #ifdef GRCACHE 592cdf0e10cSrcweir GrSegRecord * pSegRecord = NULL; 593cdf0e10cSrcweir gr::Segment * pSegment = NULL; 594cdf0e10cSrcweir // Graphite can in rare cases crash with a zero length 595cdf0e10cSrcweir if (rArgs.mnMinCharPos < rArgs.mnEndCharPos) 596cdf0e10cSrcweir { 597cdf0e10cSrcweir pSegment = CreateSegment(rArgs, &pSegRecord); 598cdf0e10cSrcweir if (!pSegment) 599cdf0e10cSrcweir return false; 600cdf0e10cSrcweir } 601cdf0e10cSrcweir else 602cdf0e10cSrcweir { 603cdf0e10cSrcweir clear(); 604cdf0e10cSrcweir return true; 605cdf0e10cSrcweir } 606cdf0e10cSrcweir // layout the glyphs as required by OpenOffice 607cdf0e10cSrcweir bool success = LayoutGlyphs(rArgs, pSegment, pSegRecord); 608cdf0e10cSrcweir 609cdf0e10cSrcweir if (pSegRecord) pSegRecord->unlock(); 610cdf0e10cSrcweir else delete pSegment; 611cdf0e10cSrcweir #else 612cdf0e10cSrcweir gr::Segment * pSegment = NULL; 613cdf0e10cSrcweir bool success = true; 614cdf0e10cSrcweir if (rArgs.mnMinCharPos < rArgs.mnEndCharPos) 615cdf0e10cSrcweir { 616cdf0e10cSrcweir pSegment = CreateSegment(rArgs); 617cdf0e10cSrcweir if (!pSegment) 618cdf0e10cSrcweir return false; 619cdf0e10cSrcweir success = LayoutGlyphs(rArgs, pSegment); 620cdf0e10cSrcweir if (pSegment) delete pSegment; 621cdf0e10cSrcweir } 622cdf0e10cSrcweir else 623cdf0e10cSrcweir { 624cdf0e10cSrcweir clear(); 625cdf0e10cSrcweir } 626cdf0e10cSrcweir #endif 627cdf0e10cSrcweir return success; 628cdf0e10cSrcweir } 629cdf0e10cSrcweir 630cdf0e10cSrcweir #ifdef GRCACHE 631cdf0e10cSrcweir class GrFontHasher : public gr::Font 632cdf0e10cSrcweir { 633cdf0e10cSrcweir public: 634cdf0e10cSrcweir GrFontHasher(const gr::Font & aFont) : gr::Font(aFont), mrRealFont(const_cast<gr::Font&>(aFont)) {}; 635cdf0e10cSrcweir ~GrFontHasher(){}; 636cdf0e10cSrcweir virtual bool bold() { return mrRealFont.bold(); }; 637cdf0e10cSrcweir virtual bool italic() { return mrRealFont.italic(); }; 638cdf0e10cSrcweir virtual float ascent() { return mrRealFont.ascent(); }; 639cdf0e10cSrcweir virtual float descent() { return mrRealFont.descent(); }; 640cdf0e10cSrcweir virtual float height() { return mrRealFont.height(); }; 641cdf0e10cSrcweir virtual gr::Font* copyThis() { return mrRealFont.copyThis(); }; 642cdf0e10cSrcweir virtual unsigned int getDPIx() { return mrRealFont.getDPIx(); }; 643cdf0e10cSrcweir virtual unsigned int getDPIy() { return mrRealFont.getDPIy(); }; 644cdf0e10cSrcweir virtual const void* getTable(gr::fontTableId32 nId, size_t* nSize) 645cdf0e10cSrcweir { return mrRealFont.getTable(nId,nSize); } 646cdf0e10cSrcweir virtual void getFontMetrics(float*pA, float*pB, float*pC) { mrRealFont.getFontMetrics(pA,pB,pC); }; 647cdf0e10cSrcweir 648cdf0e10cSrcweir sal_Int32 hashCode(const grutils::GrFeatureParser * mpFeatures) 649cdf0e10cSrcweir { 650cdf0e10cSrcweir // is this sufficient? 651cdf0e10cSrcweir ext_std::wstring aFace; 652cdf0e10cSrcweir bool bBold; 653cdf0e10cSrcweir bool bItalic; 654cdf0e10cSrcweir UniqueCacheInfo(aFace, bBold, bItalic); 655cdf0e10cSrcweir sal_Unicode uName[32]; // max length used in gr::Font 656cdf0e10cSrcweir // Note: graphite stores font names as UTF-16 even if wchar_t is 32bit 657cdf0e10cSrcweir // this conversion should be OK. 658cdf0e10cSrcweir for (size_t i = 0; i < aFace.size() && i < 32; i++) 659cdf0e10cSrcweir { 660cdf0e10cSrcweir uName[i] = aFace[i]; 661cdf0e10cSrcweir } 662cdf0e10cSrcweir size_t iSize = aFace.size(); 663cdf0e10cSrcweir if (0 == iSize) return 0; 664cdf0e10cSrcweir sal_Int32 hash = rtl_ustr_hashCode_WithLength(uName, iSize); 665cdf0e10cSrcweir hash ^= static_cast<sal_Int32>(height()); 666cdf0e10cSrcweir hash |= (bBold)? 0x1000000 : 0; 667cdf0e10cSrcweir hash |= (bItalic)? 0x2000000 : 0; 668cdf0e10cSrcweir if (mpFeatures) 669cdf0e10cSrcweir hash ^= mpFeatures->hashCode(); 670cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 671cdf0e10cSrcweir fprintf(grLog(), "font hash %x size %f\n", (int)hash, height()); 672cdf0e10cSrcweir #endif 673cdf0e10cSrcweir return hash; 674cdf0e10cSrcweir }; 675cdf0e10cSrcweir protected: 676cdf0e10cSrcweir virtual void UniqueCacheInfo( ext_std::wstring& stuFace, bool& fBold, bool& fItalic ) 677cdf0e10cSrcweir { 678cdf0e10cSrcweir #ifdef WIN32 679cdf0e10cSrcweir dynamic_cast<GraphiteWinFont&>(mrRealFont).UniqueCacheInfo(stuFace, fBold, fItalic); 680cdf0e10cSrcweir #else 681cdf0e10cSrcweir #ifdef UNX 682cdf0e10cSrcweir dynamic_cast<GraphiteFontAdaptor&>(mrRealFont).UniqueCacheInfo(stuFace, fBold, fItalic); 683cdf0e10cSrcweir #else 684cdf0e10cSrcweir #error Unknown base type for gr::Font::UniqueCacheInfo 685cdf0e10cSrcweir #endif 686cdf0e10cSrcweir #endif 687cdf0e10cSrcweir } 688cdf0e10cSrcweir private: 689cdf0e10cSrcweir gr::Font & mrRealFont; 690cdf0e10cSrcweir }; 691cdf0e10cSrcweir #endif 692cdf0e10cSrcweir 693cdf0e10cSrcweir #ifdef GRCACHE 694cdf0e10cSrcweir gr::Segment * GraphiteLayout::CreateSegment(ImplLayoutArgs& rArgs, GrSegRecord ** pSegRecord) 695cdf0e10cSrcweir #else 696cdf0e10cSrcweir gr::Segment * GraphiteLayout::CreateSegment(ImplLayoutArgs& rArgs) 697cdf0e10cSrcweir #endif 698cdf0e10cSrcweir { 699cdf0e10cSrcweir assert(rArgs.mnLength >= 0); 700cdf0e10cSrcweir 701cdf0e10cSrcweir gr::Segment * pSegment = NULL; 702cdf0e10cSrcweir 703cdf0e10cSrcweir // Set the SalLayouts values to be the inital ones. 704cdf0e10cSrcweir SalLayout::AdjustLayout(rArgs); 705cdf0e10cSrcweir // TODO check if this is needed 706cdf0e10cSrcweir if (mnUnitsPerPixel > 1) 707cdf0e10cSrcweir mfScaling = 1.0f / mnUnitsPerPixel; 708cdf0e10cSrcweir 709cdf0e10cSrcweir // Clear out any previous buffers 710cdf0e10cSrcweir clear(); 711cdf0e10cSrcweir bool bRtl = mnLayoutFlags & SAL_LAYOUT_BIDI_RTL; 712cdf0e10cSrcweir try 713cdf0e10cSrcweir { 714cdf0e10cSrcweir // Don't set RTL if font doesn't support it otherwise it forces rtl on 715cdf0e10cSrcweir // everything 716cdf0e10cSrcweir if (bRtl && (mrFont.getSupportedScriptDirections() & gr::kfsdcHorizRtl)) 717cdf0e10cSrcweir maLayout.setRightToLeft(bRtl); 718cdf0e10cSrcweir 719cdf0e10cSrcweir // Context is often needed beyond the specified end, however, we don't 720cdf0e10cSrcweir // want it if there has been a direction change, since it is hard 721cdf0e10cSrcweir // to tell between reordering within one direction and multi-directional 722cdf0e10cSrcweir // text. Extra context, can also cause problems with ligatures stradling 723cdf0e10cSrcweir // a hyphenation point, so disable if CTL is disabled. 724cdf0e10cSrcweir const int nSegCharLimit = min(rArgs.mnLength, mnEndCharPos + EXTRA_CONTEXT_LENGTH); 725cdf0e10cSrcweir int limit = rArgs.mnEndCharPos; 726cdf0e10cSrcweir if ((nSegCharLimit > limit) && !(SAL_LAYOUT_COMPLEX_DISABLED & rArgs.mnFlags)) 727cdf0e10cSrcweir { 728cdf0e10cSrcweir limit += findSameDirLimit(rArgs.mpStr + rArgs.mnEndCharPos, 729cdf0e10cSrcweir nSegCharLimit - rArgs.mnEndCharPos, bRtl); 730cdf0e10cSrcweir } 731cdf0e10cSrcweir 732cdf0e10cSrcweir #ifdef GRCACHE 733cdf0e10cSrcweir GrFontHasher hasher(mrFont); 734cdf0e10cSrcweir sal_Int32 aFontHash = hasher.hashCode(mpFeatures); 735cdf0e10cSrcweir GraphiteSegmentCache * pCache = 736cdf0e10cSrcweir (GraphiteCacheHandler::instance).getCache(aFontHash); 737cdf0e10cSrcweir if (pCache) 738cdf0e10cSrcweir { 739cdf0e10cSrcweir *pSegRecord = pCache->getSegment(rArgs, bRtl, limit); 740cdf0e10cSrcweir if (*pSegRecord) 741cdf0e10cSrcweir { 742cdf0e10cSrcweir pSegment = (*pSegRecord)->getSegment(); 743cdf0e10cSrcweir mpTextSrc = (*pSegRecord)->getTextSrc(); 744cdf0e10cSrcweir maLayout.setRightToLeft((*pSegRecord)->isRtl()); 745cdf0e10cSrcweir if (rArgs.mpStr != mpTextSrc->getLayoutArgs().mpStr || 746cdf0e10cSrcweir rArgs.mnMinCharPos != mpTextSrc->getLayoutArgs().mnMinCharPos || 747cdf0e10cSrcweir rArgs.mnEndCharPos != mpTextSrc->getLayoutArgs().mnEndCharPos || 748cdf0e10cSrcweir (SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags) ) 749cdf0e10cSrcweir { 750cdf0e10cSrcweir (*pSegRecord)->clearVectors(); 751cdf0e10cSrcweir } 752cdf0e10cSrcweir mpTextSrc->switchLayoutArgs(rArgs); 753cdf0e10cSrcweir if (limit > rArgs.mnMinCharPos && limit == rArgs.mnEndCharPos 754cdf0e10cSrcweir && pSegment->stopCharacter() != limit) 755cdf0e10cSrcweir { 756cdf0e10cSrcweir // check that the last character is not part of a ligature 757cdf0e10cSrcweir glyph_set_range_t aGlyphSet = pSegment->charToGlyphs(limit - 1); 758cdf0e10cSrcweir if (aGlyphSet.first == aGlyphSet.second) 759cdf0e10cSrcweir { 760cdf0e10cSrcweir // no glyphs associated with this glyph - occurs mid ligature 761cdf0e10cSrcweir pSegment = NULL; 762cdf0e10cSrcweir *pSegRecord = NULL; 763cdf0e10cSrcweir } 764cdf0e10cSrcweir else 765cdf0e10cSrcweir { 766cdf0e10cSrcweir while (aGlyphSet.first != aGlyphSet.second) 767cdf0e10cSrcweir { 768cdf0e10cSrcweir int lastChar = static_cast<int>((*aGlyphSet.first).lastChar()); 769cdf0e10cSrcweir if (lastChar >= limit) 770cdf0e10cSrcweir { 771cdf0e10cSrcweir pSegment = NULL; 772cdf0e10cSrcweir *pSegRecord = NULL; 773cdf0e10cSrcweir break; 774cdf0e10cSrcweir } 775cdf0e10cSrcweir aGlyphSet.first++; 776cdf0e10cSrcweir } 777cdf0e10cSrcweir } 778cdf0e10cSrcweir } 779cdf0e10cSrcweir if (pSegment) 780cdf0e10cSrcweir return pSegment; 781cdf0e10cSrcweir } 782cdf0e10cSrcweir } 783cdf0e10cSrcweir #endif 784cdf0e10cSrcweir 785cdf0e10cSrcweir // Create a new TextSource object for the engine. 786cdf0e10cSrcweir mpTextSrc = new TextSourceAdaptor(rArgs, limit); 787cdf0e10cSrcweir if (mpFeatures) mpTextSrc->setFeatures(mpFeatures); 788cdf0e10cSrcweir 789cdf0e10cSrcweir pSegment = new gr::RangeSegment((gr::Font *)&mrFont, mpTextSrc, &maLayout, mnMinCharPos, limit); 790cdf0e10cSrcweir if (pSegment != NULL) 791cdf0e10cSrcweir { 792cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 793cdf0e10cSrcweir fprintf(grLog(),"Gr::LayoutText %d-%d, context %d,len%d rtl%d/%d scaling %f\n", rArgs.mnMinCharPos, 794cdf0e10cSrcweir rArgs.mnEndCharPos, limit, rArgs.mnLength, maLayout.rightToLeft(), pSegment->rightToLeft(), mfScaling); 795cdf0e10cSrcweir #endif 796cdf0e10cSrcweir #ifdef GRCACHE 797cdf0e10cSrcweir // on a new segment rightToLeft should be correct 798cdf0e10cSrcweir *pSegRecord = pCache->cacheSegment(mpTextSrc, pSegment, pSegment->rightToLeft()); 799cdf0e10cSrcweir #endif 800cdf0e10cSrcweir } 801cdf0e10cSrcweir else 802cdf0e10cSrcweir { 803cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 804cdf0e10cSrcweir fprintf(grLog(), "Gr::LayoutText failed: "); 805cdf0e10cSrcweir for (int i = mnMinCharPos; i < limit; i++) 806cdf0e10cSrcweir { 807cdf0e10cSrcweir fprintf(grLog(), "%04x ", rArgs.mpStr[i]); 808cdf0e10cSrcweir } 809cdf0e10cSrcweir fprintf(grLog(), "\n"); 810cdf0e10cSrcweir #endif 811cdf0e10cSrcweir clear(); 812cdf0e10cSrcweir return NULL; 813cdf0e10cSrcweir } 814cdf0e10cSrcweir } 815cdf0e10cSrcweir catch (...) 816cdf0e10cSrcweir { 817cdf0e10cSrcweir clear(); // destroy the text source and any partially built segments. 818cdf0e10cSrcweir return NULL; 819cdf0e10cSrcweir } 820cdf0e10cSrcweir return pSegment; 821cdf0e10cSrcweir } 822cdf0e10cSrcweir 823cdf0e10cSrcweir #ifdef GRCACHE 824cdf0e10cSrcweir bool GraphiteLayout::LayoutGlyphs(ImplLayoutArgs& rArgs, gr::Segment * pSegment, GrSegRecord * pSegRecord) 825cdf0e10cSrcweir #else 826cdf0e10cSrcweir bool GraphiteLayout::LayoutGlyphs(ImplLayoutArgs& rArgs, gr::Segment * pSegment) 827cdf0e10cSrcweir #endif 828cdf0e10cSrcweir { 829cdf0e10cSrcweir #ifdef GRCACHE 830cdf0e10cSrcweir #ifdef GRCACHE_REUSE_VECTORS 831cdf0e10cSrcweir // if we have an exact match, then we can reuse the glyph vectors from before 832cdf0e10cSrcweir if (pSegRecord && (pSegRecord->glyphs().size() > 0) && 833cdf0e10cSrcweir (pSegRecord->fontScale() == mfScaling) && 834cdf0e10cSrcweir !(SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags) ) 835cdf0e10cSrcweir { 836cdf0e10cSrcweir mnWidth = pSegRecord->width(); 837cdf0e10cSrcweir mvGlyphs = pSegRecord->glyphs(); 838cdf0e10cSrcweir mvCharDxs = pSegRecord->charDxs(); 839cdf0e10cSrcweir mvChar2BaseGlyph = pSegRecord->char2BaseGlyph(); 840cdf0e10cSrcweir mvGlyph2Char = pSegRecord->glyph2Char(); 841cdf0e10cSrcweir return true; 842cdf0e10cSrcweir } 843cdf0e10cSrcweir #endif 844cdf0e10cSrcweir #endif 845cdf0e10cSrcweir // Calculate the initial character dxs. 846cdf0e10cSrcweir mvCharDxs.assign(mnEndCharPos - mnMinCharPos, -1); 847cdf0e10cSrcweir mvChar2BaseGlyph.assign(mnEndCharPos - mnMinCharPos, -1); 848cdf0e10cSrcweir mnWidth = 0; 849cdf0e10cSrcweir if (mvCharDxs.size() > 0) 850cdf0e10cSrcweir { 851cdf0e10cSrcweir // Discover all the clusters. 852cdf0e10cSrcweir try 853cdf0e10cSrcweir { 854cdf0e10cSrcweir // Note: we use the layout rightToLeft() because in cached segments 855cdf0e10cSrcweir // rightToLeft() may no longer be valid if the engine has been run 856cdf0e10cSrcweir // ltr since the segment was created. 857cdf0e10cSrcweir #ifdef GRCACHE 858cdf0e10cSrcweir bool bRtl = pSegRecord? pSegRecord->isRtl() : pSegment->rightToLeft(); 859cdf0e10cSrcweir #else 860cdf0e10cSrcweir bool bRtl = pSegment->rightToLeft(); 861cdf0e10cSrcweir #endif 862cdf0e10cSrcweir mvGlyphs.fill_from(*pSegment, rArgs, bRtl, 863cdf0e10cSrcweir mnWidth, mfScaling, mvChar2BaseGlyph, mvGlyph2Char, mvCharDxs); 864cdf0e10cSrcweir 865cdf0e10cSrcweir if (bRtl) 866cdf0e10cSrcweir { 867cdf0e10cSrcweir // not needed for adjacent differences, but for mouse clicks to char 868cdf0e10cSrcweir std::transform(mvCharDxs.begin(), mvCharDxs.end(), mvCharDxs.begin(), 869cdf0e10cSrcweir std::bind1st(std::minus<long>(), mnWidth)); 870cdf0e10cSrcweir // fixup last dx to ensure it always equals the width 871cdf0e10cSrcweir mvCharDxs[mvCharDxs.size() - 1] = mnWidth; 872cdf0e10cSrcweir } 873cdf0e10cSrcweir #ifdef GRCACHE 874cdf0e10cSrcweir #ifdef GRCACHE_REUSE_VECTORS 875cdf0e10cSrcweir if (pSegRecord && rArgs.maReruns.IsEmpty() && 876cdf0e10cSrcweir !(SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags)) 877cdf0e10cSrcweir { 878cdf0e10cSrcweir pSegRecord->setGlyphVectors(mnWidth, mvGlyphs, mvCharDxs, 879cdf0e10cSrcweir mvChar2BaseGlyph, mvGlyph2Char, 880cdf0e10cSrcweir mfScaling); 881cdf0e10cSrcweir } 882cdf0e10cSrcweir #endif 883cdf0e10cSrcweir #endif 884cdf0e10cSrcweir } 885adad3ae8SHerbert Dürr catch (std::exception& e) 886cdf0e10cSrcweir { 887cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 888cdf0e10cSrcweir fprintf(grLog(),"LayoutGlyphs failed %s\n", e.what()); 889cdf0e10cSrcweir #endif 890cdf0e10cSrcweir return false; 891cdf0e10cSrcweir } 892cdf0e10cSrcweir catch (...) 893cdf0e10cSrcweir { 894cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 895cdf0e10cSrcweir fprintf(grLog(),"LayoutGlyphs failed with exception"); 896cdf0e10cSrcweir #endif 897cdf0e10cSrcweir return false; 898cdf0e10cSrcweir } 899cdf0e10cSrcweir } 900cdf0e10cSrcweir else 901cdf0e10cSrcweir { 902cdf0e10cSrcweir mnWidth = 0; 903cdf0e10cSrcweir } 904cdf0e10cSrcweir return true; 905cdf0e10cSrcweir } 906cdf0e10cSrcweir 907cdf0e10cSrcweir int GraphiteLayout::GetTextBreak(long maxmnWidth, long char_extra, int factor) const 908cdf0e10cSrcweir { 909cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 910cdf0e10cSrcweir fprintf(grLog(),"Gr::GetTextBreak c[%d-%d) maxWidth %ld char extra %ld factor %d\n", 911cdf0e10cSrcweir mnMinCharPos, mnEndCharPos, maxmnWidth, char_extra, factor); 912cdf0e10cSrcweir #endif 913cdf0e10cSrcweir 914cdf0e10cSrcweir // return quickly if this segment is narrower than the target width 915cdf0e10cSrcweir if (maxmnWidth > mnWidth * factor + char_extra * (mnEndCharPos - mnMinCharPos - 1)) 916cdf0e10cSrcweir return STRING_LEN; 917cdf0e10cSrcweir 918cdf0e10cSrcweir long nWidth = mvCharDxs[0] * factor; 919cdf0e10cSrcweir int nLastBreak = -1; 920cdf0e10cSrcweir for (size_t i = 1; i < mvCharDxs.size(); i++) 921cdf0e10cSrcweir { 922cdf0e10cSrcweir nWidth += char_extra; 923cdf0e10cSrcweir if (nWidth > maxmnWidth) break; 924cdf0e10cSrcweir if (mvChar2BaseGlyph[i] != -1) 925cdf0e10cSrcweir { 926cdf0e10cSrcweir if (mvChar2BaseGlyph[i] & (WORD_BREAK_BEFORE | HYPHEN_BREAK_BEFORE)) 927cdf0e10cSrcweir nLastBreak = static_cast<int>(i); 928cdf0e10cSrcweir } 929cdf0e10cSrcweir nWidth += (mvCharDxs[i] - mvCharDxs[i-1]) * factor; 930cdf0e10cSrcweir } 931cdf0e10cSrcweir int nBreak = mnMinCharPos; 932cdf0e10cSrcweir if (nLastBreak > -1) 933cdf0e10cSrcweir nBreak += nLastBreak; 934cdf0e10cSrcweir 935cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 936cdf0e10cSrcweir fprintf(grLog(), "Gr::GetTextBreak break after %d\n", nBreak - mnMinCharPos); 937cdf0e10cSrcweir #endif 938cdf0e10cSrcweir 939cdf0e10cSrcweir if (nBreak > mnEndCharPos) nBreak = STRING_LEN; 940cdf0e10cSrcweir else if (nBreak < mnMinCharPos) nBreak = mnMinCharPos; 941cdf0e10cSrcweir return nBreak; 942cdf0e10cSrcweir } 943cdf0e10cSrcweir 944cdf0e10cSrcweir 945cdf0e10cSrcweir long GraphiteLayout::FillDXArray( sal_Int32* pDXArray ) const 946cdf0e10cSrcweir { 947cdf0e10cSrcweir if (mnEndCharPos == mnMinCharPos) 948cdf0e10cSrcweir // Then we must be zero width! 949cdf0e10cSrcweir return 0; 950cdf0e10cSrcweir 951cdf0e10cSrcweir if (pDXArray) 952cdf0e10cSrcweir { 953cdf0e10cSrcweir for (size_t i = 0; i < mvCharDxs.size(); i++) 954cdf0e10cSrcweir { 955cdf0e10cSrcweir assert( (mvChar2BaseGlyph[i] == -1) || 956cdf0e10cSrcweir ((signed)(mvChar2BaseGlyph[i] & GLYPH_INDEX_MASK) < (signed)mvGlyphs.size())); 957cdf0e10cSrcweir if (mvChar2BaseGlyph[i] != -1 && 958cdf0e10cSrcweir mvGlyphs[mvChar2BaseGlyph[i] & GLYPH_INDEX_MASK].mnGlyphIndex == GF_DROPPED) 959cdf0e10cSrcweir { 960cdf0e10cSrcweir // when used in MultiSalLayout::GetTextBreak dropped glyphs 961cdf0e10cSrcweir // must have zero width 962cdf0e10cSrcweir pDXArray[i] = 0; 963cdf0e10cSrcweir } 964cdf0e10cSrcweir else 965cdf0e10cSrcweir { 966cdf0e10cSrcweir pDXArray[i] = mvCharDxs[i]; 967cdf0e10cSrcweir if (i > 0) pDXArray[i] -= mvCharDxs[i-1]; 968cdf0e10cSrcweir } 969cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 970cdf0e10cSrcweir fprintf(grLog(),"%d,%d,%d ", (int)i, (int)mvCharDxs[i], pDXArray[i]); 971cdf0e10cSrcweir #endif 972cdf0e10cSrcweir } 973cdf0e10cSrcweir //std::adjacent_difference(mvCharDxs.begin(), mvCharDxs.end(), pDXArray); 974cdf0e10cSrcweir //for (size_t i = 0; i < mvCharDxs.size(); i++) 975cdf0e10cSrcweir // fprintf(grLog(),"%d,%d,%d ", (int)i, (int)mvCharDxs[i], pDXArray[i]); 976cdf0e10cSrcweir //fprintf(grLog(),"FillDX %ld,%d\n", mnWidth, std::accumulate(pDXArray, pDXArray + mvCharDxs.size(), 0)); 977cdf0e10cSrcweir } 978cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 979cdf0e10cSrcweir fprintf(grLog(),"FillDXArray %d-%d,%d=%ld\n", mnMinCharPos, mnEndCharPos, (int)mpTextSrc->getLength(), mnWidth); 980cdf0e10cSrcweir #endif 981cdf0e10cSrcweir return mnWidth; 982cdf0e10cSrcweir } 983cdf0e10cSrcweir 984cdf0e10cSrcweir 985cdf0e10cSrcweir void GraphiteLayout::AdjustLayout(ImplLayoutArgs& rArgs) 986cdf0e10cSrcweir { 987cdf0e10cSrcweir SalLayout::AdjustLayout(rArgs); 988cdf0e10cSrcweir if(rArgs.mpDXArray) 989cdf0e10cSrcweir { 990cdf0e10cSrcweir std::vector<int> vDeltaWidths(mvGlyphs.size(), 0); 991cdf0e10cSrcweir ApplyDXArray(rArgs, vDeltaWidths); 992cdf0e10cSrcweir 993cdf0e10cSrcweir if( (mnLayoutFlags & SAL_LAYOUT_BIDI_RTL) && 994cdf0e10cSrcweir !(rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK) ) 995cdf0e10cSrcweir { 996cdf0e10cSrcweir // check if this is a kashida script 997cdf0e10cSrcweir bool bKashidaScript = false; 998cdf0e10cSrcweir for (int i = rArgs.mnMinCharPos; i < rArgs.mnEndCharPos; i++) 999cdf0e10cSrcweir { 1000cdf0e10cSrcweir UErrorCode aStatus = U_ZERO_ERROR; 1001cdf0e10cSrcweir UScriptCode scriptCode = uscript_getScript(rArgs.mpStr[i], &aStatus); 1002cdf0e10cSrcweir if (scriptCode == USCRIPT_ARABIC || scriptCode == USCRIPT_SYRIAC) 1003cdf0e10cSrcweir { 1004cdf0e10cSrcweir bKashidaScript = true; 1005cdf0e10cSrcweir break; 1006cdf0e10cSrcweir } 1007cdf0e10cSrcweir } 1008cdf0e10cSrcweir int nKashidaWidth = 0; 1009cdf0e10cSrcweir int nKashidaIndex = getKashidaGlyph(nKashidaWidth); 1010cdf0e10cSrcweir if( nKashidaIndex != 0 && bKashidaScript) 1011cdf0e10cSrcweir { 1012cdf0e10cSrcweir kashidaJustify( vDeltaWidths, nKashidaIndex, nKashidaWidth ); 1013cdf0e10cSrcweir } 1014cdf0e10cSrcweir } 1015cdf0e10cSrcweir } 1016cdf0e10cSrcweir else if (rArgs.mnLayoutWidth > 0) 1017cdf0e10cSrcweir { 1018cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 1019cdf0e10cSrcweir fprintf(grLog(), "AdjustLayout width %ld=>%ld\n", mnWidth, rArgs.mnLayoutWidth); 1020cdf0e10cSrcweir #endif 1021cdf0e10cSrcweir expandOrCondense(rArgs); 1022cdf0e10cSrcweir } 1023cdf0e10cSrcweir } 1024cdf0e10cSrcweir 1025cdf0e10cSrcweir void GraphiteLayout::expandOrCondense(ImplLayoutArgs &rArgs) 1026cdf0e10cSrcweir { 1027cdf0e10cSrcweir int nDeltaWidth = rArgs.mnLayoutWidth - mnWidth; 1028cdf0e10cSrcweir if (nDeltaWidth > 0) // expand, just expand between clusters 1029cdf0e10cSrcweir { 1030cdf0e10cSrcweir int nClusterCount = 0; 1031cdf0e10cSrcweir for (size_t j = 0; j < mvGlyphs.size(); j++) 1032cdf0e10cSrcweir { 1033cdf0e10cSrcweir if (mvGlyphs[j].IsClusterStart()) 1034cdf0e10cSrcweir { 1035cdf0e10cSrcweir ++nClusterCount; 1036cdf0e10cSrcweir } 1037cdf0e10cSrcweir } 1038cdf0e10cSrcweir if (nClusterCount > 1) 1039cdf0e10cSrcweir { 1040cdf0e10cSrcweir float fExtraPerCluster = static_cast<float>(nDeltaWidth) / static_cast<float>(nClusterCount - 1); 1041cdf0e10cSrcweir int nCluster = 0; 1042cdf0e10cSrcweir int nOffset = 0; 1043cdf0e10cSrcweir for (size_t i = 0; i < mvGlyphs.size(); i++) 1044cdf0e10cSrcweir { 1045cdf0e10cSrcweir if (mvGlyphs[i].IsClusterStart()) 1046cdf0e10cSrcweir { 1047cdf0e10cSrcweir nOffset = FRound( fExtraPerCluster * nCluster ); 1048cdf0e10cSrcweir size_t nCharIndex = mvGlyph2Char[i]; 1049cdf0e10cSrcweir mvCharDxs[nCharIndex] += nOffset; 1050cdf0e10cSrcweir // adjust char dxs for rest of characters in cluster 1051cdf0e10cSrcweir while (++nCharIndex < mvGlyph2Char.size()) 1052cdf0e10cSrcweir { 1053cdf0e10cSrcweir int nChar2Base = (mvChar2BaseGlyph[nCharIndex] == -1)? -1 : (int)(mvChar2BaseGlyph[nCharIndex] & GLYPH_INDEX_MASK); 1054cdf0e10cSrcweir if (nChar2Base == -1 || nChar2Base == static_cast<int>(i)) 1055cdf0e10cSrcweir mvCharDxs[nCharIndex] += nOffset; 1056cdf0e10cSrcweir } 1057cdf0e10cSrcweir ++nCluster; 1058cdf0e10cSrcweir } 1059cdf0e10cSrcweir mvGlyphs[i].maLinearPos.X() += nOffset; 1060cdf0e10cSrcweir } 1061cdf0e10cSrcweir } 1062cdf0e10cSrcweir } 1063cdf0e10cSrcweir else // condense - apply a factor to all glyph positions 1064cdf0e10cSrcweir { 1065cdf0e10cSrcweir if (mvGlyphs.size() == 0) return; 1066cdf0e10cSrcweir Glyphs::iterator iLastGlyph = mvGlyphs.begin() + (mvGlyphs.size() - 1); 1067cdf0e10cSrcweir // position last glyph using original width 1068cdf0e10cSrcweir float fXFactor = static_cast<float>(rArgs.mnLayoutWidth - iLastGlyph->mnOrigWidth) / static_cast<float>(iLastGlyph->maLinearPos.X()); 1069cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 1070cdf0e10cSrcweir fprintf(grLog(), "Condense by factor %f\n", fXFactor); 1071cdf0e10cSrcweir #endif 1072cdf0e10cSrcweir iLastGlyph->maLinearPos.X() = rArgs.mnLayoutWidth - iLastGlyph->mnOrigWidth; 1073cdf0e10cSrcweir Glyphs::iterator iGlyph = mvGlyphs.begin(); 1074cdf0e10cSrcweir while (iGlyph != iLastGlyph) 1075cdf0e10cSrcweir { 1076cdf0e10cSrcweir iGlyph->maLinearPos.X() = FRound( fXFactor * iGlyph->maLinearPos.X() ); 1077cdf0e10cSrcweir ++iGlyph; 1078cdf0e10cSrcweir } 1079cdf0e10cSrcweir for (size_t i = 0; i < mvCharDxs.size(); i++) 1080cdf0e10cSrcweir { 1081cdf0e10cSrcweir mvCharDxs[i] = FRound( fXFactor * mvCharDxs[i] ); 1082cdf0e10cSrcweir } 1083cdf0e10cSrcweir } 1084cdf0e10cSrcweir mnWidth = rArgs.mnLayoutWidth; 1085cdf0e10cSrcweir } 1086cdf0e10cSrcweir 1087cdf0e10cSrcweir void GraphiteLayout::ApplyDXArray(ImplLayoutArgs &args, std::vector<int> & rDeltaWidth) 1088cdf0e10cSrcweir { 1089cdf0e10cSrcweir const size_t nChars = args.mnEndCharPos - args.mnMinCharPos; 1090cdf0e10cSrcweir if (nChars == 0) return; 1091cdf0e10cSrcweir 1092cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 1093cdf0e10cSrcweir for (size_t iDx = 0; iDx < mvCharDxs.size(); iDx++) 1094cdf0e10cSrcweir fprintf(grLog(),"%d,%d,%d ", (int)iDx, (int)mvCharDxs[iDx], args.mpDXArray[iDx]); 1095cdf0e10cSrcweir fprintf(grLog(),"ApplyDx\n"); 1096cdf0e10cSrcweir #endif 1097cdf0e10cSrcweir bool bRtl = mnLayoutFlags & SAL_LAYOUT_BIDI_RTL; 1098cdf0e10cSrcweir int nXOffset = 0; 1099cdf0e10cSrcweir if (bRtl) 1100cdf0e10cSrcweir { 1101cdf0e10cSrcweir nXOffset = args.mpDXArray[nChars - 1] - mvCharDxs[nChars - 1]; 1102cdf0e10cSrcweir } 1103cdf0e10cSrcweir int nPrevClusterGlyph = (bRtl)? (signed)mvGlyphs.size() : -1; 1104cdf0e10cSrcweir int nPrevClusterLastChar = -1; 1105cdf0e10cSrcweir for (size_t i = 0; i < nChars; i++) 1106cdf0e10cSrcweir { 1107cdf0e10cSrcweir int nChar2Base = (mvChar2BaseGlyph[i] == -1)? -1 : (int)(mvChar2BaseGlyph[i] & GLYPH_INDEX_MASK); 1108cdf0e10cSrcweir if ((nChar2Base > -1) && (nChar2Base != nPrevClusterGlyph)) 1109cdf0e10cSrcweir { 1110cdf0e10cSrcweir assert((nChar2Base > -1) && (nChar2Base < (signed)mvGlyphs.size())); 1111cdf0e10cSrcweir GlyphItem & gi = mvGlyphs[nChar2Base]; 1112cdf0e10cSrcweir if (!gi.IsClusterStart()) 1113cdf0e10cSrcweir continue; 1114cdf0e10cSrcweir 1115cdf0e10cSrcweir // find last glyph of this cluster 1116cdf0e10cSrcweir size_t j = i + 1; 1117cdf0e10cSrcweir int nLastChar = i; 1118cdf0e10cSrcweir int nLastGlyph = nChar2Base; 1119cdf0e10cSrcweir for (; j < nChars; j++) 1120cdf0e10cSrcweir { 1121cdf0e10cSrcweir int nChar2BaseJ = (mvChar2BaseGlyph[j] == -1)? -1 : (int)(mvChar2BaseGlyph[j] & GLYPH_INDEX_MASK); 1122cdf0e10cSrcweir assert((nChar2BaseJ >= -1) && (nChar2BaseJ < (signed)mvGlyphs.size())); 1123cdf0e10cSrcweir if (nChar2BaseJ != -1 && mvGlyphs[nChar2BaseJ].IsClusterStart()) 1124cdf0e10cSrcweir { 1125cdf0e10cSrcweir nLastGlyph = nChar2BaseJ + ((bRtl)? +1 : -1); 1126cdf0e10cSrcweir nLastChar = j - 1; 1127cdf0e10cSrcweir break; 1128cdf0e10cSrcweir } 1129cdf0e10cSrcweir } 1130cdf0e10cSrcweir if (nLastGlyph < 0) 1131cdf0e10cSrcweir { 1132cdf0e10cSrcweir nLastGlyph = nChar2Base; 1133cdf0e10cSrcweir } 1134cdf0e10cSrcweir // Its harder to find the last glyph rtl, since the first of 1135cdf0e10cSrcweir // cluster is still on the left so we need to search towards 1136cdf0e10cSrcweir // the previous cluster to the right 1137cdf0e10cSrcweir if (bRtl) 1138cdf0e10cSrcweir { 1139cdf0e10cSrcweir nLastGlyph = nChar2Base; 1140cdf0e10cSrcweir while (nLastGlyph + 1 < (signed)mvGlyphs.size() && 1141cdf0e10cSrcweir !mvGlyphs[nLastGlyph+1].IsClusterStart()) 1142cdf0e10cSrcweir { 1143cdf0e10cSrcweir ++nLastGlyph; 1144cdf0e10cSrcweir } 1145cdf0e10cSrcweir } 1146cdf0e10cSrcweir if (j == nChars) 1147cdf0e10cSrcweir { 1148cdf0e10cSrcweir nLastChar = nChars - 1; 1149cdf0e10cSrcweir if (!bRtl) nLastGlyph = mvGlyphs.size() - 1; 1150cdf0e10cSrcweir } 1151cdf0e10cSrcweir assert((nLastChar > -1) && (nLastChar < (signed)nChars)); 1152cdf0e10cSrcweir long nNewClusterWidth = args.mpDXArray[nLastChar]; 1153cdf0e10cSrcweir long nOrigClusterWidth = mvCharDxs[nLastChar]; 1154cdf0e10cSrcweir long nDGlyphOrigin = 0; 1155cdf0e10cSrcweir if (nPrevClusterLastChar > - 1) 1156cdf0e10cSrcweir { 1157cdf0e10cSrcweir assert(nPrevClusterLastChar < (signed)nChars); 1158cdf0e10cSrcweir nNewClusterWidth -= args.mpDXArray[nPrevClusterLastChar]; 1159cdf0e10cSrcweir nOrigClusterWidth -= mvCharDxs[nPrevClusterLastChar]; 1160cdf0e10cSrcweir nDGlyphOrigin = args.mpDXArray[nPrevClusterLastChar] - mvCharDxs[nPrevClusterLastChar]; 1161cdf0e10cSrcweir } 1162cdf0e10cSrcweir long nDWidth = nNewClusterWidth - nOrigClusterWidth; 1163cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 1164cdf0e10cSrcweir fprintf(grLog(), "c%lu last glyph %d/%lu\n", i, nLastGlyph, mvGlyphs.size()); 1165cdf0e10cSrcweir #endif 1166cdf0e10cSrcweir assert((nLastGlyph > -1) && (nLastGlyph < (signed)mvGlyphs.size())); 1167cdf0e10cSrcweir mvGlyphs[nLastGlyph].mnNewWidth += nDWidth; 1168cdf0e10cSrcweir if (gi.mnGlyphIndex != GF_DROPPED) 1169cdf0e10cSrcweir mvGlyphs[nLastGlyph].mnNewWidth += nDWidth; 1170cdf0e10cSrcweir else 1171cdf0e10cSrcweir nDGlyphOrigin += nDWidth; 1172cdf0e10cSrcweir // update glyph positions 1173cdf0e10cSrcweir if (bRtl) 1174cdf0e10cSrcweir { 1175cdf0e10cSrcweir for (int n = nChar2Base; n <= nLastGlyph; n++) 1176cdf0e10cSrcweir { 1177cdf0e10cSrcweir assert((n > - 1) && (n < (signed)mvGlyphs.size())); 1178cdf0e10cSrcweir mvGlyphs[n].maLinearPos.X() += -nDGlyphOrigin + nXOffset; 1179cdf0e10cSrcweir } 1180cdf0e10cSrcweir } 1181cdf0e10cSrcweir else 1182cdf0e10cSrcweir { 1183cdf0e10cSrcweir for (int n = nChar2Base; n <= nLastGlyph; n++) 1184cdf0e10cSrcweir { 1185cdf0e10cSrcweir assert((n > - 1) && (n < (signed)mvGlyphs.size())); 1186cdf0e10cSrcweir mvGlyphs[n].maLinearPos.X() += nDGlyphOrigin + nXOffset; 1187cdf0e10cSrcweir } 1188cdf0e10cSrcweir } 1189cdf0e10cSrcweir rDeltaWidth[nChar2Base] = nDWidth; 1190cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 1191cdf0e10cSrcweir fprintf(grLog(),"c%d g%d-%d dW%ld-%ld=%ld dX%ld x%ld\t", (int)i, nChar2Base, nLastGlyph, nNewClusterWidth, nOrigClusterWidth, nDWidth, nDGlyphOrigin, mvGlyphs[nChar2Base].maLinearPos.X()); 1192cdf0e10cSrcweir #endif 1193cdf0e10cSrcweir nPrevClusterGlyph = nChar2Base; 1194cdf0e10cSrcweir nPrevClusterLastChar = nLastChar; 1195cdf0e10cSrcweir i = nLastChar; 1196cdf0e10cSrcweir } 1197cdf0e10cSrcweir } 1198cdf0e10cSrcweir // Update the dx vector with the new values. 1199cdf0e10cSrcweir std::copy(args.mpDXArray, args.mpDXArray + nChars, 1200cdf0e10cSrcweir mvCharDxs.begin() + (args.mnMinCharPos - mnMinCharPos)); 1201cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 1202cdf0e10cSrcweir fprintf(grLog(),"ApplyDx %d(%ld)\n", args.mpDXArray[nChars - 1], mnWidth); 1203cdf0e10cSrcweir #endif 1204cdf0e10cSrcweir mnWidth = args.mpDXArray[nChars - 1]; 1205cdf0e10cSrcweir } 1206cdf0e10cSrcweir 1207cdf0e10cSrcweir void GraphiteLayout::kashidaJustify(std::vector<int>& rDeltaWidths, sal_GlyphId nKashidaIndex, int nKashidaWidth) 1208cdf0e10cSrcweir { 1209cdf0e10cSrcweir // skip if the kashida glyph in the font looks suspicious 1210cdf0e10cSrcweir if( nKashidaWidth <= 0 ) 1211cdf0e10cSrcweir return; 1212cdf0e10cSrcweir 1213cdf0e10cSrcweir // calculate max number of needed kashidas 1214cdf0e10cSrcweir Glyphs::iterator i = mvGlyphs.begin(); 1215cdf0e10cSrcweir int nKashidaCount = 0; 1216cdf0e10cSrcweir int nOrigGlyphIndex = -1; 1217cdf0e10cSrcweir int nGlyphIndex = -1; 1218cdf0e10cSrcweir while (i != mvGlyphs.end()) 1219cdf0e10cSrcweir { 1220cdf0e10cSrcweir nOrigGlyphIndex++; 1221cdf0e10cSrcweir nGlyphIndex++; 1222cdf0e10cSrcweir // only inject kashidas in RTL contexts 1223cdf0e10cSrcweir if( !(*i).IsRTLGlyph() ) 1224cdf0e10cSrcweir { 1225cdf0e10cSrcweir ++i; 1226cdf0e10cSrcweir continue; 1227cdf0e10cSrcweir } 1228cdf0e10cSrcweir // no kashida-injection for blank justified expansion either 1229cdf0e10cSrcweir if( IsSpacingGlyph( (*i).mnGlyphIndex ) ) 1230cdf0e10cSrcweir { 1231cdf0e10cSrcweir ++i; 1232cdf0e10cSrcweir continue; 1233cdf0e10cSrcweir } 1234cdf0e10cSrcweir // calculate gap, ignore if too small 1235cdf0e10cSrcweir int nGapWidth = rDeltaWidths[nOrigGlyphIndex]; 1236cdf0e10cSrcweir // worst case is one kashida even for mini-gaps 1237cdf0e10cSrcweir if( 3 * nGapWidth < nKashidaWidth ) 1238cdf0e10cSrcweir { 1239cdf0e10cSrcweir ++i; 1240cdf0e10cSrcweir continue; 1241cdf0e10cSrcweir } 1242cdf0e10cSrcweir nKashidaCount = 1 + (nGapWidth / nKashidaWidth); 1243cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 1244cdf0e10cSrcweir printf("inserting %d kashidas at %u\n", nKashidaCount, (*i).mnGlyphIndex); 1245cdf0e10cSrcweir #endif 1246cdf0e10cSrcweir GlyphItem glyphItem = *i; 1247cdf0e10cSrcweir Point aPos(0, 0); 1248cdf0e10cSrcweir aPos.X() = (*i).maLinearPos.X(); 1249cdf0e10cSrcweir GlyphItem newGi(glyphItem.mnCharPos, nKashidaIndex, aPos, 1250cdf0e10cSrcweir GlyphItem::IS_IN_CLUSTER|GlyphItem::IS_RTL_GLYPH, nKashidaWidth); 1251cdf0e10cSrcweir mvGlyphs.reserve(mvGlyphs.size() + nKashidaCount); 1252cdf0e10cSrcweir i = mvGlyphs.begin() + nGlyphIndex; 1253cdf0e10cSrcweir mvGlyphs.insert(i, nKashidaCount, newGi); 1254cdf0e10cSrcweir i = mvGlyphs.begin() + nGlyphIndex; 1255cdf0e10cSrcweir nGlyphIndex += nKashidaCount; 1256cdf0e10cSrcweir // now fix up the kashida positions 1257cdf0e10cSrcweir for (int j = 0; j < nKashidaCount; j++) 1258cdf0e10cSrcweir { 1259cdf0e10cSrcweir (*(i)).maLinearPos.X() -= nGapWidth; 1260cdf0e10cSrcweir nGapWidth -= nKashidaWidth; 1261cdf0e10cSrcweir i++; 1262cdf0e10cSrcweir } 1263cdf0e10cSrcweir 1264cdf0e10cSrcweir // fixup rightmost kashida for gap remainder 1265cdf0e10cSrcweir if( nGapWidth < 0 ) 1266cdf0e10cSrcweir { 1267cdf0e10cSrcweir if( nKashidaCount <= 1 ) 1268cdf0e10cSrcweir nGapWidth /= 2; // for small gap move kashida to middle 1269cdf0e10cSrcweir (*(i-1)).mnNewWidth += nGapWidth; // adjust kashida width to gap width 1270cdf0e10cSrcweir (*(i-1)).maLinearPos.X() += nGapWidth; 1271cdf0e10cSrcweir } 1272cdf0e10cSrcweir 1273cdf0e10cSrcweir (*i).mnNewWidth = (*i).mnOrigWidth; 1274cdf0e10cSrcweir ++i; 1275cdf0e10cSrcweir } 1276cdf0e10cSrcweir 1277cdf0e10cSrcweir } 1278cdf0e10cSrcweir 1279cdf0e10cSrcweir void GraphiteLayout::GetCaretPositions( int nArraySize, sal_Int32* pCaretXArray ) const 1280cdf0e10cSrcweir { 1281cdf0e10cSrcweir // For each character except the last discover the caret positions 1282cdf0e10cSrcweir // immediatly before and after that character. 1283cdf0e10cSrcweir // This is used for underlines in the GUI amongst other things. 1284cdf0e10cSrcweir // It may be used from MultiSalLayout, in which case it must take into account 1285cdf0e10cSrcweir // glyphs that have been moved. 1286cdf0e10cSrcweir std::fill(pCaretXArray, pCaretXArray + nArraySize, -1); 1287cdf0e10cSrcweir // the layout method doesn't modify the layout even though it isn't 1288cdf0e10cSrcweir // const in the interface 1289cdf0e10cSrcweir bool bRtl = const_cast<GraphiteLayout*>(this)->maLayout.rightToLeft(); 1290cdf0e10cSrcweir int prevBase = -1; 1291cdf0e10cSrcweir long prevClusterWidth = 0; 1292cdf0e10cSrcweir for (int i = 0, nCharSlot = 0; i < nArraySize && nCharSlot < static_cast<int>(mvCharDxs.size()); ++nCharSlot, i+=2) 1293cdf0e10cSrcweir { 1294cdf0e10cSrcweir if (mvChar2BaseGlyph[nCharSlot] != -1) 1295cdf0e10cSrcweir { 1296cdf0e10cSrcweir int nChar2Base = mvChar2BaseGlyph[nCharSlot] & GLYPH_INDEX_MASK; 1297cdf0e10cSrcweir assert((mvChar2BaseGlyph[nCharSlot] > -1) && (nChar2Base < (signed)mvGlyphs.size())); 1298cdf0e10cSrcweir GlyphItem gi = mvGlyphs[nChar2Base]; 1299cdf0e10cSrcweir if (gi.mnGlyphIndex == GF_DROPPED) 1300cdf0e10cSrcweir { 1301cdf0e10cSrcweir continue; 1302cdf0e10cSrcweir } 1303cdf0e10cSrcweir int nCluster = nChar2Base; 1304cdf0e10cSrcweir long origClusterWidth = gi.mnNewWidth; 1305cdf0e10cSrcweir long nMin = gi.maLinearPos.X(); 1306cdf0e10cSrcweir long nMax = gi.maLinearPos.X() + gi.mnNewWidth; 1307cdf0e10cSrcweir // attached glyphs are always stored after their base rtl or ltr 1308cdf0e10cSrcweir while (++nCluster < static_cast<int>(mvGlyphs.size()) && 1309cdf0e10cSrcweir !mvGlyphs[nCluster].IsClusterStart()) 1310cdf0e10cSrcweir { 1311cdf0e10cSrcweir origClusterWidth += mvGlyphs[nCluster].mnNewWidth; 1312cdf0e10cSrcweir if (mvGlyph2Char[nCluster] == nCharSlot) 1313cdf0e10cSrcweir { 1314cdf0e10cSrcweir nMin = std::min(nMin, mvGlyphs[nCluster].maLinearPos.X()); 1315cdf0e10cSrcweir nMax = std::min(nMax, mvGlyphs[nCluster].maLinearPos.X() + mvGlyphs[nCluster].mnNewWidth); 1316cdf0e10cSrcweir } 1317cdf0e10cSrcweir } 1318cdf0e10cSrcweir if (bRtl) 1319cdf0e10cSrcweir { 1320cdf0e10cSrcweir pCaretXArray[i+1] = nMin; 1321cdf0e10cSrcweir pCaretXArray[i] = nMax; 1322cdf0e10cSrcweir } 1323cdf0e10cSrcweir else 1324cdf0e10cSrcweir { 1325cdf0e10cSrcweir pCaretXArray[i] = nMin; 1326cdf0e10cSrcweir pCaretXArray[i+1] = nMax; 1327cdf0e10cSrcweir } 1328cdf0e10cSrcweir prevBase = nChar2Base; 1329cdf0e10cSrcweir prevClusterWidth = origClusterWidth; 1330cdf0e10cSrcweir } 1331cdf0e10cSrcweir else if (prevBase > -1) 1332cdf0e10cSrcweir { 1333cdf0e10cSrcweir // this could probably be improved 1334cdf0e10cSrcweir assert((prevBase > -1) && (prevBase < (signed)mvGlyphs.size())); 1335cdf0e10cSrcweir GlyphItem gi = mvGlyphs[prevBase]; 1336cdf0e10cSrcweir int nGlyph = prevBase + 1; 1337cdf0e10cSrcweir // try to find a better match, otherwise default to complete cluster 1338cdf0e10cSrcweir for (; nGlyph < static_cast<int>(mvGlyphs.size()) && 1339cdf0e10cSrcweir !mvGlyphs[nGlyph].IsClusterStart(); nGlyph++) 1340cdf0e10cSrcweir { 1341cdf0e10cSrcweir if (mvGlyph2Char[nGlyph] == nCharSlot) 1342cdf0e10cSrcweir { 1343cdf0e10cSrcweir gi = mvGlyphs[nGlyph]; 1344cdf0e10cSrcweir break; 1345cdf0e10cSrcweir } 1346cdf0e10cSrcweir } 1347cdf0e10cSrcweir long nGWidth = gi.mnNewWidth; 1348cdf0e10cSrcweir // if no match position at end of cluster 1349cdf0e10cSrcweir if (nGlyph == static_cast<int>(mvGlyphs.size()) || 1350cdf0e10cSrcweir mvGlyphs[nGlyph].IsClusterStart()) 1351cdf0e10cSrcweir { 1352cdf0e10cSrcweir nGWidth = prevClusterWidth; 1353cdf0e10cSrcweir if (bRtl) 1354cdf0e10cSrcweir { 1355cdf0e10cSrcweir pCaretXArray[i+1] = gi.maLinearPos.X(); 1356cdf0e10cSrcweir pCaretXArray[i] = gi.maLinearPos.X(); 1357cdf0e10cSrcweir } 1358cdf0e10cSrcweir else 1359cdf0e10cSrcweir { 1360cdf0e10cSrcweir pCaretXArray[i] = gi.maLinearPos.X() + prevClusterWidth; 1361cdf0e10cSrcweir pCaretXArray[i+1] = gi.maLinearPos.X() + prevClusterWidth; 1362cdf0e10cSrcweir } 1363cdf0e10cSrcweir } 1364cdf0e10cSrcweir else 1365cdf0e10cSrcweir { 1366cdf0e10cSrcweir if (bRtl) 1367cdf0e10cSrcweir { 1368cdf0e10cSrcweir pCaretXArray[i+1] = gi.maLinearPos.X(); 1369cdf0e10cSrcweir pCaretXArray[i] = gi.maLinearPos.X() + gi.mnNewWidth; 1370cdf0e10cSrcweir } 1371cdf0e10cSrcweir else 1372cdf0e10cSrcweir { 1373cdf0e10cSrcweir pCaretXArray[i] = gi.maLinearPos.X(); 1374cdf0e10cSrcweir pCaretXArray[i+1] = gi.maLinearPos.X() + gi.mnNewWidth; 1375cdf0e10cSrcweir } 1376cdf0e10cSrcweir } 1377cdf0e10cSrcweir } 1378cdf0e10cSrcweir else 1379cdf0e10cSrcweir { 1380cdf0e10cSrcweir pCaretXArray[i] = pCaretXArray[i+1] = 0; 1381cdf0e10cSrcweir } 1382cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 1383cdf0e10cSrcweir fprintf(grLog(),"%d,%d-%d\t", nCharSlot, pCaretXArray[i], pCaretXArray[i+1]); 1384cdf0e10cSrcweir #endif 1385cdf0e10cSrcweir } 1386cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 1387cdf0e10cSrcweir fprintf(grLog(),"\n"); 1388cdf0e10cSrcweir #endif 1389cdf0e10cSrcweir } 1390cdf0e10cSrcweir 1391cdf0e10cSrcweir 1392cdf0e10cSrcweir // GetNextGlyphs returns a contiguous sequence of glyphs that can be 1393cdf0e10cSrcweir // rendered together. It should never return a dropped glyph. 1394cdf0e10cSrcweir // The glyph_slot returned should be the index of the next visible 1395cdf0e10cSrcweir // glyph after the last glyph returned by this call. 1396cdf0e10cSrcweir // The char_index array should be filled with the characters corresponding 1397cdf0e10cSrcweir // to each glyph returned. 1398cdf0e10cSrcweir // glyph_adv array should be a virtual width such that if successive 1399cdf0e10cSrcweir // glyphs returned by this method are added one after the other they 1400cdf0e10cSrcweir // have the correct spacing. 1401cdf0e10cSrcweir // The logic in this method must match that expected in MultiSalLayout which 1402cdf0e10cSrcweir // is used when glyph fallback is in operation. 1403cdf0e10cSrcweir int GraphiteLayout::GetNextGlyphs( int length, sal_GlyphId * glyph_out, 1404cdf0e10cSrcweir ::Point & aPosOut, int &glyph_slot, sal_Int32 * glyph_adv, int *char_index) const 1405cdf0e10cSrcweir { 1406cdf0e10cSrcweir // Sanity check on the slot index. 1407cdf0e10cSrcweir if (glyph_slot >= signed(mvGlyphs.size())) 1408cdf0e10cSrcweir { 1409cdf0e10cSrcweir glyph_slot = mvGlyphs.size(); 1410cdf0e10cSrcweir return 0; 1411cdf0e10cSrcweir } 1412cdf0e10cSrcweir assert(glyph_slot >= 0); 1413cdf0e10cSrcweir // Find the first glyph in the substring. 1414cdf0e10cSrcweir for (; glyph_slot < signed(mvGlyphs.size()) && 1415cdf0e10cSrcweir ((mvGlyphs.begin() + glyph_slot)->mnGlyphIndex == GF_DROPPED); 1416cdf0e10cSrcweir ++glyph_slot) {}; 1417cdf0e10cSrcweir 1418cdf0e10cSrcweir // Update the length 1419cdf0e10cSrcweir const int nGlyphSlotEnd = std::min(size_t(glyph_slot + length), mvGlyphs.size()); 1420cdf0e10cSrcweir 1421cdf0e10cSrcweir // We're all out of glyphs here. 1422cdf0e10cSrcweir if (glyph_slot == nGlyphSlotEnd) 1423cdf0e10cSrcweir { 1424cdf0e10cSrcweir return 0; 1425cdf0e10cSrcweir } 1426cdf0e10cSrcweir 1427cdf0e10cSrcweir // Find as many glyphs as we can which can be drawn in one go. 1428cdf0e10cSrcweir Glyphs::const_iterator glyph_itr = mvGlyphs.begin() + glyph_slot; 1429cdf0e10cSrcweir const int glyph_slot_begin = glyph_slot; 1430cdf0e10cSrcweir const int initial_y_pos = glyph_itr->maLinearPos.Y(); 1431cdf0e10cSrcweir 1432cdf0e10cSrcweir // Set the position to the position of the start glyph. 1433cdf0e10cSrcweir ::Point aStartPos = glyph_itr->maLinearPos; 1434cdf0e10cSrcweir //aPosOut = glyph_itr->maLinearPos; 1435cdf0e10cSrcweir aPosOut = GetDrawPosition(aStartPos); 1436cdf0e10cSrcweir 1437cdf0e10cSrcweir 1438cdf0e10cSrcweir for (;;) // Forever 1439cdf0e10cSrcweir { 1440cdf0e10cSrcweir // last index of the range from glyph_to_chars does not include this glyph 1441cdf0e10cSrcweir if (char_index) 1442cdf0e10cSrcweir { 1443cdf0e10cSrcweir assert((glyph_slot >= -1) && (glyph_slot < (signed)mvGlyph2Char.size())); 1444cdf0e10cSrcweir if (mvGlyph2Char[glyph_slot] == -1) 1445cdf0e10cSrcweir *char_index++ = mvCharDxs.size(); 1446cdf0e10cSrcweir else 1447cdf0e10cSrcweir *char_index++ = mvGlyph2Char[glyph_slot]; 1448cdf0e10cSrcweir } 1449cdf0e10cSrcweir // Copy out this glyphs data. 1450cdf0e10cSrcweir ++glyph_slot; 1451cdf0e10cSrcweir *glyph_out++ = glyph_itr->mnGlyphIndex; 1452cdf0e10cSrcweir 1453cdf0e10cSrcweir // Find the actual advance - this must be correct if called from 1454cdf0e10cSrcweir // MultiSalLayout::AdjustLayout which requests one glyph at a time. 1455cdf0e10cSrcweir const long nGlyphAdvance = (glyph_slot == static_cast<int>(mvGlyphs.size()))? 1456cdf0e10cSrcweir glyph_itr->mnNewWidth : 1457cdf0e10cSrcweir ((glyph_itr+1)->maLinearPos.X() - glyph_itr->maLinearPos.X()); 1458cdf0e10cSrcweir 1459cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 1460cdf0e10cSrcweir fprintf(grLog(),"GetNextGlyphs g%d c%d x%ld,%ld adv%ld, pos %ld,%ld\n", glyph_slot - 1, 1461cdf0e10cSrcweir GLYPH_INDEX_MASK&mvGlyph2Char[glyph_slot-1], glyph_itr->maLinearPos.X(), glyph_itr->maLinearPos.Y(), nGlyphAdvance, 1462cdf0e10cSrcweir aPosOut.X(), aPosOut.Y()); 1463cdf0e10cSrcweir #endif 1464cdf0e10cSrcweir 1465cdf0e10cSrcweir if (glyph_adv) // If we are returning advance store it. 1466cdf0e10cSrcweir *glyph_adv++ = nGlyphAdvance; 1467cdf0e10cSrcweir else // Stop when next advance is unexpected. 1468cdf0e10cSrcweir if (glyph_itr->mnOrigWidth != nGlyphAdvance) break; 1469cdf0e10cSrcweir 1470cdf0e10cSrcweir // Have fetched all the glyphs we need to 1471cdf0e10cSrcweir if (glyph_slot == nGlyphSlotEnd) 1472cdf0e10cSrcweir break; 1473cdf0e10cSrcweir 1474cdf0e10cSrcweir ++glyph_itr; 1475cdf0e10cSrcweir // Stop when next y position is unexpected. 1476cdf0e10cSrcweir if (initial_y_pos != glyph_itr->maLinearPos.Y()) 1477cdf0e10cSrcweir break; 1478cdf0e10cSrcweir 1479cdf0e10cSrcweir // Stop if glyph dropped 1480cdf0e10cSrcweir if (glyph_itr->mnGlyphIndex == GF_DROPPED) 1481cdf0e10cSrcweir break; 1482cdf0e10cSrcweir } 1483cdf0e10cSrcweir int numGlyphs = glyph_slot - glyph_slot_begin; 1484cdf0e10cSrcweir // move the next glyph_slot to a glyph that hasn't been dropped 1485cdf0e10cSrcweir while (glyph_slot < static_cast<int>(mvGlyphs.size()) && 1486cdf0e10cSrcweir (mvGlyphs.begin() + glyph_slot)->mnGlyphIndex == GF_DROPPED) 1487cdf0e10cSrcweir ++glyph_slot; 1488cdf0e10cSrcweir return numGlyphs; 1489cdf0e10cSrcweir } 1490cdf0e10cSrcweir 1491cdf0e10cSrcweir 1492cdf0e10cSrcweir void GraphiteLayout::MoveGlyph( int nGlyphIndex, long nNewPos ) 1493cdf0e10cSrcweir { 1494cdf0e10cSrcweir // TODO it might be better to actualy implement simplify properly, but this 1495cdf0e10cSrcweir // needs to be done carefully so the glyph/char maps are maintained 1496cdf0e10cSrcweir // If a glyph has been dropped then it wasn't returned by GetNextGlyphs, so 1497cdf0e10cSrcweir // the index here may be wrong 1498cdf0e10cSrcweir while ((mvGlyphs[nGlyphIndex].mnGlyphIndex == GF_DROPPED) && 1499cdf0e10cSrcweir (nGlyphIndex < (signed)mvGlyphs.size())) 1500cdf0e10cSrcweir { 1501cdf0e10cSrcweir nGlyphIndex++; 1502cdf0e10cSrcweir } 1503cdf0e10cSrcweir const long dx = nNewPos - mvGlyphs[nGlyphIndex].maLinearPos.X(); 1504cdf0e10cSrcweir 1505cdf0e10cSrcweir if (dx == 0) return; 1506cdf0e10cSrcweir // GenericSalLayout only changes maLinearPos, mvCharDxs doesn't change 1507cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 1508cdf0e10cSrcweir fprintf(grLog(),"Move %d (%ld,%ld) c%d by %ld\n", nGlyphIndex, mvGlyphs[nGlyphIndex].maLinearPos.X(), nNewPos, mvGlyph2Char[nGlyphIndex], dx); 1509cdf0e10cSrcweir #endif 1510cdf0e10cSrcweir for (size_t gi = nGlyphIndex; gi < mvGlyphs.size(); gi++) 1511cdf0e10cSrcweir { 1512cdf0e10cSrcweir mvGlyphs[gi].maLinearPos.X() += dx; 1513cdf0e10cSrcweir } 1514cdf0e10cSrcweir // width does need to be updated for correct fallback 1515cdf0e10cSrcweir mnWidth += dx; 1516cdf0e10cSrcweir } 1517cdf0e10cSrcweir 1518cdf0e10cSrcweir 1519cdf0e10cSrcweir void GraphiteLayout::DropGlyph( int nGlyphIndex ) 1520cdf0e10cSrcweir { 1521cdf0e10cSrcweir if(nGlyphIndex >= signed(mvGlyphs.size())) 1522cdf0e10cSrcweir return; 1523cdf0e10cSrcweir 1524cdf0e10cSrcweir GlyphItem & glyph = mvGlyphs[nGlyphIndex]; 1525cdf0e10cSrcweir glyph.mnGlyphIndex = GF_DROPPED; 1526cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 1527cdf0e10cSrcweir fprintf(grLog(),"Dropped %d\n", nGlyphIndex); 1528cdf0e10cSrcweir #endif 1529cdf0e10cSrcweir } 1530cdf0e10cSrcweir 1531cdf0e10cSrcweir void GraphiteLayout::Simplify( bool isBaseLayout ) 1532cdf0e10cSrcweir { 1533cdf0e10cSrcweir const sal_GlyphId dropMarker = isBaseLayout ? GF_DROPPED : 0; 1534cdf0e10cSrcweir 1535cdf0e10cSrcweir Glyphs::iterator gi = mvGlyphs.begin(); 1536cdf0e10cSrcweir // TODO check whether we need to adjust positions here 1537cdf0e10cSrcweir // MultiSalLayout seems to move the glyphs itself, so it may not be needed. 1538cdf0e10cSrcweir long deltaX = 0; 1539cdf0e10cSrcweir while (gi != mvGlyphs.end()) 1540cdf0e10cSrcweir { 1541cdf0e10cSrcweir if (gi->mnGlyphIndex == dropMarker) 1542cdf0e10cSrcweir { 1543cdf0e10cSrcweir deltaX += gi->mnNewWidth; 1544cdf0e10cSrcweir gi->mnNewWidth = 0; 1545cdf0e10cSrcweir } 1546cdf0e10cSrcweir else 1547cdf0e10cSrcweir { 1548cdf0e10cSrcweir deltaX = 0; 1549cdf0e10cSrcweir } 1550cdf0e10cSrcweir //mvCharDxs[mvGlyph2Char[gi->mnCharPos]] -= deltaX; 1551cdf0e10cSrcweir ++gi; 1552cdf0e10cSrcweir } 1553cdf0e10cSrcweir #ifdef GRLAYOUT_DEBUG 1554cdf0e10cSrcweir fprintf(grLog(),"Simplify base%d dx=%ld newW=%ld\n", isBaseLayout, deltaX, mnWidth - deltaX); 1555cdf0e10cSrcweir #endif 1556cdf0e10cSrcweir // discard width from trailing dropped glyphs, but not those in the middle 1557cdf0e10cSrcweir mnWidth -= deltaX; 1558cdf0e10cSrcweir } 1559