From bcde6b6040c26bce29b6f122d3d8dea5aa28241c Mon Sep 17 00:00:00 2001 From: Ashot Khachatryan Date: Thu, 19 Jun 2025 19:03:13 +0100 Subject: [PATCH 1/8] Try to find the best matching font, if the given font name is not found. Change-Id: I7dfa9fc4e9dfc0b2270eb2f3cc70d90e46dbd7ea --- include/vcl/outdev.hxx | 1 + oox/source/drawingml/textfont.cxx | 12 +++++++ vcl/source/outdev/font.cxx | 57 +++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) 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(); From b0b1a69928ffbcb3b6c78b0963c68169e876317c Mon Sep 17 00:00:00 2001 From: Ashot Khachatryan Date: Sat, 21 Jun 2025 13:39:50 +0100 Subject: [PATCH 2/8] Early return if the current frame of xDesktop is not found. Change-Id: Iea2b8f4b36de2db61de906c8ce9cc882e56d42ff --- filter/source/svg/svgfilter.cxx | 5 +++++ 1 file changed, 5 insertions(+) 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) From ec1c1dbd2814ac03ec7ba45ed52a2ed3734ab91f Mon Sep 17 00:00:00 2001 From: Ashot Khachatryan Date: Wed, 25 Jun 2025 19:17:09 +0100 Subject: [PATCH 3/8] Extract text alignment and line height and add as attributes. Change-Id: I6c26b9ccca73723052e35c13f2f85da7902628a1 --- filter/source/svg/svgwriter.cxx | 77 ++++++++++++++++++++++++++++++++- filter/source/svg/svgwriter.hxx | 3 ++ 2 files changed, 79 insertions(+), 1 deletion(-) 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); }; From 36d4e0d50b52f0cc44ca06b8995e556e99aeff51 Mon Sep 17 00:00:00 2001 From: Hryhorii Vydria Date: Thu, 26 Jun 2025 17:52:11 +0200 Subject: [PATCH 4/8] Add GitHub Actions workflow to run commands in Docker --- .github/workflows/pipeline.yml | 76 ++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 .github/workflows/pipeline.yml diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml new file mode 100644 index 0000000000000..ad227c55478d4 --- /dev/null +++ b/.github/workflows/pipeline.yml @@ -0,0 +1,76 @@ +name: Run command in Docker and save artifact + +on: + push: + branches: + - master + - user/grigorii/add-pipeline + pull_request: + branches: + - master + - user/grigorii/add-pipeline + workflow_dispatch: + +jobs: + run-in-docker: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run command in Docker + run: | + docker run --rm -v ${{ github.workspace }}:/workspace ubuntu:24.04 bash -c \ + "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 && \ + " + +# - name: Upload result as artifact +# uses: actions/upload-artifact@v4 +# with: +# name: result +# path: result.txt \ No newline at end of file From 3de33d71f953ac04111897d516694c6fe538c5a5 Mon Sep 17 00:00:00 2001 From: Hryhorii Vydria Date: Fri, 27 Jun 2025 12:02:39 +0200 Subject: [PATCH 5/8] Add Dockerfile and docker-compose configuration for building libreoffice-core --- .github/workflows/pipeline.yml | 54 +++------------------------- Dockerfile | 64 ++++++++++++++++++++++++++++++++++ compose.yml | 11 ++++++ 3 files changed, 79 insertions(+), 50 deletions(-) create mode 100644 Dockerfile create mode 100644 compose.yml diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index ad227c55478d4..920caeae0ae90 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -12,62 +12,16 @@ on: workflow_dispatch: jobs: - run-in-docker: + build-libreoffice: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - - name: Run command in Docker + - name: Build libreoffice-core in container run: | - docker run --rm -v ${{ github.workspace }}:/workspace ubuntu:24.04 bash -c \ - "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 && \ - " + docker compose -f compose.yml up --build + ls -lah ./build # - name: Upload result as artifact # uses: actions/upload-artifact@v4 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000..a15e0cdbdfaa9 --- /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 ./libreoffice-core /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 From 6a6f5b242a40b9586917a8deda32c0f481d6a88b Mon Sep 17 00:00:00 2001 From: Hryhorii Vydria Date: Fri, 27 Jun 2025 12:21:37 +0200 Subject: [PATCH 6/8] Update Dockerfile to copy all files into the application directory --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index a15e0cdbdfaa9..96dfd00105004 100644 --- a/Dockerfile +++ b/Dockerfile @@ -53,7 +53,7 @@ RUN pip3 install --no-cache-dir setuptools lxml meson ninja --break-system-packa #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 ./libreoffice-core /APP/libreoffice-core +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 From 7aaffe713e30d3f94c7260bb08b8c6ab0b66d70a Mon Sep 17 00:00:00 2001 From: Hryhorii Vydria Date: Fri, 27 Jun 2025 15:41:52 +0200 Subject: [PATCH 7/8] Update GitHub Actions workflow to save libreoffice build as an artifact --- .github/workflows/pipeline.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index 920caeae0ae90..20c845e80f4bb 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -23,8 +23,8 @@ jobs: docker compose -f compose.yml up --build ls -lah ./build -# - name: Upload result as artifact -# uses: actions/upload-artifact@v4 -# with: -# name: result -# path: result.txt \ No newline at end of file + - name: save libreoffice as artifact + uses: actions/upload-artifact@v4 + with: + name: libreoffice-build + path: ./build \ No newline at end of file From cdb8d59a0d342b71137938165a775ed018bd4d3e Mon Sep 17 00:00:00 2001 From: Hryhorii Vydria Date: Mon, 30 Jun 2025 11:50:10 +0200 Subject: [PATCH 8/8] Remove user branch from workflow triggers in pipeline.yml --- .github/workflows/pipeline.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index 20c845e80f4bb..04f93d7fa9734 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -4,11 +4,9 @@ on: push: branches: - master - - user/grigorii/add-pipeline pull_request: branches: - master - - user/grigorii/add-pipeline workflow_dispatch: jobs: