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