diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /vcl/unx/generic/printer/cpdmgr.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vcl/unx/generic/printer/cpdmgr.cxx')
-rw-r--r-- | vcl/unx/generic/printer/cpdmgr.cxx | 754 |
1 files changed, 754 insertions, 0 deletions
diff --git a/vcl/unx/generic/printer/cpdmgr.cxx b/vcl/unx/generic/printer/cpdmgr.cxx new file mode 100644 index 000000000..bebd56833 --- /dev/null +++ b/vcl/unx/generic/printer/cpdmgr.cxx @@ -0,0 +1,754 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cstddef> +#include <unistd.h> + +#include <unx/cpdmgr.hxx> + +#include <osl/file.h> +#include <osl/thread.h> + +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> + +#include <config_dbus.h> +#include <config_gio.h> + +using namespace psp; +using namespace osl; + +#if ENABLE_DBUS && ENABLE_GIO +// Function to execute when name is acquired on the bus +void CPDManager::onNameAcquired (GDBusConnection *connection, + const gchar *, + gpointer user_data) +{ + gchar* contents; + GDBusNodeInfo *introspection_data; + + // Get Interface for introspection + g_file_get_contents (FRONTEND_INTERFACE, &contents, nullptr, nullptr); + introspection_data = g_dbus_node_info_new_for_xml (contents, nullptr); + + g_dbus_connection_register_object (connection, + "/org/libreoffice/PrintDialog", + introspection_data->interfaces[0], + nullptr, + nullptr, /* user_data */ + nullptr, /* user_data_free_func */ + nullptr); /* GError** */ + g_free(contents); + g_dbus_node_info_unref(introspection_data); + + CPDManager* current = static_cast<CPDManager*>(user_data); + std::vector<std::pair<std::string, gchar*>> backends = current->getTempBackends(); + for (auto const& backend : backends) + { + GDBusProxy *proxy; + // Get Interface for introspection + g_file_get_contents (BACKEND_INTERFACE, &contents, nullptr, nullptr); + introspection_data = g_dbus_node_info_new_for_xml (contents, nullptr); + proxy = g_dbus_proxy_new_sync (connection, + G_DBUS_PROXY_FLAGS_NONE, + introspection_data->interfaces[0], + backend.first.c_str(), + backend.second, + "org.openprinting.PrintBackend", + nullptr, + nullptr); + g_free(backend.second); + g_assert (proxy != nullptr); + g_dbus_proxy_call(proxy, "ActivateBackend", + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, nullptr, nullptr, nullptr); + + g_free(contents); + g_object_unref(proxy); + g_dbus_node_info_unref(introspection_data); + } +} + +void CPDManager::onNameLost (GDBusConnection *, + const gchar *name, + gpointer) +{ + g_message("Name Lost: %s", name); +} + +void CPDManager::printerAdded (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *, + GVariant *parameters, + gpointer user_data) +{ + CPDManager* current = static_cast<CPDManager*>(user_data); + GDBusProxy *proxy; + proxy = current->getProxy(sender_name); + if (proxy == nullptr) { + gchar* contents; + GDBusNodeInfo *introspection_data; + + // Get Interface for introspection + g_file_get_contents ("/usr/share/dbus-1/interfaces/org.openprinting.Backend.xml", &contents, nullptr, nullptr); + introspection_data = g_dbus_node_info_new_for_xml (contents, nullptr); + proxy = g_dbus_proxy_new_sync (connection, + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + introspection_data->interfaces[0], + sender_name, + object_path, + interface_name, + nullptr, + nullptr); + + g_free(contents); + g_dbus_node_info_unref(introspection_data); + std::pair<std::string, GDBusProxy *> new_backend (sender_name, proxy); + current->addBackend(new_backend); + } + CPDPrinter *pDest = static_cast<CPDPrinter *>(malloc(sizeof(CPDPrinter))); + pDest->backend = proxy; + g_variant_get (parameters, "(sssssbss)", &(pDest->id), &(pDest->name), &(pDest->info), &(pDest->location), &(pDest->make_and_model), &(pDest->is_accepting_jobs), &(pDest->printer_state), &(pDest->backend_name)); + std::stringstream printerName; + printerName << pDest->name << ", " << pDest->backend_name; + std::stringstream uniqueName; + uniqueName << pDest->id << ", " << pDest->backend_name; + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + OUString aPrinterName = OStringToOUString( printerName.str().c_str(), aEncoding ); + OUString aUniqueName = OStringToOUString( uniqueName.str().c_str(), aEncoding ); + current->addNewPrinter(aPrinterName, aUniqueName, pDest); +} + +void CPDManager::printerRemoved (GDBusConnection *, + const gchar *, + const gchar *, + const gchar *, + const gchar *, + GVariant *parameters, + gpointer user_data) +{ + // TODO: Remove every data linked to this particular printer. + CPDManager* pManager = static_cast<CPDManager*>(user_data); + char* id; + char* backend_name; + g_variant_get (parameters, "(ss)", &id, &backend_name); + std::stringstream uniqueName; + uniqueName << id << ", " << backend_name; + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + OUString aUniqueName = OStringToOUString( uniqueName.str().c_str(), aEncoding ); + std::unordered_map<OUString, CPDPrinter *>::iterator it = pManager->m_aCPDDestMap.find( aUniqueName ); + if (it == pManager->m_aCPDDestMap.end()) { + SAL_WARN("vcl.unx.print", "CPD trying to remove non-existent printer from list"); + return; + } + pManager->m_aCPDDestMap.erase(it); + std::unordered_map<OUString, Printer>::iterator printersIt = pManager->m_aPrinters.find( aUniqueName ); + if (printersIt == pManager->m_aPrinters.end()) { + SAL_WARN("vcl.unx.print", "CPD trying to remove non-existent printer from m_aPrinters"); + return; + } + pManager->m_aPrinters.erase(printersIt); +} + +GDBusProxy* CPDManager::getProxy(const std::string& target) +{ + std::unordered_map<std::string, GDBusProxy *>::const_iterator it = m_pBackends.find(target); + if (it == m_pBackends.end()) { + return nullptr; + } + return it->second; +} + +void CPDManager::addBackend(std::pair<std::string, GDBusProxy *> pair) { + m_pBackends.insert(pair); +} + +void CPDManager::addTempBackend(const std::pair<std::string, gchar*>& pair) +{ + m_tBackends.push_back(pair); +} + +std::vector<std::pair<std::string, gchar*>> const & CPDManager::getTempBackends() const { + return m_tBackends; +} + +void CPDManager::addNewPrinter(const OUString& aPrinterName, const OUString& aUniqueName, CPDPrinter *pDest) { + m_aCPDDestMap[aUniqueName] = pDest; + bool bSetToGlobalDefaults = m_aPrinters.find( aUniqueName ) == m_aPrinters.end(); + Printer aPrinter = m_aPrinters[ aUniqueName ]; + if( bSetToGlobalDefaults ) + aPrinter.m_aInfo = m_aGlobalDefaults; + aPrinter.m_aInfo.m_aPrinterName = aPrinterName; + + // TODO: I don't know how this should work when we have multiple + // sources with multiple possible defaults for each + // if( pDest->is_default ) + // m_aDefaultPrinter = aPrinterName; + + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + aPrinter.m_aInfo.m_aComment = OStringToOUString(pDest->info, aEncoding); + aPrinter.m_aInfo.m_aLocation = OStringToOUString(pDest->location, aEncoding); + // note: the parser that goes with the PrinterInfo + // is created implicitly by the JobData::operator=() + // when it detects the NULL ptr m_pParser. + // if we wanted to fill in the parser here this + // would mean we'd have to send a dbus message for each and + // every printer - which would be really bad runtime + // behaviour + aPrinter.m_aInfo.m_pParser = nullptr; + aPrinter.m_aInfo.m_aContext.setParser( nullptr ); + std::unordered_map< OUString, PPDContext >::const_iterator c_it = m_aDefaultContexts.find( aUniqueName ); + if( c_it != m_aDefaultContexts.end() ) + { + aPrinter.m_aInfo.m_pParser = c_it->second.getParser(); + aPrinter.m_aInfo.m_aContext = c_it->second; + } + aPrinter.m_aInfo.setDefaultBackend(true); + aPrinter.m_aInfo.m_aDriverName = "CPD:" + aUniqueName; + m_aPrinters[ aUniqueName ] = aPrinter; +} +#endif + +/* + * CPDManager class + */ + +CPDManager* CPDManager::tryLoadCPD() +{ + CPDManager* pManager = nullptr; +#if ENABLE_DBUS && ENABLE_GIO + static const char* pEnv = getenv("SAL_DISABLE_CPD"); + + if (!pEnv || !*pEnv) { + // interface description XML files are needed in 'onNameAcquired()' + if (!g_file_test(FRONTEND_INTERFACE, G_FILE_TEST_IS_REGULAR) || + !g_file_test(BACKEND_INTERFACE, G_FILE_TEST_IS_REGULAR)) { + return nullptr; + } + + GDir *dir; + const gchar *filename; + dir = g_dir_open(BACKEND_DIR, 0, nullptr); + if (dir != nullptr) { + while ((filename = g_dir_read_name(dir))) { + if (pManager == nullptr) { + pManager = new CPDManager(); + } + gchar* contents; + std::stringstream filepath; + filepath << BACKEND_DIR << '/' << filename; + g_file_get_contents(filepath.str().c_str(), &contents, nullptr, nullptr); + std::pair<std::string, gchar*> new_tbackend (filename, contents); + pManager->addTempBackend(new_tbackend); + } + g_dir_close(dir); + } + } +#endif + return pManager; +} + +CPDManager::CPDManager() : + PrinterInfoManager( PrinterInfoManager::Type::CPD ) +{ +#if ENABLE_DBUS && ENABLE_GIO + // Get Destinations number and pointers + GError *error = nullptr; + m_pConnection = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, &error); + g_assert_no_error (error); +#endif +} + +CPDManager::~CPDManager() +{ +#if ENABLE_DBUS && ENABLE_GIO + g_dbus_connection_emit_signal (m_pConnection, + nullptr, + "/org/libreoffice/PrintDialog", + "org.openprinting.PrintFrontend", + "StopListing", + nullptr, + nullptr); + g_dbus_connection_flush_sync (m_pConnection, + nullptr, + nullptr); + g_dbus_connection_close_sync (m_pConnection, + nullptr, + nullptr); + for (auto const& backend : m_pBackends) + { + g_object_unref(backend.second); + } + for (auto const& backend : m_aCPDDestMap) + { + free(backend.second); + } +#endif +} + + +const PPDParser* CPDManager::createCPDParser( const OUString& rPrinter ) +{ + const PPDParser* pNewParser = nullptr; +#if ENABLE_DBUS && ENABLE_GIO + OUString aPrinter; + + if( rPrinter.startsWith("CPD:") ) + aPrinter = rPrinter.copy( 4 ); + else + aPrinter = rPrinter; + + std::unordered_map< OUString, CPDPrinter * >::iterator dest_it = + m_aCPDDestMap.find( aPrinter ); + + if( dest_it != m_aCPDDestMap.end() ) + { + CPDPrinter* pDest = dest_it->second; + GVariant* ret = nullptr; + GError* error = nullptr; + ret = g_dbus_proxy_call_sync (pDest->backend, "GetAllOptions", + g_variant_new("(s)", (pDest->id)), + G_DBUS_CALL_FLAGS_NONE, + -1, nullptr, &error); + if (ret != nullptr && error == nullptr) + { + // TODO: These keys need to be redefined to preserve usage across libreoffice + // InputSlot - media-col.media-source? + // Font - not needed now as it is required only for ps and we are using pdf + // Dial? - for FAX (need to look up PWG spec) + + int num_attribute; + GVariantIter *iter_attr, *iter_supported_values; + g_variant_get (ret, "(ia(ssia(s)))", &num_attribute, &iter_attr); + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + PPDKey *pKey = nullptr; + OUString aValueName; + PPDValue* pValue; + std::vector<PPDKey*> keys; + std::vector<OUString> default_values; + for (int i = 0; i < num_attribute; i++) { + char *name, *default_value; + int num_supported_values; + g_variant_iter_loop(iter_attr, "(ssia(s))", + &name, &default_value, + &num_supported_values, &iter_supported_values); + OUString aOptionName = OStringToOUString( name, aEncoding ); + OUString aDefaultValue = OStringToOUString( default_value, aEncoding ); + if (aOptionName == "sides") { + // Duplex key is used throughout for checking Duplex Support + aOptionName = OUString("Duplex"); + } else if (aOptionName == "printer-resolution") { + // Resolution key is used in places + aOptionName = OUString("Resolution"); + } else if (aOptionName == "media") { + // PageSize key is used in many places + aOptionName = OUString("PageSize"); + } + default_values.push_back(aDefaultValue); + pKey = new PPDKey( aOptionName ); + + // If number of values are 0, this is not settable via UI + if (num_supported_values > 0 && aDefaultValue != "NA") + pKey->m_bUIOption = true; + + bool bDefaultFound = false; + + for (int j = 0; j < num_supported_values; j++) { + char* value; + g_variant_iter_loop(iter_supported_values, "(s)", &value); + aValueName = OStringToOUString( value, aEncoding ); + if (aOptionName == "Duplex") { + // Duplex key matches against very specific Values + if (aValueName == "one-sided") { + aValueName = OUString("None"); + } else if (aValueName == "two-sided-long-edge") { + aValueName = OUString("DuplexNoTumble"); + } else if (aValueName == "two-sided-short-edge") { + aValueName = OUString("DuplexTumble"); + } + } + + pValue = pKey->insertValue( aValueName, eQuoted ); + if( ! pValue ) + continue; + pValue->m_aValue = aValueName; + + if (aValueName.equals(aDefaultValue)) { + pKey->m_pDefaultValue = pValue; + bDefaultFound = true; + } + + } + // This could be done to ensure default values also appear as options: + if (!bDefaultFound && pKey->m_bUIOption) { + // pValue = pKey->insertValue( aDefaultValue, eQuoted ); + // if( pValue ) + // pValue->m_aValue = aDefaultValue; + } + keys.emplace_back(pKey); + } + + pKey = new PPDKey("ModelName"); + aValueName = OStringToOUString( "", aEncoding ); + pValue = pKey->insertValue( aValueName, eQuoted ); + if( pValue ) + pValue->m_aValue = aValueName; + pKey->m_pDefaultValue = pValue; + keys.emplace_back(pKey); + + pKey = new PPDKey("NickName"); + aValueName = OStringToOUString( pDest->name, aEncoding ); + pValue = pKey->insertValue( aValueName, eQuoted ); + if( pValue ) + pValue->m_aValue = aValueName; + pKey->m_pDefaultValue = pValue; + keys.emplace_back(pKey); + + pNewParser = new PPDParser(aPrinter, keys); + PrinterInfo& rInfo = m_aPrinters[ aPrinter ].m_aInfo; + PPDContext& rContext = m_aDefaultContexts[ aPrinter ]; + rContext.setParser( pNewParser ); + setDefaultPaper( rContext ); + std::vector<OUString>::iterator defit = default_values.begin(); + for (auto const& key : keys) + { + const PPDValue* p1Value = key->getValue( *defit ); + if( p1Value ) + { + if( p1Value != key->getDefaultValue() ) + { + rContext.setValue( key, p1Value, true ); + SAL_INFO("vcl.unx.print", "key " << pKey->getKey() << " is set to " << *defit); + } + else + SAL_INFO("vcl.unx.print", "key " << pKey->getKey() << " is defaulted to " << *defit); + } + ++defit; + } + + rInfo.m_pParser = pNewParser; + rInfo.m_aContext = rContext; + g_variant_unref(ret); + } + else + { + g_clear_error(&error); + SAL_INFO("vcl.unx.print", "CPD GetAllOptions failed, falling back to generic driver"); + } + } + else + SAL_INFO("vcl.unx.print", "no dest found for printer " << aPrinter); + + if( ! pNewParser ) + { + // get the default PPD + pNewParser = PPDParser::getParser( "SGENPRT" ); + SAL_WARN("vcl.unx.print", "Parsing default SGENPRT PPD" ); + + PrinterInfo& rInfo = m_aPrinters[ aPrinter ].m_aInfo; + + rInfo.m_pParser = pNewParser; + rInfo.m_aContext.setParser( pNewParser ); + } +#else + (void)rPrinter; +#endif + return pNewParser; +} + + +void CPDManager::initialize() +{ + // get normal printers, clear printer list + PrinterInfoManager::initialize(); +#if ENABLE_DBUS && ENABLE_GIO + g_bus_own_name_on_connection (m_pConnection, + "org.libreoffice.print-dialog", + G_BUS_NAME_OWNER_FLAGS_NONE, + onNameAcquired, + onNameLost, + this, + nullptr); + + g_dbus_connection_signal_subscribe (m_pConnection, // DBus Connection + nullptr, // Sender Name + "org.openprinting.PrintBackend", // Sender Interface + "PrinterAdded", // Signal Name + nullptr, // Object Path + nullptr, // arg0 behaviour + G_DBUS_SIGNAL_FLAGS_NONE, // Signal Flags + printerAdded, // Callback Function + this, + nullptr); + g_dbus_connection_signal_subscribe (m_pConnection, // DBus Connection + nullptr, // Sender Name + "org.openprinting.PrintBackend", // Sender Interface + "PrinterRemoved", // Signal Name + nullptr, // Object Path + nullptr, // arg0 behaviour + G_DBUS_SIGNAL_FLAGS_NONE, // Signal Flags + printerRemoved, // Callback Function + this, + nullptr); + + // remove everything that is not a CUPS printer and not + // a special purpose printer (PDF, Fax) + std::unordered_map< OUString, Printer >::iterator it = m_aPrinters.begin(); + while (it != m_aPrinters.end()) + { + if( m_aCPDDestMap.find( it->first ) != m_aCPDDestMap.end() ) + { + ++it; + continue; + } + + if( !it->second.m_aInfo.m_aFeatures.isEmpty() ) + { + ++it; + continue; + } + it = m_aPrinters.erase(it); + } +#endif +} + +void CPDManager::setupJobContextData( JobData& rData ) +{ +#if ENABLE_DBUS && ENABLE_GIO + std::unordered_map<OUString, CPDPrinter *>::iterator dest_it = + m_aCPDDestMap.find( rData.m_aPrinterName ); + + if( dest_it == m_aCPDDestMap.end() ) + return PrinterInfoManager::setupJobContextData( rData ); + + std::unordered_map< OUString, Printer >::iterator p_it = + m_aPrinters.find( rData.m_aPrinterName ); + if( p_it == m_aPrinters.end() ) // huh ? + { + SAL_WARN("vcl.unx.print", "CPD printer list in disorder, " + "no dest for printer " << rData.m_aPrinterName); + return; + } + + if( p_it->second.m_aInfo.m_pParser == nullptr ) + { + // in turn calls createCPDParser + // which updates the printer info + p_it->second.m_aInfo.m_pParser = PPDParser::getParser( p_it->second.m_aInfo.m_aDriverName ); + } + if( p_it->second.m_aInfo.m_aContext.getParser() == nullptr ) + { + OUString aPrinter; + if( p_it->second.m_aInfo.m_aDriverName.startsWith("CPD:") ) + aPrinter = p_it->second.m_aInfo.m_aDriverName.copy( 4 ); + else + aPrinter = p_it->second.m_aInfo.m_aDriverName; + + p_it->second.m_aInfo.m_aContext = m_aDefaultContexts[ aPrinter ]; + } + + rData.m_pParser = p_it->second.m_aInfo.m_pParser; + rData.m_aContext = p_it->second.m_aInfo.m_aContext; +#else + (void)rData; +#endif +} + +FILE* CPDManager::startSpool( const OUString& rPrintername, bool bQuickCommand ) +{ +#if ENABLE_DBUS && ENABLE_GIO + SAL_INFO( "vcl.unx.print", "startSpool: " << rPrintername << " " << (bQuickCommand ? "true" : "false") ); + if( m_aCPDDestMap.find( rPrintername ) == m_aCPDDestMap.end() ) + { + SAL_INFO( "vcl.unx.print", "defer to PrinterInfoManager::startSpool" ); + return PrinterInfoManager::startSpool( rPrintername, bQuickCommand ); + } + OUString aTmpURL, aTmpFile; + osl_createTempFile( nullptr, nullptr, &aTmpURL.pData ); + osl_getSystemPathFromFileURL( aTmpURL.pData, &aTmpFile.pData ); + OString aSysFile = OUStringToOString( aTmpFile, osl_getThreadTextEncoding() ); + FILE* fp = fopen( aSysFile.getStr(), "w" ); + if( fp ) + m_aSpoolFiles[fp] = aSysFile; + + return fp; +#else + (void)rPrintername; + (void)bQuickCommand; + return nullptr; +#endif +} + +#if ENABLE_DBUS && ENABLE_GIO +void CPDManager::getOptionsFromDocumentSetup( const JobData& rJob, bool bBanner, const OString& rJobName, int& rNumOptions, GVariant **arr ) +{ + GVariantBuilder *builder; + builder = g_variant_builder_new(G_VARIANT_TYPE("a(ss)")); + g_variant_builder_add(builder, "(ss)", "job-name", rJobName.getStr()); + if( rJob.m_pParser == rJob.m_aContext.getParser() && rJob.m_pParser ) { + std::size_t i; + std::size_t nKeys = rJob.m_aContext.countValuesModified(); + ::std::vector< const PPDKey* > aKeys( nKeys ); + for( i = 0; i < nKeys; i++ ) + aKeys[i] = rJob.m_aContext.getModifiedKey( i ); + for( i = 0; i < nKeys; i++ ) { + const PPDKey* pKey = aKeys[i]; + const PPDValue* pValue = rJob.m_aContext.getValue( pKey ); + OUString sPayLoad; + if (pValue) { + sPayLoad = pValue->m_bCustomOption ? pValue->m_aCustomOption : pValue->m_aOption; + } + if (!sPayLoad.isEmpty()) { + OString aKey = OUStringToOString( pKey->getKey(), RTL_TEXTENCODING_ASCII_US ); + OString aValue = OUStringToOString( sPayLoad, RTL_TEXTENCODING_ASCII_US ); + if (aKey.equals("Duplex")) { + aKey = OString("sides"); + } else if (aKey.equals("Resolution")) { + aKey = OString("printer-resolution"); + } else if (aKey.equals("PageSize")) { + aKey = OString("media"); + } + if (aKey.equals("sides")) { + if (aValue.equals("None")) { + aValue = OString("one-sided"); + } else if (aValue.equals("DuplexNoTumble")) { + aValue = OString("two-sided-long-edge"); + } else if (aValue.equals("DuplexTumble")) { + aValue = OString("two-sided-short-edge"); + } + } + g_variant_builder_add(builder, "(ss)", aKey.getStr(), aValue.getStr()); + } + } + } + if( rJob.m_nPDFDevice > 0 && rJob.m_nCopies > 1 ) + { + OString aVal( OString::number( rJob.m_nCopies ) ); + g_variant_builder_add(builder, "(ss)", "copies", aVal.getStr()); + rNumOptions++; + // TODO: something for collate + // Maybe this is the equivalent ipp attribute: + if (rJob.m_bCollate) { + g_variant_builder_add(builder, "(ss)", "multiple-document-handling", "separate-documents-collated-copies"); + } else { + g_variant_builder_add(builder, "(ss)", "multiple-document-handling", "separate-documents-uncollated-copies"); + } + rNumOptions++; + } + if( ! bBanner ) + { + g_variant_builder_add(builder, "(ss)", "job-sheets", "none"); + rNumOptions++; + } + if (rJob.m_eOrientation == orientation::Portrait) { + g_variant_builder_add(builder, "(ss)", "orientation-requested", "portrait"); + rNumOptions++; + } else if (rJob.m_eOrientation == orientation::Landscape) { + g_variant_builder_add(builder, "(ss)", "orientation-requested", "landscape"); + rNumOptions++; + } + (*arr) = g_variant_new("a(ss)", builder); + g_variant_builder_unref(builder); +} +#endif + +bool CPDManager::endSpool( const OUString& rPrintername, const OUString& rJobTitle, FILE* pFile, const JobData& rDocumentJobData, bool bBanner, const OUString& rFaxNumber ) +{ + bool success = false; +#if ENABLE_DBUS && ENABLE_GIO + SAL_INFO( "vcl.unx.print", "endSpool: " << rPrintername << "," << rJobTitle << " copy count = " << rDocumentJobData.m_nCopies ); + std::unordered_map< OUString, CPDPrinter * >::iterator dest_it = + m_aCPDDestMap.find( rPrintername ); + if( dest_it == m_aCPDDestMap.end() ) + { + SAL_INFO( "vcl.unx.print", "defer to PrinterInfoManager::endSpool" ); + return PrinterInfoManager::endSpool( rPrintername, rJobTitle, pFile, rDocumentJobData, bBanner, rFaxNumber ); + } + + std::unordered_map< FILE*, OString, FPtrHash >::const_iterator it = m_aSpoolFiles.find( pFile ); + if( it != m_aSpoolFiles.end() ) + { + fclose( pFile ); + rtl_TextEncoding aEnc = osl_getThreadTextEncoding(); + OString sJobName(OUStringToOString(rJobTitle, aEnc)); + if (!rFaxNumber.isEmpty()) + { + sJobName = OUStringToOString(rFaxNumber, aEnc); + } + OString aSysFile = it->second; + CPDPrinter* pDest = dest_it->second; + GVariant* ret; + gint job_id; + int nNumOptions = 0; + GVariant *pArr = nullptr; + getOptionsFromDocumentSetup( rDocumentJobData, bBanner, sJobName, nNumOptions, &pArr ); + ret = g_dbus_proxy_call_sync (pDest->backend, "printFile", + g_variant_new( + "(ssi@a(ss))", + (pDest->id), + aSysFile.getStr(), + nNumOptions, + pArr + ), + G_DBUS_CALL_FLAGS_NONE, + -1, nullptr, nullptr); + g_variant_get (ret, "(i)", &job_id); + if (job_id != -1) { + success = true; + } + g_variant_unref(ret); + unlink( it->second.getStr() ); + m_aSpoolFiles.erase( pFile ); + } +#else + (void)rPrintername; + (void)rJobTitle; + (void)pFile; + (void)rDocumentJobData; + (void)bBanner; + (void)rFaxNumber; +#endif + return success; +} + +bool CPDManager::checkPrintersChanged( bool ) +{ +#if ENABLE_DBUS && ENABLE_GIO + bool bChanged = m_aPrintersChanged; + m_aPrintersChanged = false; + g_dbus_connection_emit_signal (m_pConnection, + nullptr, + "/org/libreoffice/PrintDialog", + "org.openprinting.PrintFrontend", + "RefreshBackend", + nullptr, + nullptr); + return bChanged; +#else + return false; +#endif +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ + |