diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml new file mode 100644 index 0000000000000..04f93d7fa9734 --- /dev/null +++ b/.github/workflows/pipeline.yml @@ -0,0 +1,28 @@ +name: Run command in Docker and save artifact + +on: + push: + branches: + - master + pull_request: + branches: + - master + workflow_dispatch: + +jobs: + build-libreoffice: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Build libreoffice-core in container + run: | + docker compose -f compose.yml up --build + ls -lah ./build + + - name: save libreoffice as artifact + uses: actions/upload-artifact@v4 + with: + name: libreoffice-build + path: ./build \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000..96dfd00105004 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,64 @@ +FROM ubuntu:24.04 + +RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ + git \ + build-essential \ + g++ \ + python3 \ + python3-pip \ + python3-dev \ + python3-setuptools \ + openjdk-17-jdk \ + autoconf \ + automake \ + libtool \ + pkg-config \ + libnss3-dev \ + libnspr4-dev \ + libxml2-utils \ + gperf \ + xsltproc \ + uuid-runtime \ + bison \ + flex \ + zip \ + libxrender-dev \ + libxt-dev \ + libcups2-dev \ + libxrandr-dev \ + libglu1-mesa-dev \ + libcairo2-dev \ + libgstreamer1.0-dev \ + libgstreamer-plugins-base1.0-dev \ + libpng-dev \ + libjpeg-dev \ + libtiff-dev \ + libgif-dev \ + libexpat1-dev \ + libssl-dev \ + libkrb5-dev \ + libldap2-dev \ + libdbus-1-dev \ + libharfbuzz-dev \ + libcurl4-openssl-dev \ + libxslt1-dev \ + ninja-build \ + meson \ + wget \ + curl +RUN pip3 install --no-cache-dir setuptools lxml meson ninja --break-system-packages + + + +#RUN dpkg-query -W -f='${Package}\t${Version}\n' \ +# git build-essential g++ python3 python3-pip python3-dev python3-setuptools openjdk-17-jdk autoconf automake libtool pkg-config libnss3-dev libnspr4-dev libxml2-utils gperf xsltproc uuid-runtime bison flex zip libxrender-dev libxt-dev libcups2-dev libxrandr-dev libglu1-mesa-dev libcairo2-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libpng-dev libjpeg-dev libtiff-dev libgif-dev libexpat1-dev libssl-dev libkrb5-dev libldap2-dev libdbus-1-dev libharfbuzz-dev libcurl4-openssl-dev libxslt1-dev ninja-build meson wget curl 2>/dev/null | column -t + +COPY ./ /APP/libreoffice-core +WORKDIR /APP/libreoffice-core +RUN ./autogen.sh --disable-gtk3 --disable-gui --disable-dbus --disable-cairo-canvas --without-help --without-java --without-myspell-dicts --without-doxygen --without-junit --enable-release-build --disable-debug --prefix=/tmp/build/ +RUN make -j8 +RUN make install +RUN ls -laht /tmp/build/ +RUN ls -laht /APP +ENTRYPOINT [ "mv", "/tmp/build/", "/build/" ] +#ENTRYPOINT [ "tail", "-f", "/dev/null" ] \ No newline at end of file diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000000000..6622d9f73494f --- /dev/null +++ b/compose.yml @@ -0,0 +1,11 @@ + +services: + libreoffice-core: + build: + context: . + dockerfile: Dockerfile + container_name: libreoffice-core + + # Uncomment and adjust the following lines as needed: + volumes: + - ./build:/build/ \ No newline at end of file diff --git a/filter/source/svg/svgfilter.cxx b/filter/source/svg/svgfilter.cxx index b50c1c12d7f91..5b83f66279192 100644 --- a/filter/source/svg/svgfilter.cxx +++ b/filter/source/svg/svgfilter.cxx @@ -167,6 +167,11 @@ css::uno::Reference SVGFilter::getSourceController() co css::uno::Reference SVGFilter::fillDrawImpressSelectedPages() { + uno::Reference xDesktop(frame::Desktop::create(mxContext)); + if (xDesktop->getCurrentFrame() == nullptr) { + return {}; + } + uno::Reference xController = getSourceController(); uno::Reference xManager(xController, uno::UNO_QUERY); if (!xManager) diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx index fd09508efd938..52517b310a848 100644 --- a/filter/source/svg/svgwriter.cxx +++ b/filter/source/svg/svgwriter.cxx @@ -48,6 +48,9 @@ #include #include #include +#include +#include +#include #include #include @@ -1107,7 +1110,9 @@ bool SVGTextWriter::nextParagraph() Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); if( xEnumeration.is() && xEnumeration->hasMoreElements() ) { - mrTextPortionEnumeration.set( xEnumeration ); + mrTextPortionEnumeration.set(xEnumeration); + Reference xPortionPropSet(mrTextPortionEnumeration->nextElement(), UNO_QUERY); + extractTextAlignmentAndLineHeight(xPortionPropSet); } #if OSL_DEBUG_LEVEL > 0 sInfo = "Paragraph"; @@ -1156,6 +1161,63 @@ bool SVGTextWriter::nextParagraph() return true; } +void SVGTextWriter::extractTextAlignmentAndLineHeight(const Reference& xPortionPropSet) +{ + Reference xPortionPropInfo(xPortionPropSet->getPropertySetInfo()); + if (xPortionPropInfo->hasPropertyByName(u"ParaAdjust"_ustr)) + { + sal_uInt16 nTextAdjust = sal_uInt16(ParagraphAdjust_LEFT); + if (xPortionPropSet->getPropertyValue(u"ParaAdjust"_ustr) >>= nTextAdjust) + { + switch (static_cast(nTextAdjust)) + { + case ParagraphAdjust_LEFT: + msTextAlignment = "left"; + break; + case ParagraphAdjust_CENTER: + msTextAlignment = "center"; + break; + case ParagraphAdjust_RIGHT: + msTextAlignment = "right"; + break; + case ParagraphAdjust_BLOCK: + msTextAlignment = "block"; + break; + default: + msTextAlignment.clear(); + break; + } + } + } + + if (xPortionPropInfo->hasPropertyByName(u"ParaLineSpacing"_ustr)) + { + css::style::LineSpacing aLineSpacing; + if (xPortionPropSet->getPropertyValue(u"ParaLineSpacing"_ustr) >>= aLineSpacing) + { + switch (aLineSpacing.Mode) + { + case css::style::LineSpacingMode::PROP: + { + double fLineHeight = aLineSpacing.Height / 100.0; + msLineHeight = OUString::number(fLineHeight); + } + break; + + case css::style::LineSpacingMode::FIX: + { + double fPoints = aLineSpacing.Height / 35.0; + msLineHeight = OUString::number(static_cast(fPoints)) + u"pt"; + } + break; + + default: + msLineHeight = OUString::number(aLineSpacing.Height); + break; + } + } + } +} bool SVGTextWriter::nextTextPortion() { @@ -1388,6 +1450,12 @@ void SVGTextWriter::startTextParagraph() else { mrExport.AddAttribute(u"class"_ustr, u"TextParagraph"_ustr); + if (!msTextAlignment.isEmpty()) { + mrExport.AddAttribute(u"ooo:text-alignment"_ustr, msTextAlignment); + } + if (!msLineHeight.isEmpty()) { + mrExport.AddAttribute(u"ooo:line-height"_ustr, msLineHeight); + } } maParentFont = vcl::Font(); mpTextParagraphElem.reset(new SvXMLElementExport(mrExport, aXMLElemTspan, mbIWS, mbIWS)); @@ -1420,6 +1488,13 @@ void SVGTextWriter::startTextPosition( bool bExportX, bool bExportY ) if( bExportY ) mrExport.AddAttribute(aXMLAttrY, OUString::number(maTextPos.Y())); + if (!msTextAlignment.isEmpty()) { + mrExport.AddAttribute(u"ooo:text-alignment"_ustr, msTextAlignment); + } + if (!msLineHeight.isEmpty()) { + mrExport.AddAttribute(u"ooo:line-height"_ustr, msLineHeight); + } + mpTextPositionElem.reset(new SvXMLElementExport(mrExport, aXMLElemTspan, mbIWS, mbIWS)); } diff --git a/filter/source/svg/svgwriter.hxx b/filter/source/svg/svgwriter.hxx index 9c1e69fcf4bfb..f06dc87e4a545 100644 --- a/filter/source/svg/svgwriter.hxx +++ b/filter/source/svg/svgwriter.hxx @@ -226,6 +226,8 @@ class SVGTextWriter final OUString msPageCount; OUString msDateTimeType; OUString msTextFieldType; + OUString msTextAlignment; + OUString msLineHeight; bool mbIsPlaceholderShape; static const bool mbIWS = false; vcl::Font maCurrentFont; @@ -291,6 +293,7 @@ class SVGTextWriter final void implRegisterInterface( const Reference< XInterface >& rxIf ); const OUString & implGetValidIDFromInterface( const Reference< XInterface >& rxIf ); + void extractTextAlignmentAndLineHeight(const Reference& xPortionPropSet); }; diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx index aeed95955e2e0..288fccfd6f754 100644 --- a/include/vcl/outdev.hxx +++ b/include/vcl/outdev.hxx @@ -1132,6 +1132,7 @@ public: FontMetric GetFontMetricFromCollection( int nDevFontIndex ) const; int GetFontFaceCollectionCount() const; + OUString FindBestMatchingFont(const OUString& fontFamily) const; bool IsFontAvailable( std::u16string_view rFontName ) const; diff --git a/oox/source/drawingml/textfont.cxx b/oox/source/drawingml/textfont.cxx index 40cd842f945ac..445b98d642e60 100644 --- a/oox/source/drawingml/textfont.cxx +++ b/oox/source/drawingml/textfont.cxx @@ -25,6 +25,8 @@ #include #include #include +#include +#include using ::oox::core::XmlFilterBase; @@ -56,7 +58,12 @@ TextFont::TextFont() : void TextFont::setAttributes( const AttributeList& rAttribs ) { + auto device = Application::GetDefaultDevice(); maTypeface = rAttribs.getStringDefaulted( XML_typeface); + if (!device->IsFontAvailable(maTypeface)) { + // If the font is not available, try to find the best match + maTypeface = device->FindBestMatchingFont(maTypeface); + } maPanose = rAttribs.getStringDefaulted( XML_panose); mnPitchFamily = rAttribs.getInteger( XML_pitchFamily, 0 ); mnCharset = rAttribs.getInteger( XML_charset, WINDOWS_CHARSET_DEFAULT ); @@ -64,7 +71,12 @@ void TextFont::setAttributes( const AttributeList& rAttribs ) void TextFont::setAttributes( const OUString& sFontName ) { + auto device = Application::GetDefaultDevice(); maTypeface = sFontName; + if (!device->IsFontAvailable(maTypeface)) { + // If the font is not available, try to find the best match + maTypeface = device->FindBestMatchingFont(maTypeface); + } maPanose.clear(); mnPitchFamily = 0; mnCharset = WINDOWS_CHARSET_DEFAULT; diff --git a/vcl/source/outdev/font.cxx b/vcl/source/outdev/font.cxx index b3d4022eb53b0..2769f77ed4a00 100644 --- a/vcl/source/outdev/font.cxx +++ b/vcl/source/outdev/font.cxx @@ -129,6 +129,63 @@ int OutputDevice::GetFontFaceCollectionCount() const return mpFontFaceCollection->Count(); } +namespace { + rtl::OUString StripWeightSuffix(const rtl::OUString& name) + { + static const char* const suffixes[] = { + " thin", " ultralight", " light", " semilight", + " normal", " medium", " semibold", " bold", + " ultrabold", " black" + }; + + rtl::OUString lower = name.toAsciiLowerCase(); + + for (const char* suffix : suffixes) + { + rtl::OUString pattern = rtl::OUString::createFromAscii(suffix); + sal_Int32 pos = lower.lastIndexOf(pattern); + if (pos >= 0 && (pos + pattern.getLength() == lower.getLength())) + { + return name.copy(0, pos); + } + } + + return name; + } +} + + +OUString OutputDevice::FindBestMatchingFont(const OUString& fontFamily) const +{ + if(!mpFontFaceCollection) + { + if (!mxFontCollection) + { + return fontFamily; + } + + mpFontFaceCollection = mxFontCollection->GetFontFaceCollection(); + + if (!mpFontFaceCollection->Count()) + { + mpFontFaceCollection.reset(); + return fontFamily; + } + } + OUString fontName = StripWeightSuffix(fontFamily); + if (fontName != fontFamily) { + int size = mpFontFaceCollection->Count(); + for (int i = 0; i < size; ++i) { + auto fontFace = mpFontFaceCollection->Get(i); + if (fontFace->GetFamilyName() == fontName) { + // We might want to check fontFace->GetWeight() as well + return fontName; + } + } + } + return fontFamily; +} + bool OutputDevice::IsFontAvailable( std::u16string_view rFontName ) const { ImplInitFontList();