[Libreoffice-commits] core.git: Branch 'distro/collabora/cp-5.0' - 14 commits - cui/source download.lst editeng/source external/graphite external/harfbuzz framework/source sc/qa sc/source sfx2/source stoc/source sw/CppunitTest_sw_odfexport.mk sw/qa sw/source vcl/inc vcl/source vcl/unx vcl/win writerfilter/source

Mihai Varga mihai.varga at collabora.com
Mon Feb 22 07:52:39 UTC 2016


 cui/source/customize/cfg.cxx                     |    9 
 download.lst                                     |    3 
 editeng/source/editeng/impedit3.cxx              |    6 
 external/graphite/StaticLibrary_graphite.mk      |    9 
 external/graphite/UnpackedTarball_graphite.mk    |    4 
 external/graphite/graphite2.issue1115.patch.1    |    6 
 external/graphite/graphite2.win64.patch.1        |   34 
 external/graphite/ubsan.patch                    |   51 +
 external/harfbuzz/ExternalProject_harfbuzz.mk    |    1 
 framework/source/classes/menumanager.cxx         |   46 -
 framework/source/uielement/menubarmanager.cxx    |   26 
 framework/source/uielement/newmenucontroller.cxx |   11 
 sc/qa/unit/subsequent_export-test.cxx            |    3 
 sc/source/ui/app/inputwin.cxx                    |    4 
 sc/source/ui/unoobj/docuno.cxx                   |    3 
 sfx2/source/doc/objstor.cxx                      |   13 
 stoc/source/javavm/javavm.cxx                    |    9 
 sw/CppunitTest_sw_odfexport.mk                   |    1 
 sw/qa/extras/htmlexport/htmlexport.cxx           |   18 
 sw/qa/extras/inc/swmodeltestbase.hxx             |   34 
 sw/qa/extras/odfexport/data/2_MathType3.docx     |binary
 sw/qa/extras/odfexport/odfexport.cxx             |   46 +
 sw/qa/extras/ooxmlimport/data/tdf95376.docx      |binary
 sw/qa/extras/ooxmlimport/ooxmlimport.cxx         |   24 
 sw/qa/extras/rtfimport/rtfimport.cxx             |   19 
 sw/source/core/ole/ndole.cxx                     |   35 -
 sw/source/core/tox/ToxWhitespaceStripper.cxx     |    2 
 vcl/inc/graphite_layout.hxx                      |   10 
 vcl/source/control/edit.cxx                      |    2 
 vcl/source/fontsubset/sft.cxx                    |    3 
 vcl/source/gdi/pdfwriter_impl.cxx                |   15 
 vcl/source/glyphs/graphite_layout.cxx            |  799 ++++++++---------------
 vcl/unx/gtk/app/gtksys.cxx                       |    7 
 vcl/win/source/gdi/winlayout.cxx                 |   15 
 writerfilter/source/dmapper/DomainMapper.cxx     |    8 
 35 files changed, 626 insertions(+), 650 deletions(-)

New commits:
commit a59d609263a7262c4f16e8c023d41350431057a3
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Mon Nov 2 16:52:48 2015 +0200

    LOK: make use of MOUSEMOVE in calc
    
    Change-Id: Ideb7bdabd86e95cf10c7f19f0900110b816970c2
    (cherry picked from commit bcd8da6849780b9680963ef3313d14209a46e5fa)

diff --git a/sc/source/ui/unoobj/docuno.cxx b/sc/source/ui/unoobj/docuno.cxx
index 072923f..183aed8 100644
--- a/sc/source/ui/unoobj/docuno.cxx
+++ b/sc/source/ui/unoobj/docuno.cxx
@@ -614,6 +614,9 @@ void ScModelObj::postMouseEvent(int nType, int nX, int nY, int nCount, int nButt
             pGridWindow->EndTracking(TrackingEventFlags::DontCallHdl);
 
         break;
+    case LOK_MOUSEEVENT_MOUSEMOVE:
+        pGridWindow->MouseMove(aEvent);
+        break;
     default:
         assert(false);
         break;
commit fce1ec4ed2ec0a9e2c62ee5c05d2087bb47e33eb
Author: Caolán McNamara <caolanm at redhat.com>
Date:   Fri Feb 5 14:03:44 2016 +0000

    Resolves: tdf#97465 like wheel ignore swipe for sc input handler inputchanged
    
    (cherry picked from commit e35f3b6a3357fc3832a9d68ed37ddb9b5320ef0a)
    (cherry picked from commit 113f000d38c34dd4dda6903976f8febf6d95375c)
    
    Change-Id: Ide7fe1388ffe6f85a1f459037316d03193470d8a
    Reviewed-on: https://gerrit.libreoffice.org/22156
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
    (cherry picked from commit daa1882dc2360cead712c3f100cbef0f35c74f5d)

diff --git a/sc/source/ui/app/inputwin.cxx b/sc/source/ui/app/inputwin.cxx
index b3776fd..834f4a6 100644
--- a/sc/source/ui/app/inputwin.cxx
+++ b/sc/source/ui/app/inputwin.cxx
@@ -1646,6 +1646,10 @@ void ScTextWnd::Command( const CommandEvent& rCEvt )
         {
             //don't call InputChanged for CommandEventId::Wheel
         }
+        else if ( nCommand == CommandEventId::Swipe )
+        {
+            //don't call InputChanged for CommandEventId::Swipe
+        }
         else
             SC_MOD()->InputChanged( pEditView );
     }
commit 02341810df1b262ea51b2f5af71a532db08acde1
Author: Oliver Specht <oliver.specht at cib.de>
Date:   Wed Feb 17 12:07:59 2016 +0100

    tdf#98001: allow disabling file/new, wizards, recent documents menu entries
    
    disabling the dispatches '.uno:AutoPilotMenu' and '.uno:AddDirect' and
    .uno:RecentFileList via UNO API now results in disabled
    menu entries as expected
    
    Change-Id: Id99be9374306ff8c0cea919ea94ed96f715a8058
    Reviewed-on: https://gerrit.libreoffice.org/22422
    Reviewed-by: Oliver Specht <oliver.specht at cib.de>
    Tested-by: Oliver Specht <oliver.specht at cib.de>
    Reviewed-on: https://gerrit.libreoffice.org/22461
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Caolán McNamara <caolanm at redhat.com>
    Tested-by: Caolán McNamara <caolanm at redhat.com>
    (cherry picked from commit 8a50946c2868f196a1f4571e491a5ca7fb12492a)

diff --git a/framework/source/classes/menumanager.cxx b/framework/source/classes/menumanager.cxx
index 7cd0e7d..6a491bd 100644
--- a/framework/source/classes/menumanager.cxx
+++ b/framework/source/classes/menumanager.cxx
@@ -177,33 +177,33 @@ MenuManager::MenuManager(
         }
         else
         {
-            if ( nItemId == SID_NEWDOCDIRECT || aItemCommand == aSlotNewDocDirect )
+            bool isNewDoc = nItemId == SID_NEWDOCDIRECT || aItemCommand == aSlotNewDocDirect;
+            bool isAutoPilot = nItemId == SID_AUTOPILOTMENU || aItemCommand == aSlotAutoPilot;
+            if(isNewDoc || isAutoPilot)
             {
-                MenuConfiguration aMenuCfg( m_xContext );
-                BmkMenu* pSubMenu = static_cast<BmkMenu*>(aMenuCfg.CreateBookmarkMenu( rFrame, BOOKMARK_NEWMENU ));
-                pMenu->SetPopupMenu( nItemId, pSubMenu );
-
-                AddMenu(pSubMenu,OUString(),nItemId,true,false);
-                if ( bShowMenuImages && !pMenu->GetItemImage( nItemId ))
+                Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
+                URL aTargetURL;
+                aTargetURL.Complete = OUString::createFromAscii(isNewDoc ? aSlotNewDocDirect : aSlotAutoPilot);
+                m_xURLTransformer->parseStrict( aTargetURL );
+                Reference< XDispatch > xMenuItemDispatch = xDispatchProvider->queryDispatch(
+                                                            aTargetURL, OUString(), 0 );
+                if(xMenuItemDispatch == nullptr)
                 {
-                    Image aImage = GetImageFromURL( rFrame, aItemCommand, false );
-                    if ( !!aImage )
-                           pMenu->SetItemImage( nItemId, aImage );
+                    m_pVCLMenu->EnableItem( nItemId, false );
                 }
-            }
-            else if ( nItemId == SID_AUTOPILOTMENU || aItemCommand == aSlotAutoPilot )
-            {
-                MenuConfiguration aMenuCfg( m_xContext );
-                BmkMenu* pSubMenu = static_cast<BmkMenu*>(aMenuCfg.CreateBookmarkMenu( rFrame, BOOKMARK_WIZARDMENU ));
-                pMenu->SetPopupMenu( nItemId, pSubMenu );
-
-                AddMenu(pSubMenu,OUString(),nItemId,true,false);
-
-                if ( bShowMenuImages && !pMenu->GetItemImage( nItemId ))
+                else
                 {
-                    Image aImage = GetImageFromURL( rFrame, aItemCommand, false );
-                    if ( !!aImage )
-                           pMenu->SetItemImage( nItemId, aImage );
+                    MenuConfiguration aMenuCfg( m_xContext );
+                    BmkMenu* pSubMenu = static_cast<BmkMenu*>(aMenuCfg.CreateBookmarkMenu( rFrame,
+                                OUString::createFromAscii(isNewDoc ? BOOKMARK_NEWMENU : BOOKMARK_WIZARDMENU)));
+                    pMenu->SetPopupMenu( nItemId, pSubMenu );
+                    AddMenu(pSubMenu,OUString(),nItemId,true,false);
+                    if ( bShowMenuImages && !pMenu->GetItemImage( nItemId ))
+                    {
+                        Image aImage = GetImageFromURL( rFrame, aItemCommand, false );
+                        if ( !!aImage )
+                                pMenu->SetItemImage( nItemId, aImage );
+                    }
                 }
             }
             else if ( pMenu->GetItemType( i ) != MenuItemType::SEPARATOR )
diff --git a/framework/source/uielement/menubarmanager.cxx b/framework/source/uielement/menubarmanager.cxx
index 9e36073..d66f20b 100644
--- a/framework/source/uielement/menubarmanager.cxx
+++ b/framework/source/uielement/menubarmanager.cxx
@@ -872,20 +872,22 @@ IMPL_LINK_TYPED( MenuBarManager, Activate, Menu *, pMenu, bool )
                                 xMenuItemDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
 
                             bool bPopupMenu( false );
-                            if ( !pMenuItemHandler->xPopupMenuController.is() &&
-                                 m_xPopupMenuControllerFactory->hasController( aItemCommand, OUString() ))
+                            if(xMenuItemDispatch != nullptr)
                             {
-                                bPopupMenu = CreatePopupMenuController( pMenuItemHandler );
-                            }
-                            else if ( pMenuItemHandler->xPopupMenuController.is() )
-                            {
-                                // Force update of popup menu
-                                pMenuItemHandler->xPopupMenuController->updatePopupMenu();
-                                bPopupMenu = true;
-                                if (PopupMenu*  pThisPopup = pMenu->GetPopupMenu( pMenuItemHandler->nItemId ))
-                                    pMenu->EnableItem( pMenuItemHandler->nItemId, pThisPopup->GetItemCount() != 0 );
+                                if ( !pMenuItemHandler->xPopupMenuController.is() &&
+                                     m_xPopupMenuControllerFactory->hasController( aItemCommand, m_aModuleIdentifier ) )
+                                {
+                                    bPopupMenu = CreatePopupMenuController( pMenuItemHandler );
+                                }
+                                else if ( pMenuItemHandler->xPopupMenuController.is() )
+                                {
+                                    // Force update of popup menu
+                                    pMenuItemHandler->xPopupMenuController->updatePopupMenu();
+                                    bPopupMenu = true;
+                                    if (PopupMenu*  pThisPopup = pMenu->GetPopupMenu( pMenuItemHandler->nItemId ))
+                                        pMenu->EnableItem( pMenuItemHandler->nItemId, pThisPopup->GetItemCount() != 0 );
+                                }
                             }
-
                             lcl_CheckForChildren(pMenu, pMenuItemHandler->nItemId);
 
                             if ( xMenuItemDispatch.is() )
diff --git a/framework/source/uielement/newmenucontroller.cxx b/framework/source/uielement/newmenucontroller.cxx
index 9a02716..b5bd5f7 100644
--- a/framework/source/uielement/newmenucontroller.cxx
+++ b/framework/source/uielement/newmenucontroller.cxx
@@ -47,6 +47,8 @@
 #include <boost/scoped_ptr.hpp>
 
 //  Defines
+#define aSlotNewDocDirect ".uno:AddDirect"
+#define aSlotAutoPilot ".uno:AutoPilotMenu"
 
 using namespace com::sun::star::uno;
 using namespace com::sun::star::lang;
@@ -328,6 +330,13 @@ void NewMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu >& rPopup
         MenuConfiguration aMenuCfg( m_xContext );
         boost::scoped_ptr<BmkMenu> pSubMenu;
 
+        Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
+        URL aTargetURL;
+        aTargetURL.Complete = rtl::OUString::createFromAscii(m_bNewMenu ? aSlotNewDocDirect : aSlotAutoPilot);
+        m_xURLTransformer->parseStrict( aTargetURL );
+        Reference< XDispatch > xMenuItemDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
+        if(xMenuItemDispatch == nullptr)
+            return;
         if ( m_bNewMenu )
             pSubMenu.reset(static_cast<BmkMenu*>(aMenuCfg.CreateBookmarkMenu( m_xFrame, BOOKMARK_NEWMENU )));
         else
@@ -518,7 +527,7 @@ void SAL_CALL NewMenuController::initialize( const Sequence< Any >& aArguments )
             const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
 
             m_bShowImages   = rSettings.GetUseImagesInMenus();
-            m_bNewMenu      = m_aCommandURL == ".uno:AddDirect";
+            m_bNewMenu      = m_aCommandURL == aSlotNewDocDirect;
         }
     }
 }
commit 75b473258d6f06e68af3fd49fc2f168e88934b72
Author: Caolán McNamara <caolanm at redhat.com>
Date:   Thu Feb 18 13:52:55 2016 +0000

    fix inverted logic regression
    
    regression from...
    
        commit 051b29e1025253f35f87a04e297760aa8b40611f
        Author: Luboš Luňák <l.lunak at collabora.com>
        Date:   Sun Sep 14 15:45:02 2014 +0200
    
            convert Edit autocomplete Hdl to boost signals2
    
        where the conversion was...
    
        - if ( maAutocompleteHdl.IsSet() )
        + if ( autocompleteSignal.empty() )
    
        instead of the correct
    
        + if ( !autocompleteSignal.empty() )
    
    Reviewed-on: https://gerrit.libreoffice.org/22477
    Reviewed-by: Michael Stahl <mstahl at redhat.com>
    Tested-by: Michael Stahl <mstahl at redhat.com>
    (cherry picked from commit e919bfcaa5561445ebf39f73171bf1c934818d1a)
    
    Change-Id: Ie87944d66958af595e64b41236bbf515ef7a1f98
    Reviewed-on: https://gerrit.libreoffice.org/22478
    Reviewed-by: Michael Stahl <mstahl at redhat.com>
    Tested-by: Michael Stahl <mstahl at redhat.com>
    (cherry picked from commit e2aa9b7c8c62338c91a3883b0c400b1259793382)

diff --git a/vcl/source/control/edit.cxx b/vcl/source/control/edit.cxx
index 7e04604..2a492da 100644
--- a/vcl/source/control/edit.cxx
+++ b/vcl/source/control/edit.cxx
@@ -2114,7 +2114,7 @@ void Edit::Command( const CommandEvent& rCEvt )
         Invalidate();
 
         // #i25161# call auto complete handler for ext text commit also
-        if ( autocompleteSignal.empty() )
+        if (!autocompleteSignal.empty())
         {
             if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.getLength()) )
             {
commit 62d1690d23137eb1c9e873211f2c57b797912cda
Author: Michael Stahl <mstahl at redhat.com>
Date:   Thu Feb 18 14:06:35 2016 +0100

    fdo#94009: harfbuzz: don't export symbols from VCL
    
    Should fix crashes due to symbol clashes in ELF global namespace
    where system's libharfbuzz.so.0 is loaded as well.
    
    Change-Id: I35ffcbe4ac4de5a25cd8bf0cb9a8f0c11f4554c5
    (cherry picked from commit 549130ab5d9616f7eb5504db31546b386737ccb2)
    Reviewed-on: https://gerrit.libreoffice.org/22481
    Reviewed-by: Caolán McNamara <caolanm at redhat.com>
    Tested-by: Caolán McNamara <caolanm at redhat.com>
    (cherry picked from commit 395995f03dd640aee28767f6d920901d91dd3bee)

diff --git a/external/harfbuzz/ExternalProject_harfbuzz.mk b/external/harfbuzz/ExternalProject_harfbuzz.mk
index 302eed7..192ca23 100644
--- a/external/harfbuzz/ExternalProject_harfbuzz.mk
+++ b/external/harfbuzz/ExternalProject_harfbuzz.mk
@@ -33,6 +33,7 @@ $(call gb_ExternalProject_get_state_target,harfbuzz,build) :
 			--with-glib=no \
 			$(if $(VERBOSE)$(verbose),--disable-silent-rules,--enable-silent-rules) \
 			$(if $(CROSS_COMPILING),--build=$(BUILD_PLATFORM) --host=$(HOST_PLATFORM)) \
+			$(if $(filter LINUX,$(OS)),CXXFLAGS="$(CXXFLAGS) -fvisibility=hidden") \
 		&& (cd $(EXTERNAL_WORKDIR)/src && $(MAKE)) \
 	)
 
commit 58e845e408b41ebc192e7ecb9212c33a8f67f3af
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Tue Jan 26 13:21:28 2016 +0100

    tdf#95376 DOCX import: fix incorrectly indented tab stops
    
    Regression from commit f4badd9a485f32f787d78431ed673e2932973887
    (tdf#92454 DOCX import: allow overriding para prop from num style in
    para style, 2015-09-22), the problem was yet another priority
    mishandling in the maze of various styles and indentation handling.
    
    In the tdf#92454 bugdoc, both a numbering-from-paragraph-style and a
    paragraph-style defined indentation, and Word preferred the numbering,
    while Writer preferred the paragraph style, that's why the import-time
    conversion was added.
    
    However, it turns out there is a 3rd source that's still not direct
    indentation formatting: a direct numbering. So the correct priority is:
    
    direct-ind > ind-from-num > ind-from-parastyle > ind-from-num-from-parastyle
    
    Which means in this case the indentation should not be set directly: the
    two conflicting value (ind-from-num and ind-from-parastyle) will be
    resolved correctly by Writer core.
    
    Given that we always first get the para style info, and only then the
    numbering, we just need to undo the conversion added for the other bug
    if we see a direct numbering, then both the old bugdoc and this new one
    will be handled properly.
    
    Change-Id: I09cc84605d5df6159da985ad069d46b580a53358
    (cherry picked from commit 3915bf2dc877d5f1140798e24933db0f21386a4a)
    Reviewed-on: https://gerrit.libreoffice.org/22482
    Reviewed-by: Caolán McNamara <caolanm at redhat.com>
    Tested-by: Caolán McNamara <caolanm at redhat.com>
    (cherry picked from commit 6ecfd6bffb23a1a326a5433e6b9170b938fac77c)

diff --git a/sw/qa/extras/ooxmlimport/data/tdf95376.docx b/sw/qa/extras/ooxmlimport/data/tdf95376.docx
new file mode 100644
index 0000000..9bd2d05
Binary files /dev/null and b/sw/qa/extras/ooxmlimport/data/tdf95376.docx differ
diff --git a/sw/qa/extras/ooxmlimport/ooxmlimport.cxx b/sw/qa/extras/ooxmlimport/ooxmlimport.cxx
index 384bc3e..c280b54 100644
--- a/sw/qa/extras/ooxmlimport/ooxmlimport.cxx
+++ b/sw/qa/extras/ooxmlimport/ooxmlimport.cxx
@@ -2815,6 +2815,15 @@ DECLARE_OOXMLIMPORT_TEST(testTdf92454, "tdf92454.docx")
     CPPUNIT_ASSERT_EQUAL(beans::PropertyState_DIRECT_VALUE, xParagraph->getPropertyState("ParaFirstLineIndent"));
 }
 
+DECLARE_OOXMLIMPORT_TEST(testTdf95376, "tdf95376.docx")
+{
+    uno::Reference<beans::XPropertyState> xParagraph(getParagraph(2), uno::UNO_QUERY);
+    // This was beans::PropertyState_DIRECT_VALUE: indentation-from-numbering
+    // did not have priority over indentation-from-paragraph-style, due to a
+    // filter workaround that's not correct here.
+    CPPUNIT_ASSERT_EQUAL(beans::PropertyState_DEFAULT_VALUE, xParagraph->getPropertyState("ParaFirstLineIndent"));
+}
+
 DECLARE_OOXMLIMPORT_TEST(testTdf92124, "tdf92124.docx")
 {
     // Get the second paragraph's numbering style's 1st level's suffix.
diff --git a/writerfilter/source/dmapper/DomainMapper.cxx b/writerfilter/source/dmapper/DomainMapper.cxx
index 76db497..9a3ae42 100644
--- a/writerfilter/source/dmapper/DomainMapper.cxx
+++ b/writerfilter/source/dmapper/DomainMapper.cxx
@@ -1196,6 +1196,14 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, PropertyMapPtr rContext )
                     rContext->Insert( PROP_NUMBERING_RULES, aRules );
                     // erase numbering from pStyle if already set
                     rContext->Erase(PROP_NUMBERING_STYLE_NAME);
+
+                    // Indentation can came from:
+                    // 1) Paragraph style's numbering's indentation: the current non-style numId has priority over it.
+                    // 2) Numbering's indentation: Writer handles that natively, so it should not be set on rContext.
+                    // 3) Paragraph style's indentation: ditto.
+                    // 4) Direct paragraph formatting: that will came later.
+                    // So no situation where keeping indentation at this point would make sense -> erase.
+                    rContext->Erase(PROP_PARA_FIRST_LINE_INDENT);
                 }
             }
             else
commit 1aa00af07054b896c9e8500b2f409a70f7052550
Author: Caolán McNamara <caolanm at redhat.com>
Date:   Wed Feb 17 16:09:01 2016 +0000

    Resolves: rhbz#1285356 force swing not to use gtk2 if gtk3 is loaded
    
    Reviewed-on: https://gerrit.libreoffice.org/22433
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Chris Sherlock <chris.sherlock79 at gmail.com>
    (cherry picked from commit 9f7f881d11ceb4e2534758b9507a55292ec697b0)
    
    Change-Id: I6347bf4c25ce649073afdfe4225182ab2dc84af1
    Reviewed-on: https://gerrit.libreoffice.org/22464
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Samuel Mehrbrodt <Samuel.Mehrbrodt at cib.de>
    (cherry picked from commit c2acf9204dae3582adc608ad28ef018c0c82f24e)

diff --git a/stoc/source/javavm/javavm.cxx b/stoc/source/javavm/javavm.cxx
index 8ccd0c1..ccf51a3 100644
--- a/stoc/source/javavm/javavm.cxx
+++ b/stoc/source/javavm/javavm.cxx
@@ -538,8 +538,15 @@ void initVMConfiguration(
     }
 
     *pjvm= jvm;
-    setTimeZone(pjvm);
 
+    // rhbz#1285356, native look will be gtk2, which crashes
+    // when gtk3 is already loaded. Until there is a solution
+    // java-side force look and feel to something that doesn't
+    // crash when we are using gtk3
+    if (getenv("STOC_FORCE_SYSTEM_LAF"))
+        pjvm->pushProp("swing.systemlaf=javax.swing.plaf.metal.MetalLookAndFeel");
+
+    setTimeZone(pjvm);
 }
 
 class DetachCurrentThread: private boost::noncopyable {
diff --git a/vcl/unx/gtk/app/gtksys.cxx b/vcl/unx/gtk/app/gtksys.cxx
index f114221..7d687b2 100644
--- a/vcl/unx/gtk/app/gtksys.cxx
+++ b/vcl/unx/gtk/app/gtksys.cxx
@@ -40,6 +40,13 @@ GtkSalSystem::GtkSalSystem() : SalGenericSystem()
 {
     mpDisplay = gdk_display_get_default();
     countScreenMonitors();
+#if GTK_CHECK_VERSION(3,0,0)
+    // rhbz#1285356, native look will be gtk2, which crashes
+    // when gtk3 is already loaded. Until there is a solution
+    // java-side force look and feel to something that doesn't
+    // crash when we are using gtk3
+    setenv("STOC_FORCE_SYSTEM_LAF", "true", 1);
+#endif
 }
 
 GtkSalSystem::~GtkSalSystem()
commit 2b354fbaeb1b59419bd82eabc7b3f01b2da3c315
Author: Maxim Monastirsky <momonasmon at gmail.com>
Date:   Thu Jan 21 22:27:55 2016 +0200

    tdf#87178 Resetting menu shouldn't reset other resources
    
    Change-Id: I6d6334cac9fdb5dfa3e35fe0cba70e5d9adf6fc2
    (cherry picked from commit eb3ee586e420ee4e38f9ef8c579e1a37d2dc0c10)
    Reviewed-on: https://gerrit.libreoffice.org/21703
    Reviewed-by: Caolán McNamara <caolanm at redhat.com>
    Tested-by: Caolán McNamara <caolanm at redhat.com>
    (cherry picked from commit eb0aad9186953e0602ed96244492a71f203d8f94)

diff --git a/cui/source/customize/cfg.cxx b/cui/source/customize/cfg.cxx
index e4da216..92827b8 100644
--- a/cui/source/customize/cfg.cxx
+++ b/cui/source/customize/cfg.cxx
@@ -1302,7 +1302,14 @@ void MenuSaveInData::ApplyMenu(
 void
 MenuSaveInData::Reset()
 {
-    GetConfigManager()->reset();
+    try
+    {
+        GetConfigManager()->removeSettings( m_aMenuResourceURL );
+    }
+    catch ( const css::uno::Exception& )
+    {}
+
+    PersistChanges( GetConfigManager() );
 
     delete pRootEntry;
     pRootEntry = NULL;
commit 806c73ffb65db133f797e9b315f061754e7648c8
Author: Michael Stahl <mstahl at redhat.com>
Date:   Fri Feb 12 18:22:51 2016 +0100

    sfx2: related tdf#56270: loss of embedded objects imported from DOCX
    
    After the import some of these are kept in RUNNING state.  For Math
    objects imported from MathType3 OLEs in particular, first a new
    Math object is created and stored to the XStorage, only then is the
    MathType3 stream imported.  This means the Math object is modified and
    contains data that must be stored.
    
    The problem is then that SfxObjectShell::ImportFrom() simply calls
    setModified(false), clearing the flag without storing the object.
    
    For Flat ODF export we lose all the objects that are cached in sw's
    SwOLELRUCache; for the bugdoc something more inexplicable happens for
    ODT export where we lose "Object 214" (which is the first one in the
    cache) but no other ones.
    
    (The main difference is that for ODF there is an optimization to copy the
    embedded object's storage without loading the object, but for Flat ODF
    every object must be loaded and exported.)
    
    (regression from 83777cd6e0f3f1a4458af896fd13344c696ecb1e)
    
    (cherry picked from commit d81d104833f0ee9349ebcd0d79d2de84ba9a7262)
    
    Change-Id: Id1474fba9f4da2d5247c7ff4dc6819ddb9829fe8
    Reviewed-on: https://gerrit.libreoffice.org/22337
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Caolán McNamara <caolanm at redhat.com>
    Tested-by: Caolán McNamara <caolanm at redhat.com>
    (cherry picked from commit b21bb8d7b3808eee06320b634e11e3a4ab217839)

diff --git a/sfx2/source/doc/objstor.cxx b/sfx2/source/doc/objstor.cxx
index be8ecd0..7bc028d 100644
--- a/sfx2/source/doc/objstor.cxx
+++ b/sfx2/source/doc/objstor.cxx
@@ -2281,8 +2281,19 @@ bool SfxObjectShell::ImportFrom(SfxMedium& rMedium,
                 if ( nState == embed::EmbedStates::LOADED || nState == embed::EmbedStates::RUNNING )    // means that the object is not active
                 {
                     uno::Reference< util::XModifiable > xModifiable( xObj->getComponent(), uno::UNO_QUERY );
-                    if ( xModifiable.is() )
+                    if (xModifiable.is() && xModifiable->isModified())
+                    {
+                        uno::Reference<embed::XEmbedPersist> const xPers(xObj, uno::UNO_QUERY);
+                        if (xPers.is())
+                        {   // store it before resetting modified!
+                            xPers->storeOwn();
+                        }
+                        else
+                        {
+                            SAL_WARN("sfx.doc", "Modified object without persistence!");
+                        }
                         xModifiable->setModified(sal_False);
+                    }
                 }
             }
         }
diff --git a/sw/CppunitTest_sw_odfexport.mk b/sw/CppunitTest_sw_odfexport.mk
index c6ec626..0930732 100644
--- a/sw/CppunitTest_sw_odfexport.mk
+++ b/sw/CppunitTest_sw_odfexport.mk
@@ -81,6 +81,7 @@ $(eval $(call gb_CppunitTest_use_components,sw_odfexport,\
     writerfilter/util/writerfilter \
     $(if $(filter DESKTOP,$(BUILD_TYPE)),xmlhelp/util/ucpchelp1) \
     xmloff/util/xo \
+    xmlscript/util/xmlscript \
 ))
 
 $(eval $(call gb_CppunitTest_use_custom_headers,sw_odfexport,\
diff --git a/sw/qa/extras/odfexport/data/2_MathType3.docx b/sw/qa/extras/odfexport/data/2_MathType3.docx
new file mode 100644
index 0000000..0694921
Binary files /dev/null and b/sw/qa/extras/odfexport/data/2_MathType3.docx differ
diff --git a/sw/qa/extras/odfexport/odfexport.cxx b/sw/qa/extras/odfexport/odfexport.cxx
index 2506ec3..a917596 100644
--- a/sw/qa/extras/odfexport/odfexport.cxx
+++ b/sw/qa/extras/odfexport/odfexport.cxx
@@ -68,10 +68,40 @@ public:
             pBatch->commit();
             return pResetter;
         }
+        if (OString(pFilename) == "2_MathType3.docx")
+        {
+            std::unique_ptr<Resetter> pResetter(new Resetter(
+                [this] () {
+                    mpFilter = "writer8";
+                    std::shared_ptr<comphelper::ConfigurationChanges> pBatch(
+                            comphelper::ConfigurationChanges::create());
+                    officecfg::Office::Common::Cache::Writer::OLE_Objects::set(20, pBatch);
+                    return pBatch->commit();
+                }));
+            mpFilter = "OpenDocument Text Flat XML"; // doesn't happen with ODF package
+            std::shared_ptr<comphelper::ConfigurationChanges> pBatch(
+                    comphelper::ConfigurationChanges::create());
+            officecfg::Office::Common::Cache::Writer::OLE_Objects::set(1, pBatch);
+            pBatch->commit();
+            return pResetter;
+        }
         return nullptr;
     }
 };
 
+DECLARE_ODFEXPORT_TEST(testMathObjectFlatExport, "2_MathType3.docx")
+{
+    uno::Reference<util::XModifiable> xModifiable(mxComponent, uno::UNO_QUERY);
+    CPPUNIT_ASSERT(!xModifiable->isModified());
+    // see preTest(), set the OLE cache to 1 for this test
+    // and the problem was that the formulas that were in the cache
+    // (the second one) were lost
+    OUString formula1(getFormula(getRun(getParagraph(1), 1)));
+    CPPUNIT_ASSERT_EQUAL(OUString(" size 12{1+1=2} {}"), formula1);
+    OUString formula2(getFormula(getRun(getParagraph(2), 1)));
+    CPPUNIT_ASSERT_EQUAL(OUString(" size 12{2+2=4} {}"), formula2);
+}
+
 DECLARE_ODFEXPORT_TEST(testFramebackgrounds, "framebackgrounds.odt")
 {
    //Counting the Number of Frames and checking with the expected count
commit 89f45e7b255d98e2cd3693938c132072c3cfe63f
Author: Michael Stahl <mstahl at redhat.com>
Date:   Fri Feb 12 13:40:46 2016 +0100

    sw: don't crash if Office.Common/Cache/Writer/OLE_Objects set to 1
    
    (possibly regression from b717bda1f6484905aebc571c4538165a1fbfd2bb)
    
    (cherry picked from commit 60d4dd0a6c44b45ed424ca6a0ddcf857ec089b24)
    
    Change-Id: I9113fe2e769cd6ba56bdccc629ac63241b238553
    Reviewed-on: https://gerrit.libreoffice.org/22335
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Caolán McNamara <caolanm at redhat.com>
    Tested-by: Caolán McNamara <caolanm at redhat.com>
    (cherry picked from commit 1226da376576434c9cea3c80bf304190605edc4c)

diff --git a/sw/source/core/ole/ndole.cxx b/sw/source/core/ole/ndole.cxx
index ddedf77..c2bccbd 100644
--- a/sw/source/core/ole/ndole.cxx
+++ b/sw/source/core/ole/ndole.cxx
@@ -84,7 +84,7 @@ public:
     void RemoveObj( SwOLEObj& rObj );
 };
 
-SwOLELRUCache* pOLELRU_Cache = 0;
+std::shared_ptr<SwOLELRUCache> g_pOLELRU_Cache;
 
 class SwOLEListener_Impl : public ::cppu::WeakImplHelper< embed::XStateChangeListener >
 {
@@ -102,7 +102,7 @@ SwOLEListener_Impl::SwOLEListener_Impl( SwOLEObj* pObj )
 {
     if ( mpObj->IsOleRef() && mpObj->GetOleRef()->getCurrentState() == embed::EmbedStates::RUNNING )
     {
-        pOLELRU_Cache->InsertObj( *mpObj );
+        g_pOLELRU_Cache->InsertObj( *mpObj );
     }
 }
 
@@ -114,29 +114,29 @@ void SAL_CALL SwOLEListener_Impl::stateChanged( const lang::EventObject&, ::sal_
 {
     if ( mpObj && nOldState == embed::EmbedStates::LOADED && nNewState == embed::EmbedStates::RUNNING )
     {
-        if( !pOLELRU_Cache )
-            pOLELRU_Cache = new SwOLELRUCache;
-        pOLELRU_Cache->InsertObj( *mpObj );
+        if (!g_pOLELRU_Cache)
+            g_pOLELRU_Cache.reset(new SwOLELRUCache);
+        g_pOLELRU_Cache->InsertObj( *mpObj );
     }
     else if ( mpObj && nNewState == embed::EmbedStates::LOADED && nOldState == embed::EmbedStates::RUNNING )
     {
-        if ( pOLELRU_Cache )
-            pOLELRU_Cache->RemoveObj( *mpObj );
+        if (g_pOLELRU_Cache)
+            g_pOLELRU_Cache->RemoveObj( *mpObj );
     }
 }
 
 void SwOLEListener_Impl::Release()
 {
-    if ( mpObj && pOLELRU_Cache )
-        pOLELRU_Cache->RemoveObj( *mpObj );
+    if (mpObj && g_pOLELRU_Cache)
+        g_pOLELRU_Cache->RemoveObj( *mpObj );
     mpObj=0;
     release();
 }
 
 void SAL_CALL SwOLEListener_Impl::disposing( const lang::EventObject& ) throw (uno::RuntimeException, std::exception)
 {
-    if ( mpObj && pOLELRU_Cache )
-        pOLELRU_Cache->RemoveObj( *mpObj );
+    if (mpObj && g_pOLELRU_Cache)
+        g_pOLELRU_Cache->RemoveObj( *mpObj );
 }
 
 // TODO/LATER: actually SwEmbedObjectLink should be used here, but because different objects are used to control
@@ -810,9 +810,9 @@ const uno::Reference < embed::XEmbeddedObject > SwOLEObj::GetOleRef()
     else if ( xOLERef->getCurrentState() == embed::EmbedStates::RUNNING )
     {
         // move object to first position in cache
-        if( !pOLELRU_Cache )
-            pOLELRU_Cache = new SwOLELRUCache;
-        pOLELRU_Cache->InsertObj( *this );
+        if (!g_pOLELRU_Cache)
+            g_pOLELRU_Cache.reset(new SwOLELRUCache);
+        g_pOLELRU_Cache->InsertObj( *this );
     }
 
     return xOLERef.GetObject();
@@ -939,6 +939,7 @@ void SwOLELRUCache::Load()
         {
             if (nVal < m_nLRU_InitSize)
             {
+                std::shared_ptr<SwOLELRUCache> tmp(g_pOLELRU_Cache); // prevent delete this
                 // size of cache has been changed
                 sal_Int32 nCount = m_OleObjects.size();
                 sal_Int32 nPos = nCount;
@@ -972,6 +973,7 @@ void SwOLELRUCache::InsertObj( SwOLEObj& rObj )
     }
     if (it == m_OleObjects.end())
     {
+        std::shared_ptr<SwOLELRUCache> tmp(g_pOLELRU_Cache); // prevent delete this
         // try to remove objects if necessary
         sal_Int32 nCount = m_OleObjects.size();
         sal_Int32 nPos = nCount-1;
@@ -995,7 +997,10 @@ void SwOLELRUCache::RemoveObj( SwOLEObj& rObj )
     }
     if (m_OleObjects.empty())
     {
-        DELETEZ( pOLELRU_Cache );
+        if (g_pOLELRU_Cache.unique()) // test that we're not in InsertObj()
+        {
+            g_pOLELRU_Cache.reset();
+        }
     }
 }
 
commit dfb0fdfb749944faf5b88e91e9aecf8909266a96
Author: Katarina Behrens <Katarina.Behrens at cib.de>
Date:   Thu Nov 5 17:57:07 2015 +0100

    tdf#92296: Fix off-by-one formatting of text runs on OOXML export
    
    This essentially reverts commit 8865ed2efecd03722d10e522265f31c99b13b2bb
    and implements a different fix for tdf#90812:
    If the formatting of entire cell is uniform, remove formatting of the
    leading text run and create corresponding cell style. In all other cases,
    write out formattings for each individual run.
    
    Change-Id: I7724b7a474f773f2cdc39e9150d843642fb05bbe
    Reviewed-on: https://gerrit.libreoffice.org/19811
    Reviewed-by: Eike Rathke <erack at redhat.com>
    Tested-by: Eike Rathke <erack at redhat.com>
    
    Related tdf#92296, tdf#90812: Make this test more strict
    
    i.e. not only make sure that rPr is there, but also test the text
    chunk has the right font colour ( it went off-by-one in regression
    caused by the fix of tdf#90812 )
    
    Change-Id: I3788a845393686ed621743e117b7eb439e38e0b3
    Reviewed-on: https://gerrit.libreoffice.org/22420
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: David Tardon <dtardon at redhat.com>
    (cherry picked from commit 14279955b467241a70b61f993c334757c2b4a756)

diff --git a/sc/qa/unit/subsequent_export-test.cxx b/sc/qa/unit/subsequent_export-test.cxx
index b1db159..eb0865e 100644
--- a/sc/qa/unit/subsequent_export-test.cxx
+++ b/sc/qa/unit/subsequent_export-test.cxx
@@ -2640,7 +2640,8 @@ void ScExportTest::testSheetRunParagraphProperty()
     xmlDocPtr pDoc = XPathHelper::parseExport(&(*xDocSh), m_xSFactory, "xl/sharedStrings.xml", XLSX);
     CPPUNIT_ASSERT(pDoc);
 
-    assertXPath(pDoc, "/x:sst/x:si/x:r[1]/x:rPr[1]", 1);
+    OUString aColor = getXPath(pDoc, "/x:sst/x:si/x:r[1]/x:rPr[1]/x:color", "rgb");
+    CPPUNIT_ASSERT_EQUAL(OUString("FFFF0000"), aColor);
 
     xDocSh->DoClose();
 }
commit 8f45f60a0d8a8b5a1b1e6f520b685b62aec29a59
Author: Caolán McNamara <caolanm at redhat.com>
Date:   Mon Jun 29 12:26:16 2015 +0200

    update graphite to 1.3.5
    
    (cherry picked from commit c64ea526dc71da6e3aad188ac71e58047ed74b5a)
    
    and sync the various upgrade patches together
    
    Change-Id: I3287d51430d7a0901dd8bbf2458b845bcf92a8d2
    Reviewed-on: https://gerrit.libreoffice.org/22210
    Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
    Tested-by: Miklos Vajna <vmiklos at collabora.co.uk>
    (cherry picked from commit d2ef084eb0e26b5ee18133269249e7a80efb23d6)

diff --git a/download.lst b/download.lst
index 77a2dfb..6da1867 100644
--- a/download.lst
+++ b/download.lst
@@ -51,8 +51,7 @@ export FREEHAND_TARBALL := libfreehand-0.1.1.tar.bz2
 export FREETYPE_TARBALL := dbf2caca1d3afd410a29217a9809d397-freetype-2.4.8.tar.bz2
 export GLEW_TARBALL := 594eb47b4b1210e25438d51825404d5a-glew-1.10.0.zip
 export GLM_TARBALL := bae83fa5dc7f081768daace6e199adc3-glm-0.9.4.6-libreoffice.zip
-export GRAPHITE_MD5SUM := 2ef839348fe28e3b923bf8cced440227
-export GRAPHITE_TARBALL := graphite2-1.2.4.tgz
+export GRAPHITE_TARBALL := 28935e208c311761c29983c739db08d8-graphite2-minimal-1.3.5.tgz
 export HARFBUZZ_MD5SUM := 0e27e531f4c4acff601ebff0957755c2
 export HARFBUZZ_TARBALL := harfbuzz-0.9.40.tar.bz2
 export HSQLDB_TARBALL := 17410483b5b5f267aa18b7e00b65e6e0-hsqldb_1_8_0.zip
diff --git a/editeng/source/editeng/impedit3.cxx b/editeng/source/editeng/impedit3.cxx
index a0d2421..3e4bceee 100644
--- a/editeng/source/editeng/impedit3.cxx
+++ b/editeng/source/editeng/impedit3.cxx
@@ -714,6 +714,8 @@ bool ImpEditEngine::CreateLines( sal_Int32 nPara, sal_uInt32 nStartPosY )
     EditLine aSaveLine( *pLine );
     SvxFont aTmpFont( pNode->GetCharAttribs().GetDefFont() );
 
+    ImplInitLayoutMode( GetRefDevice(), nPara, nIndex );
+
     bool bCalcCharPositions = true;
     boost::scoped_array<long> pBuf(new long[ pNode->Len() ]);
 
@@ -1038,6 +1040,8 @@ bool ImpEditEngine::CreateLines( sal_Int32 nPara, sal_uInt32 nStartPosY )
                 aTmpFont.SetPhysFont( GetRefDevice() );
                 ImplInitDigitMode(GetRefDevice(), aTmpFont.GetLanguage());
 
+                pPortion->SetRightToLeft( GetRightToLeft( nPara, nTmpPos+1 ) );
+
                 if ( bCalcCharPositions || !pPortion->HasValidSize() )
                 {
                     pPortion->GetSize() = aTmpFont.QuickGetTextSize( GetRefDevice(), pParaPortion->GetNode()->GetString(), nTmpPos, pPortion->GetLen(), pBuf.get() );
@@ -1069,8 +1073,6 @@ bool ImpEditEngine::CreateLines( sal_Int32 nPara, sal_uInt32 nStartPosY )
 
                 nTmpWidth += pPortion->GetSize().Width();
 
-                pPortion->SetRightToLeft( GetRightToLeft( nPara, nTmpPos+1 ) );
-
                 sal_Int32 _nPortionEnd = nTmpPos + pPortion->GetLen();
                 if( bScriptSpace && ( _nPortionEnd < pNode->Len() ) && ( nTmpWidth < nXWidth ) && IsScriptChange( EditPaM( pNode, _nPortionEnd ) ) )
                 {
diff --git a/external/graphite/StaticLibrary_graphite.mk b/external/graphite/StaticLibrary_graphite.mk
index de3950f..ddbf995 100644
--- a/external/graphite/StaticLibrary_graphite.mk
+++ b/external/graphite/StaticLibrary_graphite.mk
@@ -19,8 +19,8 @@ $(eval $(call gb_StaticLibrary_set_include,graphite,\
 ))
 
 $(eval $(call gb_StaticLibrary_add_defs,graphite,\
-	-DDISABLE_TRACING \
-	-DGR2_STATIC \
+	-DGRAPHITE2_NTRACING \
+	-DGRAPHITE2_STATIC \
 ))
 
 ifeq ($(COM),GCC)
@@ -43,19 +43,22 @@ $(eval $(call gb_StaticLibrary_add_generated_cxxobjects,graphite,\
 	UnpackedTarball/graphite/src/gr_segment \
 	UnpackedTarball/graphite/src/gr_slot \
 	UnpackedTarball/graphite/src/json \
-	UnpackedTarball/graphite/src/Bidi \
 	UnpackedTarball/graphite/src/CachedFace \
 	UnpackedTarball/graphite/src/CmapCache \
 	UnpackedTarball/graphite/src/Code \
+	UnpackedTarball/graphite/src/Collider \
+	UnpackedTarball/graphite/src/Decompressor \
 	UnpackedTarball/graphite/src/Face \
 	UnpackedTarball/graphite/src/FeatureMap \
 	UnpackedTarball/graphite/src/FileFace \
 	UnpackedTarball/graphite/src/Font \
 	UnpackedTarball/graphite/src/GlyphCache \
 	UnpackedTarball/graphite/src/GlyphFace \
+	UnpackedTarball/graphite/src/Intervals \
 	UnpackedTarball/graphite/src/Justifier \
 	UnpackedTarball/graphite/src/NameTable \
 	UnpackedTarball/graphite/src/Pass \
+	UnpackedTarball/graphite/src/Position \
 	UnpackedTarball/graphite/src/SegCache \
 	UnpackedTarball/graphite/src/SegCacheEntry \
 	UnpackedTarball/graphite/src/SegCacheStore \
diff --git a/external/graphite/UnpackedTarball_graphite.mk b/external/graphite/UnpackedTarball_graphite.mk
index c7fa86e..a162d17 100644
--- a/external/graphite/UnpackedTarball_graphite.mk
+++ b/external/graphite/UnpackedTarball_graphite.mk
@@ -11,10 +11,12 @@ $(eval $(call gb_UnpackedTarball_UnpackedTarball,graphite))
 
 $(eval $(call gb_UnpackedTarball_set_tarball,graphite,$(GRAPHITE_TARBALL)))
 
-# http://projects.palaso.org/issues/1115
+$(eval $(call gb_UnpackedTarball_set_patchlevel,graphite,0))
+
 $(eval $(call gb_UnpackedTarball_add_patches,graphite,\
 	external/graphite/graphite2.issue1115.patch.1 \
     external/graphite/graphite2.win64.patch.1 \
+    external/graphite/ubsan.patch \
 ))
 
 # vim: set noet sw=4 ts=4:
diff --git a/external/graphite/graphite2.issue1115.patch.1 b/external/graphite/graphite2.issue1115.patch.1
index f19c8a3..454114b 100644
--- a/external/graphite/graphite2.issue1115.patch.1
+++ b/external/graphite/graphite2.issue1115.patch.1
@@ -1,6 +1,6 @@
 --- graphite/src/Code.cpp
 +++ graphite/src/Code.cpp
-@@ -169,8 +169,8 @@ Machine::Code::Code(bool is_constraint,
+@@ -175,8 +175,8 @@ Machine::Code::Code(bool is_constraint,
          bytecode_end,
          pre_context,
          rule_length,
@@ -11,7 +11,7 @@
          face.numFeatures(), 
          {1,1,1,1,1,1,1,1, 
           1,1,1,1,1,1,1,255,
-@@ -178,7 +178,7 @@ Machine::Code::Code(bool is_constraint,
+@@ -184,7 +184,7 @@ Machine::Code::Code(bool is_constraint,
           1,1,1,1,1,1,0,0, 
           0,0,0,0,0,0,0,0, 
           0,0,0,0,0,0,0,0, 
@@ -19,4 +19,4 @@
 +         0,0,0,0,0,0,0, static_cast<byte>(silf.numUser())}
      };
      
-     decoder dec(lims, *this);
+     decoder dec(lims, *this, pt);
diff --git a/external/graphite/graphite2.win64.patch.1 b/external/graphite/graphite2.win64.patch.1
index 6bf8c88..e7c36c6 100644
--- a/external/graphite/graphite2.win64.patch.1
+++ b/external/graphite/graphite2.win64.patch.1
@@ -1,7 +1,20 @@
+diff -ur graphite.org/src/inc/Main.h graphite/src/inc/Main.h
+--- graphite.org/src/inc/Main.h	2015-09-07 20:09:25.572279671 +0700
+--- graphite/src/inc/Main.h	2015-09-07 20:09:25.572279671 +0700
+@@ -25,6 +25,9 @@
+ of the License or (at your option) any later version.
+ */
+ #pragma once
++#ifdef _WIN32
++#pragma warning(disable: 4510 4610)
++#endif
+ 
+ #include <cstdlib>
+ #include "graphite2/Types.h"
 diff -ur graphite.org/src/inc/json.h graphite/src/inc/json.h
 --- graphite.org/src/inc/json.h	2015-02-03 14:49:24.408101900 +0100
 +++ graphite/src/inc/json.h	2015-02-03 14:50:59.697552200 +0100
-@@ -78,6 +78,9 @@
+@@ -85,6 +85,9 @@
      json & operator << (string) throw();
      json & operator << (number) throw();
      json & operator << (integer) throw();
@@ -14,8 +27,7 @@ diff -ur graphite.org/src/inc/json.h graphite/src/inc/json.h
 diff -ur graphite.org/src/json.cpp graphite/src/json.cpp
 --- graphite.org/src/json.cpp	2015-02-03 14:49:24.409102000 +0100
 +++ graphite/src/json.cpp	2015-02-03 14:50:49.814986900 +0100
-@@ -119,6 +119,9 @@
- json & json::operator << (json::number f) throw()   { context(seq); fprintf(_stream, "%g", f); return *this; }
+@@ -134,5 +134,8 @@
  json & json::operator << (json::integer d) throw()  { context(seq); fprintf(_stream, "%ld", d); return *this; }
  json & json::operator << (long unsigned d) throw()  { context(seq); fprintf(_stream, "%ld", d); return *this; }
 +#ifdef _WIN64
@@ -27,17 +39,17 @@ diff -ur graphite.org/src/json.cpp graphite/src/json.cpp
 diff -ur graphite.org/src/Pass.cpp graphite/src/Pass.cpp
 --- graphite.org/src/Pass.cpp	2015-02-03 14:49:24.413102200 +0100
 +++ graphite/src/Pass.cpp	2015-02-03 14:50:37.873303900 +0100
-@@ -466,7 +466,7 @@
-     {
-         if (r->rule->preContext > fsm.slots.context())  continue;
-     *fsm.dbgout << json::flat << json::object
--                    << "id"     << r->rule - m_rules
-+                    << "id"     << static_cast<size_t>(r->rule - m_rules)
+@@ -544,7 +544,7 @@
+         if (r->rule->preContext > fsm.slots.context())
+             continue;
+         *fsm.dbgout << json::flat << json::object
+-                    << "id" << r->rule - m_rules
++                    << "id" << static_cast<size_t>(r->rule - m_rules)
                      << "failed" << true
                      << "input" << json::flat << json::object
                          << "start" << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, -r->rule->preContext)))
-@@ -480,7 +480,7 @@
- void Pass::dumpRuleEventOutput(const FiniteStateMachine & fsm, const Rule & r, Slot * const last_slot) const
+@@ -558,7 +558,7 @@
+ void Pass::dumpRuleEventOutput(const FiniteStateMachine & fsm, Machine & m, const Rule & r, Slot * const last_slot) const
  {
      *fsm.dbgout     << json::item << json::flat << json::object
 -                        << "id"     << &r - m_rules
diff --git a/external/graphite/ubsan.patch b/external/graphite/ubsan.patch
new file mode 100644
index 0000000..b1617b1
--- /dev/null
+++ b/external/graphite/ubsan.patch
@@ -0,0 +1,51 @@
+--- src/Pass.cpp
++++ src/Pass.cpp
+@@ -294,7 +294,7 @@
+         s->rules = begin;
+         s->rules_end = (end - begin <= FiniteStateMachine::MAX_RULES)? end :
+             begin + FiniteStateMachine::MAX_RULES;
+-        qsort(begin, end - begin, sizeof(RuleEntry), &cmpRuleEntry);
++        if (end != begin) qsort(begin, end - begin, sizeof(RuleEntry), &cmpRuleEntry);
+     }
+ 
+     return true;
+--- src/gr_face.cpp
++++ src/gr_face.cpp
+@@ -87,7 +87,7 @@
+ 
+     Face *res = new Face(appFaceHandle, *ops);
+     if (res && load_face(*res, faceOptions))
+-        return static_cast<gr_face *>(res);
++        return reinterpret_cast<gr_face *>(res);
+ 
+     delete res;
+     return 0;
+@@ -195,7 +195,7 @@
+ 
+ void gr_face_destroy(gr_face *face)
+ {
+-    delete face;
++    delete static_cast<Face *>(face);
+ }
+ 
+ 
+--- src/gr_font.cpp
++++ src/gr_font.cpp
+@@ -50,7 +50,7 @@
+     if (face == 0)  return 0;
+ 
+     Font * const res = new Font(ppm, *face, appFontHandle, font_ops);
+-    return static_cast<gr_font*>(res);
++    return reinterpret_cast<gr_font*>(res);
+ }
+ 
+ gr_font* gr_make_font_with_advance_fn(float ppm/*pixels per em*/, const void* appFontHandle/*non-NULL*/, gr_advance_fn getAdvance, const gr_face * face/*needed for scaling*/)
+@@ -61,7 +61,7 @@
+ 
+ void gr_font_destroy(gr_font *font)
+ {
+-    delete font;
++    delete static_cast<Font *>(font);
+ }
+ 
+ 
diff --git a/vcl/inc/graphite_layout.hxx b/vcl/inc/graphite_layout.hxx
index 55d0d67..b97b351 100644
--- a/vcl/inc/graphite_layout.hxx
+++ b/vcl/inc/graphite_layout.hxx
@@ -93,9 +93,10 @@ public:
 private:
     const gr_face *         mpFace; // not owned by layout
     gr_font *               mpFont; // not owned by layout
-    int                     mnSegCharOffset; // relative to ImplLayoutArgs::mpStr
+    unsigned int            mnSegCharOffset; // relative to ImplLayoutArgs::mpStr
     long                    mnWidth;
     std::vector<int>        mvChar2BaseGlyph;
+    std::vector<int>        mvChar2Glyph;
     std::vector<int>        mvGlyph2Char;
     std::vector<int>        mvCharDxs;
     std::vector<int>        mvCharBreaks;
@@ -109,8 +110,6 @@ public:
     // used by upper layers
     virtual bool  LayoutText( ImplLayoutArgs& ) SAL_OVERRIDE;    // first step of layout
     // split into two stages to allow dc to be restored on the segment
-    gr_segment * CreateSegment(ImplLayoutArgs& rArgs);
-    bool LayoutGlyphs(ImplLayoutArgs& rArgs, gr_segment * pSegment);
 
     virtual void  AdjustLayout( ImplLayoutArgs& ) SAL_OVERRIDE;  // adjusting positions
 
@@ -145,13 +144,14 @@ public:
     static const int EXTRA_CONTEXT_LENGTH;
 private:
     void expandOrCondense(ImplLayoutArgs &rArgs);
-    void    fillFrom(gr_segment * rSeg, ImplLayoutArgs & rArgs, float fScaling);
+    void    fillFrom(gr_segment * rSeg, ImplLayoutArgs & rArgs, float fScaling, bool bRtl, int firstCharOffset);
 
     float append(gr_segment * pSeg,
                 ImplLayoutArgs & rArgs,
                 const gr_slot * pSlot, float gOrigin,
                 float nextGlyphOrigin, float fScaling,
-                long & rDXOffset, bool bIsBase, int baseChar);
+                long & rDXOffset, bool bIsBase, int baseChar, int baseGlyph, bool bRtl);
+    unsigned int ScanFwdForChar(int &findChar, bool fallback) const;
 };
 
 #endif // INCLUDED_VCL_INC_GRAPHITE_LAYOUT_HXX
diff --git a/vcl/source/fontsubset/sft.cxx b/vcl/source/fontsubset/sft.cxx
index 6758df2..fe4abe7 100644
--- a/vcl/source/fontsubset/sft.cxx
+++ b/vcl/source/fontsubset/sft.cxx
@@ -1770,6 +1770,9 @@ int GetTTGlyphComponents(TrueTypeFont *ttf, sal_uInt32 glyphID, std::vector< sal
 
     const sal_uInt8* glyf = getTable(ttf, O_glyf);
     const sal_uInt8* ptr = glyf + ttf->goffsets[glyphID];
+    const sal_uInt8* nptr = glyf + ttf->goffsets[glyphID+1];
+    if (nptr <= ptr)
+        return 0;
 
     glyphlist.push_back( glyphID );
 
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx
index f2974a2..44d1f45 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -9006,22 +9006,27 @@ void PDFWriterImpl::drawLayout( SalLayout& rLayout, const OUString& rText, bool
             else if( pCharPosAry[i] >= nMinCharPos && pCharPosAry[i] <= nMaxCharPos )
             {
                 int nChars = 1;
-                aUnicodes.push_back( rText[ pCharPosAry[i] ] );
                 pUnicodesPerGlyph[i] = 1;
                 // try to handle ligatures and such
                 if( i < nGlyphs-1 )
                 {
                     nChars = pCharPosAry[i+1] - pCharPosAry[i];
+                    int start = pCharPosAry[i];
                     // #i115618# fix for simple RTL+CTL cases
-                    // TODO: sanitize for RTL ligatures, more complex CTL, etc.
+                    // supports RTL ligatures. TODO: more complex CTL, etc.
                     if( nChars < 0 )
+                    {
                         nChars = -nChars;
-                    else if( nChars == 0 )
+                        start = pCharPosAry[i+1] + 1;
+                    }
+                    else if (nChars == 0)
                         nChars = 1;
                     pUnicodesPerGlyph[i] = nChars;
-                    for( int n = 1; n < nChars; n++ )
-                        aUnicodes.push_back( rText[ pCharPosAry[i] + n ] );
+                    for( int n = 0; n < nChars; n++ )
+                        aUnicodes.push_back( rText[ start + n ] );
                 }
+                else
+                    aUnicodes.push_back( rText[ pCharPosAry[i] ] );
                 // #i36691# hack that is needed because currently the pGlyphs[]
                 // argument is ignored for embeddable fonts and so the layout
                 // engine's glyph work is ignored (i.e. char mirroring)
diff --git a/vcl/source/glyphs/graphite_layout.cxx b/vcl/source/glyphs/graphite_layout.cxx
index a4aa99c..db50a22 100644
--- a/vcl/source/glyphs/graphite_layout.cxx
+++ b/vcl/source/glyphs/graphite_layout.cxx
@@ -27,7 +27,7 @@
 #undef NDEBUG
 #endif
 
-// #define GRLAYOUT_DEBUG 1
+//#define GRLAYOUT_DEBUG 1
 
 #include <algorithm>
 #include <cassert>
@@ -89,23 +89,6 @@ namespace
         return !(b > i) && i < e;
     }
 
-    int findSameDirLimit(const sal_Unicode* buffer, int charCount, bool rtl)
-    {
-        UErrorCode status = U_ZERO_ERROR;
-        UBiDi *ubidi = ubidi_openSized(charCount, 0, &status);
-        int limit = 0;
-        ubidi_setPara(ubidi, reinterpret_cast<const UChar *>(buffer), charCount,
-            (rtl)?UBIDI_DEFAULT_RTL:UBIDI_DEFAULT_LTR, NULL, &status);
-        UBiDiLevel level = 0;
-        ubidi_getLogicalRun(ubidi, 0, &limit, &level);
-        ubidi_close(ubidi);
-        if ((rtl && !(level & 1)) || (!rtl && (level & 1)))
-        {
-            limit = 0;
-        }
-        return limit;
-    }
-
     template <typename T>
     T maximum(T a, T b)
     {
@@ -125,43 +108,21 @@ namespace
 //        o Querying clustering relationships.
 //        o manipulations that affect neighouring glyphs.
 
-const int GraphiteLayout::EXTRA_CONTEXT_LENGTH = 10;
+const int GraphiteLayout::EXTRA_CONTEXT_LENGTH = 32;
 
-// find first slot of cluster and first slot of subsequent cluster
-static void findFirstClusterSlot(const gr_slot* base, gr_slot const** first, gr_slot const** after, int * firstChar, int * lastChar, bool bRtl)
+const gr_slot *get_next_base(const gr_slot *slot, bool bRtl)
 {
-    if (gr_slot_attached_to(base) == NULL)
-    {
-        *first = base;
-        *after = (bRtl)? gr_slot_prev_in_segment(base) :
-            gr_slot_next_in_segment(base);
-        *firstChar = gr_slot_before(base);
-        *lastChar = gr_slot_after(base);
-    }
-    const gr_slot * attachment = gr_slot_first_attachment(base);
-    while (attachment)
-    {
-        if (gr_slot_origin_X(*first) > gr_slot_origin_X(attachment))
-            *first = attachment;
-        const gr_slot* attachmentNext = (bRtl)?
-            gr_slot_prev_in_segment(attachment) : gr_slot_next_in_segment(attachment);
-        if (attachmentNext)
-        {
-            if (*after && (gr_slot_origin_X(*after) < gr_slot_origin_X(attachmentNext)))
-                *after = attachmentNext;
-        }
-        else
-        {
-            *after = NULL;
-        }
-        if (gr_slot_before(attachment) < *firstChar)
-            *firstChar = gr_slot_before(attachment);
-        if (gr_slot_after(attachment) > *lastChar)
-            *lastChar = gr_slot_after(attachment);
-        if (gr_slot_first_attachment(attachment))
-            findFirstClusterSlot(attachment, first, after, firstChar, lastChar, bRtl);
-        attachment = gr_slot_next_sibling_attachment(attachment);
-    }
+    for ( ; slot; slot = bRtl ? gr_slot_prev_in_segment(slot) : gr_slot_next_in_segment(slot))
+        if (!gr_slot_attached_to(slot) || gr_slot_can_insert_before(slot))
+            break;
+    return slot;
+}
+
+bool isWhite(sal_Unicode nChar)
+{
+    if (nChar <= 0x0020 || nChar == 0x00A0 || (nChar >= 0x2000 && nChar <= 0x200F) || nChar == 0x3000)
+        return true;
+    return false;
 }
 
 // The Graphite glyph stream is really a sequence of glyph attachment trees
@@ -169,202 +130,131 @@ static void findFirstClusterSlot(const gr_slot* base, gr_slot const** first, gr_
 //  finds each non-attached base glyph and calls append to record them as a
 //  sequence of clusters.
 void
-GraphiteLayout::fillFrom(gr_segment * pSegment, ImplLayoutArgs &rArgs, float fScaling)
+GraphiteLayout::fillFrom(gr_segment * pSegment, ImplLayoutArgs &rArgs, float fScaling, bool bRtl, int lastCharPos)
 {
-    bool bRtl(rArgs.mnFlags & SalLayoutFlags::BiDiRtl);
-    int nCharRequested = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
-    int nChar = gr_seg_n_cinfo(pSegment);
     float fMinX = gr_seg_advance_X(pSegment);
     float fMaxX = 0.0f;
     long nDxOffset = 0; // from dropped glyphs
-    int nFirstCharInCluster = 0;
-    int nLastCharInCluster = 0;
+    int origNumGlyphs = mvGlyphs.size();
     unsigned int nGlyphs = gr_seg_n_slots(pSegment);
-    mvGlyph2Char.assign(nGlyphs, -1);
-    mvGlyphs.reserve(nGlyphs);
-
-    if (bRtl)
+    mvGlyph2Char.resize(mvGlyph2Char.size() + nGlyphs, -1);
+    mvGlyphs.reserve(mvGlyphs.size() + nGlyphs);
+    int clusterStart = -1;
+    int clusterFirstChar = -1;
+    const gr_slot *nextBaseSlot;
+    const sal_Unicode *pStr = rArgs.mpStr;
+    int firstChar;
+
+    if (!nGlyphs || lastCharPos - mnSegCharOffset == 0) return;
+    const gr_slot* baseSlot = bRtl ? gr_seg_last_slot(pSegment) : gr_seg_first_slot(pSegment);
+    // find first base
+    while (baseSlot && gr_slot_attached_to(baseSlot) != NULL && !gr_slot_can_insert_before(baseSlot))
+        baseSlot = bRtl ? gr_slot_prev_in_segment(baseSlot) : gr_slot_next_in_segment(baseSlot);
+    assert(baseSlot);
+    int nextChar = gr_cinfo_base(gr_seg_cinfo(pSegment, gr_slot_before(baseSlot))) + mnSegCharOffset;
+    float thisBoundary = 0.;
+    float nextBoundary = gr_slot_origin_X(baseSlot);
+    // now loop over bases
+    for ( ; baseSlot; baseSlot = nextBaseSlot)
     {
-        const gr_slot* baseSlot = gr_seg_last_slot(pSegment);
-        // find first base
-        while (baseSlot && (gr_slot_attached_to(baseSlot) != NULL))
-            baseSlot = gr_slot_prev_in_segment(baseSlot);
-        int iChar = nChar - 1;
-        int iNextChar = nChar - 1;
-        bool reordered = false;
-        int nBaseGlyphIndex = 0;
-        // now loop over bases
-        while (baseSlot)
+        firstChar = nextChar;
+        thisBoundary = nextBoundary;
+        nextBaseSlot = get_next_base(bRtl ? gr_slot_prev_in_segment(baseSlot) : gr_slot_next_in_segment(baseSlot), bRtl);
+        nextChar = nextBaseSlot ? gr_cinfo_base(gr_seg_cinfo(pSegment, gr_slot_before(nextBaseSlot))) + mnSegCharOffset : -1;
+        nextBoundary = nextBaseSlot ? gr_slot_origin_X(nextBaseSlot) : gr_seg_advance_X(pSegment);
+
+        if (firstChar < mnMinCharPos || firstChar >= mnEndCharPos)
         {
-            bool bCluster = !reordered;
-            const gr_slot * clusterFirst = NULL;
-            const gr_slot * clusterAfter = NULL;
-            int firstChar = -1;
-            int lastChar = -1;
-            findFirstClusterSlot(baseSlot, &clusterFirst, &clusterAfter, &firstChar, &lastChar, bRtl);
-            iNextChar = minimum<int>(firstChar, iNextChar);
-            if (bCluster)
-            {
-                nBaseGlyphIndex = mvGlyphs.size();
-                mvGlyph2Char[nBaseGlyphIndex] = iChar + mnSegCharOffset;
-                nFirstCharInCluster = firstChar;
-                nLastCharInCluster = lastChar;
-            }
-            else
-            {
-                mvGlyph2Char[mvGlyphs.size()] = firstChar + mnSegCharOffset;
-                nFirstCharInCluster = minimum<int>(firstChar, nFirstCharInCluster);
-                nLastCharInCluster = maximum<int>(firstChar, nLastCharInCluster);
-            }
-            float leftBoundary = gr_slot_origin_X(clusterFirst);
-            float rightBoundary = (clusterAfter)?
-                gr_slot_origin_X(clusterAfter) : gr_seg_advance_X(pSegment);
-            if (
-                lastChar < iChar && clusterAfter &&
-                 (gr_cinfo_after(gr_seg_cinfo(pSegment, iChar)) >
-                 static_cast<int>(gr_slot_index(clusterAfter)))
-               )
-            {
-                reordered = true;
-            }
-            else
-            {
-                reordered = false;
-                iChar = iNextChar - 1;
-            }
-            if (mnSegCharOffset + nFirstCharInCluster >= mnMinCharPos &&
-                mnSegCharOffset + nFirstCharInCluster < mnEndCharPos)
-            {
-                fMinX = minimum<float>(fMinX, leftBoundary);
-                fMaxX = maximum<float>(fMaxX, rightBoundary);
-                if (!reordered)
-                {
-                    for (int i = nFirstCharInCluster; i <= nLastCharInCluster; i++)
-                    {
-                        if (mnSegCharOffset + i >= mnEndCharPos)
-                            break;
-                        // from the point of view of the dx array, the xpos is
-                        // the origin of the first glyph of the cluster rtl
-                        mvCharDxs[mnSegCharOffset + i - mnMinCharPos] =
-                            static_cast<int>(leftBoundary * fScaling) + nDxOffset;
-                        mvCharBreaks[mnSegCharOffset + i - mnMinCharPos] = gr_cinfo_break_weight(gr_seg_cinfo(pSegment, i));
-                    }
-                    mvChar2BaseGlyph[mnSegCharOffset + nFirstCharInCluster - mnMinCharPos] = nBaseGlyphIndex;
-                }
-                append(pSegment, rArgs, baseSlot, gr_slot_origin_X(baseSlot), rightBoundary, fScaling,
-                       nDxOffset, bCluster, mnSegCharOffset + firstChar);
-            }
-            if (mnSegCharOffset + nLastCharInCluster < mnMinCharPos)
-                break;
-            baseSlot = gr_slot_next_sibling_attachment(baseSlot);
+            // handle clipping of diacritic from base
+            nextBaseSlot = bRtl ? gr_slot_prev_in_segment(baseSlot) : gr_slot_next_in_segment(baseSlot);
+            nextBoundary = nextBaseSlot ? gr_slot_origin_X(nextBaseSlot) : gr_seg_advance_X(pSegment);
+            nextChar = nextBaseSlot ? gr_cinfo_base(gr_seg_cinfo(pSegment, gr_slot_before(nextBaseSlot))) + mnSegCharOffset : -1;
+            continue;
         }
-    }
-    else
-    {
-        const gr_slot* baseSlot = gr_seg_first_slot(pSegment);
-        // find first base
-        while (baseSlot && (gr_slot_attached_to(baseSlot) != NULL))
-            baseSlot = gr_slot_next_in_segment(baseSlot);
-        int iChar = 0; // relative to segment
-        int iNextChar = 0;
-        bool reordered = false;
-        int nBaseGlyphIndex = 0;
-        // now loop over bases
-        while (baseSlot)
+        // handle reordered clusters. Presumes reordered glyphs have monotonic opposite char index until the cluster base.
+        bool isReordered = (nextBaseSlot && ((bRtl != (gr_cinfo_base(gr_seg_cinfo(pSegment, gr_slot_before(nextBaseSlot))) < firstChar - mnSegCharOffset))
+                                             || gr_cinfo_base(gr_seg_cinfo(pSegment, gr_slot_before(nextBaseSlot))) == firstChar - mnSegCharOffset));
+        if (clusterStart >= 0 && !isReordered)      // we hit the base (end) of a reordered cluster
         {
-            bool bCluster = !reordered;
-            const gr_slot * clusterFirst = NULL;
-            const gr_slot * clusterAfter = NULL;
-            int firstChar = -1;
-            int lastChar = -1;
-            findFirstClusterSlot(baseSlot, &clusterFirst, &clusterAfter, &firstChar, &lastChar, bRtl);
-            iNextChar = maximum<int>(lastChar, iNextChar);
-            if (bCluster)
-            {
-                nBaseGlyphIndex = mvGlyphs.size();
-                mvGlyph2Char[nBaseGlyphIndex] = iChar + mnSegCharOffset;
-                nFirstCharInCluster = firstChar;
-                nLastCharInCluster = lastChar;
-            }
-            else
-            {
-                mvGlyph2Char[mvGlyphs.size()] = firstChar + mnSegCharOffset;
-                nFirstCharInCluster = minimum<int>(firstChar, nFirstCharInCluster);
-                nLastCharInCluster = maximum<int>(lastChar, nLastCharInCluster);
-            }
-            if (
-                firstChar > iChar &&
-                 (gr_cinfo_before(gr_seg_cinfo(pSegment, iChar)) >
-                 static_cast<int>(gr_slot_index(clusterFirst)))
-               )
+            int clusterEnd = mvGlyphs.size();
+            for (int i = clusterStart; i < clusterEnd; ++i)
+                mvGlyph2Char[i] = firstChar;
+            if (bRtl)
             {
-                reordered = true;
+                for ( ; clusterFirstChar < firstChar; ++clusterFirstChar)
+                    if (clusterFirstChar >= mnMinCharPos && clusterFirstChar < mnEndCharPos)
+                    {
+                        mvChar2BaseGlyph[clusterFirstChar - mnMinCharPos] = clusterStart;  // lowest glyphItem index
+                        mvCharDxs[clusterFirstChar - mnMinCharPos] = static_cast<int>(thisBoundary * fScaling) + mnWidth + nDxOffset;
+                    }
             }
             else
             {
-                reordered = false;
-                iChar = iNextChar + 1;
-            }
-            float leftBoundary = gr_slot_origin_X(clusterFirst);
-            float rightBoundary = (clusterAfter)?
-                gr_slot_origin_X(clusterAfter) : gr_seg_advance_X(pSegment);
-            int bFirstChar = gr_cinfo_base(gr_seg_cinfo(pSegment, nFirstCharInCluster));
-            if (mnSegCharOffset + bFirstChar >= mnMinCharPos &&
-                mnSegCharOffset + bFirstChar < mnEndCharPos)
-            {
-                fMinX = minimum<float>(fMinX, leftBoundary);
-                fMaxX = maximum<float>(fMaxX, rightBoundary);
-                if (!reordered)
-                {
-                    for (int i = nFirstCharInCluster; i <= nLastCharInCluster; i++)
+                for ( ; clusterFirstChar > firstChar; --clusterFirstChar)
+                    if (clusterFirstChar < mnEndCharPos && clusterFirstChar >= mnMinCharPos)
                     {
-                        int ibase = gr_cinfo_base(gr_seg_cinfo(pSegment, i));
-                        if (mnSegCharOffset + ibase >= mnEndCharPos)
-                            break;
-                        // from the point of view of the dx array, the xpos is
-                        // the origin of the first glyph of the next cluster ltr
-                        mvCharDxs[mnSegCharOffset + ibase - mnMinCharPos] =
-                            static_cast<int>(rightBoundary * fScaling) + nDxOffset;
-                        mvCharBreaks[mnSegCharOffset + ibase - mnMinCharPos] = gr_cinfo_break_weight(gr_seg_cinfo(pSegment, i));
+                        mvChar2BaseGlyph[clusterFirstChar - mnMinCharPos] = clusterStart;
+                        mvCharDxs[clusterFirstChar - mnMinCharPos] = static_cast<int>(nextBoundary * fScaling) + mnWidth + nDxOffset;
                     }
-                    // only set mvChar2BaseGlyph for first character of cluster
-                    mvChar2BaseGlyph[mnSegCharOffset + bFirstChar - mnMinCharPos] = nBaseGlyphIndex;
-                }
-                append(pSegment, rArgs, baseSlot, gr_slot_origin_X(baseSlot), rightBoundary, fScaling,
-                       nDxOffset, true, mnSegCharOffset + firstChar);
             }
-            if (mnSegCharOffset + bFirstChar >= mnEndCharPos)
-                break;
-            baseSlot = gr_slot_next_sibling_attachment(baseSlot);
+            clusterStart = -1;
+            clusterFirstChar = -1;
+        }
+        else if (clusterStart < 0 && isReordered) // we hit the start of a reordered cluster
+        {
+            clusterStart = mvGlyphs.size();
+            clusterFirstChar = firstChar;
+        }
+
+        int baseGlyph = mvGlyphs.size();
+        int scaledGlyphPos = round_to_long(gr_slot_origin_X(baseSlot) * fScaling) + mnWidth + nDxOffset;
+        if (mvChar2Glyph[firstChar - mnMinCharPos] == -1 || mvGlyphs[mvChar2Glyph[firstChar - mnMinCharPos]].maLinearPos.X() < scaledGlyphPos)
+        {
+            mvChar2Glyph[firstChar - mnMinCharPos] = mvGlyphs.size();
+            mvCharDxs[firstChar - mnMinCharPos] = static_cast<int>((bRtl ? thisBoundary : nextBoundary) * fScaling) + mnWidth + nDxOffset;
+            mvChar2BaseGlyph[firstChar - mnMinCharPos] = baseGlyph;
+            mvCharBreaks[firstChar - mnMinCharPos] = gr_cinfo_break_weight(gr_seg_cinfo(pSegment, gr_slot_before(baseSlot)));
         }
+        mvGlyph2Char[baseGlyph] = firstChar;
+        append(pSegment, rArgs, baseSlot, thisBoundary, nextBoundary, fScaling, nDxOffset, true, firstChar, baseGlyph, bRtl);
+        if (thisBoundary < fMinX) fMinX = thisBoundary;
+        if (nextBoundary > fMaxX && (nextChar < mnMinCharPos || nextChar >= mnEndCharPos || !isWhite(pStr[nextChar]) || fMaxX <= 0.0f))
+            fMaxX = nextBoundary;
     }
+
     long nXOffset = round_to_long(fMinX * fScaling);
-    mnWidth = round_to_long(fMaxX * fScaling) - nXOffset + nDxOffset;
-    if (mnWidth < 0)
-    {
-        // This can happen when there was no base inside the range
-        mnWidth = 0;
-    }
+    long nXEnd = round_to_long(fMaxX * fScaling);
+    int nCharRequested = minimum<int>(lastCharPos, mnEndCharPos) - mnMinCharPos;
+    int firstCharOffset = maximum<int>(mnSegCharOffset, mnMinCharPos) - mnMinCharPos;
     // fill up non-base char dx with cluster widths from previous base glyph
     if (bRtl)
     {
         if (mvCharDxs[nCharRequested-1] == -1)
-            mvCharDxs[nCharRequested-1] = 0;
+            mvCharDxs[nCharRequested-1] = nXEnd - nXOffset + mnWidth + nDxOffset;
         else
-            mvCharDxs[nCharRequested-1] -= nXOffset;
-        for (int i = nCharRequested - 2; i >= 0; i--)
+            mvCharDxs[nCharRequested-1] = nXEnd - mvCharDxs[nCharRequested-1] + 2 * (mnWidth + nDxOffset);
+#ifdef GRLAYOUT_DEBUG
+            fprintf(grLog(),"%d,%d ", nCharRequested - 1, (int)mvCharDxs[nCharRequested-1]);
+#endif
+        for (int i = nCharRequested - 2; i >= firstCharOffset; i--)
         {
             if (mvCharDxs[i] == -1) mvCharDxs[i] = mvCharDxs[i+1];
-            else mvCharDxs[i] -= nXOffset;
+            else mvCharDxs[i] = nXEnd - mvCharDxs[i] + 2 * (mnWidth + nDxOffset);
+#ifdef GRLAYOUT_DEBUG
+            fprintf(grLog(),"%d,%d ", (int)i, (int)mvCharDxs[i]);
+#endif
         }
     }
     else
     {
-        if (mvCharDxs[0] == -1)
-            mvCharDxs[0] = 0;
+        if (mvCharDxs[firstCharOffset] == -1)
+            mvCharDxs[firstCharOffset] = 0;
         else
-            mvCharDxs[0] -= nXOffset;
-        for (int i = 1; i < nCharRequested; i++)
+            mvCharDxs[firstCharOffset] -= nXOffset;
+#ifdef GRLAYOUT_DEBUG
+            fprintf(grLog(),"%d,%d ", firstCharOffset, (int)mvCharDxs[firstCharOffset]);
+#endif
+        for (int i = firstCharOffset + 1; i < nCharRequested; i++)
         {
             if (mvCharDxs[i] == -1) mvCharDxs[i] = mvCharDxs[i-1];
             else mvCharDxs[i] -= nXOffset;
@@ -376,11 +266,17 @@ GraphiteLayout::fillFrom(gr_segment * pSegment, ImplLayoutArgs &rArgs, float fSc
     // remove offset due to context if there is one
     if (nXOffset != 0)
     {
-        for (size_t i = 0; i < mvGlyphs.size(); i++)
+        for (size_t i = origNumGlyphs; i < mvGlyphs.size(); i++)
             mvGlyphs[i].maLinearPos.X() -= nXOffset;
     }
+    mnWidth += nXEnd - nXOffset + nDxOffset;
+    if (mnWidth < 0)
+    {
+        // This can happen when there was no base inside the range
+        mnWidth = 0;
+    }
 #ifdef GRLAYOUT_DEBUG
-    fprintf(grLog(), "fillFrom %" SAL_PRI_SIZET "u glyphs offset %ld width %ld\n", mvGlyphs.size(), nXOffset, mnWidth);
+    fprintf(grLog(), "fillFrom %" SAL_PRI_SIZET "u glyphs offset %ld width %ld for %d\n", mvGlyphs.size(), nXOffset, mnWidth, nCharRequested);
 #endif
 }
 
@@ -389,37 +285,29 @@ GraphiteLayout::fillFrom(gr_segment * pSegment, ImplLayoutArgs &rArgs, float fSc
 float
 GraphiteLayout::append(gr_segment *pSeg, ImplLayoutArgs &rArgs,
     const gr_slot * gi, float gOrigin, float nextGlyphOrigin, float scaling, long & rDXOffset,
-    bool bIsBase, int baseChar)
+    bool bIsBase, int baseChar, int baseGlyph, bool bRtl)
 {
-    bool bRtl(rArgs.mnFlags & SalLayoutFlags::BiDiRtl);
-    float nextOrigin;
     assert(gi);
-    assert(gr_slot_before(gi) <= gr_slot_after(gi));
-    int firstChar = gr_slot_before(gi) + mnSegCharOffset;
+    // assert(gr_slot_before(gi) <= gr_slot_after(gi));
+    int firstChar = gr_cinfo_base(gr_seg_cinfo(pSeg, gr_slot_before(gi))) + mnSegCharOffset;
     assert(mvGlyphs.size() < mvGlyph2Char.size());
-    if (!bIsBase) mvGlyph2Char[mvGlyphs.size()] = baseChar;//firstChar;
-    // is the next glyph attached or in the next cluster?
-    //glyph_set_range_t iAttached = gi.attachedClusterGlyphs();
-    const gr_slot * pFirstAttached = gr_slot_first_attachment(gi);
-    const gr_slot * pNextSibling = gr_slot_next_sibling_attachment(gi);
-    if (pFirstAttached)
-        nextOrigin = gr_slot_origin_X(pFirstAttached);
-    else if (!bIsBase && pNextSibling)
-        nextOrigin = gr_slot_origin_X(pNextSibling);
-    else
-        nextOrigin = nextGlyphOrigin;
+    if (firstChar < mnMinCharPos || firstChar >= mnEndCharPos)
+        return nextGlyphOrigin;
+
     long glyphId = gr_slot_gid(gi);
     long deltaOffset = 0;
-    int scaledGlyphPos = round_to_long(gr_slot_origin_X(gi) * scaling);
-    int glyphWidth = round_to_long((nextOrigin - gOrigin) * scaling);
-//    if (glyphWidth < 0)
-//    {
-//        nextOrigin = gOrigin;
-//        glyphWidth = 0;
-//    }
+    int scaledGlyphPos = round_to_long(gr_slot_origin_X(gi) * scaling) + mnWidth + rDXOffset;
+    int glyphWidth = round_to_long((nextGlyphOrigin - gOrigin) * scaling);
+    if (!bIsBase)
+    {
+        mvChar2BaseGlyph[firstChar - mnMinCharPos] = baseGlyph;
+        mvCharDxs[firstChar - mnMinCharPos] = mvCharDxs[baseChar - mnMinCharPos];
+        mvCharBreaks[firstChar - mnMinCharPos] = gr_cinfo_break_weight(gr_seg_cinfo(pSeg, gr_slot_before(gi)));
+    }
+
 #ifdef GRLAYOUT_DEBUG
-    fprintf(grLog(),"c%d g%ld,X%d W%d nX%f ", firstChar, glyphId,
-        (int)(gr_slot_origin_X(gi) * scaling), glyphWidth, nextOrigin * scaling);
+    fprintf(grLog(),"c%d g%ld,X%d W%d nX%f @%d=%d ", firstChar, glyphId,
+        scaledGlyphPos, glyphWidth, nextGlyphOrigin * scaling, mvChar2Glyph[firstChar-mnMinCharPos], mvCharDxs[firstChar-mnMinCharPos]);
 #endif
     if (glyphId == 0)
     {
@@ -449,11 +337,12 @@ GraphiteLayout::append(gr_segment *pSeg, ImplLayoutArgs &rArgs,
     }
     // append this glyph. Set the cluster flag if this glyph is attached to another
     long nGlyphFlags = bIsBase ? 0 : GlyphItem::IS_IN_CLUSTER;
+    if (gr_slot_attached_to(gi))
+        nGlyphFlags |= GlyphItem::IS_DIACRITIC;
     nGlyphFlags |= (bRtl)? GlyphItem::IS_RTL_GLYPH : 0;
     GlyphItem aGlyphItem(mvGlyphs.size(),
         glyphId,
-        Point(scaledGlyphPos + rDXOffset,
-            round_to_long((-gr_slot_origin_Y(gi) * scaling))),
+        Point(scaledGlyphPos, round_to_long((-gr_slot_origin_Y(gi) * scaling))),
         nGlyphFlags,
         glyphWidth);
     if (glyphId != static_cast<long>(GF_DROPPED))
@@ -464,9 +353,10 @@ GraphiteLayout::append(gr_segment *pSeg, ImplLayoutArgs &rArgs,
     rDXOffset += deltaOffset;
 
     // Recursively append all the attached glyphs.
-    float cOrigin = nextOrigin;
+    float cOrigin = nextGlyphOrigin;
     for (const gr_slot * agi = gr_slot_first_attachment(gi); agi != NULL; agi = gr_slot_next_sibling_attachment(agi))
-        cOrigin = append(pSeg, rArgs, agi, cOrigin, nextGlyphOrigin, scaling, rDXOffset, false, baseChar);
+        if (!gr_slot_can_insert_before(agi))
+            cOrigin = append(pSeg, rArgs, agi, cOrigin, nextGlyphOrigin, scaling, rDXOffset, false, baseChar, baseGlyph, bRtl);
 
     return cOrigin;
 }
@@ -500,6 +390,7 @@ void GraphiteLayout::clear()
     mvGlyphs.clear();
     mvCharDxs.clear();
     mvChar2BaseGlyph.clear();
+    mvChar2Glyph.clear();
     mvGlyph2Char.clear();
 
     // Reset the state to the empty state.
@@ -510,170 +401,61 @@ void GraphiteLayout::clear()
 // This method shouldn't be called on windows, since it needs the dc reset
 bool GraphiteLayout::LayoutText(ImplLayoutArgs & rArgs)
 {
+    clear();
     bool success = true;
-    if (rArgs.mnMinCharPos < rArgs.mnEndCharPos)
-    {
-        gr_segment * pSegment = CreateSegment(rArgs);
-        if (!pSegment)
-            return false;
-        success = LayoutGlyphs(rArgs, pSegment);
-        if (pSegment)
-        {
-            gr_seg_destroy(pSegment);
-            pSegment = NULL;
-        }
-    }
-    else
-    {
-        clear();
-    }
-    return success;
-}
-
-gr_segment * GraphiteLayout::CreateSegment(ImplLayoutArgs& rArgs)
-{
-    assert(rArgs.mnLength >= 0);
-
-    gr_segment * pSegment = NULL;
-
+    if (rArgs.mnMinCharPos >= rArgs.mnEndCharPos)
+        return success;
     // Set the SalLayouts values to be the initial ones.
     SalLayout::AdjustLayout(rArgs);
     // TODO check if this is needed
     if (mnUnitsPerPixel > 1)
         mfScaling = 1.0f / mnUnitsPerPixel;
+    mvCharDxs.assign(mnEndCharPos - mnMinCharPos, -1);
+    mvChar2BaseGlyph.assign(mnEndCharPos - mnMinCharPos, -1);
+    mvChar2Glyph.assign(mnEndCharPos - mnMinCharPos, -1);
+    mvCharBreaks.assign(mnEndCharPos - mnMinCharPos, 0);
 
-    // Clear out any previous buffers
-    clear();
-    bool bRtl(mnLayoutFlags & SalLayoutFlags::BiDiRtl);
-    try
+#ifdef GRLAYOUT_DEBUG
+    fprintf(grLog(), "New Graphite LayoutText\n");
+#endif
+    success = false;
+    while (true)
     {
-        // Don't set RTL if font doesn't support it otherwise it forces rtl on
-        // everything
-        //if (bRtl && (mrFont.getSupportedScriptDirections() & gr::kfsdcHorizRtl))
-        //    maLayout.setRightToLeft(bRtl);
-
-        // Context is often needed beyond the specified end, however, we don't
-        // want it if there has been a direction change, since it is hard
-        // to tell between reordering within one direction and multi-directional
-        // text. Extra context, can also cause problems with ligatures stradling
-        // a hyphenation point, so disable if CTL is disabled.
-        mnSegCharOffset = rArgs.mnMinCharPos;
-        int limit = rArgs.mnEndCharPos;
-        if (!(SalLayoutFlags::ComplexDisabled & rArgs.mnFlags))
-        {
-            const int nSegCharMin = maximum<int>(0, mnMinCharPos - EXTRA_CONTEXT_LENGTH);
-            const int nSegCharLimit = minimum(rArgs.mnLength, mnEndCharPos + EXTRA_CONTEXT_LENGTH);
-            if (nSegCharMin < mnSegCharOffset)
-            {
-                int sameDirEnd = findSameDirLimit(rArgs.mpStr + nSegCharMin,
-                    rArgs.mnEndCharPos - nSegCharMin, bRtl);
-                if (sameDirEnd == rArgs.mnEndCharPos)
-                    mnSegCharOffset = nSegCharMin;
-            }
-            if (nSegCharLimit > limit)
-            {
-                limit += findSameDirLimit(rArgs.mpStr + rArgs.mnEndCharPos,
-                    nSegCharLimit - rArgs.mnEndCharPos, bRtl);
-            }
-        }
+        int nBidiMinRunPos, nBidiEndRunPos;
+        bool bRightToLeft;
+        if (!rArgs.GetNextRun(&nBidiMinRunPos, &nBidiEndRunPos, &bRightToLeft))
+            break;
 
-        size_t numchars = gr_count_unicode_characters(gr_utf16, rArgs.mpStr + mnSegCharOffset,
-                 rArgs.mpStr + (rArgs.mnLength > limit + 64 ? limit + 64 : rArgs.mnLength), NULL);
-        static com::sun::star::uno::Reference< com::sun::star::i18n::XCharacterClassification > xCharClass;
-        if ( !xCharClass.is() )
-            xCharClass = vcl::unohelper::CreateCharacterClassification();
-        size_t numchars2 = rArgs.mnEndCharPos - mnSegCharOffset; // fdo#52540, fdo#68313, fdo#70666 avoid bad ligature replacement, fdo#88051 layout problem
-        if (numchars > numchars2 && (rArgs.mpStr[mnSegCharOffset + numchars2] == '\t' ||
-            xCharClass->getType(rArgs.mpStr + mnSegCharOffset, numchars2 + 1) == ::com::sun::star::i18n::UnicodeType::LOWERCASE_LETTER))
-        {
-            numchars = numchars2;
-        }
-        if (mpFeatures)
-            pSegment = gr_make_seg(mpFont, mpFace, 0, mpFeatures->values(), gr_utf16,
-                                        rArgs.mpStr + mnSegCharOffset, numchars, bRtl);
-        else
-            pSegment = gr_make_seg(mpFont, mpFace, 0, NULL, gr_utf16,
-                                        rArgs.mpStr + mnSegCharOffset, numchars, bRtl);
+        if (nBidiEndRunPos < mnMinCharPos || nBidiMinRunPos >= mnEndCharPos)
+            continue;
+
+        if (nBidiMinRunPos == mnMinCharPos)
+            nBidiMinRunPos = maximum<int>(0, nBidiMinRunPos - EXTRA_CONTEXT_LENGTH);
+        if (nBidiEndRunPos == mnEndCharPos)
+            nBidiEndRunPos = minimum<int>(rArgs.mnLength, nBidiEndRunPos + EXTRA_CONTEXT_LENGTH);
+        size_t numchars = gr_count_unicode_characters(gr_utf16, rArgs.mpStr + nBidiMinRunPos,
+                 rArgs.mpStr + nBidiEndRunPos, NULL);
+        gr_segment * pSegment = gr_make_seg(mpFont, mpFace, 0, mpFeatures ? mpFeatures->values() : NULL,
+                                gr_utf16, rArgs.mpStr + nBidiMinRunPos, numchars, 2 | int(bRightToLeft));
 
-        //pSegment = new gr::RangeSegment((gr::Font *)&mrFont, mpTextSrc, &maLayout, mnMinCharPos, limit);
         if (pSegment != NULL)
         {
+            success = true;
+            mnSegCharOffset = nBidiMinRunPos;
 #ifdef GRLAYOUT_DEBUG
-            fprintf(grLog(),"Gr::LayoutText %d-%d, context %d, len %d, numchars %" SAL_PRI_SIZET "u, rtl %d scaling %f:", rArgs.mnMinCharPos,
-               rArgs.mnEndCharPos, limit, rArgs.mnLength, numchars, bRtl, mfScaling);
-            for (int i = mnSegCharOffset; i < limit; ++i)
+            fprintf(grLog(),"Gr::LayoutText %d-%d, context %d-%d, len %d, numchars %" SAL_PRI_SIZET "u, rtl %d scaling %f:",
+                rArgs.mnMinCharPos, rArgs.mnEndCharPos,
+                nBidiMinRunPos, nBidiEndRunPos,
+                rArgs.mnLength, numchars, bRightToLeft, mfScaling);
+            for (int i = mnSegCharOffset; i < nBidiEndRunPos; ++i)
                 fprintf(grLog(), " %04X", rArgs.mpStr[i]);
             fprintf(grLog(), "\n");
 #endif
-        }
-        else
-        {
-#ifdef GRLAYOUT_DEBUG
-            fprintf(grLog(), "Gr::LayoutText failed: ");
-            for (int i = mnMinCharPos; i < limit; i++)
-            {
-                fprintf(grLog(), "%04x ", rArgs.mpStr[i]);
-            }
-            fprintf(grLog(), "\n");
-#endif
-            clear();
-            return NULL;
-        }
-    }
-    catch (...)
-    {
-        clear();  // destroy the text source and any partially built segments.
-        return NULL;
-    }
-    return pSegment;
-}
-
-bool GraphiteLayout::LayoutGlyphs(ImplLayoutArgs& rArgs, gr_segment * pSegment)
-{
-    // Calculate the initial character dxs.
-    mvCharDxs.assign(mnEndCharPos - mnMinCharPos, -1);
-    mvChar2BaseGlyph.assign(mnEndCharPos - mnMinCharPos, -1);
-    mvCharBreaks.assign(mnEndCharPos - mnMinCharPos, 0);
-    mnWidth = 0;
-    if (mvCharDxs.size() > 0)
-    {
-        // Discover all the clusters.
-        try
-        {
-            bool bRtl(mnLayoutFlags & SalLayoutFlags::BiDiRtl);
-            fillFrom(pSegment, rArgs, mfScaling);
-
-            if (bRtl)
-            {
-                // not needed for adjacent differences, but for mouse clicks to char
-                std::transform(mvCharDxs.begin(), mvCharDxs.end(), mvCharDxs.begin(),
-                    std::bind1st(std::minus<long>(), mnWidth));
-                // fixup last dx to ensure it always equals the width
-                mvCharDxs[mvCharDxs.size() - 1] = mnWidth;
-            }
-        }
-        catch (const std::exception &e)
-        {
-#ifdef GRLAYOUT_DEBUG
-            fprintf(grLog(),"LayoutGlyphs failed %s\n", e.what());
-#else
-            (void)e;
-#endif
-            return false;
-        }
-        catch (...)
-        {
-#ifdef GRLAYOUT_DEBUG
-            fprintf(grLog(),"LayoutGlyphs failed with exception");
-#endif
-            return false;
+            fillFrom(pSegment, rArgs, mfScaling, bRightToLeft, nBidiEndRunPos);
+            gr_seg_destroy(pSegment);
         }
     }
-    else
-    {
-        mnWidth = 0;
-    }
-    return true;
+    return success;
 }
 
 sal_Int32 GraphiteLayout::GetTextBreak(DeviceCoordinate maxmnWidth, DeviceCoordinate char_extra, int factor) const
@@ -695,12 +477,12 @@ sal_Int32 GraphiteLayout::GetTextBreak(DeviceCoordinate maxmnWidth, DeviceCoordi
     {
         nWidth += char_extra;
         if (nWidth > maxmnWidth) break;
-        if (mvChar2BaseGlyph[i] != -1)
+        int gi = mvChar2BaseGlyph[i];
+        if (gi != -1)
         {
-            if (
+            if (!mvGlyphs[gi].IsDiacritic() &&
                 (mvCharBreaks[i] > -35 || (mvCharBreaks[i-1] > 0 && mvCharBreaks[i-1] < 35)) &&
-                (mvCharBreaks[i-1] < 35 || (mvCharBreaks[i] < 0 && mvCharBreaks[i] > -35))
-               )
+                (mvCharBreaks[i-1] < 35 || (mvCharBreaks[i] < 0 && mvCharBreaks[i] > -35)))
             {
                 nLastBreak = static_cast<int>(i);
                 wLastBreak = nWidth;
@@ -755,10 +537,6 @@ DeviceCoordinate GraphiteLayout::FillDXArray( DeviceCoordinate* pDXArray ) const
             fprintf(grLog(),"%d,%d,%ld ", (int)i, (int)mvCharDxs[i], pDXArray[i]);
 #endif
         }
-        //std::adjacent_difference(mvCharDxs.begin(), mvCharDxs.end(), pDXArray);
-        //for (size_t i = 0; i < mvCharDxs.size(); i++)
-        //    fprintf(grLog(),"%d,%d,%d ", (int)i, (int)mvCharDxs[i], pDXArray[i]);
-        //fprintf(grLog(),"FillDX %ld,%d\n", mnWidth, std::accumulate(pDXArray, pDXArray + mvCharDxs.size(), 0));
     }
 #ifdef GRLAYOUT_DEBUG
     fprintf(grLog(),"FillDXArray %d-%d=%g\n", mnMinCharPos, mnEndCharPos, (double)mnWidth);
@@ -769,7 +547,7 @@ DeviceCoordinate GraphiteLayout::FillDXArray( DeviceCoordinate* pDXArray ) const
 void  GraphiteLayout::AdjustLayout(ImplLayoutArgs& rArgs)
 {
     SalLayout::AdjustLayout(rArgs);
-    if(rArgs.mpDXArray)
+    if(rArgs.mpDXArray && mvGlyphs.size())
     {
         std::vector<int> vDeltaWidths(mvGlyphs.size(), 0);
         ApplyDXArray(rArgs, vDeltaWidths);
@@ -817,11 +595,14 @@ void GraphiteLayout::expandOrCondense(ImplLayoutArgs &rArgs)
         int nClusterCount = 0;
         for (size_t j = 0; j < mvGlyphs.size(); j++)
         {
-            if (mvGlyphs[j].IsClusterStart())
+            if (mvGlyphs[j].IsClusterStart() && !mvGlyphs[j].IsDiacritic())
             {
                 ++nClusterCount;
             }
         }
+#ifdef GRLAYOUT_DEBUG
+            fprintf(grLog(), "Expand by width %f for %ld clusters\n", nDeltaWidth, nClusterCount);
+#endif
         if (nClusterCount > 1)
         {
             float fExtraPerCluster = static_cast<float>(nDeltaWidth) / static_cast<float>(nClusterCount - 1);
@@ -829,7 +610,7 @@ void GraphiteLayout::expandOrCondense(ImplLayoutArgs &rArgs)
             int nOffset = 0;
             for (size_t i = 0; i < mvGlyphs.size(); i++)
             {
-                if (mvGlyphs[i].IsClusterStart())
+                if (mvGlyphs[i].IsClusterStart() && !mvGlyphs[i].IsDiacritic())
                 {
                     nOffset = static_cast<int>(fExtraPerCluster * nCluster);
                     int nCharIndex = mvGlyph2Char[i];
@@ -882,134 +663,126 @@ void GraphiteLayout::expandOrCondense(ImplLayoutArgs &rArgs)
     mnWidth = rArgs.mnLayoutWidth;
 }
 
+unsigned int GraphiteLayout::ScanFwdForChar(int &findChar, bool fallback) const
+{
+    int res = mvChar2Glyph[findChar - mnMinCharPos];
+    int done = 3;
+    while (res == -1 && --done)
+    {
+        if (fallback)
+        {
+            for (++findChar; findChar - mnMinCharPos < int(mvChar2Glyph.size()); ++findChar)
+                if ((res = mvChar2Glyph[findChar - mnMinCharPos]) != -1)
+                    return res;
+        }
+        else
+        {
+            for (--findChar; findChar >= mnMinCharPos; --findChar)
+                if ((res = mvChar2Glyph[findChar - mnMinCharPos]) != -1)
+                    return res;
+        }
+        fallback = !fallback;
+    }
+    return unsigned(res);
+}
+
 void GraphiteLayout::ApplyDXArray(ImplLayoutArgs &args, std::vector<int> & rDeltaWidth)
 {
-    const size_t nChars = args.mnEndCharPos - args.mnMinCharPos;
-    if (nChars == 0) return;
+    bool bRtl(mnLayoutFlags & SalLayoutFlags::BiDiRtl);
+    int startChar = args.mnMinCharPos < mnMinCharPos ? mnMinCharPos : args.mnMinCharPos;
+    int endChar = args.mnEndCharPos >= mnEndCharPos ? mnEndCharPos - 1 : args.mnEndCharPos;
+    unsigned int startGi = ScanFwdForChar(startChar, !bRtl);
+    unsigned int endGi = ScanFwdForChar(endChar, bRtl);
+    int nChars = endChar - startChar + 1;
+    if (nChars <= 0) return;
+    if (startGi > endGi)
+    {
+        unsigned int temp = endGi;
+        endGi = startGi;
+        startGi = temp;
+    }
+    ++endGi;
 
 #ifdef GRLAYOUT_DEBUG
     for (size_t iDx = 0; iDx < mvCharDxs.size(); iDx++)
          fprintf(grLog(),"%d,%d,%ld ", (int)iDx, (int)mvCharDxs[iDx], args.mpDXArray[iDx]);
-    fprintf(grLog(),"ApplyDx\n");
+    fprintf(grLog(),"ApplyDx %d-%d=%d-%d\n", startChar, endChar, startGi, endGi);
 #endif
-    bool bRtl(mnLayoutFlags & SalLayoutFlags::BiDiRtl);
-    int nXOffset = 0;
-    if (bRtl)
-    {
-        nXOffset = args.mpDXArray[nChars - 1] - mvCharDxs[nChars - 1];
-    }
-    int nPrevClusterGlyph = (bRtl)? (signed)mvGlyphs.size() : -1;
-    int nPrevClusterLastChar = -1;
-    for (size_t i = 0; i < nChars; i++)
+
+    for (unsigned int i = startGi; i < endGi; ++i)
     {
-        int nChar2Base = mvChar2BaseGlyph[i];
-        if ((nChar2Base > -1) && (nChar2Base != nPrevClusterGlyph))
+        // calculate visual cluster bounds
+        int firstChar = mvGlyph2Char[i];
+        unsigned int nBaseGlyph = mvChar2BaseGlyph[firstChar - mnMinCharPos];
+        while (nBaseGlyph == ~0U && i < endGi)
         {
-            assert((nChar2Base > -1) && (nChar2Base < (signed)mvGlyphs.size()));
-            GlyphItem & gi = mvGlyphs[nChar2Base];
-            if (!gi.IsClusterStart())
-                continue;
-
-            // find last glyph of this cluster
-            size_t j = i + 1;
-            int nLastChar = i;
-            int nLastGlyph = nChar2Base;
-            for (; j < nChars; j++)
+            ++i;
+            firstChar = mvGlyph2Char[i];
+            nBaseGlyph = unsigned(mvChar2BaseGlyph[firstChar - mnMinCharPos]);
+        }
+        int lastChar = firstChar;
+        unsigned int nLastGlyph = i;
+        // firstGlyph = i
+        for ( ; nLastGlyph < endGi; nLastGlyph++)
+        {
+            int thisChar = mvGlyph2Char[nLastGlyph];
+            if (thisChar == -1) continue;
+            if (unsigned(mvChar2BaseGlyph[thisChar - mnMinCharPos]) != nBaseGlyph)
             {
-                const int nChar2BaseJ = mvChar2BaseGlyph[j];
-                assert((nChar2BaseJ >= -1) && (nChar2BaseJ < (signed)mvGlyphs.size()));
-                if (nChar2BaseJ != -1 )
-                {
-                    nLastGlyph = nChar2BaseJ + ((bRtl)? +1 : -1);
-                    nLastChar = j - 1;
+                if (!mvGlyphs[nLastGlyph].IsDiacritic())
                     break;
-                }
-            }
-            if (nLastGlyph < 0)
-            {
-                nLastGlyph = nChar2Base;
-            }
-            // Its harder to find the last glyph rtl, since the first of
-            // cluster is still on the left so we need to search towards
-            // the previous cluster to the right
-            if (bRtl)
-            {
-                nLastGlyph = nChar2Base;
-                while (nLastGlyph + 1 < (signed)mvGlyphs.size() &&
-                       !mvGlyphs[nLastGlyph+1].IsClusterStart())
-                {
-                    ++nLastGlyph;
-                }
-            }
-            if (j == nChars)
-            {
-                nLastChar = nChars - 1;
-                if (!bRtl) nLastGlyph = mvGlyphs.size() - 1;
-            }
-            int nBaseCount = 0;
-            // count bases within cluster - may be more than 1 with reordering
-            for (int k = nChar2Base; k <= nLastGlyph; k++)
-            {
-                if (mvGlyphs[k].IsClusterStart()) ++nBaseCount;
-            }
-            assert((nLastChar > -1) && (nLastChar < (signed)nChars));
-            long nNewClusterWidth = args.mpDXArray[nLastChar];
-            long nOrigClusterWidth = mvCharDxs[nLastChar];
-            long nDGlyphOrigin = 0;
-            if (nPrevClusterLastChar > - 1)
-            {
-                assert(nPrevClusterLastChar < (signed)nChars);
-                nNewClusterWidth -= args.mpDXArray[nPrevClusterLastChar];
-                nOrigClusterWidth -= mvCharDxs[nPrevClusterLastChar];
-                nDGlyphOrigin = args.mpDXArray[nPrevClusterLastChar] - mvCharDxs[nPrevClusterLastChar];
-            }
-            long nDWidth = nNewClusterWidth - nOrigClusterWidth;
-#ifdef GRLAYOUT_DEBUG
-            fprintf(grLog(), "c%lu last glyph %d/%lu\n", i, nLastGlyph, mvGlyphs.size());
-#endif
-            assert((nLastGlyph > -1) && (nLastGlyph < (signed)mvGlyphs.size()));
-            mvGlyphs[nLastGlyph].mnNewWidth += nDWidth;
-            if (gi.maGlyphId != GF_DROPPED)
-                mvGlyphs[nLastGlyph].mnNewWidth += nDWidth;
-            else
-                nDGlyphOrigin += nDWidth;
-            long nDOriginPerBase = (nBaseCount > 0)? nDWidth / nBaseCount : 0;
-            nBaseCount = -1;
-            // update glyph positions
-            if (bRtl)
-            {
-                for (int n = nChar2Base; n <= nLastGlyph; n++)
-                {
-                    if (mvGlyphs[n].IsClusterStart()) ++nBaseCount;
-                    assert((n > - 1) && (n < (signed)mvGlyphs.size()));
-                    mvGlyphs[n].maLinearPos.X() += -(nDGlyphOrigin + nDOriginPerBase * nBaseCount) + nXOffset;
-                }
-            }
-            else
-            {
-                for (int n = nChar2Base; n <= nLastGlyph; n++)
-                {
-                    if (mvGlyphs[n].IsClusterStart()) ++nBaseCount;
-                    assert((n > - 1) && (n < (signed)mvGlyphs.size()));
-                    mvGlyphs[n].maLinearPos.X() += nDGlyphOrigin + (nDOriginPerBase * nBaseCount) + nXOffset;
-                }
+                else
+                    nBaseGlyph = mvChar2BaseGlyph[thisChar - mnMinCharPos];
             }
-            rDeltaWidth[nChar2Base] = nDWidth;
+            if (thisChar > lastChar) lastChar = thisChar;
+            if (thisChar < firstChar) firstChar = thisChar;
+        }
+
+        // calculate visual cluster widths
+        if (lastChar > args.mnEndCharPos) lastChar = args.mnEndCharPos;
+        if (firstChar < args.mnMinCharPos) firstChar = args.mnMinCharPos;
+        long nOrigClusterWidth = mvCharDxs[lastChar - mnMinCharPos];
+        long nNewClusterWidth = args.mpDXArray[lastChar - args.mnMinCharPos];
+        long nDGlyphOrigin = 0;
+        if (firstChar > args.mnMinCharPos)
+        {
+            //nNewClusterWidth -= args.mpDXArray[firstChar - args.mnMinCharPos];
+            //nOrigClusterWidth -= mvCharDxs[firstChar - mnMinCharPos];
+            nDGlyphOrigin = args.mpDXArray[firstChar - args.mnMinCharPos - 1]
+                                - mvCharDxs[firstChar - mnMinCharPos - 1];
+        }
+
+        // update visual cluster
+        long nDWidth = nNewClusterWidth - nOrigClusterWidth;
+        if (firstChar >= args.mnMinCharPos)
+            for (int n = firstChar; n <= lastChar; ++n)
+                if (n > mnMinCharPos && mvCharDxs[n - mnMinCharPos - 1] != -1)
+                    mvCharDxs[n - mnMinCharPos - 1] += nDGlyphOrigin; // + nDWidth;
+        for (unsigned int n = i; n < nLastGlyph; n++)
+            //mvGlyphs[n].maLinearPos.X() += (nDGlyphOrigin + nDWidth) * (bRtl ? -1 : 1);
+            mvGlyphs[n].maLinearPos.X() += nDGlyphOrigin * (bRtl ? -1 : 1);
+
+        rDeltaWidth[nBaseGlyph] = nDWidth;
 #ifdef GRLAYOUT_DEBUG
-            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());
+        fprintf(grLog(),"c%d=%d g%d-%d dW%ld-%ld=%ld dX%ld x%ld @%d=%d\n", firstChar, lastChar, i, nLastGlyph, nNewClusterWidth, nOrigClusterWidth, nDWidth, nDGlyphOrigin, mvGlyphs[i].maLinearPos.X(), mvCharDxs[lastChar - mnMinCharPos], args.mpDXArray[lastChar - args.mnMinCharPos]);
 #endif
-            nPrevClusterGlyph = nChar2Base;
-            nPrevClusterLastChar = nLastChar;
-            i = nLastChar;
-        }
+        i = nLastGlyph - 1;
+        if (i >= endGi - 1)
+            mnWidth += nDGlyphOrigin + nDWidth;
     }
     // Update the dx vector with the new values.
     std::copy(args.mpDXArray, args.mpDXArray + nChars,
       mvCharDxs.begin() + (args.mnMinCharPos - mnMinCharPos));
+    //args.mpDXArray[0] = 0;
 #ifdef GRLAYOUT_DEBUG
     fprintf(grLog(),"ApplyDx %ld(%ld)\n", args.mpDXArray[nChars - 1], mnWidth);
 #endif
-    mnWidth = args.mpDXArray[nChars - 1];
+    if (bRtl)
+    {
+        int diff = mvGlyphs[0].maLinearPos.X();
+        for (size_t i = 0; i < mvGlyphs.size(); ++i)
+            mvGlyphs[i].maLinearPos.X() -= diff;
+    }
 }
 
 void GraphiteLayout::kashidaJustify(std::vector<int>& rDeltaWidths, sal_GlyphId nKashidaIndex, int nKashidaWidth)
diff --git a/vcl/win/source/gdi/winlayout.cxx b/vcl/win/source/gdi/winlayout.cxx
index d1515a1..261e1b0 100644
--- a/vcl/win/source/gdi/winlayout.cxx
+++ b/vcl/win/source/gdi/winlayout.cxx
@@ -2374,11 +2374,6 @@ GraphiteWinLayout::~GraphiteWinLayout()
 
 bool GraphiteWinLayout::LayoutText( ImplLayoutArgs & args)
 {
-    if (args.mnMinCharPos >= args.mnEndCharPos)
-    {
-        maImpl.clear();
-        return true;
-    }
     HFONT hUnRotatedFont = 0;
     if (args.mnOrientation)
     {
@@ -2392,15 +2387,7 @@ bool GraphiteWinLayout::LayoutText( ImplLayoutArgs & args)
     }
     WinLayout::AdjustLayout(args);
     maImpl.SetFontScale(WinLayout::mfFontScale);
-    gr_segment * pSegment = maImpl.CreateSegment(args);
-    bool bSucceeded = false;
-    if (pSegment)
-    {
-        // replace the DC on the font within the segment
-        // create glyph vectors
-        bSucceeded = maImpl.LayoutGlyphs(args, pSegment);
-        gr_seg_destroy(pSegment);
-    }
+    bool bSucceeded = maImpl.LayoutText(args);
     if (args.mnOrientation)
     {
         // restore the rotated font
commit 0338f652fb81f9eb6e6f4b42365b60e8fe041389
Author: Oliver Specht <oliver.specht at cib.de>
Date:   Wed Feb 10 16:57:14 2016 +0100

    tdf#89505: replace tab character with space in table of contents
    
    the ToxWhitespaceStripper now replaces also tab characters so that
    they don't break the formatting
    
    Change-Id: If66aaddcbc0f8c65461f53f07ca7031f8f1d11b4
    Reviewed-on: https://gerrit.libreoffice.org/22271
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Oliver Specht <oliver.specht at cib.de>
    (cherry picked from commit 5152c43109c9a35f30fc9b0f478c1fbaf01a1143)
    Reviewed-on: https://gerrit.libreoffice.org/22284
    Reviewed-by: Thorsten Behrens <Thorsten.Behrens at CIB.de>
    Tested-by: Thorsten Behrens <Thorsten.Behrens at CIB.de>
    (cherry picked from commit eeb7c6877d47bfb0a4871734a397be96eac46663)

diff --git a/sw/source/core/tox/ToxWhitespaceStripper.cxx b/sw/source/core/tox/ToxWhitespaceStripper.cxx
index 4918a73..c38079c 100644
--- a/sw/source/core/tox/ToxWhitespaceStripper.cxx
+++ b/sw/source/core/tox/ToxWhitespaceStripper.cxx
@@ -24,7 +24,7 @@ ToxWhitespaceStripper::ToxWhitespaceStripper(const OUString& inputString)
     for (sal_Int32 pos = 0; pos < inputString.getLength(); ++pos) {
         sal_Unicode cur = inputString[pos];
 
-        if (cur == ' ' || cur == '\n') {
+        if (cur == ' ' || cur == '\n' || cur == '\t') {
             // merge consecutive whitespaces (and translate them to spaces)
             if (!lastCharacterWasWhitespace) {
                 buffer.append(' ');
commit 70b0751d51492acf83f66060241d2352e52e715b
Author: Michael Stahl <mstahl at redhat.com>
Date:   Fri Feb 12 15:47:05 2016 +0100

    sw: ensure that configuration change in odfexporttest.cxx ...
    
    ... is reverted in case the test function throws an exception, so that
    subsequent^Wfollowing tests don't inherit the changed configuration.
    
    (cherry picked from commit e2bfae9006e6adc4de17d0167dac6661b002f126)
    
    Change-Id: I748f9edf15a7f860607ae4cce891450db254c73e
    Reviewed-on: https://gerrit.libreoffice.org/22336
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Markus Mohrhard <markus.mohrhard at googlemail.com>
    (cherry picked from commit 938ddc33ed55579ea40d25bd9cd5704d31b0034e)

diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx b/sw/qa/extras/htmlexport/htmlexport.cxx
index 2ce1040..4f24c35 100644
--- a/sw/qa/extras/htmlexport/htmlexport.cxx
+++ b/sw/qa/extras/htmlexport/htmlexport.cxx
@@ -41,7 +41,7 @@ private:
         return OString(filename) != "fdo62336.docx";
     }
 
-    void preTest(const char* filename) SAL_OVERRIDE
+    virtual std::unique_ptr<Resetter> preTest(const char* filename) SAL_OVERRIDE
     {
         if (getTestName().indexOf("SkipImage") != -1)
             setFilterOptions("SkipImages");
@@ -50,22 +50,22 @@ private:
 
         if (OString(filename) == "charborder.odt")
         {
+
             // FIXME if padding-top gets exported as inches, not cms, we get rounding errors.
             SwGlobals::ensure(); // make sure that SW_MOD() is not 0
+            std::unique_ptr<Resetter> pResetter(new Resetter(
+                [this] () {
+                    SwMasterUsrPref* pPref = const_cast<SwMasterUsrPref*>(SW_MOD()->GetUsrPref(false));
+                    pPref->SetMetric(this->m_eUnit);
+                }));
             SwMasterUsrPref* pPref = const_cast<SwMasterUsrPref*>(SW_MOD()->GetUsrPref(false));
             m_eUnit = pPref->GetMetric();
             pPref->SetMetric(FUNIT_CM);
+            return pResetter;
         }
+        return nullptr;
     }
 
-    void postTest(const char* filename) SAL_OVERRIDE
-    {
-        if (OString(filename) == "charborder.odt")
-        {
-            SwMasterUsrPref* pPref = const_cast<SwMasterUsrPref*>(SW_MOD()->GetUsrPref(false));
-            pPref->SetMetric(m_eUnit);
-        }
-    }
 };
 
 #define DECLARE_HTMLEXPORT_ROUNDTRIP_TEST(TestName, filename) DECLARE_SW_ROUNDTRIP_TEST(TestName, filename, HtmlExportTest)
diff --git a/sw/qa/extras/inc/swmodeltestbase.hxx b/sw/qa/extras/inc/swmodeltestbase.hxx
index 879c4f9..e34d76f 100644
--- a/sw/qa/extras/inc/swmodeltestbase.hxx
+++ b/sw/qa/extras/inc/swmodeltestbase.hxx
@@ -145,6 +145,31 @@ protected:
     bool mbExported; ///< Does maTempFile already contain something useful?
 
 protected:
+
+    class Resetter
+    {
+    private:
+        std::function<void ()> m_Func;
+
+    public:
+        Resetter(std::function<void ()> const& rFunc)
+            : m_Func(rFunc)
+        {
+        }
+        ~Resetter()
+        {
+            try
+            {
+                m_Func();
+            }
+            catch (...) // has to be reliable
+            {
+                fprintf(stderr, "resetter failed with exception\n");
+                abort();
+            }
+        }
+    };
+
     virtual OUString getTestName() { return OUString(); }
 
 public:
@@ -197,7 +222,7 @@ protected:
         {
             maTempFile.EnableKillingFile(false);
             header();
-            preTest(filename);
+            std::unique_ptr<Resetter> const pChanges(preTest(filename));
             load(mpTestDocumentPath, filename);

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list