[Libreoffice-commits] core.git: Branch 'distro/collabora/cp-6.4' - 2 commits - include/vcl sd/source vcl/qa vcl/source
Tomaž Vajngerl (via logerrit)
logerrit at kemper.freedesktop.org
Mon Jun 29 18:37:17 UTC 2020
include/vcl/filter/PDFiumLibrary.hxx | 30 ++++
include/vcl/pdfread.hxx | 16 ++
sd/source/filter/pdf/sdpdffilter.cxx | 26 +++-
vcl/qa/cppunit/PDFiumLibraryTest.cxx | 135 ++++++++++++++++++++++
vcl/qa/cppunit/data/PangramAcrobatAnnotations.pdf |binary
vcl/qa/cppunit/data/PangramWithAnnotations.pdf |binary
vcl/source/filter/ipdf/pdfread.cxx | 31 ++++-
vcl/source/pdf/PDFiumLibrary.cxx | 73 +++++++++++
8 files changed, 306 insertions(+), 5 deletions(-)
New commits:
commit 0a96c423e0f21d4c59c226f860e7353b17851aea
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Thu Jun 18 13:30:38 2020 +0200
Commit: Tomaž Vajngerl <quikee at gmail.com>
CommitDate: Mon Jun 29 20:37:00 2020 +0200
sd: support adding PDF text / pop-up annotations as comments
Change-Id: I3e072f011089864f3349a470a32412cc33bcc022
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/96758
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>
(cherry picked from commit 3870dd43e94c440a5094a57c47d3b7565658d73c)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/97439
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
diff --git a/include/vcl/pdfread.hxx b/include/vcl/pdfread.hxx
index a65d230279b9..d79115f40249 100644
--- a/include/vcl/pdfread.hxx
+++ b/include/vcl/pdfread.hxx
@@ -14,6 +14,7 @@
#include <tools/gen.hxx>
#include <tools/stream.hxx>
#include <vcl/graph.hxx>
+#include <basegfx/range/b2drectangle.hxx>
namespace com::sun::star::uno
{
@@ -31,16 +32,27 @@ VCL_DLLPUBLIC size_t RenderPDFBitmaps(const void* pBuffer, int nSize, std::vecto
/// Imports a PDF stream into rGraphic as VectorGraphicData.
VCL_DLLPUBLIC bool ImportPDF(SvStream& rStream, Graphic& rGraphic);
+struct PDFGraphicAnnotation
+{
+ OUString maAuthor;
+ OUString maText;
+ // In HMM
+ basegfx::B2DRectangle maRectangle;
+};
+
struct PDFGraphicResult
{
Graphic maGraphic;
-
// Size in HMM
Size maSize;
- PDFGraphicResult(Graphic const& rGraphic, Size const& rSize)
+ std::vector<PDFGraphicAnnotation> maAnnotations;
+
+ PDFGraphicResult(Graphic const& rGraphic, Size const& rSize,
+ std::vector<PDFGraphicAnnotation> const& aAnnotations)
: maGraphic(rGraphic)
, maSize(rSize)
+ , maAnnotations(aAnnotations)
{
}
};
diff --git a/sd/source/filter/pdf/sdpdffilter.cxx b/sd/source/filter/pdf/sdpdffilter.cxx
index 91eb0d1a6ae4..552e19f597e9 100644
--- a/sd/source/filter/pdf/sdpdffilter.cxx
+++ b/sd/source/filter/pdf/sdpdffilter.cxx
@@ -28,6 +28,11 @@
#include <vcl/graph.hxx>
#include <vcl/pdfread.hxx>
+#include <com/sun/star/office/XAnnotation.hpp>
+#include <com/sun/star/text/XText.hpp>
+
+using namespace css;
+
SdPdfFilter::SdPdfFilter(SfxMedium& rMedium, sd::DrawDocShell& rDocShell)
: SdFilter(rMedium, rDocShell)
{
@@ -64,11 +69,28 @@ bool SdPdfFilter::Import()
// Make the page size match the rendered image.
pPage->SetSize(aSizeHMM);
- Point aPosition(0, 0);
SdrGrafObj* pSdrGrafObj = new SdrGrafObj(pPage->getSdrModelFromSdrPage(), rGraphic,
- tools::Rectangle(aPosition, aSizeHMM));
+ tools::Rectangle(Point(), aSizeHMM));
pPage->InsertObject(pSdrGrafObj);
+
+ for (auto const& rPDFAnnotation : rPDFGraphicResult.maAnnotations)
+ {
+ uno::Reference<office::XAnnotation> xAnnotation;
+ pPage->createAnnotation(xAnnotation);
+
+ xAnnotation->setAuthor(rPDFAnnotation.maAuthor);
+
+ uno::Reference<text::XText> xText(xAnnotation->getTextRange());
+ xText->setString(rPDFAnnotation.maText);
+ // position is in mm not 100thmm
+ geometry::RealPoint2D aUnoPosition(rPDFAnnotation.maRectangle.getMinX() / 100.0,
+ rPDFAnnotation.maRectangle.getMinY() / 100.00);
+ geometry::RealSize2D aUnoSize(rPDFAnnotation.maRectangle.getWidth() / 100.0,
+ rPDFAnnotation.maRectangle.getHeight() / 100.00);
+ xAnnotation->setPosition(aUnoPosition);
+ xAnnotation->setSize(aUnoSize);
+ }
}
return true;
diff --git a/vcl/source/filter/ipdf/pdfread.cxx b/vcl/source/filter/ipdf/pdfread.cxx
index 1a5d98b7e499..d3eb243bcbd4 100644
--- a/vcl/source/filter/ipdf/pdfread.cxx
+++ b/vcl/source/filter/ipdf/pdfread.cxx
@@ -274,7 +274,36 @@ size_t ImportPDFUnloaded(const OUString& rURL, std::vector<PDFGraphicResult>& rG
Graphic aGraphic(aVectorGraphicDataPtr);
aGraphic.SetGfxLink(pGfxLink);
- rGraphics.emplace_back(std::move(aGraphic), Size(nPageWidth, nPageHeight));
+ auto pPage = pPdfDocument->openPage(nPageIndex);
+
+ std::vector<PDFGraphicAnnotation> aPDFGraphicAnnotations;
+ for (int nAnnotation = 0; nAnnotation < pPage->getAnnotationCount(); nAnnotation++)
+ {
+ auto pAnnotation = pPage->getAnnotation(nAnnotation);
+ if (pAnnotation && pAnnotation->getSubType() == 1 /*FPDF_ANNOT_TEXT*/
+ && pAnnotation->hasKey(vcl::pdf::constDictionaryKeyPopup))
+ {
+ OUString sAuthor = pAnnotation->getString(vcl::pdf::constDictionaryKeyTitle);
+ OUString sText = pAnnotation->getString(vcl::pdf::constDictionaryKeyContents);
+ auto pPopupAnnotation = pAnnotation->getLinked(vcl::pdf::constDictionaryKeyPopup);
+
+ basegfx::B2DRectangle rRectangle = pAnnotation->getRectangle();
+ basegfx::B2DRectangle rRectangleHMM(
+ convertPointToMm100(rRectangle.getMinX()),
+ convertPointToMm100(aPageSize.getY() - rRectangle.getMinY()),
+ convertPointToMm100(rRectangle.getMaxX()),
+ convertPointToMm100(aPageSize.getY() - rRectangle.getMaxY()));
+
+ PDFGraphicAnnotation aPDFGraphicAnnotation;
+ aPDFGraphicAnnotation.maRectangle = rRectangleHMM;
+ aPDFGraphicAnnotation.maAuthor = sAuthor;
+ aPDFGraphicAnnotation.maText = sText;
+ aPDFGraphicAnnotations.push_back(aPDFGraphicAnnotation);
+ }
+ }
+
+ rGraphics.emplace_back(std::move(aGraphic), Size(nPageWidth, nPageHeight),
+ aPDFGraphicAnnotations);
}
return rGraphics.size();
commit 1709ebc984e41073ade18a4428be7387ab581c17
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Mon Jun 15 14:44:19 2020 +0200
Commit: Tomaž Vajngerl <quikee at gmail.com>
CommitDate: Mon Jun 29 20:36:45 2020 +0200
vcl: Add annotation reading to PDFiumLibrary c++ wrapper
Also add tests readin annotations from Evince and Acrobat modified
PDF files.
Change-Id: I4565c6b621774fc8485a6c33bc18708664917b73
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/96756
Tested-by: Tomaž Vajngerl <quikee at gmail.com>
Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>
(cherry picked from commit 7e4dc3b1eabcb1993d4143c046a2f32fedc852ed)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/97438
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
diff --git a/include/vcl/filter/PDFiumLibrary.hxx b/include/vcl/filter/PDFiumLibrary.hxx
index 6dde31f2927b..9deff47e0ab9 100644
--- a/include/vcl/filter/PDFiumLibrary.hxx
+++ b/include/vcl/filter/PDFiumLibrary.hxx
@@ -19,12 +19,17 @@
#include <memory>
#include <rtl/instance.hxx>
#include <basegfx/vector/b2dsize.hxx>
+#include <basegfx/range/b2drectangle.hxx>
#include <rtl/ustring.hxx>
#include <fpdf_doc.h>
namespace vcl::pdf
{
+constexpr char constDictionaryKeyTitle[] = "T";
+constexpr char constDictionaryKeyContents[] = "Contents";
+constexpr char constDictionaryKeyPopup[] = "Popup";
+
class PDFiumDocument;
class VCL_DLLPUBLIC PDFium final
@@ -44,6 +49,26 @@ public:
std::unique_ptr<PDFiumDocument> openDocument(const void* pData, int nSize);
};
+class VCL_DLLPUBLIC PDFiumAnnotation final
+{
+private:
+ FPDF_ANNOTATION mpAnnotation;
+
+ PDFiumAnnotation(const PDFiumAnnotation&) = delete;
+ PDFiumAnnotation& operator=(const PDFiumAnnotation&) = delete;
+
+public:
+ PDFiumAnnotation(FPDF_ANNOTATION pAnnotation);
+ ~PDFiumAnnotation();
+ FPDF_ANNOTATION getPointer() { return mpAnnotation; }
+
+ int getSubType();
+ basegfx::B2DRectangle getRectangle();
+ bool hasKey(OString const& rKey);
+ OUString getString(OString const& rKey);
+ std::unique_ptr<PDFiumAnnotation> getLinked(OString const& rKey);
+};
+
class VCL_DLLPUBLIC PDFiumPage final
{
private:
@@ -64,6 +89,11 @@ public:
if (mpPage)
FPDF_ClosePage(mpPage);
}
+
+ int getAnnotationCount();
+ int getAnnotationIndex(std::unique_ptr<PDFiumAnnotation> const& rAnnotation);
+
+ std::unique_ptr<PDFiumAnnotation> getAnnotation(int nIndex);
};
class VCL_DLLPUBLIC PDFiumDocument final
diff --git a/vcl/qa/cppunit/PDFiumLibraryTest.cxx b/vcl/qa/cppunit/PDFiumLibraryTest.cxx
index 422325aa9b1d..c786b6edc211 100644
--- a/vcl/qa/cppunit/PDFiumLibraryTest.cxx
+++ b/vcl/qa/cppunit/PDFiumLibraryTest.cxx
@@ -32,10 +32,14 @@ class PDFiumLibraryTest : public test::BootstrapFixtureBase
void testDocument();
void testPages();
+ void testAnnotationsMadeInEvince();
+ void testAnnotationsMadeInAcrobat();
CPPUNIT_TEST_SUITE(PDFiumLibraryTest);
CPPUNIT_TEST(testDocument);
CPPUNIT_TEST(testPages);
+ CPPUNIT_TEST(testAnnotationsMadeInEvince);
+ CPPUNIT_TEST(testAnnotationsMadeInAcrobat);
CPPUNIT_TEST_SUITE_END();
};
@@ -96,6 +100,137 @@ void PDFiumLibraryTest::testPages()
CPPUNIT_ASSERT(pPage);
}
+void PDFiumLibraryTest::testAnnotationsMadeInEvince()
+{
+ OUString aURL = getFullUrl("PangramWithAnnotations.pdf");
+ SvFileStream aStream(aURL, StreamMode::READ);
+ GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
+ Graphic aGraphic = rGraphicFilter.ImportUnloadedGraphic(aStream);
+ aGraphic.makeAvailable();
+
+ auto pVectorGraphicData = aGraphic.getVectorGraphicData();
+ CPPUNIT_ASSERT(pVectorGraphicData);
+ CPPUNIT_ASSERT_EQUAL(VectorGraphicDataType::Pdf,
+ pVectorGraphicData->getVectorGraphicDataType());
+
+ const void* pData = pVectorGraphicData->getVectorGraphicDataArray().getConstArray();
+ int nLength = pVectorGraphicData->getVectorGraphicDataArrayLength();
+
+ auto pPdfium = vcl::pdf::PDFiumLibrary::get();
+ auto pDocument = pPdfium->openDocument(pData, nLength);
+ CPPUNIT_ASSERT(pDocument);
+
+ CPPUNIT_ASSERT_EQUAL(1, pDocument->getPageCount());
+
+ auto pPage = pDocument->openPage(0);
+ CPPUNIT_ASSERT(pPage);
+
+ CPPUNIT_ASSERT_EQUAL(2, pPage->getAnnotationCount());
+
+ {
+ auto pAnnotation = pPage->getAnnotation(0);
+ CPPUNIT_ASSERT(pAnnotation);
+ CPPUNIT_ASSERT_EQUAL(1, pAnnotation->getSubType()); // FPDF_ANNOT_TEXT
+
+ OUString aPopupString = pAnnotation->getString(vcl::pdf::constDictionaryKeyTitle);
+ CPPUNIT_ASSERT_EQUAL(OUString("quikee"), aPopupString);
+
+ OUString aContentsString = pAnnotation->getString(vcl::pdf::constDictionaryKeyContents);
+ CPPUNIT_ASSERT_EQUAL(OUString("Annotation test"), aContentsString);
+
+ CPPUNIT_ASSERT_EQUAL(true, pAnnotation->hasKey(vcl::pdf::constDictionaryKeyPopup));
+ auto pPopupAnnotation = pAnnotation->getLinked(vcl::pdf::constDictionaryKeyPopup);
+ CPPUNIT_ASSERT(pPopupAnnotation);
+
+ CPPUNIT_ASSERT_EQUAL(1, pPage->getAnnotationIndex(pPopupAnnotation));
+ CPPUNIT_ASSERT_EQUAL(16, pPopupAnnotation->getSubType());
+ }
+
+ {
+ auto pAnnotation = pPage->getAnnotation(1);
+ CPPUNIT_ASSERT(pAnnotation);
+ CPPUNIT_ASSERT_EQUAL(16, pAnnotation->getSubType()); // FPDF_ANNOT_POPUP
+ }
+}
+
+void PDFiumLibraryTest::testAnnotationsMadeInAcrobat()
+{
+ OUString aURL = getFullUrl("PangramAcrobatAnnotations.pdf");
+ SvFileStream aStream(aURL, StreamMode::READ);
+ GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
+ Graphic aGraphic = rGraphicFilter.ImportUnloadedGraphic(aStream);
+ aGraphic.makeAvailable();
+
+ auto pVectorGraphicData = aGraphic.getVectorGraphicData();
+ CPPUNIT_ASSERT(pVectorGraphicData);
+ CPPUNIT_ASSERT_EQUAL(VectorGraphicDataType::Pdf,
+ pVectorGraphicData->getVectorGraphicDataType());
+
+ const void* pData = pVectorGraphicData->getVectorGraphicDataArray().getConstArray();
+ int nLength = pVectorGraphicData->getVectorGraphicDataArrayLength();
+
+ auto pPdfium = vcl::pdf::PDFiumLibrary::get();
+ auto pDocument = pPdfium->openDocument(pData, nLength);
+ CPPUNIT_ASSERT(pDocument);
+
+ CPPUNIT_ASSERT_EQUAL(1, pDocument->getPageCount());
+
+ auto pPage = pDocument->openPage(0);
+ CPPUNIT_ASSERT(pPage);
+
+ CPPUNIT_ASSERT_EQUAL(4, pPage->getAnnotationCount());
+
+ {
+ auto pAnnotation = pPage->getAnnotation(0);
+ CPPUNIT_ASSERT(pAnnotation);
+ CPPUNIT_ASSERT_EQUAL(1, pAnnotation->getSubType()); // FPDF_ANNOT_TEXT
+
+ OUString aPopupString = pAnnotation->getString(vcl::pdf::constDictionaryKeyTitle);
+ CPPUNIT_ASSERT_EQUAL(OUString("quikee"), aPopupString);
+
+ OUString aContentsString = pAnnotation->getString(vcl::pdf::constDictionaryKeyContents);
+ CPPUNIT_ASSERT_EQUAL(OUString("YEEEY"), aContentsString);
+
+ CPPUNIT_ASSERT_EQUAL(true, pAnnotation->hasKey(vcl::pdf::constDictionaryKeyPopup));
+ auto pPopupAnnotation = pAnnotation->getLinked(vcl::pdf::constDictionaryKeyPopup);
+ CPPUNIT_ASSERT(pPopupAnnotation);
+
+ CPPUNIT_ASSERT_EQUAL(1, pPage->getAnnotationIndex(pPopupAnnotation));
+ CPPUNIT_ASSERT_EQUAL(16, pPopupAnnotation->getSubType());
+ }
+
+ {
+ auto pAnnotation = pPage->getAnnotation(1);
+ CPPUNIT_ASSERT(pAnnotation);
+ CPPUNIT_ASSERT_EQUAL(16, pAnnotation->getSubType()); // FPDF_ANNOT_POPUP
+ }
+
+ {
+ auto pAnnotation = pPage->getAnnotation(2);
+ CPPUNIT_ASSERT(pAnnotation);
+ CPPUNIT_ASSERT_EQUAL(1, pAnnotation->getSubType()); // FPDF_ANNOT_TEXT
+
+ OUString aPopupString = pAnnotation->getString(vcl::pdf::constDictionaryKeyTitle);
+ CPPUNIT_ASSERT_EQUAL(OUString("quikee"), aPopupString);
+
+ OUString aContentsString = pAnnotation->getString(vcl::pdf::constDictionaryKeyContents);
+ CPPUNIT_ASSERT_EQUAL(OUString("Note"), aContentsString);
+
+ CPPUNIT_ASSERT_EQUAL(true, pAnnotation->hasKey(vcl::pdf::constDictionaryKeyPopup));
+ auto pPopupAnnotation = pAnnotation->getLinked(vcl::pdf::constDictionaryKeyPopup);
+ CPPUNIT_ASSERT(pPopupAnnotation);
+
+ CPPUNIT_ASSERT_EQUAL(3, pPage->getAnnotationIndex(pPopupAnnotation));
+ CPPUNIT_ASSERT_EQUAL(16, pPopupAnnotation->getSubType());
+ }
+
+ {
+ auto pAnnotation = pPage->getAnnotation(3);
+ CPPUNIT_ASSERT(pAnnotation);
+ CPPUNIT_ASSERT_EQUAL(16, pAnnotation->getSubType()); // FPDF_ANNOT_POPUP
+ }
+}
+
CPPUNIT_TEST_SUITE_REGISTRATION(PDFiumLibraryTest);
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/vcl/qa/cppunit/data/PangramAcrobatAnnotations.pdf b/vcl/qa/cppunit/data/PangramAcrobatAnnotations.pdf
new file mode 100644
index 000000000000..f97003a7f397
Binary files /dev/null and b/vcl/qa/cppunit/data/PangramAcrobatAnnotations.pdf differ
diff --git a/vcl/qa/cppunit/data/PangramWithAnnotations.pdf b/vcl/qa/cppunit/data/PangramWithAnnotations.pdf
new file mode 100644
index 000000000000..f69ddd987060
Binary files /dev/null and b/vcl/qa/cppunit/data/PangramWithAnnotations.pdf differ
diff --git a/vcl/source/pdf/PDFiumLibrary.cxx b/vcl/source/pdf/PDFiumLibrary.cxx
index 3ea7e5b49c4a..344fac51aa76 100644
--- a/vcl/source/pdf/PDFiumLibrary.cxx
+++ b/vcl/source/pdf/PDFiumLibrary.cxx
@@ -13,6 +13,7 @@
#if HAVE_FEATURE_PDFIUM
#include <vcl/filter/PDFiumLibrary.hxx>
+#include <fpdf_annot.h>
#include <fpdf_edit.h>
namespace vcl::pdf
@@ -109,6 +110,78 @@ basegfx::B2DSize PDFiumDocument::getPageSize(int nIndex)
int PDFiumDocument::getPageCount() { return FPDF_GetPageCount(mpPdfDocument); }
+int PDFiumPage::getAnnotationCount() { return FPDFPage_GetAnnotCount(mpPage); }
+
+int PDFiumPage::getAnnotationIndex(std::unique_ptr<PDFiumAnnotation> const& rAnnotation)
+{
+ return FPDFPage_GetAnnotIndex(mpPage, rAnnotation->getPointer());
+}
+
+std::unique_ptr<PDFiumAnnotation> PDFiumPage::getAnnotation(int nIndex)
+{
+ std::unique_ptr<PDFiumAnnotation> pPDFiumAnnotation;
+ FPDF_ANNOTATION pAnnotation = FPDFPage_GetAnnot(mpPage, nIndex);
+ if (pAnnotation)
+ {
+ pPDFiumAnnotation = std::make_unique<PDFiumAnnotation>(pAnnotation);
+ }
+ return pPDFiumAnnotation;
+}
+
+PDFiumAnnotation::PDFiumAnnotation(FPDF_ANNOTATION pAnnotation)
+ : mpAnnotation(pAnnotation)
+{
+}
+
+PDFiumAnnotation::~PDFiumAnnotation()
+{
+ if (mpAnnotation)
+ FPDFPage_CloseAnnot(mpAnnotation);
+}
+
+int PDFiumAnnotation::getSubType() { return FPDFAnnot_GetSubtype(mpAnnotation); }
+
+basegfx::B2DRectangle PDFiumAnnotation::getRectangle()
+{
+ basegfx::B2DRectangle aB2DRectangle;
+ FS_RECTF aRect;
+ if (FPDFAnnot_GetRect(mpAnnotation, &aRect))
+ {
+ aB2DRectangle = basegfx::B2DRectangle(aRect.left, aRect.top, aRect.right, aRect.bottom);
+ }
+ return aB2DRectangle;
+}
+
+bool PDFiumAnnotation::hasKey(OString const& rKey)
+{
+ return FPDFAnnot_HasKey(mpAnnotation, rKey.getStr());
+}
+
+OUString PDFiumAnnotation::getString(OString const& rKey)
+{
+ OUString rString;
+ unsigned long nSize = FPDFAnnot_GetStringValue(mpAnnotation, rKey.getStr(), nullptr, 0);
+ if (nSize > 2)
+ {
+ std::unique_ptr<sal_Unicode[]> pText(new sal_Unicode[nSize]);
+ unsigned long nStringSize = FPDFAnnot_GetStringValue(
+ mpAnnotation, rKey.getStr(), reinterpret_cast<FPDF_WCHAR*>(pText.get()), nSize);
+ if (nStringSize > 0)
+ rString = OUString(pText.get());
+ }
+ return rString;
+}
+
+std::unique_ptr<PDFiumAnnotation> PDFiumAnnotation::getLinked(OString const& rKey)
+{
+ std::unique_ptr<PDFiumAnnotation> pPDFiumAnnotation;
+ FPDF_ANNOTATION pAnnotation = FPDFAnnot_GetLinkedAnnot(mpAnnotation, rKey.getStr());
+ if (pAnnotation)
+ {
+ pPDFiumAnnotation = std::make_unique<PDFiumAnnotation>(pAnnotation);
+ }
+ return pPDFiumAnnotation;
+}
} // end vcl::pdf
#endif // HAVE_FEATURE_PDFIUM
More information about the Libreoffice-commits
mailing list