Update git submodules
[LibreOffice.git] / vcl / unx / generic / printer / cpdmgr.cxx
blob00023060b07384f21ac91a1fdb734acd775e5281
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <cstddef>
23 #include <unistd.h>
25 #include <unx/cpdmgr.hxx>
27 #include <osl/file.h>
28 #include <osl/thread.h>
30 #include <rtl/ustrbuf.hxx>
31 #include <sal/log.hxx>
33 #include <config_dbus.h>
34 #include <config_gio.h>
36 using namespace psp;
38 #if ENABLE_DBUS && ENABLE_GIO
39 // Function to execute when name is acquired on the bus
40 void CPDManager::onNameAcquired(GDBusConnection* connection, const gchar*, gpointer user_data)
42 gchar* contents;
43 // Get Interface for introspection
44 if (!g_file_get_contents(FRONTEND_INTERFACE, &contents, nullptr, nullptr))
45 return;
47 GDBusNodeInfo* introspection_data = g_dbus_node_info_new_for_xml(contents, nullptr);
49 g_dbus_connection_register_object(connection, "/org/libreoffice/PrintDialog",
50 introspection_data->interfaces[0], nullptr,
51 nullptr, /* user_data */
52 nullptr, /* user_data_free_func */
53 nullptr); /* GError** */
54 g_free(contents);
55 g_dbus_node_info_unref(introspection_data);
57 CPDManager* current = static_cast<CPDManager*>(user_data);
58 std::vector<std::pair<std::string, gchar*>> backends = current->getTempBackends();
59 for (auto const& backend : backends)
61 // Get Interface for introspection
62 if (g_file_get_contents(BACKEND_INTERFACE, &contents, nullptr, nullptr))
64 introspection_data = g_dbus_node_info_new_for_xml(contents, nullptr);
65 GDBusProxy* proxy = g_dbus_proxy_new_sync(
66 connection, G_DBUS_PROXY_FLAGS_NONE, introspection_data->interfaces[0],
67 backend.first.c_str(), backend.second, "org.openprinting.PrintBackend", nullptr,
68 nullptr);
69 g_assert(proxy != nullptr);
70 g_dbus_proxy_call(proxy, "ActivateBackend", nullptr, G_DBUS_CALL_FLAGS_NONE, -1,
71 nullptr, nullptr, nullptr);
73 g_object_unref(proxy);
74 g_dbus_node_info_unref(introspection_data);
75 g_free(contents);
77 g_free(backend.second);
81 void CPDManager::onNameLost(GDBusConnection*, const gchar* name, gpointer)
83 g_message("Name Lost: %s", name);
86 void CPDManager::printerAdded(GDBusConnection* connection, const gchar* sender_name,
87 const gchar* object_path, const gchar* interface_name, const gchar*,
88 GVariant* parameters, gpointer user_data)
90 CPDManager* current = static_cast<CPDManager*>(user_data);
91 GDBusProxy* proxy;
92 proxy = current->getProxy(sender_name);
93 if (proxy == nullptr)
95 gchar* contents;
97 // Get Interface for introspection
98 if (g_file_get_contents("/usr/share/dbus-1/interfaces/org.openprinting.Backend.xml",
99 &contents, nullptr, nullptr))
101 GDBusNodeInfo* introspection_data = g_dbus_node_info_new_for_xml(contents, nullptr);
102 proxy = g_dbus_proxy_new_sync(connection, G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
103 introspection_data->interfaces[0], sender_name,
104 object_path, interface_name, nullptr, nullptr);
106 g_dbus_node_info_unref(introspection_data);
107 std::pair<std::string, GDBusProxy*> new_backend(sender_name, proxy);
108 current->addBackend(std::move(new_backend));
109 g_free(contents);
112 CPDPrinter* pDest = static_cast<CPDPrinter*>(malloc(sizeof(CPDPrinter)));
113 pDest->backend = proxy;
114 g_variant_get(parameters, "(sssssbss)", &(pDest->id), &(pDest->name), &(pDest->info),
115 &(pDest->location), &(pDest->make_and_model), &(pDest->is_accepting_jobs),
116 &(pDest->printer_state), &(pDest->backend_name));
117 std::stringstream printerName;
118 printerName << pDest->name << ", " << pDest->backend_name;
119 std::stringstream uniqueName;
120 uniqueName << pDest->id << ", " << pDest->backend_name;
121 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
122 OUString aPrinterName = OStringToOUString(printerName.str(), aEncoding);
123 OUString aUniqueName = OStringToOUString(uniqueName.str(), aEncoding);
124 current->addNewPrinter(aPrinterName, aUniqueName, pDest);
127 void CPDManager::printerRemoved(GDBusConnection*, const gchar*, const gchar*, const gchar*,
128 const gchar*, GVariant* parameters, gpointer user_data)
130 // TODO: Remove every data linked to this particular printer.
131 CPDManager* pManager = static_cast<CPDManager*>(user_data);
132 char* id;
133 char* backend_name;
134 g_variant_get(parameters, "(ss)", &id, &backend_name);
135 std::stringstream uniqueName;
136 uniqueName << id << ", " << backend_name;
137 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
138 OUString aUniqueName = OStringToOUString(uniqueName.str(), aEncoding);
139 std::unordered_map<OUString, CPDPrinter*>::iterator it
140 = pManager->m_aCPDDestMap.find(aUniqueName);
141 if (it == pManager->m_aCPDDestMap.end())
143 SAL_WARN("vcl.unx.print", "CPD trying to remove non-existent printer from list");
144 return;
146 pManager->m_aCPDDestMap.erase(it);
147 std::unordered_map<OUString, Printer>::iterator printersIt
148 = pManager->m_aPrinters.find(aUniqueName);
149 if (printersIt == pManager->m_aPrinters.end())
151 SAL_WARN("vcl.unx.print", "CPD trying to remove non-existent printer from m_aPrinters");
152 return;
154 pManager->m_aPrinters.erase(printersIt);
157 GDBusProxy* CPDManager::getProxy(const std::string& target)
159 std::unordered_map<std::string, GDBusProxy*>::const_iterator it = m_pBackends.find(target);
160 if (it == m_pBackends.end())
162 return nullptr;
164 return it->second;
167 void CPDManager::addBackend(std::pair<std::string, GDBusProxy*> pair) { m_pBackends.insert(pair); }
169 void CPDManager::addTempBackend(const std::pair<std::string, gchar*>& pair)
171 m_tBackends.push_back(pair);
174 std::vector<std::pair<std::string, gchar*>> const& CPDManager::getTempBackends() const
176 return m_tBackends;
179 void CPDManager::addNewPrinter(const OUString& aPrinterName, const OUString& aUniqueName,
180 CPDPrinter* pDest)
182 m_aCPDDestMap[aUniqueName] = pDest;
183 bool bSetToGlobalDefaults = m_aPrinters.find(aUniqueName) == m_aPrinters.end();
184 Printer aPrinter = m_aPrinters[aUniqueName];
185 if (bSetToGlobalDefaults)
186 aPrinter.m_aInfo = m_aGlobalDefaults;
187 aPrinter.m_aInfo.m_aPrinterName = aPrinterName;
189 // TODO: I don't know how this should work when we have multiple
190 // sources with multiple possible defaults for each
191 // if( pDest->is_default )
192 // m_aDefaultPrinter = aPrinterName;
194 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
195 aPrinter.m_aInfo.m_aComment = OStringToOUString(pDest->info, aEncoding);
196 aPrinter.m_aInfo.m_aLocation = OStringToOUString(pDest->location, aEncoding);
197 // note: the parser that goes with the PrinterInfo
198 // is created implicitly by the JobData::operator=()
199 // when it detects the NULL ptr m_pParser.
200 // if we wanted to fill in the parser here this
201 // would mean we'd have to send a dbus message for each and
202 // every printer - which would be really bad runtime
203 // behaviour
204 aPrinter.m_aInfo.m_pParser = nullptr;
205 aPrinter.m_aInfo.m_aContext.setParser(nullptr);
206 std::unordered_map<OUString, PPDContext>::const_iterator c_it
207 = m_aDefaultContexts.find(aUniqueName);
208 if (c_it != m_aDefaultContexts.end())
210 aPrinter.m_aInfo.m_pParser = c_it->second.getParser();
211 aPrinter.m_aInfo.m_aContext = c_it->second;
213 aPrinter.m_aInfo.m_aDriverName = "CPD:" + aUniqueName;
214 m_aPrinters[aUniqueName] = aPrinter;
216 #endif
219 * CPDManager class
222 CPDManager* CPDManager::tryLoadCPD()
224 CPDManager* pManager = nullptr;
225 #if ENABLE_DBUS && ENABLE_GIO
226 static const char* pEnv = getenv("SAL_DISABLE_CPD");
228 if (!pEnv || !*pEnv)
230 // interface description XML files are needed in 'onNameAcquired()'
231 if (!g_file_test(FRONTEND_INTERFACE, G_FILE_TEST_IS_REGULAR)
232 || !g_file_test(BACKEND_INTERFACE, G_FILE_TEST_IS_REGULAR))
234 return nullptr;
237 GDir* dir;
238 const gchar* filename;
239 dir = g_dir_open(BACKEND_DIR, 0, nullptr);
240 if (dir != nullptr)
242 while ((filename = g_dir_read_name(dir)))
244 if (pManager == nullptr)
246 pManager = new CPDManager();
248 gchar* contents;
249 std::stringstream filepath;
250 filepath << BACKEND_DIR << '/' << filename;
251 if (g_file_get_contents(filepath.str().c_str(), &contents, nullptr, nullptr))
253 std::pair<std::string, gchar*> new_tbackend(filename, contents);
254 pManager->addTempBackend(new_tbackend);
255 g_free(contents);
258 g_dir_close(dir);
261 #endif
262 return pManager;
265 CPDManager::CPDManager()
266 : PrinterInfoManager(PrinterInfoManager::Type::CPD)
268 #if ENABLE_DBUS && ENABLE_GIO
269 // Get Destinations number and pointers
270 GError* error = nullptr;
271 m_pConnection = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, &error);
272 g_assert_no_error(error);
273 #endif
276 CPDManager::~CPDManager()
278 #if ENABLE_DBUS && ENABLE_GIO
279 g_dbus_connection_emit_signal(m_pConnection, nullptr, "/org/libreoffice/PrintDialog",
280 "org.openprinting.PrintFrontend", "StopListing", nullptr,
281 nullptr);
282 g_dbus_connection_flush_sync(m_pConnection, nullptr, nullptr);
283 g_dbus_connection_close_sync(m_pConnection, nullptr, nullptr);
284 for (auto const& backend : m_pBackends)
286 g_object_unref(backend.second);
288 for (auto const& backend : m_aCPDDestMap)
290 free(backend.second);
292 #endif
295 const PPDParser* CPDManager::createCPDParser(const OUString& rPrinter)
297 const PPDParser* pNewParser = nullptr;
298 #if ENABLE_DBUS && ENABLE_GIO
299 OUString aPrinter;
301 if (rPrinter.startsWith("CPD:"))
302 aPrinter = rPrinter.copy(4);
303 else
304 aPrinter = rPrinter;
306 std::unordered_map<OUString, CPDPrinter*>::iterator dest_it = m_aCPDDestMap.find(aPrinter);
308 if (dest_it != m_aCPDDestMap.end())
310 CPDPrinter* pDest = dest_it->second;
311 GVariant* ret = nullptr;
312 GError* error = nullptr;
313 ret = g_dbus_proxy_call_sync(pDest->backend, "GetAllOptions",
314 g_variant_new("(s)", (pDest->id)), G_DBUS_CALL_FLAGS_NONE, -1,
315 nullptr, &error);
316 if (ret != nullptr && error == nullptr)
318 // TODO: These keys need to be redefined to preserve usage across libreoffice
319 // InputSlot - media-col.media-source?
320 // Font - not needed now as it is required only for ps and we are using pdf
321 // Dial? - for FAX (need to look up PWG spec)
323 int num_attribute;
324 GVariantIter *iter_attr, *iter_supported_values;
325 g_variant_get(ret, "(ia(ssia(s)))", &num_attribute, &iter_attr);
326 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
327 PPDKey* pKey = nullptr;
328 OUString aValueName;
329 PPDValue* pValue;
330 std::vector<PPDKey*> keys;
331 std::vector<OUString> default_values;
332 for (int i = 0; i < num_attribute; i++)
334 char *name, *default_value;
335 int num_supported_values;
336 g_variant_iter_loop(iter_attr, "(ssia(s))", &name, &default_value,
337 &num_supported_values, &iter_supported_values);
338 OUString aOptionName = OStringToOUString(name, aEncoding);
339 OUString aDefaultValue = OStringToOUString(default_value, aEncoding);
340 if (aOptionName == "sides")
342 // Duplex key is used throughout for checking Duplex Support
343 aOptionName = u"Duplex"_ustr;
345 else if (aOptionName == "printer-resolution")
347 // Resolution key is used in places
348 aOptionName = u"Resolution"_ustr;
350 else if (aOptionName == "media")
352 // PageSize key is used in many places
353 aOptionName = u"PageSize"_ustr;
355 default_values.push_back(aDefaultValue);
356 pKey = new PPDKey(aOptionName);
358 // If number of values are 0, this is not settable via UI
359 if (num_supported_values > 0 && aDefaultValue != "NA")
360 pKey->m_bUIOption = true;
362 bool bDefaultFound = false;
364 for (int j = 0; j < num_supported_values; j++)
366 char* value;
367 g_variant_iter_loop(iter_supported_values, "(s)", &value);
368 aValueName = OStringToOUString(value, aEncoding);
369 if (aOptionName == "Duplex")
371 // Duplex key matches against very specific Values
372 if (aValueName == "one-sided")
374 aValueName = u"None"_ustr;
376 else if (aValueName == "two-sided-long-edge")
378 aValueName = u"DuplexNoTumble"_ustr;
380 else if (aValueName == "two-sided-short-edge")
382 aValueName = u"DuplexTumble"_ustr;
386 pValue = pKey->insertValue(aValueName, eQuoted);
387 if (!pValue)
388 continue;
389 pValue->m_aValue = aValueName;
391 if (aValueName.equals(aDefaultValue))
393 pKey->m_pDefaultValue = pValue;
394 bDefaultFound = true;
397 // This could be done to ensure default values also appear as options:
398 if (!bDefaultFound && pKey->m_bUIOption)
400 // pValue = pKey->insertValue( aDefaultValue, eQuoted );
401 // if( pValue )
402 // pValue->m_aValue = aDefaultValue;
404 keys.emplace_back(pKey);
407 pKey = new PPDKey(u"ModelName"_ustr);
408 aValueName = OStringToOUString("", aEncoding);
409 pValue = pKey->insertValue(aValueName, eQuoted);
410 if (pValue)
411 pValue->m_aValue = aValueName;
412 pKey->m_pDefaultValue = pValue;
413 keys.emplace_back(pKey);
415 pKey = new PPDKey(u"NickName"_ustr);
416 aValueName = OStringToOUString(pDest->name, aEncoding);
417 pValue = pKey->insertValue(aValueName, eQuoted);
418 if (pValue)
419 pValue->m_aValue = aValueName;
420 pKey->m_pDefaultValue = pValue;
421 keys.emplace_back(pKey);
423 pNewParser = new PPDParser(aPrinter, keys);
424 PrinterInfo& rInfo = m_aPrinters[aPrinter].m_aInfo;
425 PPDContext& rContext = m_aDefaultContexts[aPrinter];
426 rContext.setParser(pNewParser);
427 setDefaultPaper(rContext);
428 std::vector<OUString>::iterator defit = default_values.begin();
429 for (auto const& key : keys)
431 const PPDValue* p1Value = key->getValue(*defit);
432 if (p1Value)
434 if (p1Value != key->getDefaultValue())
436 rContext.setValue(key, p1Value, true);
437 SAL_INFO("vcl.unx.print",
438 "key " << pKey->getKey() << " is set to " << *defit);
440 else
441 SAL_INFO("vcl.unx.print",
442 "key " << pKey->getKey() << " is defaulted to " << *defit);
444 ++defit;
447 rInfo.m_pParser = pNewParser;
448 rInfo.m_aContext = rContext;
449 g_variant_unref(ret);
451 else
453 g_clear_error(&error);
454 SAL_INFO("vcl.unx.print", "CPD GetAllOptions failed, falling back to generic driver");
457 else
458 SAL_INFO("vcl.unx.print", "no dest found for printer " << aPrinter);
460 if (!pNewParser)
462 // get the default PPD
463 pNewParser = PPDParser::getParser(u"SGENPRT"_ustr);
464 SAL_WARN("vcl.unx.print", "Parsing default SGENPRT PPD");
466 PrinterInfo& rInfo = m_aPrinters[aPrinter].m_aInfo;
468 rInfo.m_pParser = pNewParser;
469 rInfo.m_aContext.setParser(pNewParser);
471 #else
472 (void)rPrinter;
473 #endif
474 return pNewParser;
477 void CPDManager::initialize()
479 // get normal printers, clear printer list
480 PrinterInfoManager::initialize();
481 #if ENABLE_DBUS && ENABLE_GIO
482 g_bus_own_name_on_connection(m_pConnection, "org.libreoffice.print-dialog",
483 G_BUS_NAME_OWNER_FLAGS_NONE, onNameAcquired, onNameLost, this,
484 nullptr);
486 g_dbus_connection_signal_subscribe(m_pConnection, // DBus Connection
487 nullptr, // Sender Name
488 "org.openprinting.PrintBackend", // Sender Interface
489 "PrinterAdded", // Signal Name
490 nullptr, // Object Path
491 nullptr, // arg0 behaviour
492 G_DBUS_SIGNAL_FLAGS_NONE, // Signal Flags
493 printerAdded, // Callback Function
494 this, nullptr);
495 g_dbus_connection_signal_subscribe(m_pConnection, // DBus Connection
496 nullptr, // Sender Name
497 "org.openprinting.PrintBackend", // Sender Interface
498 "PrinterRemoved", // Signal Name
499 nullptr, // Object Path
500 nullptr, // arg0 behaviour
501 G_DBUS_SIGNAL_FLAGS_NONE, // Signal Flags
502 printerRemoved, // Callback Function
503 this, nullptr);
505 // remove everything that is not a CUPS printer and not
506 // a special purpose printer (PDF, Fax)
507 std::unordered_map<OUString, Printer>::iterator it = m_aPrinters.begin();
508 while (it != m_aPrinters.end())
510 if (m_aCPDDestMap.contains(it->first))
512 ++it;
513 continue;
516 if (!it->second.m_aInfo.m_aFeatures.isEmpty())
518 ++it;
519 continue;
521 it = m_aPrinters.erase(it);
523 #endif
526 void CPDManager::setupJobContextData(JobData& rData)
528 #if ENABLE_DBUS && ENABLE_GIO
529 std::unordered_map<OUString, CPDPrinter*>::iterator dest_it
530 = m_aCPDDestMap.find(rData.m_aPrinterName);
532 if (dest_it == m_aCPDDestMap.end())
533 return PrinterInfoManager::setupJobContextData(rData);
535 std::unordered_map<OUString, Printer>::iterator p_it = m_aPrinters.find(rData.m_aPrinterName);
536 if (p_it == m_aPrinters.end()) // huh ?
538 SAL_WARN("vcl.unx.print", "CPD printer list in disorder, "
539 "no dest for printer "
540 << rData.m_aPrinterName);
541 return;
544 if (p_it->second.m_aInfo.m_pParser == nullptr)
546 // in turn calls createCPDParser
547 // which updates the printer info
548 p_it->second.m_aInfo.m_pParser = PPDParser::getParser(p_it->second.m_aInfo.m_aDriverName);
550 if (p_it->second.m_aInfo.m_aContext.getParser() == nullptr)
552 OUString aPrinter;
553 if (p_it->second.m_aInfo.m_aDriverName.startsWith("CPD:"))
554 aPrinter = p_it->second.m_aInfo.m_aDriverName.copy(4);
555 else
556 aPrinter = p_it->second.m_aInfo.m_aDriverName;
558 p_it->second.m_aInfo.m_aContext = m_aDefaultContexts[aPrinter];
561 rData.m_pParser = p_it->second.m_aInfo.m_pParser;
562 rData.m_aContext = p_it->second.m_aInfo.m_aContext;
563 #else
564 (void)rData;
565 #endif
568 FILE* CPDManager::startSpool(const OUString& rPrintername, bool bQuickCommand)
570 #if ENABLE_DBUS && ENABLE_GIO
571 SAL_INFO("vcl.unx.print",
572 "startSpool: " << rPrintername << " " << (bQuickCommand ? "true" : "false"));
573 if (m_aCPDDestMap.find(rPrintername) == m_aCPDDestMap.end())
575 SAL_INFO("vcl.unx.print", "defer to PrinterInfoManager::startSpool");
576 return PrinterInfoManager::startSpool(rPrintername, bQuickCommand);
578 OUString aTmpURL, aTmpFile;
579 osl_createTempFile(nullptr, nullptr, &aTmpURL.pData);
580 osl_getSystemPathFromFileURL(aTmpURL.pData, &aTmpFile.pData);
581 OString aSysFile = OUStringToOString(aTmpFile, osl_getThreadTextEncoding());
582 FILE* fp = fopen(aSysFile.getStr(), "w");
583 if (fp)
584 m_aSpoolFiles[fp] = aSysFile;
586 return fp;
587 #else
588 (void)rPrintername;
589 (void)bQuickCommand;
590 return nullptr;
591 #endif
594 #if ENABLE_DBUS && ENABLE_GIO
595 void CPDManager::getOptionsFromDocumentSetup(const JobData& rJob, bool bBanner,
596 const OString& rJobName, int& rNumOptions,
597 GVariant** arr)
599 GVariantBuilder* builder;
600 builder = g_variant_builder_new(G_VARIANT_TYPE("a(ss)"));
601 g_variant_builder_add(builder, "(ss)", "job-name", rJobName.getStr());
602 if (rJob.m_pParser == rJob.m_aContext.getParser() && rJob.m_pParser)
604 std::size_t i;
605 std::size_t nKeys = rJob.m_aContext.countValuesModified();
606 ::std::vector<const PPDKey*> aKeys(nKeys);
607 for (i = 0; i < nKeys; i++)
608 aKeys[i] = rJob.m_aContext.getModifiedKey(i);
609 for (i = 0; i < nKeys; i++)
611 const PPDKey* pKey = aKeys[i];
612 const PPDValue* pValue = rJob.m_aContext.getValue(pKey);
613 OUString sPayLoad;
614 if (pValue)
616 sPayLoad = pValue->m_bCustomOption ? pValue->m_aCustomOption : pValue->m_aOption;
618 if (!sPayLoad.isEmpty())
620 OString aKey = OUStringToOString(pKey->getKey(), RTL_TEXTENCODING_ASCII_US);
621 OString aValue = OUStringToOString(sPayLoad, RTL_TEXTENCODING_ASCII_US);
622 if (aKey.equals("Duplex"_ostr))
624 aKey = "sides"_ostr;
626 else if (aKey.equals("Resolution"_ostr))
628 aKey = "printer-resolution"_ostr;
630 else if (aKey.equals("PageSize"_ostr))
632 aKey = "media"_ostr;
634 if (aKey.equals("sides"_ostr))
636 if (aValue.equals("None"_ostr))
638 aValue = "one-sided"_ostr;
640 else if (aValue.equals("DuplexNoTumble"_ostr))
642 aValue = "two-sided-long-edge"_ostr;
644 else if (aValue.equals("DuplexTumble"_ostr))
646 aValue = "two-sided-short-edge"_ostr;
649 g_variant_builder_add(builder, "(ss)", aKey.getStr(), aValue.getStr());
653 if (rJob.m_nCopies > 1)
655 OString aVal(OString::number(rJob.m_nCopies));
656 g_variant_builder_add(builder, "(ss)", "copies", aVal.getStr());
657 rNumOptions++;
658 // TODO: something for collate
659 // Maybe this is the equivalent ipp attribute:
660 if (rJob.m_bCollate)
662 g_variant_builder_add(builder, "(ss)", "multiple-document-handling",
663 "separate-documents-collated-copies");
665 else
667 g_variant_builder_add(builder, "(ss)", "multiple-document-handling",
668 "separate-documents-uncollated-copies");
670 rNumOptions++;
672 if (!bBanner)
674 g_variant_builder_add(builder, "(ss)", "job-sheets", "none");
675 rNumOptions++;
677 if (rJob.m_eOrientation == orientation::Portrait)
679 g_variant_builder_add(builder, "(ss)", "orientation-requested", "portrait");
680 rNumOptions++;
682 else if (rJob.m_eOrientation == orientation::Landscape)
684 g_variant_builder_add(builder, "(ss)", "orientation-requested", "landscape");
685 rNumOptions++;
687 (*arr) = g_variant_new("a(ss)", builder);
688 g_variant_builder_unref(builder);
690 #endif
692 bool CPDManager::endSpool(const OUString& rPrintername, const OUString& rJobTitle, FILE* pFile,
693 const JobData& rDocumentJobData, bool bBanner, const OUString& rFaxNumber)
695 bool success = false;
696 #if ENABLE_DBUS && ENABLE_GIO
697 SAL_INFO("vcl.unx.print", "endSpool: " << rPrintername << "," << rJobTitle
698 << " copy count = " << rDocumentJobData.m_nCopies);
699 std::unordered_map<OUString, CPDPrinter*>::iterator dest_it = m_aCPDDestMap.find(rPrintername);
700 if (dest_it == m_aCPDDestMap.end())
702 SAL_INFO("vcl.unx.print", "defer to PrinterInfoManager::endSpool");
703 return PrinterInfoManager::endSpool(rPrintername, rJobTitle, pFile, rDocumentJobData,
704 bBanner, rFaxNumber);
707 std::unordered_map<FILE*, OString, FPtrHash>::const_iterator it = m_aSpoolFiles.find(pFile);
708 if (it != m_aSpoolFiles.end())
710 fclose(pFile);
711 rtl_TextEncoding aEnc = osl_getThreadTextEncoding();
712 OString sJobName(OUStringToOString(rJobTitle, aEnc));
713 if (!rFaxNumber.isEmpty())
715 sJobName = OUStringToOString(rFaxNumber, aEnc);
717 OString aSysFile = it->second;
718 CPDPrinter* pDest = dest_it->second;
719 GVariant* ret;
720 gint job_id;
721 int nNumOptions = 0;
722 GVariant* pArr = nullptr;
723 getOptionsFromDocumentSetup(rDocumentJobData, bBanner, sJobName, nNumOptions, &pArr);
724 ret = g_dbus_proxy_call_sync(
725 pDest->backend, "printFile",
726 g_variant_new("(ssi@a(ss))", (pDest->id), aSysFile.getStr(), nNumOptions, pArr),
727 G_DBUS_CALL_FLAGS_NONE, -1, nullptr, nullptr);
728 g_variant_get(ret, "(i)", &job_id);
729 if (job_id != -1)
731 success = true;
733 g_variant_unref(ret);
734 unlink(it->second.getStr());
735 m_aSpoolFiles.erase(it);
737 #else
738 (void)rPrintername;
739 (void)rJobTitle;
740 (void)pFile;
741 (void)rDocumentJobData;
742 (void)bBanner;
743 (void)rFaxNumber;
744 #endif
745 return success;
748 bool CPDManager::checkPrintersChanged(bool)
750 #if ENABLE_DBUS && ENABLE_GIO
751 bool bChanged = m_aPrintersChanged;
752 m_aPrintersChanged = false;
753 g_dbus_connection_emit_signal(m_pConnection, nullptr, "/org/libreoffice/PrintDialog",
754 "org.openprinting.PrintFrontend", "RefreshBackend", nullptr,
755 nullptr);
756 return bChanged;
757 #else
758 return false;
759 #endif
762 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */