Do you own a Debenu Quick PDF Library version 7, 8, 9, 10, 11, 12, 13 or iSEDQuickPDF license? Upgrade to Debenu Quick PDF Library 14 today!

Debenu Quick PDF Library - PDF SDK Community Forum Homepage
Forum Home Forum Home > For Users of the Library > I need help - I can help
  New Posts New Posts RSS Feed - Show Printer Dialog and use options when printing
  FAQ FAQ  Forum Search   Register Register  Login Login

Show Printer Dialog and use options when printing

 Post Reply Post Reply
Author
Message
Kent View Drop Down
Beginner
Beginner


Joined: 15 Mar 16
Location: New Zealand
Status: Offline
Points: 10
Post Options Post Options   Thanks (0) Thanks(0)   Quote Kent Quote  Post ReplyReply Direct Link To This Post Topic: Show Printer Dialog and use options when printing
    Posted: 15 Mar 16 at 4:06AM
Hi,

I want to show the standard windows print dialog - allowing the user to set printer specific options (eg. Duplex, Paper Tray, Booklet Mode, Staple + Fold, etc.) and then use this when printing the PDF.

Something along the lines of below.   However, 'SetPrinterDevModeFromString'  requires a string...

Can I just create a std::wstring  and copy the 'DevMode' to it or is there a special way to do this ?

thanks
Kent.
----------------------------------------------------
PRINTDLG pd;
memset(&pd, 0, sizeof(pd));
pd.lStructSize = sizeof(PRINTDLG);
pd.Flags = PD_RETURNDC | PD_DISABLEPRINTTOFILE | PD_HIDEPRINTTOFILE;
if (PrintDlg(&pd) == TRUE)
{
pdf->SetPrinterDevModeFromString(pd.hDevMode);
int options = pdf->PrintOptions(0,0, L"Printing Test");
pdf->PrintDocument( L"Office", 0, 9999, options);
}
Back to Top
Ingo View Drop Down
Moderator Group
Moderator Group
Avatar

Joined: 29 Oct 05
Status: Offline
Points: 3524
Post Options Post Options   Thanks (0) Thanks(0)   Quote Ingo Quote  Post ReplyReply Direct Link To This Post Posted: 15 Mar 16 at 10:38PM
Hi Kent,

using virtual keycodes could be the easiest solution.
Ctrl+P shows always the printer dialog.

Cheers and welcome here,
Ingo

Cheers,
Ingo

Back to Top
Kent View Drop Down
Beginner
Beginner


Joined: 15 Mar 16
Location: New Zealand
Status: Offline
Points: 10
Post Options Post Options   Thanks (0) Thanks(0)   Quote Kent Quote  Post ReplyReply Direct Link To This Post Posted: 15 Mar 16 at 10:43PM
Hi Ingo,

I'm not sure I understand your answer, sorry.

What I want, is to show the standard Printer dialog  (which I can do) - allow the user to specify settings  (which can be specific to the printer / copier)  and then use the selected settings for the resulting print jobs.

I could query the results from the print dialog, and try to set them using the SDK,  which will work for things like Duplex maybe -  but not the 1000's printer specific settings  (such as booklet, fold & staple, etc).

I figure the 'SetPrinterDevModeFromString' would be the way - but how ?

thanks
Kent.
Back to Top
Kent View Drop Down
Beginner
Beginner


Joined: 15 Mar 16
Location: New Zealand
Status: Offline
Points: 10
Post Options Post Options   Thanks (0) Thanks(0)   Quote Kent Quote  Post ReplyReply Direct Link To This Post Posted: 15 Mar 16 at 10:45PM
Or, is there a way from the SDK to show a Printer Dialog - which is then used for printouts ?

Which I could then store (using the 'GetPrinterDevModeAsString' function) and then used for all future printings using the 'SetPrinterDevModeFromString' ?

Kent.
Back to Top
JensBP View Drop Down
Beginner
Beginner
Avatar

Joined: 19 Dec 06
Location: Denmark
Status: Offline
Points: 9
Post Options Post Options   Thanks (0) Thanks(0)   Quote JensBP Quote  Post ReplyReply Direct Link To This Post Posted: 17 Mar 16 at 10:43AM
Hi Kent
 
In an application we have we use the following code snippets for getting the Printer Settings including Custom setings in a way that the QuickPDF function can use, this code is written in VB.NET so you may have to modify and extrapolate to your development environment and language:
 
Private printerName As String = ""
Private devModeBytes() As Byte
Sub SavePrinterSettings()
    If PrintDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then
        printerName = PrintDialog1.PrinterSettings.PrinterName
        'Get Printer Setup as binary data Byte Array
        Dim hDevMode As IntPtr 'handle to the DEVMODE
        Dim pDevMode As IntPtr 'pointer to the DEVMODE
        Dim devMode As DEVMODE 'the actual DEVMODE structure
        Dim printerSettings As Drawing.Printing.PrinterSettings = PrintDialog1.PrinterSettings
        'Get a handle to a DEVMODE for the printer settings
        hDevMode = printerSettings.GetHdevmode()
        'Obtain a lock on the handle and get an actual pointer so Windows won't
        'move it around while we're futzing with it
        pDevMode = GlobalLock(hDevMode)
        'Marshal the memory at that pointer into our P/Invoke version of DEVMODE
        devMode = DirectCast(Marshal.PtrToStructure(pDevMode, GetType(DEVMODE)), DEVMODE)
        'Read the bytes into a Byte Array
        ReDim devModeBytes(devMode.dmSize + (devMode.dmDriverExtra - 1))
        For i As Integer = 0 To devMode.dmSize + (devMode.dmDriverExtra - 1)
            devModeBytes(i) = Marshal.ReadByte(pDevMode, i)
        Next
        'Unlock the handle, we're done futzing around with memory
        GlobalUnlock(hDevMode)
        'And to boot, we don't need that DEVMODE anymore, either
        GlobalFree(hDevMode)
       
        'Here we have a devModeBytes array which can be used directly in the QuickPDF function SetPrinterDevModeFromString
        'QuickPDF.SetPrinterDevModeFromString(printDevMode)
    Else
        printerName = ""
    End If
End Sub
'The above code needs some Window API definitions:
<Flags()> _
Enum DM As Integer
    Orientation = &H1
    PaperSize = &H2
    PaperLength = &H4
    PaperWidth = &H8
    Scale = &H10
    Position = &H20
    NUP = &H40
    DisplayOrientation = &H80
    Copies = &H100
    DefaultSource = &H200
    PrintQuality = &H400
    Color = &H800
    Duplex = &H1000
    YResolution = &H2000
    TTOption = &H4000
    Collate = &H8000
    FormName = &H10000
    LogPixels = &H20000
    BitsPerPixel = &H40000
    PelsWidth = &H80000
    PelsHeight = &H100000
    DisplayFlags = &H200000
    DisplayFrequency = &H400000
    ICMMethod = &H800000
    ICMIntent = &H1000000
    MediaType = &H2000000
    DitherType = &H4000000
    PanningWidth = &H8000000
    PanningHeight = &H10000000
    DisplayFixedOutput = &H20000000
End Enum
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _
Public Structure DEVMODE
    Public Const CCHDEVICENAME As Integer = 32
    Public Const CCHFORMNAME As Integer = 32
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=CCHDEVICENAME)> _
    Public dmDeviceName As String
    Public dmSpecVersion As Short
    Public dmDriverVersion As Short
    Public dmSize As Short
    Public dmDriverExtra As Short
    Public dmFields As DM
    Public dmOrientation As Short
    Public dmPaperSize As Short
    Public dmPaperLength As Short
    Public dmPaperWidth As Short
    Public dmScale As Short
    Public dmCopies As Short
    Public dmDefaultSource As Short
    Public dmPrintQuality As Short
    Public dmColor As Short
    Public dmDuplex As Short
    Public dmYResolution As Short
    Public dmTTOption As Short
    Public dmCollate As Short
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=CCHFORMNAME)> _
    Public dmFormName As String
    Public dmLogPixels As Short
    Public dmBitsPerPel As Integer ' Declared wrong in the full framework
    Public dmPelsWidth As Integer
    Public dmPelsHeight As Integer
    Public dmDisplayFlags As Integer
    Public dmDisplayFrequency As Integer
    Public dmICMMethod As Integer
    Public dmICMIntent As Integer
    Public dmMediaType As Integer
    Public dmDitherType As Integer
    Public dmReserved1 As Integer
    Public dmReserved2 As Integer
    Public dmPanningWidth As Integer
    Public dmPanningHeight As Integer
    'Public dmPositionX As Integer ' Using a PointL Struct does not work
    'Public dmPositionY As Integer
End Structure
<DllImport("kernel32.dll", ExactSpelling:=True)> _
Friend Shared Function GlobalFree(ByVal handle As IntPtr) As IntPtr
End Function
<DllImport("kernel32.dll", ExactSpelling:=True)> _
Friend Shared Function GlobalLock(ByVal handle As IntPtr) As IntPtr
End Function
<DllImport("kernel32.dll", ExactSpelling:=True)> _
Friend Shared Function GlobalUnlock(ByVal handle As IntPtr) As IntPtr
End Function
This code snippet has been put together from some other sample code on the QuickPDF forum and some code find using google.
 
The important parts is that the SetPrinterDevModeFromString function (at least in VB.NET) needs a byte array, and how to get this from the Printer Settings object.
 
Regards,
Jens
Back to Top
Ingo View Drop Down
Moderator Group
Moderator Group
Avatar

Joined: 29 Oct 05
Status: Offline
Points: 3524
Post Options Post Options   Thanks (0) Thanks(0)   Quote Ingo Quote  Post ReplyReply Direct Link To This Post Posted: 17 Mar 16 at 9:29PM
Hi Kent,

what i wanna tell you was that it's possible showing the normal printer dialog using virtual keycodes inside your app. But i've read now that you need a lot more ;-)
Regarding different outputformats the SetupCustomPrinter can be a solution:
http://www.debenu.com/docs/pdf_library_reference/SetupCustomPrinter.php

Cheers,
Ingo

Back to Top
Kent View Drop Down
Beginner
Beginner


Joined: 15 Mar 16
Location: New Zealand
Status: Offline
Points: 10
Post Options Post Options   Thanks (0) Thanks(0)   Quote Kent Quote  Post ReplyReply Direct Link To This Post Posted: 17 Mar 16 at 11:04PM
Hi JensBP,

Thanks - that's the approach that I'm trying to take - but haven't yet got the magic touch happening.
Or, is it that Duplex has to be set via the API,  but the other settings via the DEVMODE.

This is what I'm trying to do  (I'll worry about different printer names later).  So far, it doesn't appear to be working.

thanks
Kent.

PRINTDLG pd;
memset(&pd, 0, sizeof(pd));
pd.lStructSize = sizeof(PRINTDLG);
pd.Flags = PD_RETURNDC | PD_DISABLEPRINTTOFILE | PD_HIDEPRINTTOFILE | PD_NOPAGENUMS | PD_NOSELECTION;
if (PrintDlg(&pd) == TRUE)
{
std::string orig; orig = pdf->GetPrinterDevModeToString(L"Office");

DEVMODE * hDevMode = (DEVMODE *)pd.hDevMode;
WORD s = sizeof(DEVMODE) + hDevMode->dmDriverExtra;

std::string devmode; devmode.assign((const char *)pd.hDevMode, s);

pdf->SetPrinterDevModeFromString(devmode);
int options = pdf->PrintOptions(0, 0, L"Printing Test");
pdf->PrintDocument(L"Office", 0, 9999, options);

}



Back to Top
Kent View Drop Down
Beginner
Beginner


Joined: 15 Mar 16
Location: New Zealand
Status: Offline
Points: 10
Post Options Post Options   Thanks (0) Thanks(0)   Quote Kent Quote  Post ReplyReply Direct Link To This Post Posted: 17 Mar 16 at 11:19PM
Hi Ingo,

Yeah - I need to be able to take advantage of photo copier specific features - such as folding / staple / etc.  and it doesn't look like I can control any of these specific functions through the API.

I can see it's possible to control things like duplex, etc.   but I need more.

Kent.
Back to Top
JensBP View Drop Down
Beginner
Beginner
Avatar

Joined: 19 Dec 06
Location: Denmark
Status: Offline
Points: 9
Post Options Post Options   Thanks (0) Thanks(0)   Quote JensBP Quote  Post ReplyReply Direct Link To This Post Posted: 18 Mar 16 at 8:14AM
Hi Kent
 
When I used this advanced functionality in our application it took some time, and I several times saved the DevMode structure to a binary file so I could compare the structure when trying different settings in the Printer settings dialog.
 
I have no experience with C++ and the QuickPDF interface, but just know that when using .NET the DevMode has to be handled as a raw array of bytes in the call to the QuickPDF function.
 
Regards,
Jens
Back to Top
Kent View Drop Down
Beginner
Beginner


Joined: 15 Mar 16
Location: New Zealand
Status: Offline
Points: 10
Post Options Post Options   Thanks (0) Thanks(0)   Quote Kent Quote  Post ReplyReply Direct Link To This Post Posted: 18 Mar 16 at 8:45AM
Hi Jens,

Thanks for the reply - still playing.

In your VB.NET code,  why did you subtract 1 from the declaration  (devMode.dmDriverExtra - 1)  ?

>       ReDim devModeBytes(devMode.dmSize + (devMode.dmDriverExtra - 1))
>       For i As Integer = 0 To devMode.dmSize + (devMode.dmDriverExtra - 1)
>           devModeBytes(i) = Marshal.ReadByte(pDevMode, i)
>       Next

I don't know VB, but assuming the array is zero based - won't this create an array 1 byte too small ?

Kent.


Back to Top
Kent View Drop Down
Beginner
Beginner


Joined: 15 Mar 16
Location: New Zealand
Status: Offline
Points: 10
Post Options Post Options   Thanks (0) Thanks(0)   Quote Kent Quote  Post ReplyReply Direct Link To This Post Posted: 18 Mar 16 at 10:37AM
Hi Jens,

This is where I've got to.  I'm getting the printer name okay - and am fairly certain I have the 'DEVMODE' string - but obviously missing something as it's not making a difference.

Ideas appreciated.
Kent.


//========================================================================================================================================================
#define PRINTERMAX 256
PRINTDLGEX pd2;
std::string devmodestr;
std::wstring printername;

BOOL haveDonePrinterSetup = false;


void initPrinterSettings(void)
{
//  Initialize the PRINTDLGEX structure.
pd2.lStructSize = sizeof(PRINTDLGEX);
pd2.hwndOwner = GetForegroundWindow();
pd2.hDevMode = NULL;
pd2.hDevNames = NULL;
pd2.hDC = NULL;
pd2.Flags = PD_RETURNDC | PD_HIDEPRINTTOFILE | PD_NOCURRENTPAGE | PD_NOSELECTION | PD_COLLATE;
pd2.Flags2 = 0;
pd2.ExclusionFlags = 0;
pd2.nPageRanges = 1;
pd2.nMaxPageRanges = 1;
pd2.nMinPage = 1;
pd2.nMaxPage = 9999;
pd2.nCopies = 1;
pd2.hInstance = 0;
pd2.lpPrintTemplateName = NULL;
pd2.lpCallback = NULL;
pd2.nPropertyPages = 0;
pd2.lphPropertyPages = NULL;
pd2.nStartPage = START_PAGE_GENERAL;
pd2.dwResultAction = 0;
}

//========================================================================================================================================================
static long pdf_PrintDialog(void)
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646844(v=vs.85).aspx

PRINTPAGERANGE pageRanges[2];
pageRanges->nFromPage = 1;
pageRanges->nToPage = 999;
pd2.lpPageRanges = pageRanges;
pd2.hwndOwner = GetForegroundWindow();

HRESULT err = PrintDlgEx(&pd2);
if (err != S_OK)
{
return -1;
}


DWORD action = pd2.dwResultAction;
if (action == PD_RESULT_CANCEL)  return 0;


if (pd2.hDevMode) // get the printer settings   (colour, paper size, etc)
{
DEVMODE * pDevMode;
pDevMode = (DEVMODE*)GlobalLock(pd2.hDevMode);   // Make sure it doesn't get moved while we are trying to copy it.

int dwBytesNeeded = pDevMode->dmSize + pDevMode->dmDriverExtra; // sizeof( DEVMODE ) + dmDriverExtra info
char * str = new char[dwBytesNeeded];

memcpy(str, pDevMode, dwBytesNeeded);

devmodestr.assign(str, dwBytesNeeded);

haveDonePrinterSetup = true;

delete str;

GlobalUnlock(pd2.hDevMode);
}


if (pd2.hDevNames) // get the printer Name
{
DEVNAMES * pDevNames;
pDevNames = (DEVNAMES*)GlobalLock(pd2.hDevNames);

WORD devOS = pDevNames->wDeviceOffset;

long i;
char * pDN = (char *)pDevNames;
wchar_t tmp[PRINTERMAX];
i = 0;
while ((i < (PRINTERMAX - 1)) && (pDN[devOS + i]))
{
tmp = pDN[devOS + i]; i++;
}
tmp = 0; // Copy the 'Printer' name

printername.assign(tmp, i);

GlobalUnlock(pd2.hDevNames);
}

return 1;
}


//========================================================================================================================================================

static void pdf_Print( void )
{
if (haveDonePrinterSetup)
{
std::string tmp;
pdf->SetPrinterDevModeFromString(devmodestr);
}

int options = pdf->PrintOptions(0, 0, L"Printing Test");

pdf->PrintDocument(printername, 0, 9999, options);
}

Back to Top
JensBP View Drop Down
Beginner
Beginner
Avatar

Joined: 19 Dec 06
Location: Denmark
Status: Offline
Points: 9
Post Options Post Options   Thanks (0) Thanks(0)   Quote JensBP Quote  Post ReplyReply Direct Link To This Post Posted: 18 Mar 16 at 1:49PM
Hi Kent
 
Yes VB.NET arrays are zero based but when declaring an array the number specified is the index for the last element, so a Dim or ReDim someArray(someSize) as SomeType has a length of someSize+1 elements.
 
In C based languages the number in the declaration is the number of elements, so this is the difference.
 
Regards,
Jens
Back to Top
JensBP View Drop Down
Beginner
Beginner
Avatar

Joined: 19 Dec 06
Location: Denmark
Status: Offline
Points: 9
Post Options Post Options   Thanks (0) Thanks(0)   Quote JensBP Quote  Post ReplyReply Direct Link To This Post Posted: 18 Mar 16 at 2:03PM
Hi Kent
 
I looked at my code and see one potential difference.
 
In my code the sequence is the following:
 
myPrintOptions = QP.PrintOptions(<some arguments here>)
 
QP.SetPrinterDevModeFromString(devModeString)
 
QP.PrintDocument(<some arguments here>)
 
In your sample the first two staments are switched, maybe the sequence is important?
 
The QP.PrintOptions() maybe modifies the devMode for the active printer?
 
Regards,
Jens
Back to Top
Kent View Drop Down
Beginner
Beginner


Joined: 15 Mar 16
Location: New Zealand
Status: Offline
Points: 10
Post Options Post Options   Thanks (0) Thanks(0)   Quote Kent Quote  Post ReplyReply Direct Link To This Post Posted: 18 Mar 16 at 7:12PM
Hi Jens,

Thanks for taking the time and effort to read through my code.

I didn't believe that would make any difference - but gave it a try anyway with no joys - I'm at the point of trying anything :)

I've emailed Debenu support as well - but they seem to be extremely slow in replying - is this normal ?

Kent.

----------------------
static void pdf_Print( void )
{
int options = pdf->PrintOptions(0, 0, L"Printing Test");

if (haveDonePrinterSetup)
{
std::string tmp;
pdf->SetPrinterDevModeFromString(devmodestr);
}

pdf->PrintDocument(printername, 0, 9999, options);
}
Back to Top
Kent View Drop Down
Beginner
Beginner


Joined: 15 Mar 16
Location: New Zealand
Status: Offline
Points: 10
Post Options Post Options   Thanks (0) Thanks(0)   Quote Kent Quote  Post ReplyReply Direct Link To This Post Posted: 21 Mar 16 at 2:58AM
Hi All,

For anyone else that battles this like I have - I discovered today that the size of my DEVMODE was always 64 bytes smaller than the one returned by 'GetPrinterDevModeToString' returned.

After a bit of research, I discovered this was because I needed to use 'PRINTDLGEXW'  instead of 'PRINTDLGEX'  (ie.  the Unicode version).   (the main difference being 'dmDeviceName' and 'dmFormName ' - both 32 characters - which un unicode explain the extra 64 bytes).

Once I had changed this  (and adjusted my other code appropriately) - it now appears to be working :)

cheers

Kent.
Back to Top
 Post Reply Post Reply
  Share Topic   

Forum Jump Forum Permissions View Drop Down

Forum Software by Web Wiz Forums® version 11.01
Copyright ©2001-2014 Web Wiz Ltd.

Copyright © 2017 Debenu. Debenu Quick PDF Library is a PDF SDK. All rights reserved. AboutContactBlogSupportOnline Store