Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can not reuse brush to draw both text and rectangle

INTRODUCTION AND RELEVANT INFORMATION:

I am trying to learn to print with XPS Document API.

For a simple start, I have decided to draw a rectangle, and some text below it.

After going through the official examples I was able to achieve my goal.

PROBLEM:

Basically, I have concatenated 2 code examples provided from the above link. Now I wanted to polish the code, mainly to use single brush to draw both rectangle and the text.

After rewriting the code, I get the following error:

First-chance exception at 0x7555D3CF in XPS printing.exe: Microsoft C++ exception: SplException::THResultException at memory location 0x002CEF9C.

If there is a handler for this exception, the program may be safely continued.

SSCCEE:

Below is the function I rewrote. I have marked the crashing point with the appropriate comments.

void XPS_TEST()
{
    IXpsOMObjectFactory *xpsFactory;

    HRESULT hr = S_OK;

    // Init COM for this thread if it hasn't 
    //  been initialized, yet.
    hr = CoInitializeEx(0, COINIT_MULTITHREADED);

    hr = CoCreateInstance(
        __uuidof(XpsOMObjectFactory),
        NULL,
        CLSCTX_INPROC_SERVER,
        __uuidof(IXpsOMObjectFactory),
        reinterpret_cast<LPVOID*>(&xpsFactory));

    if (SUCCEEDED(hr))
    {
        // Declare the variables used in this section.
        IOpcPartUri                   *opcPartUri = NULL;
        IXpsOMPackage                 *xpsPackage = NULL;
        IXpsOMDocumentSequence        *xpsFDS = NULL;
        IXpsOMDocumentCollection      *fixedDocuments = NULL;
        IXpsOMDocument                *xpsFD = NULL;
        IXpsOMPage                    *xpsPage = NULL;
        IXpsOMPageReferenceCollection *pageRefs = NULL;
        IXpsOMPageReference           *xpsPageRef = NULL;

        // test size of the document
        XPS_SIZE pageSize = { 200, 200 };

        // Create the package.
        hr = xpsFactory->CreatePackage(&xpsPackage);

        // Create the URI for the fixed document sequence part and then  
        //  create the fixed document sequence
        hr = xpsFactory->CreatePartUri(
            L"/FixedDocumentSequence.fdseq", &opcPartUri);
        hr = xpsFactory->CreateDocumentSequence(opcPartUri, &xpsFDS);
        // Release this URI to reuse the interface pointer.
        if (NULL != opcPartUri) { opcPartUri->Release(); opcPartUri = NULL; }

        // Create the URI for the document part and then create the document.
        hr = xpsFactory->CreatePartUri(
            L"/Documents/1/FixedDocument.fdoc", &opcPartUri);
        hr = xpsFactory->CreateDocument(opcPartUri, &xpsFD);
        // Release this URI to reuse the interface pointer.
        if (NULL != opcPartUri) { opcPartUri->Release(); opcPartUri = NULL; }

        // Create a blank page.
        hr = xpsFactory->CreatePartUri(
            L"/Documents/1/Pages/1.fpage", &opcPartUri);
        hr = xpsFactory->CreatePage(
            &pageSize,                  // Page size
            L"en-US",                   // Page language
            opcPartUri,                 // Page part name
            &xpsPage);
        // Release this URI to reuse the interface pointer.
        if (NULL != opcPartUri) { opcPartUri->Release(); opcPartUri = NULL; }

        // Create a page reference for the page.
        hr = xpsFactory->CreatePageReference(&pageSize, &xpsPageRef);

        // Add the fixed document sequence to the package.
        hr = xpsPackage->SetDocumentSequence(xpsFDS);

        // Get the document collection of the fixed document sequence
        //  and then add the document to the collection.
        hr = xpsFDS->GetDocuments(&fixedDocuments);
        hr = fixedDocuments->Append(xpsFD);

        // Get the page reference collection from the document
        //  and add the page reference and blank page.
        hr = xpsFD->GetPageReferences(&pageRefs);
        hr = pageRefs->Append(xpsPageRef);
        hr = xpsPageRef->SetPage(xpsPage);

        //======================== draw rectangle ====================//

        XPS_COLOR             xpsColor;
        IXpsOMSolidColorBrush *xpsFillBrush = NULL;
        // the brush I want to reuse !!
        IXpsOMSolidColorBrush *xpsStrokeBrush = NULL;

        // Set the fill brush color to RED.
        xpsColor.colorType = XPS_COLOR_TYPE_SRGB;
        xpsColor.value.sRGB.alpha = 0xFF;
        xpsColor.value.sRGB.red = 0xFF;
        xpsColor.value.sRGB.green = 0x00;
        xpsColor.value.sRGB.blue = 0x00;

        // Use the object factory to create the brush.
        hr = xpsFactory->CreateSolidColorBrush(
            &xpsColor,
            NULL,          // color profile resource
            &xpsFillBrush);
        // The color profile resource parameter is NULL because
        //  this color type does not use a color profile resource.

        // Set the stroke brush color to BLACK.
        xpsColor.colorType = XPS_COLOR_TYPE_SRGB;
        xpsColor.value.sRGB.alpha = 0xFF;
        xpsColor.value.sRGB.red = 0x00;
        xpsColor.value.sRGB.green = 0x00;
        xpsColor.value.sRGB.blue = 0x00;

        // Use the object factory to create the brush.
        hr = xpsFactory->CreateSolidColorBrush(
            &xpsColor,
            NULL, // This color type does not use a color profile resource.
            &xpsStrokeBrush);

        // test rectangle
        XPS_RECT                            rect = { 0, 0, 200, 20 };
        IXpsOMGeometryFigure                *rectFigure;
        IXpsOMGeometry                      *imageRectGeometry;
        IXpsOMGeometryFigureCollection      *geomFigureCollection;

        // Define the start point and create an empty figure.
        XPS_POINT                           origin = { rect.x, rect.y };
        hr = xpsFactory->CreateGeometryFigure(&origin, &rectFigure);
        // Define the segments of the geometry figure.
        //  First, define the type of each segment.
        XPS_SEGMENT_TYPE segmentTypes[3] = 
        {
            XPS_SEGMENT_TYPE_LINE,  // each segment is a straight line
            XPS_SEGMENT_TYPE_LINE,
            XPS_SEGMENT_TYPE_LINE
        };

        // Define the x and y coordinates of each corner of the figure
        //  the start point has already been defined so only the 
        //  remaining three corners need to be defined.
        FLOAT segmentData[6] = 
        {
            rect.x, (rect.y + rect.height),
            (rect.x + rect.width), (rect.y + rect.height),
            (rect.x + rect.width), rect.y
        };

        // Describe if the segments are stroked (that is if the segment lines
        //  should be drawn as a line).
        BOOL segmentStrokes[3] = 
        {
            TRUE, TRUE, TRUE // Yes, draw each of the segment lines.
        };

        // Add the segment data to the figure.
        hr = rectFigure->SetSegments(
            3,
            6,
            segmentTypes,
            segmentData,
            segmentStrokes);

        // Set the closed and filled properties of the figure.
        hr = rectFigure->SetIsClosed(TRUE);
        hr = rectFigure->SetIsFilled(TRUE);

        // Create the geometry object.
        hr = xpsFactory->CreateGeometry(&imageRectGeometry);

        // Get a pointer to the figure collection interface of the geometry...
        hr = imageRectGeometry->GetFigures(&geomFigureCollection);

        // ...and then add the figure created above to this geometry.
        hr = geomFigureCollection->Append(rectFigure);
        // If not needed for anything else, release the rectangle figure.
        rectFigure->Release();

        // when done adding figures, release the figure collection. 
        geomFigureCollection->Release();

        IXpsOMPath                *rectPath = NULL;
        IXpsOMVisualCollection    *pageVisuals = NULL;

        // Create the new path object.
        hr = xpsFactory->CreatePath(&rectPath);

        // Add the geometry to the path.
        //  imageRectGeometry is initialized outside of this example.
        hr = rectPath->SetGeometryLocal(imageRectGeometry);

        // Set the short description of the path to provide
        //  a textual description of the object for accessibility.
        hr = rectPath->SetAccessibilityShortDescription(L"Red Rectangle");

        // Set the fill and stroke brushes to use the brushes 
        //  created in the first section.
        hr = rectPath->SetFillBrushLocal(xpsFillBrush);
        hr = rectPath->SetStrokeBrushLocal(xpsStrokeBrush);

        // Get the visual collection of this page and add this path to it.
        hr = xpsPage->GetVisuals(&pageVisuals);
        hr = pageVisuals->Append(rectPath);

        // If not needed for anything else, release the rectangle path.
        rectPath->Release();

        // When finished with the brushes, release the interface pointers.
        if (NULL != xpsFillBrush) xpsFillBrush->Release();
        //******************** I have commented out below code, ****************//
        //******************** because I plan to use the brush to draw text ****//
        //if (NULL != xpsStrokeBrush) xpsStrokeBrush->Release();
        // When done with the geometry interface, release it.
        imageRectGeometry->Release();

        //========================= draw text =====================//
        GUID                          fontNameGuid;
        WCHAR                         guidString[128] = { 0 };
        WCHAR                         uriString[256] = { 0 };

        IStream                       *fontStream = NULL;
        IOpcPartUri                   *fontUri = NULL;
        IXpsOMFontResource            *fontResource = NULL;

        // Create font stream. 
        hr = xpsFactory->CreateReadOnlyStreamOnFile(
            // I have hardcoded Arial here, just for testing
            L"C:\\Windows\\Fonts\\Arial.ttf",
            &fontStream); 

        // Create new obfuscated part name for this resource using a GUID.
        hr = CoCreateGuid(&fontNameGuid);
        hr = StringFromGUID2(
            fontNameGuid,
            guidString,
            ARRAYSIZE(guidString));

        // Create a URI string for this font resource that will place 
        //  the font part in the /Resources/Fonts folder of the package.
        wcscpy_s(uriString, ARRAYSIZE(uriString), L"/Resources/Fonts/");

        // Create the part name using the GUID string as the name and 
        //  ".odttf" as the extension GUID string start and ends with 
        //  curly braces so they are removed.
        wcsncat_s(uriString, ARRAYSIZE(uriString),
            guidString + 1, wcslen(guidString) - 2);
        wcscat_s(uriString, ARRAYSIZE(uriString), L".odttf");

        // Create the font URI interface.
        hr = xpsFactory->CreatePartUri(
            uriString,
            &fontUri);
        // Create the font resource.
        hr = xpsFactory->CreateFontResource(
            fontStream,
            XPS_FONT_EMBEDDING_OBFUSCATED,
            fontUri,
            FALSE,     // isObfSourceStream
            &fontResource);
        if (NULL != fontUri) fontUri->Release();

        LPCWSTR unicodeString = L"Test string";

        // move test string below our rectangle
        origin.y += 30.0f;

        FLOAT                   fontEmSize = 7.56f;
        IXpsOMGlyphsEditor      *glyphsEditor = NULL;
        IXpsOMGlyphs            *xpsGlyphs = NULL;

        // Create a new Glyphs object and set its properties.
        hr = xpsFactory->CreateGlyphs(fontResource, &xpsGlyphs);
        hr = xpsGlyphs->SetOrigin(&origin);
        hr = xpsGlyphs->SetFontRenderingEmSize(fontEmSize);
        //*************** I GET A CRASH BELOW !!!! ***************//
        hr = xpsGlyphs->SetFillBrushLocal(xpsStrokeBrush); // <<--- 

        // Some properties are inter-dependent so they
        //    must be changed by using a GlyphsEditor.
        hr = xpsGlyphs->GetGlyphsEditor(&glyphsEditor);
        hr = glyphsEditor->SetUnicodeString(unicodeString);
        hr = glyphsEditor->ApplyEdits();

        // Add the new Glyphs object to the page
        hr = pageVisuals->Append(xpsGlyphs);

        // Release interface pointers.
        if (NULL != xpsGlyphs) xpsGlyphs->Release();
        if (NULL != glyphsEditor) glyphsEditor->Release();
        if (NULL != pageVisuals) pageVisuals->Release();
        //******************** Releasing the brush here *******//
        if (NULL != xpsStrokeBrush) xpsStrokeBrush->Release();

        //========================= write to file ====================//
        hr = xpsPackage->WriteToFile(
            L"C:\\Users\\Smiljkovic\\Desktop\\xpsTest.xps",
            NULL,                    // LPSECURITY_ATTRIBUTES
            FILE_ATTRIBUTE_NORMAL,
            FALSE);                  // Optimize Markup Size
        //========================== cleanup ==================//

        // Release interface pointer
        if (NULL != xpsPage) xpsPage->Release();
        if (NULL != pageRefs) pageRefs->Release();
        if (NULL != fixedDocuments) fixedDocuments->Release();
        if (NULL != xpsPageRef) xpsPageRef->Release();
        if (NULL != xpsFD) xpsFD->Release();
        if (NULL != xpsFDS) xpsFDS->Release();
        if (NULL != xpsPackage) xpsPackage->Release();

        xpsFactory->Release();
    }

    // Uninitialize COM when finished
    CoUninitialize();
}

QUESTION:

How can I use the same brush ( xpsStrokeBrush from the above example ) for drawing both the text and the rectangle outline?

like image 990
AlwaysLearningNewStuff Avatar asked Oct 19 '22 21:10

AlwaysLearningNewStuff


1 Answers

per SetStrokeBrushLocal documentation:

After you call SetStrokeBrushLocal, the stroke brush lookup key is released and GetStrokeBrushLookup returns a NULL pointer in the lookup parameter.

You could use Clone on the brush before using it.

But, if you plan on re-using brushes, then use the CreateDictionary, SetDictionaryLocal and then Append your brush there; which will let you use SetFillBrushLookup.

like image 161
dnozay Avatar answered Nov 13 '22 01:11

dnozay