diff options
author | Yash Srivastav <yash111998@gmail.com> | 2017-07-27 08:11:34 +0530 |
---|---|---|
committer | Caolán McNamara <caolanm@redhat.com> | 2017-09-12 10:54:52 +0200 |
commit | 7fbf98d99737ac916cc2dc374f3139e2ea81e47b (patch) | |
tree | f67793fd3d086d0a5c39ad45cc3b510ce1dffb6d /vcl | |
parent | d2335e83c3e798cc72a9cf3b3e31eeefe4b5a39c (diff) |
Add Common Printing Dialog Functionality
Summary:
These set of commits add support for the Common Printing
Dialog Backends being developed as part of GSoC'17.
All backends exist as dbus-services which can be queried
for printers, their options, etc.
Test Plan:
Firstly, without adding any CPD backend, LO printing should
work as it works now with graceful fallback to CUPS.
Next, we need to install a backend. The backend providing
CUPS interface is at git@github.com:NilanjanaLodh/PrintDialog_Backend.git
See README.md for installation instructions.
After this, run LO and existing CUPS Printers should show up.
Also printing does send a job to cups which can be seen at:
http://localhost:631/jobs?which_jobs=completed
Due to LO shutdown not being handled properly currently, the
backend might need to be killed via `pkill print_backend_cups`
Future Plans:
* Fix shutdown actions.
Change-Id: I3bdea5d3272ec4c9c0dfe510f5848fcb398b4b14
Reviewed-on: https://gerrit.libreoffice.org/40565
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
Tested-by: Caolán McNamara <caolanm@redhat.com>
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/inc/unx/cpdmgr.hxx | 78 | ||||
-rw-r--r-- | vcl/unx/generic/printer/cpdmgr.cxx | 654 | ||||
-rw-r--r-- | vcl/unx/generic/printer/ppdparser.cxx | 124 |
3 files changed, 695 insertions, 161 deletions
diff --git a/vcl/inc/unx/cpdmgr.hxx b/vcl/inc/unx/cpdmgr.hxx index d60f098435fb..48fb02ab3a2a 100644 --- a/vcl/inc/unx/cpdmgr.hxx +++ b/vcl/inc/unx/cpdmgr.hxx @@ -30,52 +30,100 @@ #include "printerinfomanager.hxx" #include "cupsmgr.hxx" +#define BACKEND_DIR "/usr/share/print-backends" +#define FRONTEND_INTERFACE "/usr/share/dbus-1/interfaces/org.openprinting.Frontend.xml" +#define BACKEND_INTERFACE "/usr/share/dbus-1/interfaces/org.openprinting.Backend.xml" + namespace psp { +class PPDParser; + struct CPDPrinter { - std::string name; - std::string info; - std::string location; - std::string make_and_model; - std::string printer_state; + const char* id; + const char* name; + const char* info; + const char* location; + const char* make_and_model; + const char* printer_state; + const char* backend_name; bool is_accepting_jobs; GDBusProxy* backend; }; +struct CPDPrinterOption +{ + OUString name; + OUString default_value; + int num_supported_values; + std::vector<OUString> supported_values; +}; + class CPDManager : public PrinterInfoManager { #if ENABLE_DBUS && ENABLE_GIO GDBusConnection * m_pConnection = nullptr; bool m_aPrintersChanged = true; + std::vector<std::pair<std::string, gchar*>> m_tBackends; std::unordered_map< std::string, GDBusProxy * > m_pBackends; std::unordered_map< FILE*, OString, FPtrHash > m_aSpoolFiles; std::unordered_map< OUString, CPDPrinter *, OUStringHash > m_aCPDDestMap; + std::unordered_map< OUString, PPDContext, OUStringHash > m_aDefaultContexts; #endif CPDManager(); + // Function called when CPDManager is destroyed + virtual ~CPDManager() override; + + static void onNameAcquired(GDBusConnection *connection, const char* name, void* user_data); + static void onNameLost (GDBusConnection *, const char *name, void*); + static void printerAdded (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data); + static void printerRemoved (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data); + virtual void initialize() override; + static void getOptionsFromDocumentSetup( const JobData& rJob, bool bBanner, const OString& rJobName, int& rNumOptions, GVariant **arr ); + + public: + // Functions involved in initialization GDBusProxy * getProxy( std::string target ); void addBackend( std::pair< std::string, GDBusProxy * > pair ); - void addDestination( std::pair< OUString, CPDPrinter * > pair ); + void addTempBackend( std::pair< std::string, gchar* > pair ); + std::vector<std::pair<std::string, gchar*>> getTempBackends(); + void addNewPrinter( const OUString&, const OUString&, CPDPrinter * ); + + // Create CPDManager static CPDManager* tryLoadCPD(); - virtual ~CPDManager() override; + + // Create a PPDParser for CPD Printers + const PPDParser* createCPDParser( const OUString& rPrinter ); + + // Functions related to printing + virtual FILE* startSpool( const OUString& rPrinterName, bool bQuickCommand ) override; + virtual bool endSpool( const OUString& rPrinterName, const OUString& rJobTitle, FILE* pFile, const JobData& rDocumentJobData, bool bBanner, const OUString& rFaxNumber ) override; virtual void setupJobContextData( JobData& rData ) override; - /// check if the printer configuration has changed + + // check if the printer configuration has changed virtual bool checkPrintersChanged( bool bWait ) override; + // members for administration - // disable for CUPS + // disable for CPD virtual bool addPrinter( const OUString& rPrinterName, const OUString& rDriverName ) override; virtual bool removePrinter( const OUString& rPrinterName, bool bCheckOnly ) override; - virtual bool writePrinterConfig() override; virtual bool setDefaultPrinter( const OUString& rPrinterName ) override; - - virtual FILE* startSpool( const OUString& rPrinterName, bool bQuickCommand ) override; - virtual bool endSpool( const OUString& rPrinterName, const OUString& rJobTitle, FILE* pFile, const JobData& rDocumentJobData, bool bBanner, const OUString& rFaxNumber ) override; - - }; } // namespace psp diff --git a/vcl/unx/generic/printer/cpdmgr.cxx b/vcl/unx/generic/printer/cpdmgr.cxx index 1dbdc45eab6c..1cd48e626351 100644 --- a/vcl/unx/generic/printer/cpdmgr.cxx +++ b/vcl/unx/generic/printer/cpdmgr.cxx @@ -35,18 +35,15 @@ using namespace psp; using namespace osl; // Function to execute when name is acquired on the bus -static void on_name_acquired (GDBusConnection *connection, - const gchar *name, - gpointer) +void CPDManager::onNameAcquired (GDBusConnection *connection, + const gchar *, + gpointer user_data) { - g_message("Name Acquired %s", name); - - GError *local_error, *error; gchar* contents; - GDBusNodeInfo *introspection_data = nullptr; + GDBusNodeInfo *introspection_data; // Get Interface for introspection - g_file_get_contents ("/home/yash/bigGit/PrintDialog_Backend/org.openprinting.Frontend.xml", &contents, nullptr, &error); + 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, @@ -56,72 +53,118 @@ static void on_name_acquired (GDBusConnection *connection, nullptr, /* user_data */ nullptr, /* user_data_free_func */ nullptr); /* GError** */ - local_error = nullptr; - g_dbus_connection_emit_signal (connection, - nullptr, - "/org/libreoffice/PrintDialog", - "org.openprinting.PrintFrontend", - "GetBackend", - nullptr, - &local_error); - g_assert_no_error (local_error); - g_message("Emitted Signal GetBackend"); + 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 (std::vector<std::pair<std::string, gchar*>>::iterator it = backends.begin(); it != backends.end(); ++it) { + 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], + it -> first.c_str(), + it -> second, + "org.openprinting.PrintBackend", + nullptr, + nullptr); + g_free(it -> 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); + } } -static void on_name_lost (GDBusConnection *, - const gchar *name, - gpointer) +void CPDManager::onNameLost (GDBusConnection *, + const gchar *name, + gpointer) { g_message("Name Lost: %s", name); } -static void got_signal (GDBusConnection *connection, - const gchar *sender_name, - const gchar *object_path, - const gchar *interface_name, - const gchar *signal_name, - GVariant *parameters, - gpointer user_data) +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); - g_message("Received %s from %s at %s", signal_name, sender_name, object_path); - if (g_strcmp0 (signal_name, "PrinterAdded") == 0) - { - GDBusProxy *proxy; - proxy = current -> getProxy(sender_name); - if (proxy == nullptr) { - gchar* contents; - GDBusNodeInfo *introspection_data = nullptr; - - // Get Interface for introspection - g_file_get_contents ("/home/yash/bigGit/PrintDialog_Backend/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); - std::pair<std::string, GDBusProxy *> new_backend (sender_name, proxy); - current -> addBackend(new_backend); - } - CPDPrinter *pDest = static_cast<CPDPrinter *>(malloc(sizeof(CPDPrinter))); - // const gchar *name, *info, *location, *make_and_model, *state; - // gboolean is_accepting_jobs; - g_variant_get (parameters, "(ssssbs)", &(pDest -> name), &(pDest -> info), &(pDest -> location), &(pDest -> make_and_model), &(pDest -> is_accepting_jobs), &(pDest -> printer_state)); - g_message("State: %s", (pDest -> printer_state).c_str()); - const char *pName = (pDest -> name).c_str(); - rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); - OUString aPrinterName = OStringToOUString( pName, aEncoding ); - std::pair<OUString, CPDPrinter *> newDest (aPrinterName, pDest); - current -> addDestination(newDest); - } else if (g_strcmp0 (signal_name, "UpdatePrinter") == 0) - { - } else if (g_strcmp0 (signal_name, "DeletePrinter") == 0) - { - } + 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 *, OUStringHash>::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, OUStringHash>::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(std::string target) { @@ -134,50 +177,61 @@ GDBusProxy * CPDManager::getProxy(std::string target) { void CPDManager::addBackend(std::pair<std::string, GDBusProxy *> pair) { this -> m_pBackends.insert(pair); - g_message("Add new backend"); } -void CPDManager::addDestination(std::pair<OUString, CPDPrinter * > pair) { - // initialize printer with possible configuration from psprint.conf - OUString aPrinterName = pair.first; - CPDPrinter *pDest = pair.second; - std::unordered_map<OUString, CPDPrinter *, OUStringHash>::iterator it = m_aCPDDestMap.find( aPrinterName ); +void CPDManager::addTempBackend(std::pair<std::string, gchar*> pair) { + this -> m_tBackends.push_back(pair); +} + +std::vector<std::pair<std::string, gchar*>> CPDManager::getTempBackends() { + return this -> m_tBackends; +} + +void CPDManager::addNewPrinter(const OUString& aPrinterName, const OUString& aUniqueName, CPDPrinter *pDest) { + std::pair<OUString, CPDPrinter *> newPrinter (aUniqueName, pDest); + std::unordered_map<OUString, CPDPrinter *, OUStringHash>::iterator it = m_aCPDDestMap.find( aUniqueName ); if (it == m_aCPDDestMap.end()) { - m_aCPDDestMap.insert(pair); + m_aCPDDestMap.insert(newPrinter); } else { m_aCPDDestMap.erase(it); - m_aCPDDestMap.insert(pair); + m_aCPDDestMap.insert(newPrinter); } - bool bSetToGlobalDefaults = m_aPrinters.find( aPrinterName ) == m_aPrinters.end(); - Printer aPrinter = m_aPrinters[ aPrinterName ]; + 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(); - const char *pInfo = (pDest -> info).c_str(); - aPrinter.m_aInfo.m_aComment = OStringToOUString(pInfo, aEncoding); - const char *pLocation = (pDest -> location).c_str(); - aPrinter.m_aInfo.m_aLocation = OStringToOUString(pLocation, aEncoding); + aPrinter.m_aInfo.m_aComment = OStringToOUString(pDest -> info, aEncoding); + aPrinter.m_aInfo.m_aLocation = OStringToOUString(pDest -> location, aEncoding); OUStringBuffer aBuf( 256 ); aBuf.append( "CPD:" ); - aBuf.append( aPrinterName ); + aBuf.append( aUniqueName ); // 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 download PPDs for each and + // 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, OUStringHash >::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 = aBuf.makeStringAndClear(); aPrinter.m_bModified = false; - m_aPrinters[ aPrinter.m_aInfo.m_aPrinterName ] = aPrinter; - g_message("Add new Printer"); + m_aPrinters[ aUniqueName ] = aPrinter; } /* @@ -190,9 +244,26 @@ CPDManager* CPDManager::tryLoadCPD() #if ENABLE_DBUS && ENABLE_GIO static const char* pEnv = getenv("SAL_DISABLE_CPD"); - if (!pEnv || !*pEnv) - pManager = new CPDManager(); - g_message("Loaded CPD\n"); + if (!pEnv || !*pEnv) { + GDir *dir; + GError *error = nullptr; + const gchar *filename; + dir = g_dir_open(BACKEND_DIR, 0, &error); + 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; } @@ -202,7 +273,6 @@ CPDManager::CPDManager() : { // Get Destinations number and pointers GError *error = nullptr; - g_message("Created CPDManager\n"); m_pConnection = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, &error); g_assert_no_error (error); } @@ -216,51 +286,245 @@ CPDManager::~CPDManager() "StopListing", nullptr, nullptr); - g_message("Sent StopListing\n"); g_dbus_connection_flush_sync (m_pConnection, nullptr, nullptr); g_dbus_connection_close_sync (m_pConnection, nullptr, nullptr); - // Free pointers to destinations in cpd - fprintf( stderr, "Destroyed CPDManager\n"); + std::unordered_map<std::string, GDBusProxy *>::iterator it = this -> m_pBackends.begin(); + for(; it != m_pBackends.end(); ++it) + { + g_object_unref(it->second); + } + std::unordered_map<OUString, CPDPrinter *, OUStringHash>::iterator dest_it = + m_aCPDDestMap.begin(); + for(; dest_it != m_aCPDDestMap.end(); ++dest_it) + { + free(dest_it->second); + } } +const PPDParser* CPDManager::createCPDParser( const OUString& rPrinter ) +{ + const PPDParser* pNewParser = nullptr; + OUString aPrinter; + + if( rPrinter.startsWith("CPD:") ) + aPrinter = rPrinter.copy( 4 ); + else + aPrinter = rPrinter; + + std::unordered_map< OUString, CPDPrinter *, OUStringHash >::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.push_back(pKey); + } + + pKey = new PPDKey("ModelName"); + aValueName = OStringToOUString( "", aEncoding ); + pValue = pKey->insertValue( aValueName, eQuoted ); + if( pValue ) + pValue->m_aValue = aValueName; + pKey -> m_pDefaultValue = pValue; + + pKey = new PPDKey("NickName"); + aValueName = OStringToOUString( pDest -> name, aEncoding ); + pValue = pKey->insertValue( aValueName, eQuoted ); + if( pValue ) + pValue->m_aValue = aValueName; + pKey -> m_pDefaultValue = pValue; + + pNewParser = new PPDParser(aPrinter, keys); + PrinterInfo& rInfo = m_aPrinters[ aPrinter ].m_aInfo; + PPDContext& rContext = m_aDefaultContexts[ aPrinter ]; + rContext.setParser( pNewParser ); + setDefaultPaper( rContext ); + std::vector<PPDKey*>::iterator keyit; + std::vector<OUString>::iterator defit; + for (keyit = keys.begin(), defit = default_values.begin(); keyit != keys.end(); keyit++, defit++ ) { + pKey = *keyit; + const PPDValue* p1Value = pKey->getValue( *defit ); + if( p1Value ) + { + if( p1Value != pKey->getDefaultValue() ) + { + rContext.setValue( pKey, 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); + } + } + + rInfo.m_pParser = pNewParser; + rInfo.m_aContext = rContext; + g_variant_unref(ret); + } + else + 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 ); + } + + return pNewParser; +} void CPDManager::initialize() { // get normal printers, clear printer list PrinterInfoManager::initialize(); - g_message("Initialised CPDManager\n"); g_bus_own_name_on_connection (m_pConnection, "org.libreoffice.print-dialog", G_BUS_NAME_OWNER_FLAGS_NONE, - on_name_acquired, - on_name_lost, - NULL, - NULL); + onNameAcquired, + onNameLost, + this, + nullptr); g_dbus_connection_signal_subscribe (m_pConnection, // DBus Connection - NULL, // Sender Name + 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 - NULL, // Signal Name - NULL, // Object Path - NULL, // arg0 behaviour + "PrinterRemoved", // Signal Name + nullptr, // Object Path + nullptr, // arg0 behaviour G_DBUS_SIGNAL_FLAGS_NONE, // Signal Flags - got_signal, // Callback Function + printerRemoved, // Callback Function this, - NULL); - // If password CB is needed - //cpdSetPasswordCB( setPasswordCallback ); + nullptr); + + // remove everything that is not a CUPS printer and not + // a special purpose printer (PDF, Fax) + std::list< OUString > aRemovePrinters; + for( std::unordered_map< OUString, Printer, OUStringHash >::iterator it = m_aPrinters.begin(); + it != m_aPrinters.end(); ++it ) + { + if( m_aCPDDestMap.find( it->first ) != m_aCPDDestMap.end() ) + continue; + + if( !it->second.m_aInfo.m_aFeatures.isEmpty() ) + continue; + aRemovePrinters.push_back( it->first ); + } + while( aRemovePrinters.begin() != aRemovePrinters.end() ) + { + m_aPrinters.erase( aRemovePrinters.front() ); + aRemovePrinters.pop_front(); + } } void CPDManager::setupJobContextData( JobData& rData ) { - std::unordered_map< OUString, CPDPrinter *, OUStringHash >::iterator dest_it = + std::unordered_map<OUString, CPDPrinter *, OUStringHash>::iterator dest_it = m_aCPDDestMap.find( rData.m_aPrinterName ); if( dest_it == m_aCPDDestMap.end() ) @@ -269,22 +533,41 @@ void CPDManager::setupJobContextData( JobData& rData ) std::unordered_map< OUString, Printer, OUStringHash >::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; - } - g_message("Setup JobContextData called"); + { + 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; } FILE* CPDManager::startSpool( const OUString& rPrintername, bool bQuickCommand ) { 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 ); - } + { + 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 ); @@ -296,38 +579,126 @@ FILE* CPDManager::startSpool( const OUString& rPrintername, bool bQuickCommand ) return fp; } +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 ) { + int i; + int 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(OString("Duplex"))) { + aKey = OString("sides"); + } else if (aKey.equals(OString("Resolution"))) { + aKey = OString("printer-resolution"); + } else if (aKey.equals(OString("PageSize"))) { + aKey = OString("media"); + } + if (aKey.equals(OString("sides"))) { + if (aValue.equals(OString("None"))) { + aValue = OString("one-sided"); + } else if (aValue.equals(OString("DuplexNoTumble"))) { + aValue = OString("two-sided-long-edge"); + } else if (aValue.equals(OString("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); +} + bool CPDManager::endSpool( const OUString& rPrintername, const OUString& rJobTitle, FILE* pFile, const JobData& rDocumentJobData, bool bBanner, const OUString& rFaxNumber ) { + bool success = false; SAL_INFO( "vcl.unx.print", "endSpool: " << rPrintername << "," << rJobTitle << " copy count = " << rDocumentJobData.m_nCopies ); std::unordered_map< OUString, CPDPrinter *, OUStringHash >::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 ); - } + { + 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()) { - fclose( pFile ); - rtl_TextEncoding aEnc = osl_getThreadTextEncoding(); - OString sJobName(OUStringToOString(rJobTitle, aEnc)); - if (!rFaxNumber.isEmpty()) - { - sJobName = OUStringToOString(rFaxNumber, aEnc); - } - CPDPrinter* pDest = dest_it->second; - g_message("Printing"); - // g_dbus_proxy_call(pDest -> backend, "printJob", - // g_variant_new("(s)", "yo"), - // G_DBUS_CALL_FLAGS_NONE, - // -1, nullptr, nullptr, nullptr); - unlink( it->second.getStr() ); - m_aSpoolFiles.erase( pFile ); + 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 ); + } - return true; + return success; } bool CPDManager::checkPrintersChanged( bool ) @@ -368,20 +739,15 @@ bool CPDManager::setDefaultPrinter( const OUString& rName ) std::unordered_map< OUString, CPDPrinter *, OUStringHash >::iterator nit = m_aCPDDestMap.find( rName ); if( nit != m_aCPDDestMap.end()) - { - m_aDefaultPrinter = rName; - bSuccess = true; - } + { + m_aDefaultPrinter = rName; + bSuccess = true; + } else bSuccess = PrinterInfoManager::setDefaultPrinter( rName ); return bSuccess; } -bool CPDManager::writePrinterConfig() -{ - return PrinterInfoManager::writePrinterConfig(); -} - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/printer/ppdparser.cxx b/vcl/unx/generic/printer/ppdparser.cxx index 927e6a284d4a..97551db1983b 100644 --- a/vcl/unx/generic/printer/ppdparser.cxx +++ b/vcl/unx/generic/printer/ppdparser.cxx @@ -27,6 +27,7 @@ #include <unx/helper.hxx> #include "unx/cupsmgr.hxx" +#include "unx/cpdmgr.hxx" #include "tools/urlobj.hxx" #include "tools/stream.hxx" @@ -45,6 +46,10 @@ #include <unordered_map> +#ifdef ENABLE_CUPS +#include <cups/cups.h> +#endif + namespace psp { class PPDTranslator @@ -539,7 +544,7 @@ const PPDParser* PPDParser::getParser( const OUString& rFile ) ::osl::Guard< ::osl::Mutex > aGuard( aMutex ); OUString aFile = rFile; - if( !rFile.startsWith( "CUPS:" ) ) + if( !rFile.startsWith( "CUPS:" ) && !rFile.startsWith( "CPD:" ) ) aFile = getPPDFile( rFile ); if( aFile.isEmpty() ) { @@ -558,7 +563,7 @@ const PPDParser* PPDParser::getParser( const OUString& rFile ) return *it; PPDParser* pNewParser = nullptr; - if( !aFile.startsWith( "CUPS:" ) ) + if( !aFile.startsWith( "CUPS:" ) && !aFile.startsWith( "CPD:" ) ) pNewParser = new PPDParser( aFile ); else { @@ -568,6 +573,9 @@ const PPDParser* PPDParser::getParser( const OUString& rFile ) #ifdef ENABLE_CUPS pNewParser = const_cast<PPDParser*>(static_cast<CUPSManager&>(rMgr).createCUPSParser( aFile )); #endif + } else if ( rMgr.getType() == PrinterInfoManager::Type::CPD ) + { + pNewParser = const_cast<PPDParser*>(static_cast<CPDManager&>(rMgr).createCPDParser( aFile )); } } if( pNewParser ) @@ -581,6 +589,118 @@ const PPDParser* PPDParser::getParser( const OUString& rFile ) return pNewParser; } +PPDParser::PPDParser( const OUString& rFile, std::vector<PPDKey*> keys) : + m_aFile( rFile ), + m_bColorDevice( false ), + m_bType42Capable( false ), + m_nLanguageLevel( 0 ), + m_aFileEncoding( RTL_TEXTENCODING_MS_1252 ), + m_pDefaultImageableArea( nullptr ), + m_pImageableAreas( nullptr ), + m_pDefaultPaperDimension( nullptr ), + m_pPaperDimensions( nullptr ), + m_pDefaultInputSlot( nullptr ), + m_pInputSlots( nullptr ), + m_pDefaultResolution( nullptr ), + m_pResolutions( nullptr ), + m_pFontList( nullptr ), + m_pTranslator( new PPDTranslator() ) +{ + for (PPDKey* key: keys) + { + insertKey( key -> getKey(), key ); + } + + // fill in shortcuts + const PPDKey* pKey; + + pKey = getKey( OUString( "PageSize" ) ); + + if ( pKey ) { + PPDKey* pImageableAreas = new PPDKey("ImageableArea"); + PPDKey* pPaperDimensions = new PPDKey("PaperDimension"); +#if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 7) || CUPS_VERSION_MAJOR > 1 + for (int i = 0; i < pKey->countValues(); i++) { + const PPDValue* pValue = pKey -> getValue(i); + OUString aValueName = pValue -> m_aOption; + PPDValue* pImageableAreaValue = pImageableAreas -> insertValue( aValueName, eQuoted ); + PPDValue* pPaperDimensionValue = pPaperDimensions -> insertValue( aValueName, eQuoted ); + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + OString o = OUStringToOString( aValueName, aEncoding ); + pwg_media_t *pPWGMedia = pwgMediaForPWG(o.pData->buffer); + if (pPWGMedia != nullptr) { + OUStringBuffer aBuf( 256 ); + aBuf.append( "0 0 " ); + aBuf.append( PWG_TO_POINTS(pPWGMedia -> width) ); + aBuf.append( " " ); + aBuf.append( PWG_TO_POINTS(pPWGMedia -> length) ); + if ( pImageableAreaValue ) + pImageableAreaValue->m_aValue = aBuf.makeStringAndClear(); + aBuf.append( PWG_TO_POINTS(pPWGMedia -> width) ); + aBuf.append( " " ); + aBuf.append( PWG_TO_POINTS(pPWGMedia -> length) ); + if ( pPaperDimensionValue ) + pPaperDimensionValue->m_aValue = aBuf.makeStringAndClear(); + if ((aValueName).equals(pKey -> getDefaultValue() -> m_aOption)) { + pImageableAreas -> m_pDefaultValue = pImageableAreaValue; + pPaperDimensions -> m_pDefaultValue = pPaperDimensionValue; + } + } + } +#endif // HAVE_CUPS_API_1_7 + insertKey("ImageableArea", pImageableAreas); + insertKey("PaperDimension", pPaperDimensions); + } + + m_pImageableAreas = getKey( OUString( "ImageableArea" ) ); + if( m_pImageableAreas ) + m_pDefaultImageableArea = m_pImageableAreas->getDefaultValue(); + if (m_pImageableAreas == nullptr) { + SAL_WARN( "vcl.unx.print", "no ImageableArea in " << m_aFile); + } + if (m_pDefaultImageableArea == nullptr) { + SAL_WARN( "vcl.unx.print", "no DefaultImageableArea in " << m_aFile); + } + + m_pPaperDimensions = getKey( OUString( "PaperDimension" ) ); + if( m_pPaperDimensions ) + m_pDefaultPaperDimension = m_pPaperDimensions->getDefaultValue(); + if (m_pPaperDimensions == nullptr) { + SAL_WARN( "vcl.unx.print", "no PaperDimensions in " << m_aFile); + } + if (m_pDefaultPaperDimension == nullptr) { + SAL_WARN( "vcl.unx.print", "no DefaultPaperDimensions in " << m_aFile); + } + + m_pResolutions = getKey( OUString( "Resolution" ) ); + if( m_pResolutions ) + m_pDefaultResolution = m_pResolutions->getDefaultValue(); + if (m_pResolutions == nullptr) { + SAL_WARN( "vcl.unx.print", "no Resolution in " << m_aFile); + } + SAL_INFO_IF(!m_pDefaultResolution, "vcl.unx.print", "no DefaultResolution in " + m_aFile); + + m_pInputSlots = getKey( OUString( "InputSlot" ) ); + if( m_pInputSlots ) + m_pDefaultInputSlot = m_pInputSlots->getDefaultValue(); + SAL_INFO_IF(!m_pInputSlots, "vcl.unx.print", "no InputSlot in " << m_aFile); + SAL_INFO_IF(!m_pDefaultInputSlot, "vcl.unx.print", "no DefaultInputSlot in " << m_aFile); + + m_pFontList = getKey( OUString( "Font" ) ); + if (m_pFontList == nullptr) { + SAL_WARN( "vcl.unx.print", "no Font in " << m_aFile); + } + + // fill in direct values + if( (pKey = getKey( OUString( "ModelName" ) )) ) + m_aPrinterName = pKey->getValue( 0 )->m_aValue; + if( (pKey = getKey( OUString( "NickName" ) )) ) + m_aNickName = pKey->getValue( 0 )->m_aValue; + if( (pKey = getKey( OUString( "print-color-mode" ) )) ) + m_bColorDevice = pKey->countValues() > 1; + +} + PPDParser::PPDParser( const OUString& rFile ) : m_aFile( rFile ), m_bColorDevice( false ), |