不能重复使用画笔来绘制文本和矩形

Can not reuse brush to draw both text and rectangle

本文关键字:文本 绘制 画笔 不能      更新时间:2023-10-16

简介和相关信息:

我正在努力学习用XPS文档API打印。

作为一个简单的开始,我决定画一个矩形,以及它下面的一些文本

在经历了官方的例子之后,我终于实现了我的目标。

问题:

基本上,我已经连接了从上面的链接提供的2个代码示例。现在我想对代码进行润色,主要是用单刷绘制矩形和文本。

重写代码后,我得到以下错误:

XPS printing.exe中0x7555D3CF处的首次机会异常:内存位置0x002CEF9C处的Microsoft C++异常:SplException::THResultException。

如果存在此异常的处理程序,则程序可以安全地继续运行。

SSCCEE:

下面是我重写的函数。我已经用适当的评论标记了崩溃点。

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();
}

问题:

如何使用相同的画笔(上例中的xpsStrokeBrush)绘制文本和矩形轮廓?

根据SetStrokeBrushLocal文档:

调用SetStrokeBrushLocal后,笔划笔刷查找键将释放,GetStrokeBrusheLookup将在查找参数中返回NULL指针。

你可以在使用前在刷子上使用Clone

但是,如果你打算重新使用笔刷,那么在那里使用CreateDictionarySetDictionaryLocalAppend笔刷;这将允许您使用CCD_ 7。