oox: extend ThemeExport to export font and format scheme of a theme

Also use the ThemeExport when exporting PPTX documents and remove
all the hard-coded theme bits.

Change-Id: I03791e23d6ac4023748b5a553e4824b72ed63a93
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/149363
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
diff --git a/include/docmodel/theme/FormatScheme.hxx b/include/docmodel/theme/FormatScheme.hxx
index feec16e..420d5c1 100644
--- a/include/docmodel/theme/FormatScheme.hxx
+++ b/include/docmodel/theme/FormatScheme.hxx
@@ -79,6 +79,8 @@ struct DOCMODEL_DLLPUBLIC ColorDefinition
    ThemeColorType meSchemeType = ThemeColorType::Unknown;
    std::vector<Transformation> maTransformations;

    Color getRGBColor() const { return Color(mnComponent1, mnComponent2, mnComponent3); }

    void setCRGB(sal_Int32 nR, sal_Int32 nG, sal_Int32 nB)
    {
        mnComponent1 = nR;
@@ -436,9 +438,9 @@ enum class LineEndLength

struct DOCMODEL_DLLPUBLIC LineEnd
{
    LineEndType meType;
    LineEndWidth meWidth;
    LineEndLength meLength;
    LineEndType meType = LineEndType::None;
    LineEndWidth meWidth = LineEndWidth::Unset;
    LineEndLength meLength = LineEndLength::Unset;
};

struct DOCMODEL_DLLPUBLIC DashStop
@@ -527,6 +529,42 @@ public:

    const OUString& getName() const { return maName; }

    std::vector<FillStyle> const& getFillStyleList() const { return maFillStyleList; }

    FillStyle* addFillStyle()
    {
        if (maFillStyleList.size() > 3)
            return nullptr;
        auto& rFillStyle = maFillStyleList.emplace_back();
        return &rFillStyle;
    }

    void ensureFillStyleList() const
    {
        if (!maFillStyleList.empty())
            return;

        auto* pThis = const_cast<FormatScheme*>(this);
        {
            FillStyle* pFillStyle = pThis->addFillStyle();
            auto pFill = std::make_shared<SolidFill>();
            pFill->maColorDefinition.meType = model::ColorType::Placeholder;
            pFillStyle->mpFill = pFill;
        }
        {
            FillStyle* pFillStyle = pThis->addFillStyle();
            auto pFill = std::make_shared<SolidFill>();
            pFill->maColorDefinition.meType = model::ColorType::Placeholder;
            pFillStyle->mpFill = pFill;
        }
        {
            FillStyle* pFillStyle = pThis->addFillStyle();
            auto pFill = std::make_shared<SolidFill>();
            pFill->maColorDefinition.meType = model::ColorType::Placeholder;
            pFillStyle->mpFill = pFill;
        }
    }

    std::vector<LineStyle> const& getLineStyleList() const { return maLineStyleList; }

    LineStyle* addLineStyle()
@@ -537,6 +575,51 @@ public:
        return &rLineStyle;
    }

    void ensureLineStyleList() const
    {
        if (!maLineStyleList.empty())
            return;

        auto* pThis = const_cast<FormatScheme*>(this);

        {
            LineStyle* pLineStyle = pThis->addLineStyle();
            pLineStyle->mnWidth = 6350;
            pLineStyle->meCapType = CapType::Flat;
            pLineStyle->mePenAlignment = PenAlignmentType::Center;
            pLineStyle->meCompoundLineType = CompoundLineType::Single;
            pLineStyle->maLineDash.mePresetType = PresetDashType::Solid;
            pLineStyle->maLineJoin.meType = LineJoinType::Miter;
            auto pFill = std::make_shared<SolidFill>();
            pFill->maColorDefinition.meType = model::ColorType::Placeholder;
            pLineStyle->maLineFillStyle.mpFill = pFill;
        }
        {
            LineStyle* pLineStyle = pThis->addLineStyle();
            pLineStyle->mnWidth = 6350;
            pLineStyle->meCapType = CapType::Flat;
            pLineStyle->mePenAlignment = PenAlignmentType::Center;
            pLineStyle->meCompoundLineType = CompoundLineType::Single;
            pLineStyle->maLineDash.mePresetType = PresetDashType::Solid;
            pLineStyle->maLineJoin.meType = LineJoinType::Miter;
            auto pFill = std::make_shared<SolidFill>();
            pFill->maColorDefinition.meType = model::ColorType::Placeholder;
            pLineStyle->maLineFillStyle.mpFill = pFill;
        }
        {
            LineStyle* pLineStyle = pThis->addLineStyle();
            pLineStyle->mnWidth = 6350;
            pLineStyle->meCapType = CapType::Flat;
            pLineStyle->mePenAlignment = PenAlignmentType::Center;
            pLineStyle->meCompoundLineType = CompoundLineType::Single;
            pLineStyle->maLineDash.mePresetType = PresetDashType::Solid;
            pLineStyle->maLineJoin.meType = LineJoinType::Miter;
            auto pFill = std::make_shared<SolidFill>();
            pFill->maColorDefinition.meType = model::ColorType::Placeholder;
            pLineStyle->maLineFillStyle.mpFill = pFill;
        }
    }

    std::vector<EffectStyle> const& getEffectStyleList() const { return maEffectStyleList; }

    EffectStyle* addEffectStyle()
@@ -547,14 +630,16 @@ public:
        return &rEffectStyle;
    }

    std::vector<FillStyle> const& getFillStyleList() const { return maFillStyleList; }

    FillStyle* addFillStyle()
    void ensureEffectStyleList() const
    {
        if (maFillStyleList.size() > 3)
            return nullptr;
        auto& rFillStyle = maFillStyleList.emplace_back();
        return &rFillStyle;
        if (!maEffectStyleList.empty())
            return;

        auto* pThis = const_cast<FormatScheme*>(this);

        pThis->addEffectStyle();
        pThis->addEffectStyle();
        pThis->addEffectStyle();
    }

    std::vector<FillStyle> const& getBackgroundFillStyleList() const
@@ -569,6 +654,33 @@ public:
        auto& rBackgroundFillStyle = maBackgroundFillStyleList.emplace_back();
        return &rBackgroundFillStyle;
    }

    void ensureBackgroundFillStyleList() const
    {
        if (!maBackgroundFillStyleList.empty())
            return;

        auto* pThis = const_cast<FormatScheme*>(this);

        {
            FillStyle* pFillStyle = pThis->addBackgroundFillStyle();
            auto pFill = std::make_shared<SolidFill>();
            pFill->maColorDefinition.meType = model::ColorType::Placeholder;
            pFillStyle->mpFill = pFill;
        }
        {
            FillStyle* pFillStyle = pThis->addBackgroundFillStyle();
            auto pFill = std::make_shared<SolidFill>();
            pFill->maColorDefinition.meType = model::ColorType::Placeholder;
            pFillStyle->mpFill = pFill;
        }
        {
            FillStyle* pFillStyle = pThis->addBackgroundFillStyle();
            auto pFill = std::make_shared<SolidFill>();
            pFill->maColorDefinition.meType = model::ColorType::Placeholder;
            pFillStyle->mpFill = pFill;
        }
    }
};

} // end of namespace svx
diff --git a/include/docmodel/theme/Theme.hxx b/include/docmodel/theme/Theme.hxx
index 70766a8..9a5c9fe 100644
--- a/include/docmodel/theme/Theme.hxx
+++ b/include/docmodel/theme/Theme.hxx
@@ -37,7 +37,7 @@ struct DOCMODEL_DLLPUBLIC ThemeFont
    OUString maPanose;
    sal_Int16 maPitch = 0;
    sal_Int16 maFamily = 0;
    sal_Int32 maCharset = 0;
    sal_Int32 maCharset = 1;

    sal_Int16 getPitchFamily() const { return (maPitch & 0x0F) | (maFamily & 0x0F) << 4; }
};
@@ -59,12 +59,29 @@ private:
    std::vector<ThemeSupplementalFont> maMajorSupplementalFontList;

public:
    FontScheme() = default;
    FontScheme()
        : maName("Office")
    {
    }

    FontScheme(OUString const& rName)
        : maName(rName)
    {
    }

    static FontScheme getDefault()
    {
        FontScheme aDefault;
        aDefault.maMinorLatin.maTypeface = "Arial";
        aDefault.maMinorAsian.maTypeface = "DejaVu Sans";
        aDefault.maMinorComplex.maTypeface = "DejaVu Sans";

        aDefault.maMajorLatin.maTypeface = "Arial";
        aDefault.maMajorAsian.maTypeface = "DejaVu Sans";
        aDefault.maMajorComplex.maTypeface = "DejaVu Sans";
        return aDefault;
    }

    const OUString& getName() const { return maName; }

    ThemeFont const& getMinorLatin() const { return maMinorLatin; }
@@ -143,7 +160,7 @@ private:
    OUString maName;
    std::unique_ptr<model::ColorSet> mpColorSet;

    FontScheme maFontScheme;
    FontScheme maFontScheme = FontScheme::getDefault();
    FormatScheme maFormatScheme;

public:
diff --git a/include/oox/export/ThemeExport.hxx b/include/oox/export/ThemeExport.hxx
index 1889709..8e35b80 100644
--- a/include/oox/export/ThemeExport.hxx
+++ b/include/oox/export/ThemeExport.hxx
@@ -17,6 +17,7 @@ namespace model
{
class Theme;
class FontScheme;
class FormatScheme;
}

namespace oox
@@ -35,7 +36,8 @@ private:
    static bool writeColorSet(sax_fastparser::FSHelperPtr pFS, model::Theme const& rTheme);
    static bool writeFontScheme(sax_fastparser::FSHelperPtr pFS,
                                model::FontScheme const& rFontScheme);
    static bool writeFormatScheme(sax_fastparser::FSHelperPtr pFS);
    static bool writeFormatScheme(sax_fastparser::FSHelperPtr pFS,
                                  model::FormatScheme const& rFormatScheme);
};

} // end namespace oox
diff --git a/oox/source/export/ThemeExport.cxx b/oox/source/export/ThemeExport.cxx
index 3abc2cb..b198aae 100644
--- a/oox/source/export/ThemeExport.cxx
+++ b/oox/source/export/ThemeExport.cxx
@@ -14,6 +14,7 @@
#include <oox/token/tokens.hxx>
#include <oox/export/utils.hxx>
#include <docmodel/theme/Theme.hxx>
#include <docmodel/theme/FormatScheme.hxx>
#include <sax/fshelper.hxx>
#include <sax/fastattribs.hxx>
#include <unordered_map>
@@ -49,8 +50,9 @@ void ThemeExport::write(OUString const& rPath, model::Theme const& rTheme)
    writeFontScheme(pFS, rFontScheme);
    pFS->endElementNS(XML_a, XML_fontScheme);

    model::FormatScheme const& rFormatScheme = rTheme.getFormatScheme();
    pFS->startElementNS(XML_a, XML_fmtScheme);
    writeFormatScheme(pFS);
    writeFormatScheme(pFS, rFormatScheme);
    pFS->endElementNS(XML_a, XML_fmtScheme);

    pFS->endElementNS(XML_a, XML_themeElements);
@@ -64,8 +66,14 @@ namespace
void fillAttrList(rtl::Reference<sax_fastparser::FastAttributeList> const& pAttrList,
                  model::ThemeFont const& rThemeFont)
{
    if (rThemeFont.maTypeface.isEmpty())
        return;

    pAttrList->add(XML_typeface, rThemeFont.maTypeface);
    pAttrList->add(XML_panose, rThemeFont.maPanose);

    if (!rThemeFont.maPanose.isEmpty())
        pAttrList->add(XML_panose, rThemeFont.maPanose);

    pAttrList->add(XML_pitchFamily, OString::number(rThemeFont.getPitchFamily()));
    pAttrList->add(XML_charset, OString::number(rThemeFont.maCharset));
}
@@ -118,105 +126,714 @@ bool ThemeExport::writeFontScheme(sax_fastparser::FSHelperPtr pFS,
    return true;
}

