Gtk-WARNING gtktreestore.c:1047: Invalid column number 1 added to iter
[LibreOffice.git] / sc / source / core / data / documen9.cxx
blob0a3c8f30c2c3dae1c2d3002c7a42914c835ba587
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <scitems.hxx>
21 #include <editeng/eeitem.hxx>
23 #include <editeng/autokernitem.hxx>
24 #include <editeng/fontitem.hxx>
25 #include <editeng/langitem.hxx>
26 #include <o3tl/unit_conversion.hxx>
27 #include <osl/thread.h>
28 #include <osl/diagnose.h>
29 #include <svl/asiancfg.hxx>
30 #include <svx/svditer.hxx>
31 #include <svx/svdograf.hxx>
32 #include <svx/svdoole2.hxx>
33 #include <svx/svdpage.hxx>
34 #include <svx/svdundo.hxx>
35 #include <svx/xtable.hxx>
36 #include <sfx2/printer.hxx>
38 #include <document.hxx>
39 #include <docoptio.hxx>
40 #include <docsh.hxx>
41 #include <table.hxx>
42 #include <drwlayer.hxx>
43 #include <markdata.hxx>
44 #include <patattr.hxx>
45 #include <rechead.hxx>
46 #include <poolhelp.hxx>
47 #include <docpool.hxx>
48 #include <stlpool.hxx>
49 #include <editutil.hxx>
50 #include <charthelper.hxx>
51 #include <conditio.hxx>
52 #include <documentlinkmgr.hxx>
54 using namespace ::com::sun::star;
56 SfxBroadcaster* ScDocument::GetDrawBroadcaster()
58 return mpDrawLayer.get();
61 void ScDocument::BeginDrawUndo()
63 if (mpDrawLayer)
64 mpDrawLayer->BeginCalcUndo(false);
67 void ScDocument::TransferDrawPage(const ScDocument& rSrcDoc, SCTAB nSrcPos, SCTAB nDestPos)
69 if (mpDrawLayer && rSrcDoc.mpDrawLayer)
71 SdrPage* pOldPage = rSrcDoc.mpDrawLayer->GetPage(static_cast<sal_uInt16>(nSrcPos));
72 SdrPage* pNewPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nDestPos));
74 if (pOldPage && pNewPage)
76 SdrObjListIter aIter( pOldPage, SdrIterMode::Flat );
77 SdrObject* pOldObject = aIter.Next();
78 while (pOldObject)
80 // Copy style sheet
81 auto pStyleSheet = pOldObject->GetStyleSheet();
82 if (pStyleSheet)
83 GetStyleSheetPool()->CopyStyleFrom(rSrcDoc.GetStyleSheetPool(),
84 pStyleSheet->GetName(), pStyleSheet->GetFamily(), true);
86 // Clone to target SdrModel
87 rtl::Reference<SdrObject> pNewObject(pOldObject->CloneSdrObject(*mpDrawLayer));
88 pNewObject->NbcMove(Size(0,0));
89 pNewPage->InsertObject( pNewObject.get() );
91 if (mpDrawLayer->IsRecording())
92 mpDrawLayer->AddCalcUndo( std::make_unique<SdrUndoInsertObj>( *pNewObject ) );
94 pOldObject = aIter.Next();
99 // make sure the data references of charts are adapted
100 // (this must be after InsertObject!)
101 ScChartHelper::AdjustRangesOfChartsOnDestinationPage( rSrcDoc, *this, nSrcPos, nDestPos );
102 ScChartHelper::UpdateChartsOnDestinationPage(*this, nDestPos);
105 void ScDocument::InitDrawLayer( ScDocShell* pDocShell )
107 if (pDocShell && !mpShell)
109 ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
110 mpShell = pDocShell;
113 if (mpDrawLayer)
114 return;
116 ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
117 OUString aName;
118 if ( mpShell && !mpShell->IsLoading() ) // don't call GetTitle while loading
119 aName = mpShell->GetTitle();
120 mpDrawLayer.reset(new ScDrawLayer( this, aName ));
122 sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(bAutoCalc);
123 if (pMgr)
124 mpDrawLayer->SetLinkManager(pMgr);
126 // set DrawingLayer's SfxItemPool at Calc's SfxItemPool as
127 // secondary pool to support DrawingLayer FillStyle ranges (and similar)
128 // in SfxItemSets using the Calc SfxItemPool. This is e.g. needed when
129 // the PageStyle using SvxBrushItem is visualized and will be potentially
130 // used more intense in the future
131 if (mxPoolHelper.is() && !IsClipOrUndo()) //Using IsClipOrUndo as a proxy for SharePooledResources called
133 ScDocumentPool* pLocalPool = mxPoolHelper->GetDocPool();
135 if (pLocalPool)
137 OSL_ENSURE(!pLocalPool->GetSecondaryPool(), "OOps, already a secondary pool set where the DrawingLayer ItemPool is to be placed (!)");
138 pLocalPool->SetSecondaryPool(&mpDrawLayer->GetItemPool());
140 mpDrawLayer->CreateDefaultStyles();
143 // Drawing pages are accessed by table number, so they must also be present
144 // for preceding table numbers, even if the tables aren't allocated
145 // (important for clipboard documents).
147 SCTAB nDrawPages = 0;
148 SCTAB nTab;
149 for (nTab = 0; nTab < GetTableCount(); nTab++)
150 if (maTabs[nTab])
151 nDrawPages = nTab + 1; // needed number of pages
153 for (nTab = 0; nTab < nDrawPages && nTab < GetTableCount(); nTab++)
155 mpDrawLayer->ScAddPage( nTab ); // always add page, with or without the table
156 if (maTabs[nTab])
158 OUString aTabName = maTabs[nTab]->GetName();
159 mpDrawLayer->ScRenamePage( nTab, aTabName );
161 maTabs[nTab]->SetDrawPageSize(false,false); // set the right size immediately
165 mpDrawLayer->SetDefaultTabulator( GetDocOptions().GetTabDistance() );
167 UpdateDrawPrinter();
169 // set draw defaults directly
170 SfxItemPool& rDrawPool = mpDrawLayer->GetItemPool();
171 rDrawPool.SetUserDefaultItem( SvxAutoKernItem( true, EE_CHAR_PAIRKERNING ) );
173 UpdateDrawLanguages();
174 if (bImportingXML)
175 mpDrawLayer->EnableAdjust(false);
177 mpDrawLayer->SetForbiddenCharsTable( xForbiddenCharacters );
178 mpDrawLayer->SetCharCompressType( GetAsianCompression() );
179 mpDrawLayer->SetKernAsianPunctuation( GetAsianKerning() );
182 void ScDocument::UpdateDrawLanguages()
184 if (mpDrawLayer)
186 SfxItemPool& rDrawPool = mpDrawLayer->GetItemPool();
187 rDrawPool.SetUserDefaultItem( SvxLanguageItem( eLanguage, EE_CHAR_LANGUAGE ) );
188 rDrawPool.SetUserDefaultItem( SvxLanguageItem( eCjkLanguage, EE_CHAR_LANGUAGE_CJK ) );
189 rDrawPool.SetUserDefaultItem( SvxLanguageItem( eCtlLanguage, EE_CHAR_LANGUAGE_CTL ) );
193 void ScDocument::UpdateDrawPrinter()
195 if (mpDrawLayer)
197 // use the printer even if IsValid is false
198 // Application::GetDefaultDevice causes trouble with changing MapModes
199 mpDrawLayer->SetRefDevice(GetRefDevice());
203 void ScDocument::SetDrawPageSize(SCTAB nTab)
205 if (ScTable* pTable = FetchTable(nTab))
206 pTable->SetDrawPageSize();
209 bool ScDocument::IsChart( const SdrObject* pObject )
211 // IsChart() implementation moved to svx drawinglayer
212 if(pObject && SdrObjKind::OLE2 == pObject->GetObjIdentifier())
214 return static_cast<const SdrOle2Obj*>(pObject)->IsChart();
217 return false;
220 IMPL_LINK( ScDocument, GetUserDefinedColor, sal_uInt16, nColorIndex, Color* )
222 rtl::Reference<XColorList> xColorList;
223 if (mpDrawLayer)
224 xColorList = mpDrawLayer->GetColorList();
225 else
227 ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
228 if (!pColorList.is())
229 pColorList = XColorList::CreateStdColorList();
230 xColorList = pColorList;
232 return const_cast<Color*>(&(xColorList->GetColor(nColorIndex)->GetColor()));
235 bool ScDocument::DrawGetPrintArea( ScRange& rRange, bool bSetHor, bool bSetVer ) const
237 return mpDrawLayer->GetPrintArea( rRange, bSetHor, bSetVer );
240 void ScDocument::DeleteObjectsInArea( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
241 const ScMarkData& rMark, bool bAnchored)
243 if (!mpDrawLayer)
244 return;
246 SCTAB nTabCount = GetTableCount();
247 for (const auto& rTab : rMark)
249 if (rTab >= nTabCount)
250 break;
251 if (maTabs[rTab])
252 mpDrawLayer->DeleteObjectsInArea( rTab, nCol1, nRow1, nCol2, nRow2, bAnchored);
256 void ScDocument::DeleteObjectsInSelection( const ScMarkData& rMark )
258 if (!mpDrawLayer)
259 return;
261 mpDrawLayer->DeleteObjectsInSelection( rMark );
264 bool ScDocument::HasOLEObjectsInArea( const ScRange& rRange, const ScMarkData* pTabMark )
266 // pTabMark is used only for selected tables. If pTabMark is 0, all tables of rRange are used.
268 if (!mpDrawLayer)
269 return false;
271 SCTAB nStartTab = 0;
272 SCTAB nEndTab = GetTableCount();
273 if ( !pTabMark )
275 nStartTab = rRange.aStart.Tab();
276 nEndTab = rRange.aEnd.Tab();
279 for (SCTAB nTab = nStartTab; nTab <= nEndTab; nTab++)
281 if ( !pTabMark || pTabMark->GetTableSelect(nTab) )
283 tools::Rectangle aMMRect = GetMMRect( rRange.aStart.Col(), rRange.aStart.Row(),
284 rRange.aEnd.Col(), rRange.aEnd.Row(), nTab );
286 SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
287 OSL_ENSURE(pPage,"Page ?");
288 if (pPage)
290 SdrObjListIter aIter( pPage, SdrIterMode::Flat );
291 SdrObject* pObject = aIter.Next();
292 while (pObject)
294 if ( pObject->GetObjIdentifier() == SdrObjKind::OLE2 &&
295 aMMRect.Contains( pObject->GetCurrentBoundRect() ) )
296 return true;
298 pObject = aIter.Next();
304 return false;
307 void ScDocument::StartAnimations( SCTAB nTab )
309 if (!mpDrawLayer)
310 return;
311 SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
312 OSL_ENSURE(pPage,"Page ?");
313 if (!pPage)
314 return;
316 SdrObjListIter aIter( pPage, SdrIterMode::Flat );
317 SdrObject* pObject = aIter.Next();
318 while (pObject)
320 if (SdrGrafObj* pGrafObj = dynamic_cast<SdrGrafObj*>(pObject))
322 if ( pGrafObj->IsAnimated() )
324 pGrafObj->StartAnimation();
327 pObject = aIter.Next();
331 bool ScDocument::HasBackgroundDraw( SCTAB nTab, const tools::Rectangle& rMMRect ) const
333 // Are there objects in the background layer who are (partly) affected by rMMRect
334 // (for Drawing optimization, no deletion in front of the background
335 if (!mpDrawLayer)
336 return false;
337 SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
338 OSL_ENSURE(pPage,"Page ?");
339 if (!pPage)
340 return false;
342 bool bFound = false;
344 SdrObjListIter aIter( pPage, SdrIterMode::Flat );
345 SdrObject* pObject = aIter.Next();
346 while (pObject && !bFound)
348 if ( pObject->GetLayer() == SC_LAYER_BACK && pObject->GetCurrentBoundRect().Overlaps( rMMRect ) )
349 bFound = true;
350 pObject = aIter.Next();
353 return bFound;
356 bool ScDocument::HasAnyDraw( SCTAB nTab, const tools::Rectangle& rMMRect ) const
358 // Are there any objects at all who are (partly) affected by rMMRect?
359 // (To detect blank pages when printing)
360 if (!mpDrawLayer)
361 return false;
362 SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
363 OSL_ENSURE(pPage,"Page ?");
364 if (!pPage)
365 return false;
367 bool bFound = false;
369 SdrObjListIter aIter( pPage, SdrIterMode::Flat );
370 SdrObject* pObject = aIter.Next();
371 while (pObject && !bFound)
373 if ( pObject->GetCurrentBoundRect().Overlaps( rMMRect ) )
374 bFound = true;
375 pObject = aIter.Next();
378 return bFound;
381 void ScDocument::EnsureGraphicNames()
383 if (mpDrawLayer)
384 mpDrawLayer->EnsureGraphicNames();
387 SdrObject* ScDocument::GetObjectAtPoint( SCTAB nTab, const Point& rPos )
389 // for Drag&Drop on draw object
390 SdrObject* pFound = nullptr;
391 if (mpDrawLayer && nTab < GetTableCount() && maTabs[nTab])
393 SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
394 OSL_ENSURE(pPage,"Page ?");
395 if (pPage)
397 SdrObjListIter aIter( pPage, SdrIterMode::Flat );
398 SdrObject* pObject = aIter.Next();
399 while (pObject)
401 if ( pObject->GetCurrentBoundRect().Contains(rPos) )
403 // Intern is of no interest
404 // Only object form background layer, when no object form another layer is found
405 SdrLayerID nLayer = pObject->GetLayer();
406 if ( (nLayer != SC_LAYER_INTERN) && (nLayer != SC_LAYER_HIDDEN) )
408 if ( nLayer != SC_LAYER_BACK ||
409 !pFound || pFound->GetLayer() == SC_LAYER_BACK )
411 pFound = pObject;
415 // Continue search -> take last (on top) found object
416 pObject = aIter.Next();
420 return pFound;
423 bool ScDocument::IsPrintEmpty( SCCOL nStartCol, SCROW nStartRow,
424 SCCOL nEndCol, SCROW nEndRow,
425 SCTAB nTab, bool bLeftIsEmpty,
426 ScRange* pLastRange, tools::Rectangle* pLastMM ) const
428 if (!IsBlockEmpty( nStartCol, nStartRow, nEndCol, nEndRow, nTab ))
429 return false;
431 if (HasAttrib(ScRange(nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab), HasAttrFlags::Lines))
432 // We want to print sheets with borders even if there is no cell content.
433 return false;
435 tools::Rectangle aMMRect;
436 if ( pLastRange && pLastMM && nTab == pLastRange->aStart.Tab() &&
437 nStartRow == pLastRange->aStart.Row() && nEndRow == pLastRange->aEnd.Row() )
439 // keep vertical part of aMMRect, only update horizontal position
440 aMMRect = *pLastMM;
442 tools::Long nLeft = GetColWidth(0, nStartCol-1, nTab);
443 tools::Long nRight = nLeft + GetColWidth(nStartCol,nEndCol, nTab);
445 aMMRect.SetLeft(o3tl::convert(nLeft, o3tl::Length::twip, o3tl::Length::mm100));
446 aMMRect.SetRight(o3tl::convert(nRight, o3tl::Length::twip, o3tl::Length::mm100));
448 else
449 aMMRect = GetMMRect( nStartCol, nStartRow, nEndCol, nEndRow, nTab );
451 if ( pLastRange && pLastMM )
453 *pLastRange = ScRange( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab );
454 *pLastMM = aMMRect;
457 if ( HasAnyDraw( nTab, aMMRect ))
458 return false;
460 if ( nStartCol > 0 && !bLeftIsEmpty )
462 // similar to in ScPrintFunc::AdjustPrintArea
463 // ExtendPrintArea starting only from the start column of the print area
465 SCCOL nExtendCol = nStartCol - 1;
466 SCROW nTmpRow = nEndRow;
468 // ExtendMerge() is non-const, but called without refresh. GetPrinter()
469 // might create and assign a printer.
470 ScDocument* pThis = const_cast<ScDocument*>(this);
472 pThis->ExtendMerge( 0,nStartRow, nExtendCol,nTmpRow, nTab ); // no Refresh, incl. Attrs
474 OutputDevice* pDev = pThis->GetPrinter();
475 pDev->SetMapMode(MapMode(MapUnit::MapPixel)); // Important for GetNeededSize
476 ExtendPrintArea( pDev, nTab, 0, nStartRow, nExtendCol, nEndRow );
477 if ( nExtendCol >= nStartCol )
478 return false;
481 return true;
484 void ScDocument::Clear( bool bFromDestructor )
486 for (auto& rxTab : maTabs)
487 if (rxTab)
488 rxTab->GetCondFormList()->clear();
490 maTabs.clear();
491 pSelectionAttr.reset();
493 if (mpDrawLayer)
495 mpDrawLayer->ClearModel( bFromDestructor );
499 bool ScDocument::HasDetectiveObjects(SCTAB nTab) const
501 // looks for detective objects, annotations don't count
502 // (used to adjust scale so detective objects hit their cells better)
504 bool bFound = false;
506 if (mpDrawLayer)
508 SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
509 OSL_ENSURE(pPage,"Page ?");
510 if (pPage)
512 SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
513 SdrObject* pObject = aIter.Next();
514 while (pObject && !bFound)
516 // anything on the internal layer except captions (annotations)
517 if ( (pObject->GetLayer() == SC_LAYER_INTERN) && !ScDrawLayer::IsNoteCaption( pObject ) )
518 bFound = true;
520 pObject = aIter.Next();
525 return bFound;
528 void ScDocument::UpdateFontCharSet()
530 // In old versions (until 4.0 without SP), when switching between systems,
531 // the Font attribute was not adjusted.
532 // This has to be redone for Documents until SP2:
533 // Everything that is not SYMBOL is set to system CharSet.
534 // CharSet should be correct for new documents (version SC_FONTCHARSET)
536 bool bUpdateOld = ( nSrcVer < SC_FONTCHARSET );
538 rtl_TextEncoding eSysSet = osl_getThreadTextEncoding();
539 if ( !(eSrcSet != eSysSet || bUpdateOld) )
540 return;
542 ScDocumentPool* pPool = mxPoolHelper->GetDocPool();
544 pPool->iterateItemSurrogates(ATTR_FONT, [&](SfxItemPool::SurrogateData& rData)
546 const SvxFontItem& rSvxFontItem(static_cast<const SvxFontItem&>(rData.getItem()));
547 if (eSrcSet == rSvxFontItem.GetCharSet() || (bUpdateOld && RTL_TEXTENCODING_SYMBOL != rSvxFontItem.GetCharSet()))
549 SvxFontItem* pNew(rSvxFontItem.Clone(pPool));
550 pNew->SetCharSet(eSysSet);
551 rData.setItem(std::unique_ptr<SfxPoolItem>(pNew));
553 return true; // continue callbacks
556 if ( mpDrawLayer )
558 pPool->iterateItemSurrogates(EE_CHAR_FONTINFO, [&](SfxItemPool::SurrogateData& rData)
560 const SvxFontItem& rSvxFontItem(static_cast<const SvxFontItem&>(rData.getItem()));
561 if (eSrcSet == rSvxFontItem.GetCharSet() || (bUpdateOld && RTL_TEXTENCODING_SYMBOL != rSvxFontItem.GetCharSet()))
563 SvxFontItem* pNew(rSvxFontItem.Clone(pPool));
564 pNew->SetCharSet(eSysSet);
565 rData.setItem(std::unique_ptr<SfxPoolItem>(pNew));
567 return true; // continue callbacks
572 void ScDocument::SetLoadingMedium( bool bVal )
574 bLoadingMedium = bVal;
575 for (auto& rxTab : maTabs)
577 if (!rxTab)
578 return;
580 rxTab->SetLoadingMedium(bVal);
584 void ScDocument::SetImportingXML( bool bVal )
586 bImportingXML = bVal;
587 if (mpDrawLayer)
588 mpDrawLayer->EnableAdjust(!bImportingXML);
590 if ( !bVal )
592 // #i57869# after loading, do the real RTL mirroring for the sheets that have the LoadingRTL flag set
594 for (SCTAB nTab = 0; nTab < GetTableCount() && maTabs[nTab]; nTab++)
595 if ( maTabs[nTab]->IsLoadingRTL() )
597 // SetLayoutRTL => SetDrawPageSize => ScDrawLayer::SetPageSize, includes RTL-mirroring;
598 // bImportingXML must be cleared first
599 maTabs[nTab]->SetLoadingRTL( false );
600 SetLayoutRTL( nTab, true, ScObjectHandling::MoveRTLMode );
604 SetLoadingMedium(bVal);
607 const std::shared_ptr<SvxForbiddenCharactersTable>& ScDocument::GetForbiddenCharacters() const
609 return xForbiddenCharacters;
612 void ScDocument::SetForbiddenCharacters(const std::shared_ptr<SvxForbiddenCharactersTable>& rNew)
614 xForbiddenCharacters = rNew;
615 if ( mpEditEngine )
616 EditEngine::SetForbiddenCharsTable( xForbiddenCharacters );
617 if ( mpDrawLayer )
618 mpDrawLayer->SetForbiddenCharsTable( xForbiddenCharacters );
621 bool ScDocument::IsValidAsianCompression() const
623 return nAsianCompression != CharCompressType::Invalid;
626 CharCompressType ScDocument::GetAsianCompression() const
628 if ( nAsianCompression == CharCompressType::Invalid )
629 return CharCompressType::NONE;
630 else
631 return nAsianCompression;
634 void ScDocument::SetAsianCompression(CharCompressType nNew)
636 nAsianCompression = nNew;
637 if ( mpEditEngine )
638 mpEditEngine->SetAsianCompressionMode( nAsianCompression );
639 if ( mpDrawLayer )
640 mpDrawLayer->SetCharCompressType( nAsianCompression );
643 bool ScDocument::IsValidAsianKerning() const
645 return ( nAsianKerning != SC_ASIANKERNING_INVALID );
648 bool ScDocument::GetAsianKerning() const
650 if ( nAsianKerning == SC_ASIANKERNING_INVALID )
651 return false;
652 else
653 return static_cast<bool>(nAsianKerning);
656 void ScDocument::SetAsianKerning(bool bNew)
658 nAsianKerning = static_cast<sal_uInt8>(bNew);
659 if ( mpEditEngine )
660 mpEditEngine->SetKernAsianPunctuation( static_cast<bool>( nAsianKerning ) );
661 if ( mpDrawLayer )
662 mpDrawLayer->SetKernAsianPunctuation( static_cast<bool>( nAsianKerning ) );
665 void ScDocument::ApplyAsianEditSettings( ScEditEngineDefaulter& rEngine )
667 EditEngine::SetForbiddenCharsTable( xForbiddenCharacters );
668 rEngine.SetAsianCompressionMode( GetAsianCompression() );
669 rEngine.SetKernAsianPunctuation( GetAsianKerning() );
672 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */