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