1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
25 #include <unx/cpdmgr.hxx>
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>
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
)
43 // Get Interface for introspection
44 if (!g_file_get_contents(FRONTEND_INTERFACE
, &contents
, nullptr, nullptr))
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** */
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,
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
);
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
);
92 proxy
= current
->getProxy(sender_name
);
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
));
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
);
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");
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");
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())
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
179 void CPDManager::addNewPrinter(const OUString
& aPrinterName
, const OUString
& aUniqueName
,
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
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
;
222 CPDManager
* CPDManager::tryLoadCPD()
224 CPDManager
* pManager
= nullptr;
225 #if ENABLE_DBUS && ENABLE_GIO
226 static const char* pEnv
= getenv("SAL_DISABLE_CPD");
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
))
238 const gchar
* filename
;
239 dir
= g_dir_open(BACKEND_DIR
, 0, nullptr);
242 while ((filename
= g_dir_read_name(dir
)))
244 if (pManager
== nullptr)
246 pManager
= new CPDManager();
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
);
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
);
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,
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
);
295 const PPDParser
* CPDManager::createCPDParser(const OUString
& rPrinter
)
297 const PPDParser
* pNewParser
= nullptr;
298 #if ENABLE_DBUS && ENABLE_GIO
301 if (rPrinter
.startsWith("CPD:"))
302 aPrinter
= rPrinter
.copy(4);
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,
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)
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;
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
++)
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
);
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 );
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
);
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
);
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
);
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
);
441 SAL_INFO("vcl.unx.print",
442 "key " << pKey
->getKey() << " is defaulted to " << *defit
);
447 rInfo
.m_pParser
= pNewParser
;
448 rInfo
.m_aContext
= rContext
;
449 g_variant_unref(ret
);
453 g_clear_error(&error
);
454 SAL_INFO("vcl.unx.print", "CPD GetAllOptions failed, falling back to generic driver");
458 SAL_INFO("vcl.unx.print", "no dest found for printer " << aPrinter
);
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
);
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,
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
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
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
))
516 if (!it
->second
.m_aInfo
.m_aFeatures
.isEmpty())
521 it
= m_aPrinters
.erase(it
);
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
);
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)
553 if (p_it
->second
.m_aInfo
.m_aDriverName
.startsWith("CPD:"))
554 aPrinter
= p_it
->second
.m_aInfo
.m_aDriverName
.copy(4);
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
;
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");
584 m_aSpoolFiles
[fp
] = aSysFile
;
594 #if ENABLE_DBUS && ENABLE_GIO
595 void CPDManager::getOptionsFromDocumentSetup(const JobData
& rJob
, bool bBanner
,
596 const OString
& rJobName
, int& rNumOptions
,
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
)
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
);
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
))
626 else if (aKey
.equals("Resolution"_ostr
))
628 aKey
= "printer-resolution"_ostr
;
630 else if (aKey
.equals("PageSize"_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());
658 // TODO: something for collate
659 // Maybe this is the equivalent ipp attribute:
662 g_variant_builder_add(builder
, "(ss)", "multiple-document-handling",
663 "separate-documents-collated-copies");
667 g_variant_builder_add(builder
, "(ss)", "multiple-document-handling",
668 "separate-documents-uncollated-copies");
674 g_variant_builder_add(builder
, "(ss)", "job-sheets", "none");
677 if (rJob
.m_eOrientation
== orientation::Portrait
)
679 g_variant_builder_add(builder
, "(ss)", "orientation-requested", "portrait");
682 else if (rJob
.m_eOrientation
== orientation::Landscape
)
684 g_variant_builder_add(builder
, "(ss)", "orientation-requested", "landscape");
687 (*arr
) = g_variant_new("a(ss)", builder
);
688 g_variant_builder_unref(builder
);
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())
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
;
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
);
733 g_variant_unref(ret
);
734 unlink(it
->second
.getStr());
735 m_aSpoolFiles
.erase(it
);
741 (void)rDocumentJobData
;
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,
762 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */