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