bool ThemeExport::writeFormatScheme(sax_fastparser::FSHelperPtr pFS)
namespace
{
void writeColorTransformations(sax_fastparser::FSHelperPtr pFS,
                               std::vector<model::Transformation> const& rTransformations)
{
    static std::unordered_map<model::TransformationType, sal_Int32> constTransformationTypeTokenMap
        = {
              { model::TransformationType::Tint, XML_tint },
              { model::TransformationType::Shade, XML_shade },
              { model::TransformationType::LumMod, XML_lumMod },
              { model::TransformationType::LumOff, XML_lumOff },
          };

    for (model::Transformation const& rTransformation : rTransformations)
    {
        auto iterator = constTransformationTypeTokenMap.find(rTransformation.meType);
        if (iterator != constTransformationTypeTokenMap.end())
        {
            sal_Int32 nToken = iterator->second;
            pFS->singleElementNS(XML_a, nToken, XML_val,
                                 OString::number(rTransformation.mnValue * 10));
        }
    }
}

void writeColorRGB(sax_fastparser::FSHelperPtr pFS, model::ColorDefinition const& rColorDefinition)
{
    auto aColor = rColorDefinition.getRGBColor();
    pFS->startElementNS(XML_a, XML_srgbClr, XML_val, I32SHEX(sal_Int32(aColor)));
    pFS->endElementNS(XML_a, XML_srgbClr);
}

void writeColorCRGB(sax_fastparser::FSHelperPtr pFS, model::ColorDefinition const& rColorDefinition)
{
    pFS->startElementNS(XML_a, XML_scrgbClr, XML_r, OString::number(rColorDefinition.mnComponent1),
                        XML_g, OString::number(rColorDefinition.mnComponent2), XML_b,
                        OString::number(rColorDefinition.mnComponent3));
    writeColorTransformations(pFS, rColorDefinition.maTransformations);
    pFS->endElementNS(XML_a, XML_scrgbClr);
}

void writeColorHSL(sax_fastparser::FSHelperPtr pFS, model::ColorDefinition const& rColorDefinition)
{
    pFS->startElementNS(XML_a, XML_hslClr, XML_hue, OString::number(rColorDefinition.mnComponent1),
                        XML_sat, OString::number(rColorDefinition.mnComponent2), XML_lum,
                        OString::number(rColorDefinition.mnComponent3));
    writeColorTransformations(pFS, rColorDefinition.maTransformations);
    pFS->endElementNS(XML_a, XML_hslClr);
}

void writeColorScheme(sax_fastparser::FSHelperPtr pFS,
                      model::ColorDefinition const& rColorDefinition)
{
    static std::unordered_map<model::ThemeColorType, const char*> constThemeColorTypeTokenMap
        = { { model::ThemeColorType::Dark1, "dk1" },
            { model::ThemeColorType::Light1, "lt1" },
            { model::ThemeColorType::Dark2, "dk2" },
            { model::ThemeColorType::Light2, "lt2" },
            { model::ThemeColorType::Accent1, "accent1" },
            { model::ThemeColorType::Accent2, "accent2" },
            { model::ThemeColorType::Accent3, "accent3" },
            { model::ThemeColorType::Accent4, "accent4" },
            { model::ThemeColorType::Accent5, "accent5" },
            { model::ThemeColorType::Accent6, "accent6" },
            { model::ThemeColorType::Hyperlink, "hlink" },
            { model::ThemeColorType::FollowedHyperlink, "folHlink" } };
    auto iterator = constThemeColorTypeTokenMap.find(rColorDefinition.meSchemeType);
    if (iterator != constThemeColorTypeTokenMap.end())
    {
        const char* sValue = iterator->second;
        pFS->startElementNS(XML_a, XML_schemeClr, XML_val, sValue);
        writeColorTransformations(pFS, rColorDefinition.maTransformations);
        pFS->endElementNS(XML_a, XML_schemeClr);
    }
}

void writeColorSystem(sax_fastparser::FSHelperPtr pFS,
                      model::ColorDefinition const& rColorDefinition)
{
    static std::unordered_map<model::SystemColorType, const char*> constThemeColorTypeTokenMap = {
        { model::SystemColorType::DarkShadow3D, "3dDkShadow" },
        { model::SystemColorType::Light3D, "3dLight" },
        { model::SystemColorType::ActiveBorder, "activeBorder" },
        { model::SystemColorType::ActiveCaption, "activeCaption" },
        { model::SystemColorType::AppWorkspace, "appWorkspace" },
        { model::SystemColorType::Background, "background" },
        { model::SystemColorType::ButtonFace, "btnFace" },
        { model::SystemColorType::ButtonHighlight, "btnHighlight" },
        { model::SystemColorType::ButtonShadow, "btnShadow" },
        { model::SystemColorType::ButtonText, "btnText" },
        { model::SystemColorType::CaptionText, "captionText" },
        { model::SystemColorType::GradientActiveCaption, "gradientActiveCaption" },
        { model::SystemColorType::GradientInactiveCaption, "gradientInactiveCaption" },
        { model::SystemColorType::GrayText, "grayText" },
        { model::SystemColorType::Highlight, "highlight" },
        { model::SystemColorType::HighlightText, "highlightText" },
        { model::SystemColorType::HotLight, "hotLight" },
        { model::SystemColorType::InactiveBorder, "inactiveBorder" },
        { model::SystemColorType::InactiveCaption, "inactiveCaption" },
        { model::SystemColorType::InactiveCaptionText, "inactiveCaptionText" },
        { model::SystemColorType::InfoBack, "infoBk" },
        { model::SystemColorType::InfoText, "infoText" },
        { model::SystemColorType::Menu, "menu" },
        { model::SystemColorType::MenuBar, "menuBar" },
        { model::SystemColorType::MenuHighlight, "menuHighlight" },
        { model::SystemColorType::MenuText, "menuText" },
        { model::SystemColorType::ScrollBar, "scrollBar" },
        { model::SystemColorType::Window, "window" },
        { model::SystemColorType::WindowFrame, "windowFrame" },
        { model::SystemColorType::WindowText, "windowText" },
    };
    auto iterator = constThemeColorTypeTokenMap.find(rColorDefinition.meSystemColorType);
    if (iterator != constThemeColorTypeTokenMap.end())
    {
        const char* sValue = iterator->second;
        pFS->startElementNS(XML_a, XML_sysClr, XML_val, sValue);
        //XML_lastClr
        writeColorTransformations(pFS, rColorDefinition.maTransformations);
        pFS->endElementNS(XML_a, XML_schemeClr);
    }
}

void writeColorPlaceholder(sax_fastparser::FSHelperPtr pFS,
                           model::ColorDefinition const& rColorDefinition)
{
    pFS->startElementNS(XML_a, XML_schemeClr, XML_val, "phClr");
    writeColorTransformations(pFS, rColorDefinition.maTransformations);
    pFS->endElementNS(XML_a, XML_schemeClr);
}

void writeColorDefinition(sax_fastparser::FSHelperPtr pFS,
                          model::ColorDefinition const& rColorDefinition)
{
    switch (rColorDefinition.meType)
    {
        case model::ColorType::Unused:
            break;
        case model::ColorType::RGB:
            writeColorRGB(pFS, rColorDefinition);
            break;
        case model::ColorType::CRGB:
            writeColorCRGB(pFS, rColorDefinition);
            break;
        case model::ColorType::HSL:
            writeColorHSL(pFS, rColorDefinition);
            break;
        case model::ColorType::Scheme:
            writeColorScheme(pFS, rColorDefinition);
            break;
        case model::ColorType::Palette:
            break;
        case model::ColorType::System:
            writeColorSystem(pFS, rColorDefinition);
            break;
        case model::ColorType::Placeholder:
            writeColorPlaceholder(pFS, rColorDefinition);
            break;
    }
}

void writeSolidFill(sax_fastparser::FSHelperPtr pFS, model::SolidFill const& rSolidFill)
{
    pFS->startElementNS(XML_a, XML_solidFill);
    writeColorDefinition(pFS, rSolidFill.maColorDefinition);
    pFS->endElementNS(XML_a, XML_solidFill);
}

void writeRelativeRectangle(sax_fastparser::FSHelperPtr pFS, sal_Int32 nToken,
                            model::RelativeRectangle const& rRelativeRectangle)
{
    pFS->singleElementNS(XML_a, nToken, XML_l, OString::number(rRelativeRectangle.mnLeft), XML_t,
                         OString::number(rRelativeRectangle.mnTop), XML_r,
                         OString::number(rRelativeRectangle.mnRight), XML_b,
                         OString::number(rRelativeRectangle.mnBottom));
}

void writeGradientFill(sax_fastparser::FSHelperPtr pFS, model::GradientFill const& rGradientFill)
{
    pFS->startElementNS(XML_a, XML_gradFill);
    pFS->startElementNS(XML_a, XML_gsLst);
    for (auto const& rStop : rGradientFill.maGradientStops)
    {
        pFS->startElementNS(XML_a, XML_gs, XML_pos,
                            OString::number(sal_Int32(rStop.mfPosition * 100000.0)));
        writeColorDefinition(pFS, rStop.maColor);
        pFS->endElementNS(XML_a, XML_gs);
    }
    pFS->endElementNS(XML_a, XML_gsLst);

    if (rGradientFill.meGradientType == model::GradientType::Linear)
    {
        pFS->singleElementNS(XML_a, XML_lin, XML_ang,
                             OString::number(rGradientFill.maLinearGradient.mnAngle), XML_scaled,
                             rGradientFill.maLinearGradient.mbScaled ? "1" : "0");
    }
    else
    {
        OString sPathType;
        switch (rGradientFill.meGradientType)
        {
            case model::GradientType::Circle:
                sPathType = "circle";
                break;
            case model::GradientType::Rectangle:
                sPathType = "rect";
                break;
            case model::GradientType::Shape:
                sPathType = "shape";
                break;
            default:
                break;
        }

        if (!sPathType.isEmpty())
        {
            pFS->startElementNS(XML_a, XML_path, XML_path, sPathType);
            writeRelativeRectangle(pFS, XML_fillToRect, rGradientFill.maFillToRectangle);
            pFS->endElementNS(XML_a, XML_path);
        }
    }
    writeRelativeRectangle(pFS, XML_tileRect, rGradientFill.maTileRectangle);
    pFS->endElementNS(XML_a, XML_gradFill);
}

void writePatternFill(sax_fastparser::FSHelperPtr pFS, model::PatternFill const& rPatternFill)
{
    OString sPresetType;
    switch (rPatternFill.mePatternPreset)
    {
        case model::PatternPreset::Percent_5:
            sPresetType = "pct5";
            break;
        case model::PatternPreset::Percent_10:
            sPresetType = "pct10";
            break;
        case model::PatternPreset::Percent_20:
            sPresetType = "pct20";
            break;
        case model::PatternPreset::Percent_25:
            sPresetType = "pct25";
            break;
        case model::PatternPreset::Percent_30:
            sPresetType = "pct30";
            break;
        case model::PatternPreset::Percent_40:
            sPresetType = "pct40";
            break;
        case model::PatternPreset::Percent_50:
            sPresetType = "pct50";
            break;
        case model::PatternPreset::Percent_60:
            sPresetType = "pct60";
            break;
        case model::PatternPreset::Percent_70:
            sPresetType = "pct70";
            break;
        case model::PatternPreset::Percent_75:
            sPresetType = "pct75";
            break;
        case model::PatternPreset::Percent_80:
            sPresetType = "pct80";
            break;
        case model::PatternPreset::Percent_90:
            sPresetType = "pct90";
            break;
        case model::PatternPreset::Horizontal:
            sPresetType = "horz";
            break;
        case model::PatternPreset::Vertical:
            sPresetType = "vert";
            break;
        case model::PatternPreset::LightHorizontal:
            sPresetType = "ltHorz";
            break;
        case model::PatternPreset::LightVertical:
            sPresetType = "ltVert";
            break;
        case model::PatternPreset::DarkHorizontal:
            sPresetType = "dkHorz";
            break;
        case model::PatternPreset::DarkVertical:
            sPresetType = "dkVert";
            break;
        case model::PatternPreset::NarrowHorizontal:
            sPresetType = "narHorz";
            break;
        case model::PatternPreset::NarrowVertical:
            sPresetType = "narVert";
            break;
        case model::PatternPreset::DashedHorizontal:
            sPresetType = "dashHorz";
            break;
        case model::PatternPreset::DashedVertical:
            sPresetType = "dashVert";
            break;
        case model::PatternPreset::Cross:
            sPresetType = "cross";
            break;
        case model::PatternPreset::DownwardDiagonal:
            sPresetType = "dnDiag";
            break;
        case model::PatternPreset::UpwardDiagonal:
            sPresetType = "upDiag";
            break;
        case model::PatternPreset::LightDownwardDiagonal:
            sPresetType = "ltDnDiag";
            break;
        case model::PatternPreset::LightUpwardDiagonal:
            sPresetType = "ltUpDiag";
            break;
        case model::PatternPreset::DarkDownwardDiagonal:
            sPresetType = "dkDnDiag";
            break;
        case model::PatternPreset::DarkUpwardDiagonal:
            sPresetType = "dkUpDiag";
            break;
        case model::PatternPreset::WideDownwardDiagonal:
            sPresetType = "wdDnDiag";
            break;
        case model::PatternPreset::WideUpwardDiagonal:
            sPresetType = "wdUpDiag";
            break;
        case model::PatternPreset::DashedDownwardDiagonal:
            sPresetType = "dashDnDiag";
            break;
        case model::PatternPreset::DashedUpwardDiagonal:
            sPresetType = "dashUpDiag";
            break;
        case model::PatternPreset::DiagonalCross:
            sPresetType = "diagCross";
            break;
        case model::PatternPreset::SmallCheckerBoard:
            sPresetType = "smCheck";
            break;
        case model::PatternPreset::LargeCheckerBoard:
            sPresetType = "lgCheck";
            break;
        case model::PatternPreset::SmallGrid:
            sPresetType = "smGrid";
            break;
        case model::PatternPreset::LargeGrid:
            sPresetType = "lgGrid";
            break;
        case model::PatternPreset::DottedGrid:
            sPresetType = "dotGrid";
            break;
        case model::PatternPreset::SmallConfetti:
            sPresetType = "smConfetti";
            break;
        case model::PatternPreset::LargeConfetti:
            sPresetType = "lgConfetti";
            break;
        case model::PatternPreset::HorizontalBrick:
            sPresetType = "horzBrick";
            break;
        case model::PatternPreset::DiagonalBrick:
            sPresetType = "diagBrick";
            break;
        case model::PatternPreset::SolidDiamond:
            sPresetType = "solidDmnd";
            break;
        case model::PatternPreset::OpenDiamond:
            sPresetType = "openDmnd";
            break;
        case model::PatternPreset::DottedDiamond:
            sPresetType = "dotDmnd";
            break;
        case model::PatternPreset::Plaid:
            sPresetType = "plaid";
            break;
        case model::PatternPreset::Sphere:
            sPresetType = "sphere";
            break;
        case model::PatternPreset::Weave:
            sPresetType = "weave";
            break;
        case model::PatternPreset::Divot:
            sPresetType = "divot";
            break;
        case model::PatternPreset::Shingle:
            sPresetType = "shingle";
            break;
        case model::PatternPreset::Wave:
            sPresetType = "wave";
            break;
        case model::PatternPreset::Trellis:
            sPresetType = "trellis";
            break;
        case model::PatternPreset::ZigZag:
            sPresetType = "zigZag";
            break;
        default:
            break;
    }

    if (!sPresetType.isEmpty())
    {
        pFS->startElementNS(XML_a, XML_pattFill, XML_prst, sPresetType);

        pFS->startElementNS(XML_a, XML_fgClr);
        writeColorDefinition(pFS, rPatternFill.maForegroundColor);
        pFS->endElementNS(XML_a, XML_fgClr);

        pFS->startElementNS(XML_a, XML_bgClr);
        writeColorDefinition(pFS, rPatternFill.maBackgroundColor);
        pFS->endElementNS(XML_a, XML_bgClr);

        pFS->endElementNS(XML_a, XML_pattFill);
    }
}

OString convertFlipMode(model::FlipMode eFlipMode)
{
    switch (eFlipMode)
    {
        case model::FlipMode::X:
            return "x";
        case model::FlipMode::Y:
            return "y";
        case model::FlipMode::XY:
            return "xy";
        case model::FlipMode::None:
            return "none";
    }
    return "none";
}

OString convertRectangleAlignment(model::RectangleAlignment eFlipMode)
{
    switch (eFlipMode)
    {
        case model::RectangleAlignment::TopLeft:
            return "tl";
        case model::RectangleAlignment::Top:
            return "t";
        case model::RectangleAlignment::TopRight:
            return "tr";
        case model::RectangleAlignment::Left:
            return "l";
        case model::RectangleAlignment::Center:
            return "ctr";
        case model::RectangleAlignment::Right:
            return "r";
        case model::RectangleAlignment::BottomLeft:
            return "bl";
        case model::RectangleAlignment::Bottom:
            return "b";
        case model::RectangleAlignment::BottomRight:
            return "br";
        case model::RectangleAlignment::Unset:
            break;
    }
    return {};
}

void writeBlip(sax_fastparser::FSHelperPtr pFS, model::BlipFill const& /*rBlipFill*/)
{
    // TODO - reuse WriteXGraphicBlip
    pFS->startElementNS(XML_a, XML_blip);
    pFS->endElementNS(XML_a, XML_blip);
}

void writeBlipFill(sax_fastparser::FSHelperPtr pFS, model::BlipFill const& rBlipFill)
{
    pFS->startElementNS(XML_a, XML_blipFill, XML_rotWithShape,
                        rBlipFill.mbRotateWithShape ? "1" : "0"
                        /*XML_dpi*/);

    writeBlip(pFS, rBlipFill);

    writeRelativeRectangle(pFS, XML_srcRect, rBlipFill.maClipRectangle);

    if (rBlipFill.meMode == model::BitmapMode::Tile)
    {
        OString aFlipMode = convertFlipMode(rBlipFill.meTileFlipMode);
        OString aAlignment = convertRectangleAlignment(rBlipFill.meTileAlignment);

        pFS->startElementNS(XML_a, XML_tile, XML_tx, OString::number(rBlipFill.mnTileOffsetX),
                            XML_ty, OString::number(rBlipFill.mnTileOffsetY), XML_sx,
                            OString::number(rBlipFill.mnTileScaleX), XML_sy,
                            OString::number(rBlipFill.mnTileScaleY), XML_flip, aFlipMode, XML_algn,
                            aAlignment);
        pFS->endElementNS(XML_a, XML_tile);
    }
    else if (rBlipFill.meMode == model::BitmapMode::Stretch)
    {
        pFS->startElementNS(XML_a, XML_stretch);
        writeRelativeRectangle(pFS, XML_fillRect, rBlipFill.maFillRectangle);
        pFS->endElementNS(XML_a, XML_stretch);
    }

    pFS->endElementNS(XML_a, XML_blipFill);
}

void writeFillStyle(sax_fastparser::FSHelperPtr pFS, model::FillStyle const& rFillStyle)
{
    switch (rFillStyle.mpFill->meType)
    {
        case model::FillType::None:
        case model::FillType::Solid:
        {
            auto* pSolidFill = static_cast<model::SolidFill*>(rFillStyle.mpFill.get());
            writeSolidFill(pFS, *pSolidFill);
        }
        break;
        case model::FillType::Gradient:
        {
            auto* pGradientFill = static_cast<model::GradientFill*>(rFillStyle.mpFill.get());
            writeGradientFill(pFS, *pGradientFill);
        }
        break;
        case model::FillType::Pattern:
        {
            auto* pPatternFill = static_cast<model::PatternFill*>(rFillStyle.mpFill.get());
            writePatternFill(pFS, *pPatternFill);
        }
        break;
        case model::FillType::Blip:
        {
            auto* pBlipFill = static_cast<model::BlipFill*>(rFillStyle.mpFill.get());
            writeBlipFill(pFS, *pBlipFill);
        }
        break;
    }
}

void writeBackgroundFillStyle(sax_fastparser::FSHelperPtr pFS, model::FillStyle const& rFillStyle)
{
    writeFillStyle(pFS, rFillStyle);
}

void writeLineStyle(sax_fastparser::FSHelperPtr pFS, model::LineStyle const& rLineStyle)
{
    OString sCap;
    switch (rLineStyle.meCapType)
    {
        case model::CapType::Flat:
            sCap = "flat";
            break;
        case model::CapType::Round:
            sCap = "rnd";
            break;
        case model::CapType::Square:
            sCap = "sq";
            break;
        case model::CapType::Unset:
            break;
    }

    OString sPenAlign;
    switch (rLineStyle.mePenAlignment)
    {
        case model::PenAlignmentType::Center:
            sPenAlign = "ctr";
            break;
        case model::PenAlignmentType::Inset:
            sPenAlign = "in";
            break;
        case model::PenAlignmentType::Unset:
            break;
    }

    OString sCompoundLine;
    switch (rLineStyle.meCompoundLineType)
    {
        case model::CompoundLineType::Single:
            sCompoundLine = "sng";
            break;
        case model::CompoundLineType::Double:
            sCompoundLine = "dbl";
            break;
        case model::CompoundLineType::ThickThin_Double:
            sCompoundLine = "thickThin";
            break;
        case model::CompoundLineType::ThinThick_Double:
            sCompoundLine = "thinThick";
            break;
        case model::CompoundLineType::Triple:
            sCompoundLine = "tri";
            break;
        case model::CompoundLineType::Unset:
            break;
    }

    pFS->startElementNS(XML_a, XML_ln, XML_w, OString::number(rLineStyle.mnWidth), XML_cap,
                        sax_fastparser::UseIf(sCap, !sCap.isEmpty()), XML_cmpd,
                        sax_fastparser::UseIf(sCompoundLine, !sCompoundLine.isEmpty()), XML_algn,
                        sax_fastparser::UseIf(sPenAlign, !sPenAlign.isEmpty()));

    if (rLineStyle.maLineDash.mePresetType != model::PresetDashType::Unset)
    {
        OString sPresetType;
        switch (rLineStyle.maLineDash.mePresetType)
        {
            case model::PresetDashType::Dot:
                sPresetType = "dot";
                break;
            case model::PresetDashType::Dash:
                sPresetType = "dash";
                break;
            case model::PresetDashType::LargeDash:
                sPresetType = "lgDash";
                break;
            case model::PresetDashType::DashDot:
                sPresetType = "dashDot";
                break;
            case model::PresetDashType::LargeDashDot:
                sPresetType = "lgDashDot";
                break;
            case model::PresetDashType::LargeDashDotDot:
                sPresetType = "lgDashDotDot";
                break;
            case model::PresetDashType::Solid:
                sPresetType = "solid";
                break;
            case model::PresetDashType::SystemDash:
                sPresetType = "sysDash";
                break;
            case model::PresetDashType::SystemDot:
                sPresetType = "sysDot";
                break;
            case model::PresetDashType::SystemDashDot:
                sPresetType = "sysDashDot";
                break;
            case model::PresetDashType::SystemDashDotDot:
                sPresetType = "sysDashDotDot";
                break;
            case model::PresetDashType::Unset:
                break;
        }
        pFS->singleElementNS(XML_a, XML_prstDash, XML_val, sPresetType);
    }

    if (rLineStyle.maLineJoin.meType != model::LineJoinType::Unset)
    {
        switch (rLineStyle.maLineJoin.meType)
        {
            case model::LineJoinType::Round:
                pFS->singleElementNS(XML_a, XML_round);
                break;
            case model::LineJoinType::Bevel:
                pFS->singleElementNS(XML_a, XML_bevel);
                break;
            case model::LineJoinType::Miter:
            {
                sal_Int32 nMiterLimit = rLineStyle.maLineJoin.mnMiterLimit;
                pFS->singleElementNS(
                    XML_a, XML_miter, XML_lim,
                    sax_fastparser::UseIf(OString::number(nMiterLimit), nMiterLimit > 0));
            }
            break;
            case model::LineJoinType::Unset:
                break;
        }
    }

    pFS->endElementNS(XML_a, XML_ln);
}

void writeEffectStyle(sax_fastparser::FSHelperPtr pFS, model::EffectStyle const& /*rEffectStyle*/)
{
    pFS->startElementNS(XML_a, XML_effectStyle);
    pFS->singleElementNS(XML_a, XML_effectLst);
    pFS->endElementNS(XML_a, XML_effectStyle);
}

} // end anonymous ns

bool ThemeExport::writeFormatScheme(sax_fastparser::FSHelperPtr pFS,
                                    model::FormatScheme const& rFormatScheme)
{
    // Format Scheme: 3 or more per list but only 3 will be used currently

    // Fill Style List
    rFormatScheme.ensureFillStyleList();
    pFS->startElementNS(XML_a, XML_fillStyleLst);
    for (auto const& rFillStyle : rFormatScheme.getFillStyleList())
    {
        pFS->startElementNS(XML_a, XML_solidFill);
        pFS->singleElementNS(XML_a, XML_schemeClr, XML_val, "phClr");
        pFS->endElementNS(XML_a, XML_solidFill);

        pFS->startElementNS(XML_a, XML_solidFill);
        pFS->singleElementNS(XML_a, XML_schemeClr, XML_val, "phClr");
        pFS->endElementNS(XML_a, XML_solidFill);

        pFS->startElementNS(XML_a, XML_solidFill);
        pFS->singleElementNS(XML_a, XML_schemeClr, XML_val, "phClr");
        pFS->endElementNS(XML_a, XML_solidFill);
        writeFillStyle(pFS, rFillStyle);
    }
    pFS->endElementNS(XML_a, XML_fillStyleLst);

    // Line Style List
    rFormatScheme.ensureLineStyleList();
    pFS->startElementNS(XML_a, XML_lnStyleLst);
    for (auto const& rLineStyle : rFormatScheme.getLineStyleList())
    {
        pFS->startElementNS(XML_a, XML_ln, XML_w, "6350", XML_cap, "flat", XML_cmpd, "sng",
                            XML_algn, "ctr");
        {
            pFS->startElementNS(XML_a, XML_solidFill);
            pFS->startElementNS(XML_a, XML_schemeClr, XML_val, "phClr");
            pFS->singleElementNS(XML_a, XML_shade, XML_val, "95000");
            pFS->endElementNS(XML_a, XML_schemeClr);
            pFS->endElementNS(XML_a, XML_solidFill);

            pFS->singleElementNS(XML_a, XML_prstDash, XML_val, "solid");

            pFS->singleElementNS(XML_a, XML_miter);
        }
        pFS->endElementNS(XML_a, XML_ln);
    }
    {
        pFS->startElementNS(XML_a, XML_ln, XML_w, "6350", XML_cap, "flat", XML_cmpd, "sng",
                            XML_algn, "ctr");
        {
            pFS->startElementNS(XML_a, XML_solidFill);
            pFS->startElementNS(XML_a, XML_schemeClr, XML_val, "phClr");
            pFS->singleElementNS(XML_a, XML_shade, XML_val, "95000");
            pFS->endElementNS(XML_a, XML_schemeClr);
            pFS->endElementNS(XML_a, XML_solidFill);

            pFS->singleElementNS(XML_a, XML_prstDash, XML_val, "solid");

            pFS->singleElementNS(XML_a, XML_miter);
        }
        pFS->endElementNS(XML_a, XML_ln);
    }
    {
        pFS->startElementNS(XML_a, XML_ln, XML_w, "6350", XML_cap, "flat", XML_cmpd, "sng",
                            XML_algn, "ctr");
        {
            pFS->startElementNS(XML_a, XML_solidFill);
            pFS->startElementNS(XML_a, XML_schemeClr, XML_val, "phClr");
            pFS->singleElementNS(XML_a, XML_shade, XML_val, "95000");
            pFS->endElementNS(XML_a, XML_schemeClr);
            pFS->endElementNS(XML_a, XML_solidFill);

            pFS->singleElementNS(XML_a, XML_prstDash, XML_val, "solid");

            pFS->singleElementNS(XML_a, XML_miter);
        }
        pFS->endElementNS(XML_a, XML_ln);
        writeLineStyle(pFS, rLineStyle);
    }
    pFS->endElementNS(XML_a, XML_lnStyleLst);

    // Effect Style List
    rFormatScheme.ensureEffectStyleList();
    pFS->startElementNS(XML_a, XML_effectStyleLst);
    {
        pFS->startElementNS(XML_a, XML_effectStyle);
        pFS->singleElementNS(XML_a, XML_effectLst);
        pFS->endElementNS(XML_a, XML_effectStyle);

        pFS->startElementNS(XML_a, XML_effectStyle);
        pFS->singleElementNS(XML_a, XML_effectLst);
        pFS->endElementNS(XML_a, XML_effectStyle);

        pFS->startElementNS(XML_a, XML_effectStyle);
        pFS->singleElementNS(XML_a, XML_effectLst);
        pFS->endElementNS(XML_a, XML_effectStyle);
        for (auto const& rEffectStyle : rFormatScheme.getEffectStyleList())
        {
            writeEffectStyle(pFS, rEffectStyle);
        }
    }
    pFS->endElementNS(XML_a, XML_effectStyleLst);

    // Background Fill Style List
    rFormatScheme.ensureBackgroundFillStyleList();
    pFS->startElementNS(XML_a, XML_bgFillStyleLst);
    for (auto const& rFillStyle : rFormatScheme.getBackgroundFillStyleList())
    {
        pFS->startElementNS(XML_a, XML_solidFill);
        pFS->singleElementNS(XML_a, XML_schemeClr, XML_val, "phClr");
        pFS->endElementNS(XML_a, XML_solidFill);

        pFS->startElementNS(XML_a, XML_solidFill);
        pFS->singleElementNS(XML_a, XML_schemeClr, XML_val, "phClr");
        pFS->endElementNS(XML_a, XML_solidFill);

        pFS->startElementNS(XML_a, XML_solidFill);
        pFS->singleElementNS(XML_a, XML_schemeClr, XML_val, "phClr");
        pFS->endElementNS(XML_a, XML_solidFill);
        writeBackgroundFillStyle(pFS, rFillStyle);
    }
    pFS->endElementNS(XML_a, XML_bgFillStyleLst);

diff --git a/sd/qa/filter/eppt/eppt.cxx b/sd/qa/filter/eppt/eppt.cxx
index 32d9b33..f5420a6 100644
--- a/sd/qa/filter/eppt/eppt.cxx
+++ b/sd/qa/filter/eppt/eppt.cxx
@@ -12,6 +12,8 @@
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
#include <com/sun/star/drawing/XMasterPageTarget.hpp>
#include <com/sun/star/util/Color.hpp>
#include <com/sun/star/util/XTheme.hpp>

#include <test/xmldocptr.hxx>
#include <docmodel/uno/UnoTheme.hxx>
@@ -56,40 +58,57 @@ CPPUNIT_TEST_FIXTURE(Test, testThemeExport)
{
    // Given a document with a master slide and a theme, lt1 is set to 0x000002:
    mxComponent = loadFromDesktop("private:factory/simpress");
    uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
    uno::Reference<drawing::XMasterPageTarget> xDrawPage(
        xDrawPagesSupplier->getDrawPages()->getByIndex(0), uno::UNO_QUERY);
    uno::Reference<beans::XPropertySet> xMasterPage(xDrawPage->getMasterPage(), uno::UNO_QUERY);
    {
        uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
        uno::Reference<drawing::XMasterPageTarget> xDrawPage(
            xDrawPagesSupplier->getDrawPages()->getByIndex(0), uno::UNO_QUERY);
        uno::Reference<beans::XPropertySet> xMasterPage(xDrawPage->getMasterPage(), uno::UNO_QUERY);

    auto pTheme = std::make_shared<model::Theme>("mytheme");
    std::unique_ptr<model::ColorSet> pColorSet(new model::ColorSet("mycolorscheme"));
    pColorSet->add(model::ThemeColorType::Dark1, 0x1);
    pColorSet->add(model::ThemeColorType::Light1, 0x2);
    pColorSet->add(model::ThemeColorType::Dark2, 0x3);
    pColorSet->add(model::ThemeColorType::Light2, 0x4);
    pColorSet->add(model::ThemeColorType::Accent1, 0x5);
    pColorSet->add(model::ThemeColorType::Accent2, 0x6);
    pColorSet->add(model::ThemeColorType::Accent3, 0x7);
    pColorSet->add(model::ThemeColorType::Accent4, 0x8);
    pColorSet->add(model::ThemeColorType::Accent5, 0x9);
    pColorSet->add(model::ThemeColorType::Accent6, 0xa);
    pColorSet->add(model::ThemeColorType::Hyperlink, 0xb);
    pColorSet->add(model::ThemeColorType::FollowedHyperlink, 0xc);
    pTheme->SetColorSet(std::move(pColorSet));
        auto pTheme = std::make_shared<model::Theme>("mytheme");
        std::unique_ptr<model::ColorSet> pColorSet(new model::ColorSet("mycolorscheme"));
        pColorSet->add(model::ThemeColorType::Dark1, 0x111111);
        pColorSet->add(model::ThemeColorType::Light1, 0x222222);
        pColorSet->add(model::ThemeColorType::Dark2, 0x333333);
        pColorSet->add(model::ThemeColorType::Light2, 0x444444);
        pColorSet->add(model::ThemeColorType::Accent1, 0x555555);
        pColorSet->add(model::ThemeColorType::Accent2, 0x666666);
        pColorSet->add(model::ThemeColorType::Accent3, 0x777777);
        pColorSet->add(model::ThemeColorType::Accent4, 0x888888);
        pColorSet->add(model::ThemeColorType::Accent5, 0x999999);
        pColorSet->add(model::ThemeColorType::Accent6, 0xaaaaaa);
        pColorSet->add(model::ThemeColorType::Hyperlink, 0xbbbbbb);
        pColorSet->add(model::ThemeColorType::FollowedHyperlink, 0xcccccc);
        pTheme->SetColorSet(std::move(pColorSet));

    xMasterPage->setPropertyValue("Theme", uno::Any(model::theme::createXTheme(pTheme)));
        xMasterPage->setPropertyValue("Theme", uno::Any(model::theme::createXTheme(pTheme)));
    }

    // When exporting to PPTX:
    save("Impress Office Open XML");
    // Export to PPTX and load again:
    saveAndReload("Impress Office Open XML");

    // Then verify that this color is not lost:
    // Verify that this color is not lost:
    xmlDocUniquePtr pXmlDoc = parseExport("ppt/theme/theme1.xml");
    assertXPath(pXmlDoc, "//a:clrScheme/a:lt1/a:srgbClr", "val", "000002");
    // Without the fix in place, this test would have failed with:
    // - Expected: 1
    // - Actual  : 0
    // - XPath '//a:clrScheme/a:lt1/a:srgbClr' number of nodes is incorrect
    // i.e. the RGB color was lost on export.
    assertXPath(pXmlDoc, "//a:clrScheme/a:lt1/a:srgbClr", "val",
                "222222"); // expected color 22-22-22

    // Check the theme after loading again
    {
        uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
        uno::Reference<drawing::XMasterPageTarget> xDrawPage(
            xDrawPagesSupplier->getDrawPages()->getByIndex(0), uno::UNO_QUERY);
        uno::Reference<beans::XPropertySet> xMasterPage(xDrawPage->getMasterPage(), uno::UNO_QUERY);
        uno::Reference<util::XTheme> xTheme(xMasterPage->getPropertyValue("Theme"), uno::UNO_QUERY);
        CPPUNIT_ASSERT_EQUAL(true, xTheme.is());

        auto* pUnoTheme = dynamic_cast<UnoTheme*>(xTheme.get());
        CPPUNIT_ASSERT(pUnoTheme);
        auto pTheme = pUnoTheme->getTheme();

        CPPUNIT_ASSERT_EQUAL(OUString("mytheme"), pTheme->GetName());
        CPPUNIT_ASSERT_EQUAL(OUString("mycolorscheme"), pTheme->GetColorSet()->getName());
        CPPUNIT_ASSERT_EQUAL(OUString("Office"), pTheme->getFontScheme().getName());
        CPPUNIT_ASSERT_EQUAL(OUString(""), pTheme->getFormatScheme().getName());
    }
}

CPPUNIT_TEST_FIXTURE(Test, testLoopingFromAnimation)
diff --git a/sd/source/filter/eppt/epptooxml.hxx b/sd/source/filter/eppt/epptooxml.hxx
index 48c928d..0c7644c 100644
--- a/sd/source/filter/eppt/epptooxml.hxx
+++ b/sd/source/filter/eppt/epptooxml.hxx
@@ -90,13 +90,6 @@ private:
    virtual void ImplWriteNotes( sal_uInt32 nPageNum ) override;
    virtual void ImplWriteSlideMaster( sal_uInt32 nPageNum, css::uno::Reference< css::beans::XPropertySet > const & aXBackgroundPropSet ) override;
    void ImplWritePPTXLayout( sal_Int32 nOffset, sal_uInt32 nMasterNum );

    /// Export the color set part of a theme.
    static bool WriteColorSets(const FSHelperPtr& pFS, model::Theme* pTheme);

    /// Same as WriteColorSets(), but works from a grab-bag.
    bool WriteColorSchemes(const FSHelperPtr& pFS, const OUString& rThemePath);

    static void WriteDefaultColorSchemes(const FSHelperPtr& pFS);
    void WriteTheme( sal_Int32 nThemeNum, model::Theme* pTheme );

diff --git a/sd/source/filter/eppt/pptx-epptooxml.cxx b/sd/source/filter/eppt/pptx-epptooxml.cxx
index c0215ec..9e207ba 100644
--- a/sd/source/filter/eppt/pptx-epptooxml.cxx
+++ b/sd/source/filter/eppt/pptx-epptooxml.cxx
@@ -56,6 +56,7 @@
#include <comphelper/diagnose_ex.hxx>

#include <oox/export/utils.hxx>
#include <oox/export/ThemeExport.hxx>
#include <docmodel/theme/Theme.hxx>

#include "pptx-animations.hxx"
@@ -1938,172 +1939,6 @@ ShapeExport& PowerPointShapeExport::WritePlaceholderReferenceTextBody(
    return *this;
}

#define SYS_COLOR_SCHEMES "      <a:dk1>\
        <a:sysClr val=\"windowText\" lastClr=\"000000\"/>\
      </a:dk1>\
      <a:lt1>\
        <a:sysClr val=\"window\" lastClr=\"FFFFFF\"/>\
      </a:lt1>"

#define MINIMAL_THEME "    <a:fontScheme name=\"Office\">\
      <a:majorFont>\
        <a:latin typeface=\"Arial\"/>\
        <a:ea typeface=\"DejaVu Sans\"/>\
        <a:cs typeface=\"DejaVu Sans\"/>\
      </a:majorFont>\
      <a:minorFont>\
        <a:latin typeface=\"Arial\"/>\
        <a:ea typeface=\"DejaVu Sans\"/>\
        <a:cs typeface=\"DejaVu Sans\"/>\
      </a:minorFont>\
    </a:fontScheme>\
    <a:fmtScheme name=\"Office\">\
      <a:fillStyleLst>\
        <a:solidFill>\
          <a:schemeClr val=\"phClr\"/>\
        </a:solidFill>\
        <a:gradFill rotWithShape=\"1\">\
          <a:gsLst>\
            <a:gs pos=\"0\">\
              <a:schemeClr val=\"phClr\">\
                <a:tint val=\"50000\"/>\
                <a:satMod val=\"300000\"/>\
              </a:schemeClr>\
            </a:gs>\
            <a:gs pos=\"35000\">\
              <a:schemeClr val=\"phClr\">\
                <a:tint val=\"37000\"/>\
                <a:satMod val=\"300000\"/>\
              </a:schemeClr>\
            </a:gs>\
            <a:gs pos=\"100000\">\
              <a:schemeClr val=\"phClr\">\
                <a:tint val=\"15000\"/>\
                <a:satMod val=\"350000\"/>\
              </a:schemeClr>\
            </a:gs>\
          </a:gsLst>\
          <a:lin ang=\"16200000\" scaled=\"1\"/>\
        </a:gradFill>\
        <a:gradFill rotWithShape=\"1\">\
          <a:gsLst>\
            <a:gs pos=\"0\">\
              <a:schemeClr val=\"phClr\">\
                <a:shade val=\"51000\"/>\
                <a:satMod val=\"130000\"/>\
              </a:schemeClr>\
            </a:gs>\
            <a:gs pos=\"80000\">\
              <a:schemeClr val=\"phClr\">\
                <a:shade val=\"93000\"/>\
                <a:satMod val=\"130000\"/>\
              </a:schemeClr>\
            </a:gs>\
            <a:gs pos=\"100000\">\
              <a:schemeClr val=\"phClr\">\
                <a:shade val=\"94000\"/>\
                <a:satMod val=\"135000\"/>\
              </a:schemeClr>\
            </a:gs>\
          </a:gsLst>\
          <a:lin ang=\"16200000\" scaled=\"0\"/>\
        </a:gradFill>\
      </a:fillStyleLst>\
      <a:lnStyleLst>\
        <a:ln w=\"6350\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\">\
          <a:solidFill>\
            <a:schemeClr val=\"phClr\">\
              <a:shade val=\"95000\"/>\
              <a:satMod val=\"105000\"/>\
            </a:schemeClr>\
          </a:solidFill>\
          <a:prstDash val=\"solid\"/>\
          <a:miter/>\
        </a:ln>\
        <a:ln w=\"12700\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\">\
          <a:solidFill>\
            <a:schemeClr val=\"phClr\"/>\
          </a:solidFill>\
          <a:prstDash val=\"solid\"/>\
          <a:miter/>\
        </a:ln>\
        <a:ln w=\"19050\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\">\
          <a:solidFill>\
            <a:schemeClr val=\"phClr\"/>\
          </a:solidFill>\
          <a:prstDash val=\"solid\"/>\
          <a:miter/>\
        </a:ln>\
      </a:lnStyleLst>\
      <a:effectStyleLst>\
        <a:effectStyle>\
          <a:effectLst/>\
        </a:effectStyle>\
        <a:effectStyle>\
          <a:effectLst/>\
        </a:effectStyle>\
        <a:effectStyle>\
          <a:effectLst>\
            <a:outerShdw blurRad=\"40000\" dist=\"23000\" dir=\"5400000\" rotWithShape=\"0\">\
              <a:srgbClr val=\"000000\">\
                <a:alpha val=\"35000\"/>\
              </a:srgbClr>\
            </a:outerShdw>\
          </a:effectLst>\
        </a:effectStyle>\
      </a:effectStyleLst>\
      <a:bgFillStyleLst>\
        <a:solidFill>\
          <a:schemeClr val=\"phClr\"/>\
        </a:solidFill>\
        <a:gradFill rotWithShape=\"1\">\
          <a:gsLst>\
            <a:gs pos=\"0\">\
              <a:schemeClr val=\"phClr\">\
                <a:tint val=\"40000\"/>\
                <a:satMod val=\"350000\"/>\
              </a:schemeClr>\
            </a:gs>\
            <a:gs pos=\"40000\">\
              <a:schemeClr val=\"phClr\">\
                <a:tint val=\"45000\"/>\
                <a:shade val=\"99000\"/>\
                <a:satMod val=\"350000\"/>\
              </a:schemeClr>\
            </a:gs>\
            <a:gs pos=\"100000\">\
              <a:schemeClr val=\"phClr\">\
                <a:shade val=\"20000\"/>\
                <a:satMod val=\"255000\"/>\
              </a:schemeClr>\
            </a:gs>\
          </a:gsLst>\
          <a:path path=\"circle\">\
            <a:fillToRect l=\"50000\" t=\"-80000\" r=\"50000\" b=\"180000\"/>\
          </a:path>\
        </a:gradFill>\
        <a:gradFill rotWithShape=\"1\">\
          <a:gsLst>\
            <a:gs pos=\"0\">\
              <a:schemeClr val=\"phClr\">\
                <a:tint val=\"80000\"/>\
                <a:satMod val=\"300000\"/>\
              </a:schemeClr>\
            </a:gs>\
            <a:gs pos=\"100000\">\
              <a:schemeClr val=\"phClr\">\
                <a:shade val=\"30000\"/>\
                <a:satMod val=\"200000\"/>\
              </a:schemeClr>\
            </a:gs>\
          </a:gsLst>\
          <a:path path=\"circle\">\
            <a:fillToRect l=\"50000\" t=\"50000\" r=\"50000\" b=\"50000\"/>\
          </a:path>\
        </a:gradFill>\
      </a:bgFillStyleLst>\
    </a:fmtScheme>"

void PowerPointExport::WriteDefaultColorSchemes(const FSHelperPtr& pFS)
{
    for (int nId = PredefinedClrSchemeId::dk2; nId != PredefinedClrSchemeId::Count; nId++)
@@ -2155,155 +1990,15 @@ void PowerPointExport::WriteDefaultColorSchemes(const FSHelperPtr& pFS)
    }
}

bool PowerPointExport::WriteColorSets(const FSHelperPtr& pFS, model::Theme* pTheme)
{
    static std::map<PredefinedClrSchemeId, sal_Int32> aPredefinedClrTokens =
    {
        { dk1, XML_dk1 },
        { lt1, XML_lt1 },
        { dk2, XML_dk2 },
        { lt2, XML_lt2 },
        { accent1, XML_accent1 },
        { accent2, XML_accent2 },
        { accent3, XML_accent3 },
        { accent4, XML_accent4 },
        { accent5, XML_accent5 },
        { accent6, XML_accent6 },
        { hlink, XML_hlink },
        { folHlink, XML_folHlink }
    };

    if (!pTheme)
    {
        return false;
    }

    model::ColorSet* pColorSet = pTheme->GetColorSet();
    if (!pColorSet)
    {
        return false;
    }

    for (int nId = PredefinedClrSchemeId::dk1; nId < PredefinedClrSchemeId::Count; nId++)
    {
        sal_Int32 nToken = aPredefinedClrTokens[static_cast<PredefinedClrSchemeId>(nId)];
        pFS->startElementNS(XML_a, nToken);
        model::ThemeColorType eType = model::convertToThemeColorType(nId);
        pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, I32SHEX(static_cast<sal_Int32>(pColorSet->getColor(eType))));
        pFS->endElementNS(XML_a, nToken);
    }

    return true;
}

bool PowerPointExport::WriteColorSchemes(const FSHelperPtr& pFS, const OUString& rThemePath)
{
    try
    {
        uno::Reference<beans::XPropertySet> xDocProps(getModel(), uno::UNO_QUERY);
        if (xDocProps.is())
        {
            uno::Reference<beans::XPropertySetInfo> xPropsInfo = xDocProps->getPropertySetInfo();

            static const OUStringLiteral aGrabBagPropName = u"InteropGrabBag";
            if (xPropsInfo.is() && xPropsInfo->hasPropertyByName(aGrabBagPropName))
            {
                comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue(aGrabBagPropName));
                uno::Sequence<beans::PropertyValue> aCurrentTheme;

                aGrabBag.getValue(rThemePath) >>= aCurrentTheme;

                if (!aCurrentTheme.hasElements())
                    return false;

                // Order is important
                for (int nId = PredefinedClrSchemeId::dk2; nId != PredefinedClrSchemeId::Count; nId++)
                {
                    OUString sName = PredefinedClrNames[static_cast<PredefinedClrSchemeId>(nId)];
                    sal_Int32 nColor = 0;

                    for (auto aIt = std::cbegin(aCurrentTheme); aIt != std::cend(aCurrentTheme); aIt++)
                    {
                        if (aIt->Name == sName)
                        {
                            aIt->Value >>= nColor;
                            break;
                        }
                    }

                    OUString sOpenColorScheme ="<a:" + sName + ">";
                    pFS->write(sOpenColorScheme);

                    pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, I32SHEX(nColor));

                    OUString sCloseColorScheme = "</a:" + sName + ">";
                    pFS->write(sCloseColorScheme);
                }

                // TODO: write complete color schemes & only if successful, protection against partial export
                return true;
            }
        }
    }
    catch (const uno::Exception&)
    {
        SAL_WARN("writerfilter", "Failed to save documents grab bag");
    }

    return false;
}

void PowerPointExport::WriteTheme(sal_Int32 nThemeNum, model::Theme* pTheme)
{
    if (!pTheme)
        return;
    OUString sThemePath = "ppt/theme/theme" + OUString::number(nThemeNum + 1) + ".xml";

    FSHelperPtr pFS = openFragmentStreamWithSerializer(sThemePath,
                      "application/vnd.openxmlformats-officedocument.theme+xml");
    oox::ThemeExport aThemeExport(this);

    OUString aThemeName("Office Theme");
    if (pTheme)
    {
        aThemeName = pTheme->GetName();
    }
    pFS->startElementNS(XML_a, XML_theme,
                        FSNS(XML_xmlns, XML_a), this->getNamespaceURL(OOX_NS(dml)),
                        XML_name, aThemeName);

    pFS->startElementNS(XML_a, XML_themeElements);
    OUString aColorSchemeName("Office");
    if (pTheme)
    {
        model::ColorSet* pColorSet = pTheme->GetColorSet();
        if (pColorSet)
        {
            aColorSchemeName = pColorSet->getName();
        }
    }
    pFS->startElementNS(XML_a, XML_clrScheme, XML_name, aColorSchemeName);

    if (!WriteColorSets(pFS, pTheme))
    {
        pFS->write(SYS_COLOR_SCHEMES);
        if (!WriteColorSchemes(pFS, sThemePath))
        {
            // if style is not defined, try to use first one
            if (!WriteColorSchemes(pFS, "ppt/theme/theme1.xml"))
            {
                // color schemes are required - use default values
                WriteDefaultColorSchemes(pFS);
            }
        }
    }

    pFS->endElementNS(XML_a, XML_clrScheme);

    // export remaining part
    pFS->write(MINIMAL_THEME);

    pFS->endElementNS(XML_a, XML_themeElements);
    pFS->endElementNS(XML_a, XML_theme);

    pFS->endDocument();
    aThemeExport.write(sThemePath, *pTheme);
}

bool PowerPointExport::ImplCreateDocument()
@@ -2344,6 +2039,7 @@ void PowerPointExport::WriteNotesMaster()
        openFragmentStreamWithSerializer("ppt/notesMasters/notesMaster1.xml",
                                         "application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml");
    // write theme per master

    WriteTheme(mnMasterPages, nullptr);

    // add implicit relation to the presentation theme
diff --git a/svx/source/svdraw/svdpage.cxx b/svx/source/svdraw/svdpage.cxx
index 235fc58..e685c40 100644
--- a/svx/source/svdraw/svdpage.cxx
+++ b/svx/source/svdraw/svdpage.cxx
@@ -1223,9 +1223,9 @@ SdrPageProperties::SdrPageProperties(SdrPage& rSdrPage)
        maProperties.Put(XFillStyleItem(drawing::FillStyle_NONE));
    }

    if (rSdrPage.getSdrModelFromSdrPage().IsWriter())
    if (rSdrPage.getSdrModelFromSdrPage().IsWriter() || rSdrPage.IsMasterPage())
    {
        mpTheme.reset(new model::Theme("Office"));
        mpTheme.reset(new model::Theme("Office Theme"));
        auto const* pColorSet = svx::ColorSets::get().getColorSet(u"LibreOffice");
        if (pColorSet)
        {