1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 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/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 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 . 18 */ 19 20 #include <sal/config.h> 21 22 #include <cstddef> 23 #include <unistd.h> 24 25 #include <unx/cpdmgr.hxx> 26 27 #include <osl/file.h> 28 #include <osl/thread.h> 29 30 #include <rtl/ustrbuf.hxx> 31 #include <sal/log.hxx> 32 33 #include <config_dbus.h> 34 #include <config_gio.h> 35 36 using namespace psp; 37 using namespace osl; 38 39 #if ENABLE_DBUS && ENABLE_GIO 40 // Function to execute when name is acquired on the bus 41 void CPDManager::onNameAcquired (GDBusConnection *connection, 42 const gchar *, 43 gpointer user_data) 44 { 45 gchar* contents; 46 GDBusNodeInfo *introspection_data; 47 48 // Get Interface for introspection 49 g_file_get_contents (FRONTEND_INTERFACE, &contents, nullptr, nullptr); 50 introspection_data = g_dbus_node_info_new_for_xml (contents, nullptr); 51 52 g_dbus_connection_register_object (connection, 53 "/org/libreoffice/PrintDialog", 54 introspection_data->interfaces[0], 55 nullptr, 56 nullptr, /* user_data */ 57 nullptr, /* user_data_free_func */ 58 nullptr); /* GError** */ 59 g_free(contents); 60 g_dbus_node_info_unref(introspection_data); 61 62 CPDManager* current = static_cast<CPDManager*>(user_data); 63 std::vector<std::pair<std::string, gchar*>> backends = current->getTempBackends(); 64 for (auto const& backend : backends) 65 { 66 GDBusProxy *proxy; 67 // Get Interface for introspection 68 g_file_get_contents (BACKEND_INTERFACE, &contents, nullptr, nullptr); 69 introspection_data = g_dbus_node_info_new_for_xml (contents, nullptr); 70 proxy = g_dbus_proxy_new_sync (connection, 71 G_DBUS_PROXY_FLAGS_NONE, 72 introspection_data->interfaces[0], 73 backend.first.c_str(), 74 backend.second, 75 "org.openprinting.PrintBackend", 76 nullptr, 77 nullptr); 78 g_free(backend.second); 79 g_assert (proxy != nullptr); 80 g_dbus_proxy_call(proxy, "ActivateBackend", 81 nullptr, 82 G_DBUS_CALL_FLAGS_NONE, 83 -1, nullptr, nullptr, nullptr); 84 85 g_free(contents); 86 g_object_unref(proxy); 87 g_dbus_node_info_unref(introspection_data); 88 } 89 } 90 91 void CPDManager::onNameLost (GDBusConnection *, 92 const gchar *name, 93 gpointer) 94 { 95 g_message("Name Lost: %s", name); 96 } 97 98 void CPDManager::printerAdded (GDBusConnection *connection, 99 const gchar *sender_name, 100 const gchar *object_path, 101 const gchar *interface_name, 102 const gchar *, 103 GVariant *parameters, 104 gpointer user_data) 105 { 106 CPDManager* current = static_cast<CPDManager*>(user_data); 107 GDBusProxy *proxy; 108 proxy = current->getProxy(sender_name); 109 if (proxy == nullptr) { 110 gchar* contents; 111 GDBusNodeInfo *introspection_data; 112 113 // Get Interface for introspection 114 g_file_get_contents ("/usr/share/dbus-1/interfaces/org.openprinting.Backend.xml", &contents, nullptr, nullptr); 115 introspection_data = g_dbus_node_info_new_for_xml (contents, nullptr); 116 proxy = g_dbus_proxy_new_sync (connection, 117 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, 118 introspection_data->interfaces[0], 119 sender_name, 120 object_path, 121 interface_name, 122 nullptr, 123 nullptr); 124 125 g_free(contents); 126 g_dbus_node_info_unref(introspection_data); 127 std::pair<std::string, GDBusProxy *> new_backend (sender_name, proxy); 128 current->addBackend(new_backend); 129 } 130 CPDPrinter *pDest = static_cast<CPDPrinter *>(malloc(sizeof(CPDPrinter))); 131 pDest->backend = proxy; 132 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)); 133 std::stringstream printerName; 134 printerName << pDest->name << ", " << pDest->backend_name; 135 std::stringstream uniqueName; 136 uniqueName << pDest->id << ", " << pDest->backend_name; 137 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); 138 OUString aPrinterName = OStringToOUString( printerName.str().c_str(), aEncoding ); 139 OUString aUniqueName = OStringToOUString( uniqueName.str().c_str(), aEncoding ); 140 current->addNewPrinter(aPrinterName, aUniqueName, pDest); 141 } 142 143 void CPDManager::printerRemoved (GDBusConnection *, 144 const gchar *, 145 const gchar *, 146 const gchar *, 147 const gchar *, 148 GVariant *parameters, 149 gpointer user_data) 150 { 151 // TODO: Remove every data linked to this particular printer. 152 CPDManager* pManager = static_cast<CPDManager*>(user_data); 153 char* id; 154 char* backend_name; 155 g_variant_get (parameters, "(ss)", &id, &backend_name); 156 std::stringstream uniqueName; 157 uniqueName << id << ", " << backend_name; 158 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); 159 OUString aUniqueName = OStringToOUString( uniqueName.str().c_str(), aEncoding ); 160 std::unordered_map<OUString, CPDPrinter *>::iterator it = pManager->m_aCPDDestMap.find( aUniqueName ); 161 if (it == pManager->m_aCPDDestMap.end()) { 162 SAL_WARN("vcl.unx.print", "CPD trying to remove non-existent printer from list"); 163 return; 164 } 165 pManager->m_aCPDDestMap.erase(it); 166 std::unordered_map<OUString, Printer>::iterator printersIt = pManager->m_aPrinters.find( aUniqueName ); 167 if (printersIt == pManager->m_aPrinters.end()) { 168 SAL_WARN("vcl.unx.print", "CPD trying to remove non-existent printer from m_aPrinters"); 169 return; 170 } 171 pManager->m_aPrinters.erase(printersIt); 172 } 173 174 GDBusProxy* CPDManager::getProxy(const std::string& target) 175 { 176 std::unordered_map<std::string, GDBusProxy *>::const_iterator it = m_pBackends.find(target); 177 if (it == m_pBackends.end()) { 178 return nullptr; 179 } 180 return it->second; 181 } 182 183 void CPDManager::addBackend(std::pair<std::string, GDBusProxy *> pair) { 184 m_pBackends.insert(pair); 185 } 186 187 void CPDManager::addTempBackend(const std::pair<std::string, gchar*>& pair) 188 { 189 m_tBackends.push_back(pair); 190 } 191 192 std::vector<std::pair<std::string, gchar*>> const & CPDManager::getTempBackends() const { 193 return m_tBackends; 194 } 195 196 void CPDManager::addNewPrinter(const OUString& aPrinterName, const OUString& aUniqueName, CPDPrinter *pDest) { 197 m_aCPDDestMap[aUniqueName] = pDest; 198 bool bSetToGlobalDefaults = m_aPrinters.find( aUniqueName ) == m_aPrinters.end(); 199 Printer aPrinter = m_aPrinters[ aUniqueName ]; 200 if( bSetToGlobalDefaults ) 201 aPrinter.m_aInfo = m_aGlobalDefaults; 202 aPrinter.m_aInfo.m_aPrinterName = aPrinterName; 203 204 // TODO: I don't know how this should work when we have multiple 205 // sources with multiple possible defaults for each 206 // if( pDest->is_default ) 207 // m_aDefaultPrinter = aPrinterName; 208 209 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); 210 aPrinter.m_aInfo.m_aComment = OStringToOUString(pDest->info, aEncoding); 211 aPrinter.m_aInfo.m_aLocation = OStringToOUString(pDest->location, aEncoding); 212 // note: the parser that goes with the PrinterInfo 213 // is created implicitly by the JobData::operator=() 214 // when it detects the NULL ptr m_pParser. 215 // if we wanted to fill in the parser here this 216 // would mean we'd have to send a dbus message for each and 217 // every printer - which would be really bad runtime 218 // behaviour 219 aPrinter.m_aInfo.m_pParser = nullptr; 220 aPrinter.m_aInfo.m_aContext.setParser( nullptr ); 221 std::unordered_map< OUString, PPDContext >::const_iterator c_it = m_aDefaultContexts.find( aUniqueName ); 222 if( c_it != m_aDefaultContexts.end() ) 223 { 224 aPrinter.m_aInfo.m_pParser = c_it->second.getParser(); 225 aPrinter.m_aInfo.m_aContext = c_it->second; 226 } 227 aPrinter.m_aInfo.setDefaultBackend(true); 228 aPrinter.m_aInfo.m_aDriverName = "CPD:" + aUniqueName; 229 m_aPrinters[ aUniqueName ] = aPrinter; 230 } 231 #endif 232 233 /* 234 * CPDManager class 235 */ 236 237 CPDManager* CPDManager::tryLoadCPD() 238 { 239 CPDManager* pManager = nullptr; 240 #if ENABLE_DBUS && ENABLE_GIO 241 static const char* pEnv = getenv("SAL_DISABLE_CPD"); 242 243 if (!pEnv || !*pEnv) { 244 // interface description XML files are needed in 'onNameAcquired()' 245 if (!g_file_test(FRONTEND_INTERFACE, G_FILE_TEST_IS_REGULAR) || 246 !g_file_test(BACKEND_INTERFACE, G_FILE_TEST_IS_REGULAR)) { 247 return nullptr; 248 } 249 250 GDir *dir; 251 const gchar *filename; 252 dir = g_dir_open(BACKEND_DIR, 0, nullptr); 253 if (dir != nullptr) { 254 while ((filename = g_dir_read_name(dir))) { 255 if (pManager == nullptr) { 256 pManager = new CPDManager(); 257 } 258 gchar* contents; 259 std::stringstream filepath; 260 filepath << BACKEND_DIR << '/' << filename; 261 g_file_get_contents(filepath.str().c_str(), &contents, nullptr, nullptr); 262 std::pair<std::string, gchar*> new_tbackend (filename, contents); 263 pManager->addTempBackend(new_tbackend); 264 } 265 g_dir_close(dir); 266 } 267 } 268 #endif 269 return pManager; 270 } 271 272 CPDManager::CPDManager() : 273 PrinterInfoManager( PrinterInfoManager::Type::CPD ) 274 { 275 #if ENABLE_DBUS && ENABLE_GIO 276 // Get Destinations number and pointers 277 GError *error = nullptr; 278 m_pConnection = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, &error); 279 g_assert_no_error (error); 280 #endif 281 } 282 283 CPDManager::~CPDManager() 284 { 285 #if ENABLE_DBUS && ENABLE_GIO 286 g_dbus_connection_emit_signal (m_pConnection, 287 nullptr, 288 "/org/libreoffice/PrintDialog", 289 "org.openprinting.PrintFrontend", 290 "StopListing", 291 nullptr, 292 nullptr); 293 g_dbus_connection_flush_sync (m_pConnection, 294 nullptr, 295 nullptr); 296 g_dbus_connection_close_sync (m_pConnection, 297 nullptr, 298 nullptr); 299 for (auto const& backend : m_pBackends) 300 { 301 g_object_unref(backend.second); 302 } 303 for (auto const& backend : m_aCPDDestMap) 304 { 305 free(backend.second); 306 } 307 #endif 308 } 309 310 311 const PPDParser* CPDManager::createCPDParser( const OUString& rPrinter ) 312 { 313 const PPDParser* pNewParser = nullptr; 314 #if ENABLE_DBUS && ENABLE_GIO 315 OUString aPrinter; 316 317 if( rPrinter.startsWith("CPD:") ) 318 aPrinter = rPrinter.copy( 4 ); 319 else 320 aPrinter = rPrinter; 321 322 std::unordered_map< OUString, CPDPrinter * >::iterator dest_it = 323 m_aCPDDestMap.find( aPrinter ); 324 325 if( dest_it != m_aCPDDestMap.end() ) 326 { 327 CPDPrinter* pDest = dest_it->second; 328 GVariant* ret = nullptr; 329 GError* error = nullptr; 330 ret = g_dbus_proxy_call_sync (pDest->backend, "GetAllOptions", 331 g_variant_new("(s)", (pDest->id)), 332 G_DBUS_CALL_FLAGS_NONE, 333 -1, nullptr, &error); 334 if (ret != nullptr && error == nullptr) 335 { 336 // TODO: These keys need to be redefined to preserve usage across libreoffice 337 // InputSlot - media-col.media-source? 338 // Font - not needed now as it is required only for ps and we are using pdf 339 // Dial? - for FAX (need to look up PWG spec) 340 341 int num_attribute; 342 GVariantIter *iter_attr, *iter_supported_values; 343 g_variant_get (ret, "(ia(ssia(s)))", &num_attribute, &iter_attr); 344 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); 345 PPDKey *pKey = nullptr; 346 OUString aValueName; 347 PPDValue* pValue; 348 std::vector<PPDKey*> keys; 349 std::vector<OUString> default_values; 350 for (int i = 0; i < num_attribute; i++) { 351 char *name, *default_value; 352 int num_supported_values; 353 g_variant_iter_loop(iter_attr, "(ssia(s))", 354 &name, &default_value, 355 &num_supported_values, &iter_supported_values); 356 OUString aOptionName = OStringToOUString( name, aEncoding ); 357 OUString aDefaultValue = OStringToOUString( default_value, aEncoding ); 358 if (aOptionName == "sides") { 359 // Duplex key is used throughout for checking Duplex Support 360 aOptionName = OUString("Duplex"); 361 } else if (aOptionName == "printer-resolution") { 362 // Resolution key is used in places 363 aOptionName = OUString("Resolution"); 364 } else if (aOptionName == "media") { 365 // PageSize key is used in many places 366 aOptionName = OUString("PageSize"); 367 } 368 default_values.push_back(aDefaultValue); 369 pKey = new PPDKey( aOptionName ); 370 371 // If number of values are 0, this is not settable via UI 372 if (num_supported_values > 0 && aDefaultValue != "NA") 373 pKey->m_bUIOption = true; 374 375 bool bDefaultFound = false; 376 377 for (int j = 0; j < num_supported_values; j++) { 378 char* value; 379 g_variant_iter_loop(iter_supported_values, "(s)", &value); 380 aValueName = OStringToOUString( value, aEncoding ); 381 if (aOptionName == "Duplex") { 382 // Duplex key matches against very specific Values 383 if (aValueName == "one-sided") { 384 aValueName = OUString("None"); 385 } else if (aValueName == "two-sided-long-edge") { 386 aValueName = OUString("DuplexNoTumble"); 387 } else if (aValueName == "two-sided-short-edge") { 388 aValueName = OUString("DuplexTumble"); 389 } 390 } 391 392 pValue = pKey->insertValue( aValueName, eQuoted ); 393 if( ! pValue ) 394 continue; 395 pValue->m_aValue = aValueName; 396 397 if (aValueName.equals(aDefaultValue)) { 398 pKey->m_pDefaultValue = pValue; 399 bDefaultFound = true; 400 } 401 402 } 403 // This could be done to ensure default values also appear as options: 404 if (!bDefaultFound && pKey->m_bUIOption) { 405 // pValue = pKey->insertValue( aDefaultValue, eQuoted ); 406 // if( pValue ) 407 // pValue->m_aValue = aDefaultValue; 408 } 409 keys.emplace_back(pKey); 410 } 411 412 pKey = new PPDKey("ModelName"); 413 aValueName = OStringToOUString( "", aEncoding ); 414 pValue = pKey->insertValue( aValueName, eQuoted ); 415 if( pValue ) 416 pValue->m_aValue = aValueName; 417 pKey->m_pDefaultValue = pValue; 418 keys.emplace_back(pKey); 419 420 pKey = new PPDKey("NickName"); 421 aValueName = OStringToOUString( pDest->name, aEncoding ); 422 pValue = pKey->insertValue( aValueName, eQuoted ); 423 if( pValue ) 424 pValue->m_aValue = aValueName; 425 pKey->m_pDefaultValue = pValue; 426 keys.emplace_back(pKey); 427 428 pNewParser = new PPDParser(aPrinter, keys); 429 PrinterInfo& rInfo = m_aPrinters[ aPrinter ].m_aInfo; 430 PPDContext& rContext = m_aDefaultContexts[ aPrinter ]; 431 rContext.setParser( pNewParser ); 432 setDefaultPaper( rContext ); 433 std::vector<OUString>::iterator defit = default_values.begin(); 434 for (auto const& key : keys) 435 { 436 const PPDValue* p1Value = key->getValue( *defit ); 437 if( p1Value ) 438 { 439 if( p1Value != key->getDefaultValue() ) 440 { 441 rContext.setValue( key, p1Value, true ); 442 SAL_INFO("vcl.unx.print", "key " << pKey->getKey() << " is set to " << *defit); 443 } 444 else 445 SAL_INFO("vcl.unx.print", "key " << pKey->getKey() << " is defaulted to " << *defit); 446 } 447 ++defit; 448 } 449 450 rInfo.m_pParser = pNewParser; 451 rInfo.m_aContext = rContext; 452 g_variant_unref(ret); 453 } 454 else 455 { 456 g_clear_error(&error); 457 SAL_INFO("vcl.unx.print", "CPD GetAllOptions failed, falling back to generic driver"); 458 } 459 } 460 else 461 SAL_INFO("vcl.unx.print", "no dest found for printer " << aPrinter); 462 463 if( ! pNewParser ) 464 { 465 // get the default PPD 466 pNewParser = PPDParser::getParser( "SGENPRT" ); 467 SAL_WARN("vcl.unx.print", "Parsing default SGENPRT PPD" ); 468 469 PrinterInfo& rInfo = m_aPrinters[ aPrinter ].m_aInfo; 470 471 rInfo.m_pParser = pNewParser; 472 rInfo.m_aContext.setParser( pNewParser ); 473 } 474 #else 475 (void)rPrinter; 476 #endif 477 return pNewParser; 478 } 479 480 481 void CPDManager::initialize() 482 { 483 // get normal printers, clear printer list 484 PrinterInfoManager::initialize(); 485 #if ENABLE_DBUS && ENABLE_GIO 486 g_bus_own_name_on_connection (m_pConnection, 487 "org.libreoffice.print-dialog", 488 G_BUS_NAME_OWNER_FLAGS_NONE, 489 onNameAcquired, 490 onNameLost, 491 this, 492 nullptr); 493 494 g_dbus_connection_signal_subscribe (m_pConnection, // DBus Connection 495 nullptr, // Sender Name 496 "org.openprinting.PrintBackend", // Sender Interface 497 "PrinterAdded", // Signal Name 498 nullptr, // Object Path 499 nullptr, // arg0 behaviour 500 G_DBUS_SIGNAL_FLAGS_NONE, // Signal Flags 501 printerAdded, // Callback Function 502 this, 503 nullptr); 504 g_dbus_connection_signal_subscribe (m_pConnection, // DBus Connection 505 nullptr, // Sender Name 506 "org.openprinting.PrintBackend", // Sender Interface 507 "PrinterRemoved", // Signal Name 508 nullptr, // Object Path 509 nullptr, // arg0 behaviour 510 G_DBUS_SIGNAL_FLAGS_NONE, // Signal Flags 511 printerRemoved, // Callback Function 512 this, 513 nullptr); 514 515 // remove everything that is not a CUPS printer and not 516 // a special purpose printer (PDF, Fax) 517 std::unordered_map< OUString, Printer >::iterator it = m_aPrinters.begin(); 518 while (it != m_aPrinters.end()) 519 { 520 if( m_aCPDDestMap.find( it->first ) != m_aCPDDestMap.end() ) 521 { 522 ++it; 523 continue; 524 } 525 526 if( !it->second.m_aInfo.m_aFeatures.isEmpty() ) 527 { 528 ++it; 529 continue; 530 } 531 it = m_aPrinters.erase(it); 532 } 533 #endif 534 } 535 536 void CPDManager::setupJobContextData( JobData& rData ) 537 { 538 #if ENABLE_DBUS && ENABLE_GIO 539 std::unordered_map<OUString, CPDPrinter *>::iterator dest_it = 540 m_aCPDDestMap.find( rData.m_aPrinterName ); 541 542 if( dest_it == m_aCPDDestMap.end() ) 543 return PrinterInfoManager::setupJobContextData( rData ); 544 545 std::unordered_map< OUString, Printer >::iterator p_it = 546 m_aPrinters.find( rData.m_aPrinterName ); 547 if( p_it == m_aPrinters.end() ) // huh ? 548 { 549 SAL_WARN("vcl.unx.print", "CPD printer list in disorder, " 550 "no dest for printer " << rData.m_aPrinterName); 551 return; 552 } 553 554 if( p_it->second.m_aInfo.m_pParser == nullptr ) 555 { 556 // in turn calls createCPDParser 557 // which updates the printer info 558 p_it->second.m_aInfo.m_pParser = PPDParser::getParser( p_it->second.m_aInfo.m_aDriverName ); 559 } 560 if( p_it->second.m_aInfo.m_aContext.getParser() == nullptr ) 561 { 562 OUString aPrinter; 563 if( p_it->second.m_aInfo.m_aDriverName.startsWith("CPD:") ) 564 aPrinter = p_it->second.m_aInfo.m_aDriverName.copy( 4 ); 565 else 566 aPrinter = p_it->second.m_aInfo.m_aDriverName; 567 568 p_it->second.m_aInfo.m_aContext = m_aDefaultContexts[ aPrinter ]; 569 } 570 571 rData.m_pParser = p_it->second.m_aInfo.m_pParser; 572 rData.m_aContext = p_it->second.m_aInfo.m_aContext; 573 #else 574 (void)rData; 575 #endif 576 } 577 578 FILE* CPDManager::startSpool( const OUString& rPrintername, bool bQuickCommand ) 579 { 580 #if ENABLE_DBUS && ENABLE_GIO 581 SAL_INFO( "vcl.unx.print", "startSpool: " << rPrintername << " " << (bQuickCommand ? "true" : "false") ); 582 if( m_aCPDDestMap.find( rPrintername ) == m_aCPDDestMap.end() ) 583 { 584 SAL_INFO( "vcl.unx.print", "defer to PrinterInfoManager::startSpool" ); 585 return PrinterInfoManager::startSpool( rPrintername, bQuickCommand ); 586 } 587 OUString aTmpURL, aTmpFile; 588 osl_createTempFile( nullptr, nullptr, &aTmpURL.pData ); 589 osl_getSystemPathFromFileURL( aTmpURL.pData, &aTmpFile.pData ); 590 OString aSysFile = OUStringToOString( aTmpFile, osl_getThreadTextEncoding() ); 591 FILE* fp = fopen( aSysFile.getStr(), "w" ); 592 if( fp ) 593 m_aSpoolFiles[fp] = aSysFile; 594 595 return fp; 596 #else 597 (void)rPrintername; 598 (void)bQuickCommand; 599 return nullptr; 600 #endif 601 } 602 603 #if ENABLE_DBUS && ENABLE_GIO 604 void CPDManager::getOptionsFromDocumentSetup( const JobData& rJob, bool bBanner, const OString& rJobName, int& rNumOptions, GVariant **arr ) 605 { 606 GVariantBuilder *builder; 607 builder = g_variant_builder_new(G_VARIANT_TYPE("a(ss)")); 608 g_variant_builder_add(builder, "(ss)", "job-name", rJobName.getStr()); 609 if( rJob.m_pParser == rJob.m_aContext.getParser() && rJob.m_pParser ) { 610 std::size_t i; 611 std::size_t nKeys = rJob.m_aContext.countValuesModified(); 612 ::std::vector< const PPDKey* > aKeys( nKeys ); 613 for( i = 0; i < nKeys; i++ ) 614 aKeys[i] = rJob.m_aContext.getModifiedKey( i ); 615 for( i = 0; i < nKeys; i++ ) { 616 const PPDKey* pKey = aKeys[i]; 617 const PPDValue* pValue = rJob.m_aContext.getValue( pKey ); 618 OUString sPayLoad; 619 if (pValue) { 620 sPayLoad = pValue->m_bCustomOption ? pValue->m_aCustomOption : pValue->m_aOption; 621 } 622 if (!sPayLoad.isEmpty()) { 623 OString aKey = OUStringToOString( pKey->getKey(), RTL_TEXTENCODING_ASCII_US ); 624 OString aValue = OUStringToOString( sPayLoad, RTL_TEXTENCODING_ASCII_US ); 625 if (aKey.equals("Duplex")) { 626 aKey = OString("sides"); 627 } else if (aKey.equals("Resolution")) { 628 aKey = OString("printer-resolution"); 629 } else if (aKey.equals("PageSize")) { 630 aKey = OString("media"); 631 } 632 if (aKey.equals("sides")) { 633 if (aValue.equals("None")) { 634 aValue = OString("one-sided"); 635 } else if (aValue.equals("DuplexNoTumble")) { 636 aValue = OString("two-sided-long-edge"); 637 } else if (aValue.equals("DuplexTumble")) { 638 aValue = OString("two-sided-short-edge"); 639 } 640 } 641 g_variant_builder_add(builder, "(ss)", aKey.getStr(), aValue.getStr()); 642 } 643 } 644 } 645 if( rJob.m_nPDFDevice > 0 && rJob.m_nCopies > 1 ) 646 { 647 OString aVal( OString::number( rJob.m_nCopies ) ); 648 g_variant_builder_add(builder, "(ss)", "copies", aVal.getStr()); 649 rNumOptions++; 650 // TODO: something for collate 651 // Maybe this is the equivalent ipp attribute: 652 if (rJob.m_bCollate) { 653 g_variant_builder_add(builder, "(ss)", "multiple-document-handling", "separate-documents-collated-copies"); 654 } else { 655 g_variant_builder_add(builder, "(ss)", "multiple-document-handling", "separate-documents-uncollated-copies"); 656 } 657 rNumOptions++; 658 } 659 if( ! bBanner ) 660 { 661 g_variant_builder_add(builder, "(ss)", "job-sheets", "none"); 662 rNumOptions++; 663 } 664 if (rJob.m_eOrientation == orientation::Portrait) { 665 g_variant_builder_add(builder, "(ss)", "orientation-requested", "portrait"); 666 rNumOptions++; 667 } else if (rJob.m_eOrientation == orientation::Landscape) { 668 g_variant_builder_add(builder, "(ss)", "orientation-requested", "landscape"); 669 rNumOptions++; 670 } 671 (*arr) = g_variant_new("a(ss)", builder); 672 g_variant_builder_unref(builder); 673 } 674 #endif 675 676 bool CPDManager::endSpool( const OUString& rPrintername, const OUString& rJobTitle, FILE* pFile, const JobData& rDocumentJobData, bool bBanner, const OUString& rFaxNumber ) 677 { 678 bool success = false; 679 #if ENABLE_DBUS && ENABLE_GIO 680 SAL_INFO( "vcl.unx.print", "endSpool: " << rPrintername << "," << rJobTitle << " copy count = " << rDocumentJobData.m_nCopies ); 681 std::unordered_map< OUString, CPDPrinter * >::iterator dest_it = 682 m_aCPDDestMap.find( rPrintername ); 683 if( dest_it == m_aCPDDestMap.end() ) 684 { 685 SAL_INFO( "vcl.unx.print", "defer to PrinterInfoManager::endSpool" ); 686 return PrinterInfoManager::endSpool( rPrintername, rJobTitle, pFile, rDocumentJobData, bBanner, rFaxNumber ); 687 } 688 689 std::unordered_map< FILE*, OString, FPtrHash >::const_iterator it = m_aSpoolFiles.find( pFile ); 690 if( it != m_aSpoolFiles.end() ) 691 { 692 fclose( pFile ); 693 rtl_TextEncoding aEnc = osl_getThreadTextEncoding(); 694 OString sJobName(OUStringToOString(rJobTitle, aEnc)); 695 if (!rFaxNumber.isEmpty()) 696 { 697 sJobName = OUStringToOString(rFaxNumber, aEnc); 698 } 699 OString aSysFile = it->second; 700 CPDPrinter* pDest = dest_it->second; 701 GVariant* ret; 702 gint job_id; 703 int nNumOptions = 0; 704 GVariant *pArr = nullptr; 705 getOptionsFromDocumentSetup( rDocumentJobData, bBanner, sJobName, nNumOptions, &pArr ); 706 ret = g_dbus_proxy_call_sync (pDest->backend, "printFile", 707 g_variant_new( 708 "(ssi@a(ss))", 709 (pDest->id), 710 aSysFile.getStr(), 711 nNumOptions, 712 pArr 713 ), 714 G_DBUS_CALL_FLAGS_NONE, 715 -1, nullptr, nullptr); 716 g_variant_get (ret, "(i)", &job_id); 717 if (job_id != -1) { 718 success = true; 719 } 720 g_variant_unref(ret); 721 unlink( it->second.getStr() ); 722 m_aSpoolFiles.erase( pFile ); 723 } 724 #else 725 (void)rPrintername; 726 (void)rJobTitle; 727 (void)pFile; 728 (void)rDocumentJobData; 729 (void)bBanner; 730 (void)rFaxNumber; 731 #endif 732 return success; 733 } 734 735 bool CPDManager::checkPrintersChanged( bool ) 736 { 737 #if ENABLE_DBUS && ENABLE_GIO 738 bool bChanged = m_aPrintersChanged; 739 m_aPrintersChanged = false; 740 g_dbus_connection_emit_signal (m_pConnection, 741 nullptr, 742 "/org/libreoffice/PrintDialog", 743 "org.openprinting.PrintFrontend", 744 "RefreshBackend", 745 nullptr, 746 nullptr); 747 return bChanged; 748 #else 749 return false; 750 #endif 751 } 752 753 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 754 755
