/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2025, Open Design Alliance (the "Alliance"). // All rights reserved. // // This software and its documentation and related materials are owned by // the Alliance. The software may only be incorporated into application // programs owned by members of the Alliance, subject to a signed // Membership Agreement and Supplemental Software License Agreement with the // Alliance. The structure and organization of this software are the valuable // trade secrets of the Alliance and its suppliers. The software is also // protected by copyright law and international treaty provisions. Application // programs incorporating this software must include the following statement // with their copyright notices: // // This application incorporates Open Design Alliance software pursuant to a license // agreement with Open Design Alliance. // Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance. // All rights reserved. // // By use of this software, its documentation or related materials, you // acknowledge and accept the above terms. /////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // // ////////////////////////////////////////////////////////////////////// #include "PdfExportCommon.h" #include "Pdf2dExportView.h" #include "OdRound.h" #include "AbstractViewPE.h" #include "RxVariantValue.h" #include "ColorMapping.h" #include "Gi/GiRasterWrappers.h" #include "Ge/GeScale3d.h" #include "Gi/GiDrawableImpl.h" #include "Ge/GeNurbCurve2d.h" #include "Ge/GeNurbCurve3d.h" #include "Ge/GeCircArc2d.h" #include "Ge/GeEllipArc3d.h" #include "OleItemHandler.h" #include "RxRasterServices.h" #include "MemoryStream.h" #include "PrcContextForPdfExport.h" #include "Pdf2PrcExportParams.h" #include "Ge/GeExtents3d.h" #include "Gs/GsDbRootLinkage.h" #include "Gi/GiPathNode.h" #include "Gi/GiShmDataStorage.h" #include "DynamicLinker.h" #include "DbHyperlink.h" #include "Gs/GsBlockReferenceNode.h" #include "Gs/GsLayerNode.h" #include "Gs/GsBlockNode.h" #include "Gs/GsDisplayContext.h" #include "Pdf2dExportGsCacheSupport.h" #include "PdfExport.h" #include "PdfExportPE.h" #include "../DevInclude/DbRoot/OdDbStub.h" #include "PdfXObjectForm.h" #include "SSet.h" #include "BmpTilesGen.h" #include "ShellDrawable.h" #include "Ge/avgpc.h" #if defined(ODA_WINDOWS) && !defined(_WINRT) && !defined(_WIN32_WCE) #include "../../../Extensions/ExRender/win/GdiBmp.h" #else #define drawWithGdiBmp(pDrawable, pVect, origin, u, v, bitmapWidth, bitmapHeight, rect, bMonochrome) #endif namespace TD_PDF_2D_EXPORT { OdUInt32 clipStatus(const OdGeExtents3d& extents, OdGiConveyorGeometry& geom) { OdGeBoundBlock3d bbChk(extents.minPoint(), extents.maxPoint()); OdUInt32 bbStatus = 0; geom.conveyorBoundaryInfoProc(bbChk, bbStatus); return bbStatus; } bool isFullyClipped(const OdGeExtents3d& extents, OdGiConveyorGeometry& geom) { return GETBIT(clipStatus(extents, geom), OdGiConveyorGeometry::kBoundaryProcClipFull); } bool isPartiallyClipped(const OdGeExtents3d& extents, OdGiConveyorGeometry& geom) { return GETBIT(clipStatus(extents, geom), OdGiConveyorGeometry::kBoundaryProcClip); } class PDF2dExportHLRContext : public OdGiHLRContext { public: PDF2dExportHLRContext(PDF2dExportView& view) : m_view(view) {} void getObscuredColor(OdCmEntityColor& clr) ODRX_OVERRIDE { int obscuredClr = m_view.device()->obscuredColor(); if (obscuredClr == 0 || obscuredClr == 256) { if (m_view.isPlayingMetafile()) clr = m_view.m_objectObscuredColor; //ByBlock and ByLayer value had been resolved and stored in metafile else clr = m_view.getObjectObscuredColor(); //ByBlock and ByLayer can be taken from vectorizer data } else if ((obscuredClr > 0) && (obscuredClr < 256)) clr.setColorIndex(obscuredClr); } void beginHiddenOutput() ODRX_OVERRIDE { m_view.m_bObscuredLinesOutput = true; } void endHiddenOutput() ODRX_OVERRIDE { m_view.m_bObscuredLinesOutput = false; } private: PDF2dExportView& m_view; }; PDF2dExportView::~PDF2dExportView() { delete m_hlrCtx; } OdGeMatrix3d PDF2dExportView::getTransformForMetafileGeometry() const { return metafileTransform(); /*OdGeMatrix3d m; m_pMfPlay->transform(m); return m;*/ } void PDF2dExportView::layerName(OdString& layerName, OdDbStub* layer, OdGiAuxiliaryData* pAuxData) { layerName.empty(); if(!layer) return; OdRxObjectPtr pLayer(userGiContext()->openDrawable(layer)); if(pLayer.isNull()) return; layerName = pAuxData ? OdDbBaseLayerPEPtr(pLayer)->name(pLayer, pAuxData) : OdDbBaseLayerPEPtr(pLayer)->name(pLayer); if(device()->isOffLayersEnabled()) { //Layer from traits is the layer from actual db, in case of Xref we should get the layer from main db OdDbStub* stb = OdDbBaseDatabasePEPtr(drawContext()->giContext().database())->getId(pLayer); const bool bVpFrozen = !view().isLayerVisible(stb ? stb : layer) && device()->exportParamsHolder()->isLayerOriginallyVisible(layerName); //layer may be be frozen in viewport if(bVpFrozen) { layerName += PDF_VPFROZEN_LAYER; if(!device()->isFrozenLayer(layerName)) device()->addFrozenLayer(layerName); } } } void PDF2dExportView::onTraitsModified() { Od2dExportView::onTraitsModified(); if(m_pMetafiler->metafile()) { const OdGiSubEntityTraitsData& entityTraits = effectiveTraits(); if(entityTraits.selectionGeom() && !m_pDetachedOutput) //do not put the selection geometry into metafile { ODA_ASSERT(!m_pDetachedOutput); m_pDetachedOutput = &(output().destGeometry()); output().setDestGeometry(OdGiEmptyGeometry::kVoid); return; } else if(!entityTraits.selectionGeom() && m_pDetachedOutput) { ODA_ASSERT(!!m_pDetachedOutput); output().setDestGeometry(*m_pDetachedOutput); m_pDetachedOutput = NULL; } ODA_ASSERT(!deviceGeom()); //no geometry, recording metafile m_bDisableColorMerge = GETBIT(OdGiBaseVectorizer::drawableAttributes(), OdGiDrawable::kDrawableMergeControlOff); if(m_bDisableColorMerge) { m_pMetafiler->add(new RecDisableColorMerge()); m_bDisableColorMerge = false; } m_pMetafiler->saveTraits(effectiveTraits(), byBlockTraits()); //obscured color by block and bylayer traits if (device()->obscuredLtype() && (view().mode() == OdGsView::kHiddenLine) && (device()->obscuredColor() == 0 || device()->obscuredColor() == 256)) { OdCmEntityColor clr = getObjectObscuredColor(); m_pMetafiler->add(new RecTraitsObscuredColor(clr)); } //m_pMetafiler->flush(); // All next code is used during display only. It's redundant to call it if we process metafile. return; } if(deviceGeom()) exportTraits(); } void PDF2dExportView::passHyperlinkToMetafiler() { if(m_pMetafiler->metafile()) m_pMetafiler->add(new RecHyperlink(*m_curHl)); } void PDF2dExportView::beginMetafile(OdRxObject* pMetafile) { super::beginMetafile(pMetafile); } void PDF2dExportView::endMetafileRecording() { if(deviceGeom() || !m_curHl) { super::endMetafileRecording(); return; } if(!m_pMetafiler->metafile() && gsWriter().isRecordingMetafile()) { onTraitsModified(); //forces creation of a new geometry portion ODA_ASSERT(m_pMetafiler->metafile()); } if(m_pMetafiler->metafile()) { if(m_curHl) { passHyperlinkToMetafiler(); m_curHl = NULL; //this indicates that the data have been processed } } super::endMetafileRecording(); } void PDF2dExportView::playMetafile(const OdRxObject* pMetafile) { //reset disable color merge flag, the next entity will set it, if necessary if(GETBIT(vectorizingFlags(), kDisplaying)) m_bDisableColorMerge = false; if (m_iExportNonGrapchicalInfo) { if(metafilePlayMode() != kMfExtents) // dynamicSubEntHltEnabled m_nSelectionMarker = kNullSubentIndex; OdSaveState ss(m_bPlayingMetafile, true); const OdGiGeometryMetafile::Record* pCurr = dynamic_cast(pMetafile)->firstRecord();//m_pHead; while(pCurr) { const OdGiGeometryMetafile::Record* pTail = pCurr->tail(); const RecAnnot* pAnnot = dynamic_cast(pCurr); if(pAnnot) pAnnot->play(&m_mfPlayEntryPoint.destGeometry(), drawContext()); pCurr = pTail; } } else super::playMetafile(pMetafile); } void fixPdfName(OdString& name) { OdString ret_name; for(int i = 0; i < name.getLength(); ++i) { OdChar c = name.getAt(i); switch(c) { case L'\n': ret_name += "#0A"; break; case L'\t': ret_name += "#09"; break; case L'#': ret_name += "#23"; break; case L'(': ret_name += "#28"; break; case L')': ret_name += "#29"; break; case L'<': ret_name += "#3c"; break; case L'>': ret_name += "#3e"; break; case L'[': ret_name += "#5b"; break; case L']': ret_name += "#5d"; break; case L'{': ret_name += "#7b"; break; case L'}': ret_name += "#7d"; break; case L'/': ret_name += "#2f"; break; case L'\\': ret_name += "#5c"; break; case L' ': ret_name += "#20"; break; case L'~': ret_name += "#7e"; break; case L'!': ret_name += "#21"; break; case L'%': ret_name += "#25"; break; case L'&': ret_name += "#26"; break; case L'@': ret_name += "#40"; break; case L'?': ret_name += "#3f"; break; case L'\'': ret_name += "#60"; break; default: if(c) ret_name += c; break; } } name = ret_name; } OdString PDF2dExportView::currentBlockName(OdGsEntityNode &node, bool& bXref, OdGsBaseContext* pContext) const { OdString blockName; const OdGiDrawableDesc* pDrawableDesc = currentDrawableDesc(); const bool bDisplaying = GETBIT(vectorizingFlags(), kDisplaying); OdRxObjectPtr pBlockRef; if(bDisplaying && pDrawableDesc && pDrawableDesc->persistId) { pBlockRef = userGiContext()->openDrawable(pDrawableDesc->persistId); if(!pBlockRef.isNull()) { OdDbStub* pBlockId = OdDbBaseBlockRefPEPtr(pBlockRef)->blockId(pBlockRef); if(pBlockId) { OdRxObjectPtr pBlock(userGiContext()->openDrawable(pBlockId)); if(!pBlock.isNull()) { blockName = OdDbBaseBlockPEPtr(pBlock)->getName(pBlock); bXref = OdDbBaseBlockPEPtr(pBlock)->isFromExternalReference(pBlock); } } } if(blockName.isEmpty() && !pBlockRef.isNull()) blockName = pDrawableDesc->persistId->getHandle().ascii(); } if(!blockName.isEmpty()) { ODA_ASSERT(node.isReference()); OdGsBlockReferenceNode* pBlockRefNode = static_cast(&node); if(!pBlockRefNode->sharedDefinition(view())) //cannot correctly place not shared blocks blockName.empty(); else { OdGsEntityNode* pAttr = pBlockRefNode->firstAttrib(); if(pAttr) { OdGsDisplayContext* pDispCont = dynamic_cast(pContext); if(pDispCont) pDispCont->setDisableBlockAttributes(true); } OdGsBlockNode *pBlock = pBlockRefNode->blockNode(); OdGsBlockRefNodeDesc nodeDesc; ODA_VERIFY(pBlock->findDef((OdGsSharedRefDefinition*)pBlockRefNode->sharedDefinition(view()), nodeDesc)); OdGeMatrix3d nodeTransform; bool bTr = pBlockRefNode->getSharedTransform(nodeTransform); OdGeMatrix3d matTrans = metafileToDeviceMatrix(); if(bTr) matTrans *= nodeTransform; fixPdfName(blockName); //fix forbidden symbols ////////////////////////////////////////////////////////////////////////// OdGeVector3d viewDir = this->position() - this->target(); OdGeMatrix3d mmm = OdDbBaseBlockRefPEPtr(pBlockRef)->blockTransform(pBlockRef); if(m_MFTransforms.size()) for(size_t i = 0; i < m_MFTransforms.size(); i++) mmm *= m_MFTransforms[(OdUInt32)i]; OdGeVector3d blockNorm = OdDbBaseBlockRefPEPtr(pBlockRef)->blockTransform(pBlockRef).getCsZAxis(); //block reference's normal ///check rotation plane OdGeVector3d blockNormTransformed = blockNorm; blockNormTransformed.transformBy(mmm); OdGePlane rotationPlane(OdGePoint3d(), blockNorm, blockNormTransformed); if (rotationPlane.normal().isZeroLength() || rotationPlane.normal().isParallelTo(viewDir)) { ///check view direction mmm = mmm.inverse(); viewDir.transformBy(mmm); if(!blockNorm.isParallelTo(viewDir) && !blockNorm.isPerpendicularTo(viewDir)) viewDir.set(0., 0., 0.); } else viewDir.set(0., 0., 0.); OdAnsiString lwstr; odDToStr(lwstr.getBufferSetLength(512), device()->currentPdfPageNum(), 'f', 0); lwstr.releaseBuffer(); OdString sPageInd(lwstr); blockName = sPageInd + OdString(L"_") + blockName; //create block name and put it into blocks map device()->findBlockNameByDesc(blockName, nodeDesc, matTrans, viewDir); } } return blockName; } bool PDF2dExportView::playNonGraphicInfo(const OdGsGeomPortion* pPortion, OdGsBaseContext* pContext, OdGsEntityNode &node) { bool bPlayed = false; m_iExportNonGrapchicalInfo++; bPlayed = super::playNestedMetafile(pPortion, pContext, node); m_iExportNonGrapchicalInfo--; return bPlayed; } OdGeMatrix3d PDF2dExportView::processFullBlockTransform() { OdGeMatrix3d fullTransform = metafileToDeviceMatrix(); if(m_MFTransforms.size()) { fullTransform *= m_MFTransforms.last(); m_MFTransforms.removeLast(); //remove last transformation } return fullTransform; } bool PDF2dExportView::processPdfBlocks(const OdGsGeomPortion* pPortion, OdGsBaseContext* pContext, OdGsEntityNode &node, const OdString& blockName, const OdGsLayerNode* pLayer) { if(!pPortion || !pContext) return false; bool bProcessMfLayers = pLayer != 0; bool bPlayed = false; PDFXObjectFormPtr pXobj = device()->findBlock(blockName); //find block in blocks map if(pXobj.isNull()) { pXobj = device()->createBlock(blockName); //create block in pdf document if(bProcessMfLayers) bProcessMfLayers = processLayerFromMf(pLayer->underlyingDrawableId(), true); //process frozen layers if they are here deviceGeom()->suspendStream(); //close layers (except frozen), polylines in current stream //set new pdf stream to draw block there PDFIContentCommands* pPrevStream = deviceGeom()->setPdfContentStream(pXobj); //set current traits to new stream deviceGeom()->flushTraits(); m_pViewportClip->disable(); m_iExportToBlock++; m_MFTransforms.push_back(OdGeMatrix3d::kIdentity); bPlayed = super::playNestedMetafile(pPortion, pContext, node); m_iExportToBlock--; m_pViewportClip->enable(); deviceGeom()->suspendStream(); //close layers, polylines in block etc //return previous pdf stream deviceGeom()->setPdfContentStream(pPrevStream); OdGeMatrix3d transform = processFullBlockTransform(); device()->setPdfBlockTransform(blockName, transform.invert()); //keep transformations from first appearance to apply them (inverted) for next references deviceGeom()->placeBlock(blockName, OdGeMatrix3d::kIdentity, pXobj); if(bProcessMfLayers) processLayerFromMf(pLayer->underlyingDrawableId(), false); //close frozen layer if necessary deviceGeom()->restoreTraits(); //restore previous traits OdGsDisplayContext* pDispCont = dynamic_cast(pContext); if(pDispCont && pDispCont->isBlockAttributesDisabled()) { pDispCont->setDisableBlockAttributes(false); pDispCont->setDisableBlockContent(true); super::playNestedMetafile(pPortion, pContext, node); pDispCont->setDisableBlockContent(false); } //calculate block extents in WCS OdGeExtents3d ext3d = node.extents(); ext3d.transformBy(metafileToDeviceMatrix()); OdGeExtents2d ext2d; ext3d.convert2d(ext2d); //expand BBox to take lw into account PDFRectanglePtr pRect = PDFRectangle::createObject(deviceGeom()->document(), false); double halfLw = pXobj->getMaxLw() / 2; OdGeVector2d lwVect(halfLw, halfLw); ext2d.expandBy(lwVect); ext2d.expandBy(-lwVect); pRect->set(ext2d); pXobj->dictionary()->setBBox(pRect); PDFXObjectForm* pXobj1 = dynamic_cast(pPrevStream); if(pXobj1) //block placed in other block, so expand the parent one pXobj1->expandMaxLw(pXobj->getMaxLw()); } else { m_MFTransforms.push_back(OdGeMatrix3d::kIdentity); bPlayed = playNonGraphicInfo(pPortion, pContext, node); if(!m_iExportNonGrapchicalInfo) { OdGeMatrix3d transform = processFullBlockTransform(); transform *= device()->getPdfBlockTransform(blockName); //apply inverted transformations from the first appearance if(bProcessMfLayers) bProcessMfLayers = processLayerFromMf(pLayer->underlyingDrawableId(), true); //process frozen layers if they are here deviceGeom()->suspendStream(); //close layers(except frozen), polylines in current stream deviceGeom()->placeBlock(blockName, transform, pXobj); if(bProcessMfLayers) processLayerFromMf(pLayer->underlyingDrawableId(), false); //close frozen layer if necessary } else { ODA_ASSERT(m_MFTransforms.size()); m_MFTransforms.removeLast(); } OdGsDisplayContext* pDispCont = dynamic_cast(pContext); if(pDispCont && pDispCont->isBlockAttributesDisabled()) { pDispCont->setDisableBlockAttributes(false); pDispCont->setDisableBlockContent(true); super::playNestedMetafile(pPortion, pContext, node); pDispCont->setDisableBlockContent(false); } } return bPlayed; } bool PDF2dExportView::playNestedMetafile(const OdGsGeomPortion* pPortion, OdGsBaseContext* pContext, OdGsEntityNode &node) { if(!pPortion) return false; OdGsLayerNode* pLayer = pPortion->m_pLayer; bool bRes = (pLayer != 0); bool bPlayed = false; if(bRes) bRes = processLayerFromMf(pLayer->underlyingDrawableId(), true); OdString blockName; bool bXref = false; if(deviceGeom()->exportParams().isUsePdfBlocks() && !m_iSkipXrefBlock && node.extents().isValidExtents() && (view().mode() < OdGsView::kHiddenLine) && //HLR algorithm post-process geometry, so it won't be placed in block !view().isPerspective()) //TODO: processing pdf blocks for perspective views should be implemented later blockName = currentBlockName(node, bXref, pContext); //TODO: provide name to be no longer than 127 characters if(!blockName.isEmpty()) { if(bXref && !deviceGeom()->exportParams().isXrefsAsPdfBlocks()) { m_iSkipXrefBlock++; OdGsDisplayContext* pDispCont = dynamic_cast(pContext); if(pDispCont && pDispCont->isBlockAttributesDisabled()) pDispCont->setDisableBlockAttributes(false); bPlayed = super::playNestedMetafile(pPortion, pContext, node); } else bPlayed = processPdfBlocks(pPortion, pContext, node, blockName, pLayer); } else bPlayed = super::playNestedMetafile(pPortion, pContext, node); if(bRes) processLayerFromMf(pLayer->underlyingDrawableId(), false); if(bXref && m_iSkipXrefBlock) m_iSkipXrefBlock--; return bPlayed; } bool PDF2dExportView::processLayerFromMf(OdDbStub* layer, bool bOpen) { if(!layer) return false; const bool isOffLayersEnabled = device()->isLayersSupported() && device()->isOffLayersEnabled(); if(!isOffLayersEnabled) return false; OdString name; layerName(name, layer); if(!device()->frozenLayerSimulationEnabled() || !device()->isFrozenLayer(name)) return false; if(bOpen) name += PDF_FROZEN_LAYER; else name.empty(); processFrozenLayer(name); return true; } void PDF2dExportView::exportTraits() { ODA_ASSERT(deviceGeom()); //it is either direct output or playing cache ////////////////////////////////////////////////////////////////////////// if (!deviceGeom()->recordingEnabled() && deviceGeom()->currentPdfPage()) deviceGeom()->enableRecording(); OdPsPlotStyleData plotStyle = effectivePlotStyle(); const bool bGenerateObscuredLtype = m_bObscuredLinesOutput && (device()->obscuredLtype() > 1); if (m_bObscuredLinesOutput) { //Can it be that isPlotGeneration() is false? if (bGenerateObscuredLtype && (plotStyle.linetype() == OdPs::kLtpUseObject)) { plotStyle.setLinetype(OdPs::LineType(device()->obscuredLtype() - 1)); if(!userGiContext()->isPlotGeneration() || (userGiContext()->plotStyleType() == OdGiContext::kPsNone)) //if no plotstyle is imposed by gi context then make it adaptive, it looks like ACAD does it plotStyle.setAdaptiveLinetype(true); } } const OdGiSubEntityTraitsData& entityTraits = effectiveTraits(); OdCmTransparency tr = entityTraits.transparency(); OdUInt8 alpha = tr.alpha(); double alphaPercent = tr.alphaPercent(); if (alphaPercent == 0) { deviceGeom()->enableRecording(false, false); //we shouldn't plot completely transparent entities return; } // color if (userGiContext()->isPlotGeneration()) { if (plotStyle.color().isByColor()) deviceGeom()->set_Color(ODTOCOLORREF(plotStyle.color())); else if (plotStyle.color().colorMethod() != OdCmEntityColor::kNone) deviceGeom()->set_ColorIndex(plotStyle.color().colorIndex()); //else continue export with previous color } else { if (entityTraits.trueColor().isByColor()) deviceGeom()->set_Color(ODTOCOLORREF(entityTraits.trueColor())); else if (entityTraits.trueColor().colorMethod() != OdCmEntityColor::kNone) deviceGeom()->set_ColorIndex(entityTraits.color()); //else continue export with previous color } OdString layerName; this->layerName(layerName, entityTraits.layer(), entityTraits.auxData()); if(!layerName.isEmpty() && device()->isFrozenLayer(layerName)) layerName += PDF_FROZEN_LAYER; deviceGeom()->set_Layer(layerName); bool bLwDisplay = view().hasLweights() || view().lineweightToDcScale() != 0.0 || hasLineweightOverride(); double lw = OdDb::kLnWt000; //Can it be that isPlotGeneration() is false? if (userGiContext()->isPlotGeneration() && (userGiContext()->plotStyleType() != OdGiContext::kPsNone || bGenerateObscuredLtype)) { //LineWeight lw = lineweightToPixels(plotStyle.lineweight()); OdPsPlotStyleData zeroLwPlotStyle = plotStyle; zeroLwPlotStyle.setLineweight(0); plotGenerator().setPlotStyle(zeroLwPlotStyle); plotGenerator().enable(true); OdPs::LineEndStyle lnEndStyle = plotStyle.endStyle(); OdPs::LineJoinStyle lnJoinStyle = plotStyle.joinStyle(); if (hasLinestyleOverride()) { lnEndStyle = currentLineweightOverride()->endStyleOverride(); lnJoinStyle = currentLineweightOverride()->joinStyleOverride(); } TD_PDF::PDFLineCap uLineCap = kRoundCap; TD_PDF::PDFLineJoin uLineJoin = kRoundJoin; if (lnEndStyle == OdPs::kLesSquare) uLineCap = kProjectingSquareCap; else if (lnEndStyle == OdPs::kLesButt) uLineCap = kButtCap; if(lnJoinStyle == OdPs::kLjsMiter) uLineJoin = kMiterJoin; else if(lnJoinStyle == OdPs::kLjsBevel) uLineJoin = kBevelJoin; deviceGeom()->setCapJoin(uLineCap, uLineJoin); const OdPs::LineType lt(plotStyle.linetype()); bool ltHasDashes = (lt != OdPs::kLtpSolid && lt != OdPs::kLtpDotted && lt != OdPs::kLtpSparseDot && lt != OdPs::kLtpISODot && lt != OdPs::kLtpUseObject); if(ltHasDashes && uLineCap != kButtCap && !bGenerateObscuredLtype) { plotGenerator().setPlotStyle(plotStyle); } } else { lw = lineweightToPixels(entityTraits.lineWeight()); plotGenerator().enable(false); ////////////////////////////////////////////////////////////////////////// if (entityTraits.lineWeight() != OdDb::kLnWt000 && lw == 0.) { if(deviceGeom()->exportParams().get720DPIMode()) { lw = entityTraits.lineWeight() * lineweightToDcScale(); lw = (OdRoundToLong(10. * lw)) / 10.; } } } if (deviceGeom()->isDrawingContour()) //if (!m_pSolidHatch.isNull()) lw = OdDb::kLnWt000; //for hatch we will draw just contour, so there is no need for lw, even if we won't stroke the path (kFillStrokeCloseRule or other rule) if (deviceGeom()->exportParams().archived() != PDFExportParams::kPDFA_1b) { const bool bPlotTransparency = isPlotTransparency(); if ((!m_bDisableColorMerge) && !isPlayingMetafile()) m_bDisableColorMerge = GETBIT(OdGiBaseVectorizer::drawableAttributes(), OdGiDrawable::kDrawableMergeControlOff); const bool bMergeLines = GETBIT(deviceGeom()->exportParams().exportFlags(), PDFExportParams::kMergeLines); if (bMergeLines) deviceGeom()->dc_transparency(1, 255, PDF2dExportGeometry::kDarken); //cannot cache blend mode and transparency groups now, because they are stackable if (bPlotTransparency/* && !(bDisableColorMerge && alpha == 255 && bMergeLines)*/) { if (bMergeLines && !m_bDisableColorMerge && alpha == 255) deviceGeom()->dc_transparency(1, 255, PDF2dExportGeometry::kRestoreDarken); else deviceGeom()->dc_transparency(alphaPercent, alpha, PDF2dExportGeometry::kTransparency); } else if (bMergeLines) { if (m_bDisableColorMerge) deviceGeom()->dc_transparency(1, 255, PDF2dExportGeometry::kTransparency); else deviceGeom()->dc_transparency(1, 255, PDF2dExportGeometry::kRestoreDarken); } m_bDisableColorMerge = false; } if (bLwDisplay) deviceGeom()->set_LineWeight(!deviceGeom()->exportParams().get720DPIMode() ? OdRoundToLong(lw) : lw); else deviceGeom()->set_LineWeight(OdDb::kLnWt000); // Avoid dynamic sublists creation if 'lwdisplay' disabled, but now regeneration needed if 'lwdisplay' sysvar changed. // secondary color if( GETBIT(entityTraits.drawFlags(), OdGiSubEntityTraits::kDrawContourFill) ) { OdPsPlotStyleData plotStyleSecondary; if( userGiContext()->isPlotGeneration() ) { OdCmEntityColor oldColor = m_effectiveEntityTraitsData.trueColor(); m_effectiveEntityTraitsData.setTrueColor(entityTraits.secondaryTrueColor()); plotStyleSecondary = effectivePlotStyle(); m_effectiveEntityTraitsData.setTrueColor(oldColor); } if( userGiContext()->isPlotGeneration() ) { if (plotStyleSecondary.color().isByColor()) deviceGeom()->set_SecondaryColor(ODTOCOLORREF(plotStyleSecondary.color())); else if (plotStyleSecondary.color().colorMethod() != OdCmEntityColor::kNone) deviceGeom()->set_SecondaryColorIndex(plotStyleSecondary.color().colorIndex()); //else continue export with previous color } else { if (entityTraits.secondaryTrueColor().isByColor()) deviceGeom()->set_SecondaryColor(ODTOCOLORREF(entityTraits.secondaryTrueColor())); else if (entityTraits.secondaryTrueColor().colorMethod() != OdCmEntityColor::kNone) deviceGeom()->set_SecondaryColorIndex(entityTraits.secondaryTrueColor().colorIndex()); //else continue export with previous color } } if(userGiContext()->isPlotGeneration() && userGiContext()->plotStyleType() != OdGiContext::kPsNone) { // CORE-16404 fix OdCmEntityColor oldColor = m_effectiveEntityTraitsData.trueColor(); m_effectiveEntityTraitsData.setTrueColor(plotStyle.color()); deviceGeom()->onTraitsModified(m_effectiveEntityTraitsData); m_effectiveEntityTraitsData.setTrueColor(oldColor); } } class TextOnlyGeometry: public OdGiEmptyGeometry { OdGiConveyorGeometry& m_passGeom; public: TextOnlyGeometry(OdGiConveyorGeometry& passGeom): m_passGeom(passGeom) {} void textProc(const OdGePoint3d& position, const OdGeVector3d& direction, const OdGeVector3d& upVector, const OdChar* msg, OdInt32 numChars, bool raw, const OdGiTextStyle* pTextStyle, const OdGeVector3d* pExtrusion = 0) override { m_passGeom.textProc(position, direction, upVector, msg, numChars, raw, pTextStyle, pExtrusion); } static void connect(OdGiConveyorConnector& ep, OdGiConveyorOutput& o) { //NS: replace destination geometry by TextOnlyGeometry OdGiConveyorGeometry* textGeom = new TextOnlyGeometry(o.destGeometry()); ep.addSourceNode(o); ep.setDestGeometry(*textGeom); } static void disconnect(OdGiConveyorConnector& ep, OdGiConveyorOutput& o) { //NS: restore conveyor and delete TextOnlyGeometry OdGiConveyorGeometry* pTextOnlyGeom = &ep.destGeometry(); ODA_ASSERT(dynamic_cast(pTextOnlyGeom)); ep.removeSourceNode(o); delete pTextOnlyGeom; } }; void PDF2dExportView::switchTextOnlyGeomForDisplay(bool bOn) { if(!exportSearchableTextOnly()) return; if(bOn) { ODA_ASSERT(!m_bTextGeomConnected); TextOnlyGeometry::connect(m_textOnlyEntryPointForDisplay, plotGenerator().output()); m_bTextGeomConnected = true; } else if(m_bTextGeomConnected) { TextOnlyGeometry::disconnect(m_textOnlyEntryPointForDisplay, plotGenerator().output()); m_bTextGeomConnected = false; } } static const int cMfDefaultOptions = OdGiMetafiler::kTextAsText | OdGiMetafiler::kNurbsAsNurbs | OdGiMetafiler::kPlineAsPline | OdGiMetafiler::kDisableImageConversion; void PDF2dExportView::beginViewVectorization() { m_bDisableColorMerge = false; const bool bDisplaying = GETBIT(vectorizingFlags(), kDisplaying); if(bDisplaying) { setDeviceGeom(device()->getGeomForOutput()); //if the view is used only for the hidden text output then set this flag to geometry once, //from the beginning deviceGeom()->setExportHiddenText(exportSearchableTextOnly()); deviceGeom()->setFillRule(PDF2dExportGeometry::defaultFillRule()); if(isGsCacheEnabled()) { m_pViewportClip->setTextsNoClip(true); //disable text clipping with viewport when text is stored in the metafiles m_pViewportClip->setSkipExtentsCheck(true); } } else { m_pMetafiler->setOptions(cMfDefaultOptions); } Od2dExportView::beginViewVectorization(); if(bDisplaying) { ODA_ASSERT(deviceGeom()); //it is either direct output or playing cache deviceGeom()->m_bRasterClipping = false; } const OdUInt16 geomDPI = device()->exportParams().getGeomDPI(); //NS: non-modifying access double scale = 72./(double)geomDPI; if (bDisplaying && GETBIT(device()->exportParams().exportFlags(), PDFExportParams::kZoomToExtentsMode) != 0 && deviceGeom()->m_LwToDcScale != 0 && !hasLweights()) //We need to calculate LW without plotSettings in Z2E mode (CORE-11936) setLineweightToDcScale(deviceGeom()->m_LwToDcScale); if(bDisplaying) { if(giContext().isPlotGeneration() && !updateExtentsOnly()) // In extents mode destGeometry() should be empty. Code below change destGeometry(). So we don't use plot generator in the extents mode. { OdGiPlotGenerator& pgen = plotGenerator(); pgen.setDrawContext(drawContext()); OdInt32 dpi = 72; if(!device()->properties().isNull() && device()->properties()->has(OD_T("DPI"))) dpi = OdRxVariantValue(device()->properties()->getAt(OD_T("DPI")))->getInt32(); pgen.dot_per_inch(dpi); pgen.input().addSourceNode(screenOutput()); pgen.output().setDestGeometry(*deviceGeom()); switchTextOnlyGeomForDisplay(true); // BR : rev6501 // PDF page size is set in mm, therefore deviation was calculated too rough. // Now we take into account dpi of PDF during calculation deviation. const OdGiDeviation& deviationObj = m_pModelToEyeProc->eyeDeviation(); OdGeDoubleArray values(5); // WE should use the same deviation as we have for 'system' PDF printing. // No extra deviation adjustment is needed since v4.03 pgen.dot_per_inch(geomDPI); #if 0 values.append(deviationObj.deviation(kOdGiMaxDevForCircle, OdGePoint3d::kOrigin)/pgen.dot_per_mm()); values.append(deviationObj.deviation(kOdGiMaxDevForCurve, OdGePoint3d::kOrigin)/pgen.dot_per_mm()); values.append(deviationObj.deviation(kOdGiMaxDevForBoundary, OdGePoint3d::kOrigin)/pgen.dot_per_mm()); values.append(deviationObj.deviation(kOdGiMaxDevForIsoline, OdGePoint3d::kOrigin)/pgen.dot_per_mm()); values.append(deviationObj.deviation(kOdGiMaxDevForFacet, OdGePoint3d::kOrigin)/pgen.dot_per_mm()); pgen.setDeviation(values); #endif } ////////////////////////////////////////////////////////////////////////// OdGeDoubleArray dev(5); dev.append(calcScaledDeviation(kOdGiMaxDevForCircle, OdGePoint3d::kOrigin)); dev.append(calcScaledDeviation(kOdGiMaxDevForCurve, OdGePoint3d::kOrigin)); dev.append(calcScaledDeviation(kOdGiMaxDevForBoundary, OdGePoint3d::kOrigin)); dev.append(calcScaledDeviation(kOdGiMaxDevForIsoline, OdGePoint3d::kOrigin)); dev.append(calcScaledDeviation(kOdGiMaxDevForFacet, OdGePoint3d::kOrigin)); deviceGeom()->setDeviation(dev); ////////////////////////////////////////////////////////////////////////// deviceGeom()->StartOptimizator(); OdUInt32 uCapStyle = giContext().lineWeightConfiguration(OdGiContext::kLineCapStyle); OdUInt32 uJointStyle = giContext().lineWeightConfiguration(OdGiContext::kLineJoinStyle); TD_PDF::PDFLineCap uLineCap = kRoundCap; TD_PDF::PDFLineJoin uLineJoin = kRoundJoin; if(uCapStyle == 0) uLineCap = kButtCap; else if(uCapStyle == 1) uLineCap = kProjectingSquareCap; if(uJointStyle == 0) uLineJoin = kMiterJoin; else if(uJointStyle == 1) uLineJoin = kBevelJoin; deviceGeom()->setCapJoin(uLineCap, uLineJoin); // Init GsPdfView class members ... // m_curRGB = device()->getBackgroundColor(); //device()->set_Color(device()->getBackgroundColor()); // m_lineWeight = OdDb::LineWeight(-100); deviceGeom()->set_LineWeight(0); setViewportBorderVisibility(false); deviceGeom()->set_Color(subEntityTraits().trueColor().color()); deviceGeom()->processModifiedTraits(); deviceGeom()->dc_mx(); pushViewportClip(); } else { setViewportBorderVisibility(false); if (mode() > kHiddenLine && device()->exportParams().forceDisableGsDevice() && device()->exportParams().useGsCache()) m_regenerationType = kOdGiStandardDisplay; //to provide wires in metafile } setDrawContextFlags(OdGiConveyorContext::kSpatialFilterSimplPline, false); //notify caller about start of vectorization, to give a chance to set vectorization parameters specific to viewport, if(GETBIT(vectorizingFlags(), kVectorizing) && (threadIndex() == 0)) //NS: beginViewVectorization() is called in the main thread first { if(device()->exportParams().exportReactor()) device()->exportParams().exportReactor()->beginViewVectorization(*this); } } void PDF2dExportView::endViewVectorization() { const bool bDisplaying = GETBIT(vectorizingFlags(), kDisplaying); if(bDisplaying) { ODA_ASSERT(deviceGeom()); //it is either direct output or playing cache if(!deviceGeom()->recordingEnabled() && deviceGeom()->currentPdfPage()) deviceGeom()->enableRecording(); } { OdSaveState sse(m_bObscuredLinesOutput); super::endViewVectorization(); } if(bDisplaying && giContext().isPlotGeneration()) { switchTextOnlyGeomForDisplay(false); plotGenerator().input().removeSourceNode(screenOutput()); screenOutput().setDestGeometry(*deviceGeom()); } if(bDisplaying) { if(isGsCacheEnabled()) { //restore default settings values m_pViewportClip->setTextsNoClip(false); m_pViewportClip->setSkipExtentsCheck(false); } deviceGeom()->StopOptimizator(); popViewportClip(); //reset blend params if (GETBIT(device()->exportParams().exportFlags(), PDFExportParams::kMergeLines) && device()->exportParams().archived() != PDFExportParams::kPDFA_1b) deviceGeom()->dc_transparency(1, 255, PDF2dExportGeometry::kUndefined); deviceGeom()->setExportHiddenText(false); setDeviceGeom(NULL); } if(GETBIT(vectorizingFlags(), kVectorizing)) { //notify caller about end of vectorization if (device()->exportParams().exportReactor() && (threadIndex() == 0)) //NS: endViewVectorization() is called in the main thread last device()->exportParams().exportReactor()->endViewVectorization(*this); } m_pHLRemover->setHLRemoverContext(m_hlrCtx); delete m_hlrCtx; m_hlrCtx = 0; } OdCmEntityColor PDF2dExportView::getObjectObscuredColor() const { switch (device()->obscuredColor()) { case 0: if (byBlockTraits()) return byBlockTraits()->trueColor(); break; case 256: { OdGiSubEntityTraitsData to; to.setLayer(effectiveTraits().layer()); updateLayerTraits(to); if (m_effectiveLayerId) return effectiveLayerTraits().trueColor(); break; } default: ODA_ASSERT(false); } return effectiveTraits().trueColor(); } void PDF2dExportView::loadViewport() { ODA_ASSERT(deviceGeom()); //it is either direct output or playing cache ODA_ASSERT(&(m_pXYProjector->output()) == &(screenOutput())); OdGiConveyorGeometry& xyProjectorDestGeom = screenOutput().destGeometry(); Od2dExportView::loadViewport(); if (m_pHLRemover->enabled() && device()->obscuredLtype()) { m_pHLRemover->keepHidden(true); m_hlrCtx = new PDF2dExportHLRContext(*this); m_pHLRemover->setHLRemoverContext(m_hlrCtx); } OdGsDCPoint min, max; screenRect(min, max); ////////////////////////////////////////////////////////////////////////// OdGsDCRect rr = OdGsDCRect(min, max); rr.normalize(); OdGsDCRect tmp = m_screenRect; tmp.intersectWith(rr); if (!tmp.is_null()) m_screenRect = tmp; const int nrcCountsOut = 4; OdGsDCPointArray nrcPointsOut; OdGeExtents2d devPaperClip; nrcPointsOut.resize(4); nrcPointsOut[0] = m_screenRect.m_min; nrcPointsOut[1].x = m_screenRect.m_max.x; nrcPointsOut[1].y = m_screenRect.m_min.y; nrcPointsOut[2] = m_screenRect.m_max; nrcPointsOut[3].x = m_screenRect.m_min.x; nrcPointsOut[3].y = m_screenRect.m_max.y; devPaperClip.set(OdGePoint2d(double(m_screenRect.m_min.x), double(m_screenRect.m_min.y)), OdGePoint2d(OdRoundToLong(double(m_screenRect.m_max.x)), OdRoundToLong(double(m_screenRect.m_max.y)))); deviceGeom()->m_paperClip = devPaperClip; deviceGeom()->dc_pushClip(1, &nrcCountsOut, nrcPointsOut); //expand clip box because of OdGiOrthoClipper clips the entities on the border if (m_screenRect.m_min.x > 0) m_screenRect.m_min.x--; if (m_screenRect.m_min.y > 0) m_screenRect.m_min.y--; m_screenRect.m_max.x++; m_screenRect.m_max.y++; ////////////////////////////////////////////////////////////////////////// const OdGePoint2d pts[4] = { OdGePoint2d(m_screenRect.m_min.x, m_screenRect.m_min.y), OdGePoint2d((double)m_screenRect.m_max.x, (double)m_screenRect.m_min.y), OdGePoint2d(m_screenRect.m_max.x, m_screenRect.m_max.y), OdGePoint2d((double)m_screenRect.m_min.x, (double)m_screenRect.m_max.y) }; const int ptsCounts = 4; m_pViewportClip->popClipStage(); if (!view().isPerspective()) //workaround till CORE-11417 { OdGeMatrix3d xToDc = view().eyeToScreenMatrix(); setEyeToOutputTransform(xToDc); OdGeVector3d zAxis = OdGeVector3d::kZAxis; OdGeVector3d dcFront(0., 0., frontClip()); OdGeVector3d dcBack(0., 0., backClip()); dcFront.transformBy(xToDc); dcBack.transformBy(xToDc); zAxis.transformBy(xToDc); if (zAxis.z > 0.) { m_pViewportClip->set(ptsCounts, pts, isBackClipped(), dcBack.z, isFrontClipped(), dcFront.z); } else { m_pViewportClip->set(ptsCounts, pts, isFrontClipped(), dcFront.z, isBackClipped(), dcBack.z); } m_pXYProjector->enable(); } else { m_pViewportClip->pushClipStage(1, &ptsCounts, 4, pts); } OdGeDoubleArray dev(5); dev.append(calcScaledDeviation(kOdGiMaxDevForCircle, OdGePoint3d::kOrigin)); dev.append(calcScaledDeviation(kOdGiMaxDevForCurve, OdGePoint3d::kOrigin)); dev.append(calcScaledDeviation(kOdGiMaxDevForBoundary, OdGePoint3d::kOrigin)); dev.append(calcScaledDeviation(kOdGiMaxDevForIsoline, OdGePoint3d::kOrigin)); dev.append(calcScaledDeviation(kOdGiMaxDevForFacet, OdGePoint3d::kOrigin)); m_pViewportClip->setDeviation(dev); //m_pViewportClip->disable(); if(view().isPerspective()) { if(giContext().isPlotGeneration() && &xyProjectorDestGeom != deviceGeom() && &xyProjectorDestGeom != &(OdGiEmptyGeometry::kVoid)) { // PDF2dExportView::beginViewVectorization() sets the destination geometry of the XY projector output // to the plot generator via pgen.input().addSourceNode(screenOutput()). // Od2dExportView::loadViewport() sets the destination geometry of the XY projector output // to Od2dExportDevice via m_pXYProjector->output().setDestGeometry(*device()). // Thus it is necessary to restore here the destination geometry of the XY projector output // to the plot generator to not skip the plot generator during vectorization. ODA_ASSERT(&(screenOutput().destGeometry()) == deviceGeom()); OdGiPlotGenerator& pgen = plotGenerator(); pgen.input().removeSourceNode(screenOutput()); ODA_ASSERT(&(screenOutput().destGeometry()) == &(OdGiEmptyGeometry::kVoid)); pgen.input().addSourceNode(screenOutput()); ODA_ASSERT(&(screenOutput().destGeometry()) != deviceGeom()); ODA_ASSERT(&(screenOutput().destGeometry()) != &(OdGiEmptyGeometry::kVoid)); } m_pXYProjector->disable(); } } void savePalleteFromDeviceToParams(PDF2PRCExportParams ¶ms, const OdGsBaseVectorizeDevice &device) { // get color pallet from rendering device int numcolors; OdUInt64 ppallete = (OdUInt64) device.getLogicalPalette(numcolors); params.m_extraOptions->putAt("Pallet", OdRxVariantValue(ppallete)); params.m_extraOptions->putAt("NumColors", OdRxVariantValue((OdUInt32)numcolors)); } long OdSafeRoundToLong(double a) { if (a >= 0.) { a += .5; if (a > double(LONG_MAX)) return LONG_MAX; return long(a); } else { a -= .5; if (a < double(LONG_MIN)) return LONG_MIN; return long(a); } } void PDF2dExportView::pushViewportClip() { long min_x, max_x, min_y, max_y; OdGsDCPoint wtMin, wtMax; screenRect(wtMin, wtMax); m_screenRect = OdGsDCRect(wtMin, wtMax); m_screenRect.normalize(); OdGsDCRect outRect = device()->outputRect(); outRect.normalize(); m_screenRect.intersectWith(outRect); if (device()->m_clipPoints.size() == 4) //for now there is always rectangular clipping { OdGePoint2dArray clipPoints = device()->m_clipPoints; int nrcCountsOut = 4; //find min & max points of clip rectangle min_x = static_cast(clipPoints[0].x); max_x = static_cast(clipPoints[0].x); min_y = static_cast(clipPoints[0].y); max_y = static_cast(clipPoints[0].y); for (int f = 1; f < nrcCountsOut - 1; ++f) { if (clipPoints[f].x < min_x) min_x = static_cast(clipPoints[f].x); else if (clipPoints[f].x > max_x) max_x = static_cast(clipPoints[f].x); if (clipPoints[f].y < min_y) min_y = static_cast(clipPoints[f].y); else if (clipPoints[f].y > max_y) max_y = static_cast(clipPoints[f].y); } OdGsDCRect clipRect(OdGsDCPoint(min_x, min_y), OdGsDCPoint(max_x, max_y)); OdGePoint2d crMinP(min_x, min_y), crMaxP(max_x, max_y), srMinP(m_screenRect.m_min.x, m_screenRect.m_min.y), srMaxP(m_screenRect.m_max.x, m_screenRect.m_max.y); //if difference between these points is just 1 unit (distanceTo <= sqrt(2)), then additional clip is not needed if (!crMinP.isEqualTo(srMinP, sqrt(2.)) || !crMaxP.isEqualTo(srMaxP, sqrt(2.))) m_screenRect.intersectWith(clipRect); if (m_screenRect.is_null()) //screen rect is not intersecting with clip rect m_screenRect = OdGsDCRect(0, 0, 1, 1); } if(isNonRectClipped()) { OdGsDCPointArray nrcPoints; OdIntArray nrcCounts; viewportClipRegion(nrcCounts, nrcPoints); deviceGeom()->dc_pushClip(nrcCounts.size(), nrcCounts.getPtr(), nrcPoints); m_bAdditionalClip = true; } //temporarily fix for screen rect (till translation matrix (cm) is not added) OdUInt16 geomDPI = device()->exportParams().getGeomDPI(); double scale = 72. / (double)geomDPI; m_screenRect.m_min.x = static_cast(scale * m_screenRect.m_min.x); m_screenRect.m_min.y = static_cast(scale * m_screenRect.m_min.y); if (m_screenRect.m_max.x > 0) m_screenRect.m_max.x = OdSafeRoundToLong(scale * m_screenRect.m_max.x) + 1; //we have to expand clipping borders a bit if (m_screenRect.m_max.y > 0) m_screenRect.m_max.y = OdSafeRoundToLong(scale * m_screenRect.m_max.y) + 1; if (m_screenRect.m_min.x > 0) m_screenRect.m_min.x -= 1; if (m_screenRect.m_min.y > 0) m_screenRect.m_min.y -= 1; m_screenRect.m_min.x = static_cast(m_screenRect.m_min.x / scale); m_screenRect.m_min.y = static_cast(m_screenRect.m_min.y / scale); m_screenRect.m_max.x = static_cast(m_screenRect.m_max.x / scale); m_screenRect.m_max.y = static_cast(m_screenRect.m_max.y / scale); // The code below was moved in loadViewport() to provide more accurate clipping // const int nrcCountsOut = 4; // OdGsDCPointArray nrcPointsOut; // OdGeExtents2d devPaperClip; // nrcPointsOut.resize(4); // nrcPointsOut[0] = m_screenRect.m_min; // nrcPointsOut[1].x = m_screenRect.m_max.x; // nrcPointsOut[1].y = m_screenRect.m_min.y; // nrcPointsOut[2] = m_screenRect.m_max; // nrcPointsOut[3].x = m_screenRect.m_min.x; // nrcPointsOut[3].y = m_screenRect.m_max.y; // // devPaperClip.set(OdGePoint2d(double(m_screenRect.m_min.x), double(m_screenRect.m_min.y)), // OdGePoint2d(OdRoundToLong(double(m_screenRect.m_max.x)), OdRoundToLong(double(m_screenRect.m_max.y)))); // // device()->m_paperClip = devPaperClip; // device()->dc_pushClip(1, &nrcCountsOut, nrcPointsOut); } void PDF2dExportView::popViewportClip() { if (m_bAdditionalClip) { deviceGeom()->dc_popClip(); m_bAdditionalClip = false; } deviceGeom()->dc_popClip(); deviceGeom()->m_paperClip = OdGeExtents2d(); } OdGiPlotGenerator& PDF2dExportView::plotGenerator() { ODA_ASSERT(deviceGeom()); //it is either direct output or playing cache if(m_pPlotstyler.isNull()) { m_pPlotstyler = OdGiPlotGenerator::createObject(); OdGeDoubleArray devs(5,1); devs.insert(devs.end(), 5, .5); m_pPlotstyler->setDeviation(devs); m_pPlotstyler->setExternalEllipseOut(deviceGeom()); } return *m_pPlotstyler; } bool PDF2dExportView::viewExtents(OdGeBoundBlock3d& extents) const { //see CORE-9754: //Geometry located at Frozen layer shouldn't be included to "extents". //in case if some geometry out of pdf paper area, this geometry will not be exported to .pdf. bool bFrozenLayersExist = device()->isLayersSupported() && device()->isOffLayersEnabled() && device()->frozenLayerSimulationEnabled(); if(isGsCacheEnabled() && !bFrozenLayersExist) { // #CORE-16421 : Generate cache for current viewport if it is not already available. PDF2dExportDevice *pDevice = (PDF2dExportDevice*)device(); if(pDevice) { // Actually we can avoid hiding of additional views and generate entire graphics cache in result, but // typically extents acquired for single view, so in this case we will require rebuilding of entire cache // graphics, which is redundant. OdIntArray unhideViews; for(int nView = 0; nView < pDevice->numViews(); nView++) { if(pDevice->viewAt(nView) != this && pDevice->viewAt(nView)->isVisible()) unhideViews.push_back(nView), pDevice->viewAt(nView)->hide(); } // Initialize MtRegen and call updateGeometry(). OdSaveStateFlag(OdUInt32, eEnableUpdateExtentsOnly, _ssVectorizationOnly, pDevice->m_vectPerfData.m_options, true); //pDevice->OdGsBaseVectorizeDevice::update(NULL); // Here we can enable parallel vectorization, like this is done in OdGsBaseVectorizeDevice::update, if it will be required in future. pDevice->doUpdateGeometry(); // Show previously hidden views for(unsigned nHide = 0; nHide < unhideViews.size(); nHide++) pDevice->viewAt(unhideViews.getPtr()[nHide])->show(); } // Now we can run standard viewExtents, which now will be able to get extents from already pre-built graphics cache. } bool b = Od2dExportView::viewExtents(extents); OdGeExtents3d ext(extents.minPoint(), extents.maxPoint()); if(ext.isValidExtents()) { if(OdEqual(ext.minPoint().y, ext.maxPoint().y)) //just one point or horizontal line in drawing { extents.extend(OdGePoint3d(ext.minPoint().x, ext.minPoint().y - 1, ext.minPoint().z)); extents.extend(OdGePoint3d(ext.maxPoint().x, ext.maxPoint().y + 1, ext.maxPoint().z)); } if(OdEqual(ext.minPoint().x, ext.maxPoint().x)) //just one point or vertical line in drawing { extents.extend(OdGePoint3d(ext.minPoint().x - 1, ext.minPoint().y, ext.minPoint().z)); extents.extend(OdGePoint3d(ext.maxPoint().x + 1, ext.maxPoint().y, ext.maxPoint().z)); } b = true; } return b; } void invalidateViewModelChanges(OdGsViewImpl& view) { if(view.cachedDrawables() != 0) { // Get list of models OdGsModelArray models = view.getModelList(); for(OdUInt32 nModel = 0; nModel < models.size(); nModel++) { // For each model we can check aware flags difference from previous cache building pass and invalidate // parts of cache, which is influenced by view changes. OdGsBaseModel *pModel = static_cast(models.at(nModel)); if(pModel->isViewPropsValid(view.localViewportId(pModel))) { ViewProps viewProps; viewProps.set(view); const OdUInt32 propsDiff = viewProps.difference(pModel->viewProps(view.localViewportId(pModel))); if(propsDiff) pModel->invalidate(view, propsDiff); } } } } #define ENABLE_HATCH_AS_BITMAP //#ifdef ENABLE_HATCH_AS_BITMAP static OdGsDevicePtr getRenderingDevice() { OdGsModulePtr pGs; try { pGs = odrxDynamicLinker()->loadModule(OdWinBitmapModuleName, false); } catch (...) { } if (pGs.isNull()) return OdGsDevicePtr(); OdGsDevicePtr pDevice = pGs->createBitmapDevice(); // AMark : enable precision triangles rendering mode for bitmap device OdRxDictionaryPtr pDevProps = pDevice->properties(); if (!pDevProps.isNull()) { const OdString pm(OD_T("PrecisionMode")); const OdString bpp(OD_T("BitPerPixel")); if (pDevProps->has(pm)) pDevProps->putAt(pm, OdRxVariantValue(true)); if(pDevProps->has(bpp)) pDevice->properties()->putAt(bpp, OdRxVariantValue(OdUInt32(24))); } ODCOLORREF bg = ODRGB(255, 255, 255); // palette background const ODCOLORREF* palette = odcmAcadPalette(bg); ODGSPALETTE pPalCpy; pPalCpy.insert(pPalCpy.begin(), palette, palette + 256); pDevice->setLogicalPalette(pPalCpy.asArrayPtr(), 256); return pDevice; } static OdGeNurbCurve3d* convert2dto3d(const OdGeNurbCurve2d* pNc2d) { OdGePoint3dArray ctrlPts; ctrlPts.resize(pNc2d->numControlPoints()); OdGeDoubleArray weights; weights.resize(pNc2d->numWeights()); ODA_ASSERT(weights.size()==0 || weights.size()==ctrlPts.size()); for(OdInt32 i = 0; i < pNc2d->numControlPoints(); i ++) { ctrlPts[i].x = pNc2d->controlPointAt(i).x; ctrlPts[i].y = pNc2d->controlPointAt(i).y; ctrlPts[i].z = 0.0; if(pNc2d->numWeights()) weights[i] = pNc2d->weightAt(i); } return new OdGeNurbCurve3d(pNc2d->degree(), pNc2d->knots(), ctrlPts, weights); } static OdGeNurbCurve3d* ellipse2dtoNurbs3d(const OdGeEllipArc2d& arc) { OdGeEllipArc3d arc3d; OdGePoint3d center(arc.center().x, arc.center().y, 0.); OdGeVector3d axisMajor(arc.majorAxis().x, arc.majorAxis().y, 0.); OdGeVector3d axisMinor(arc.minorAxis().x, arc.minorAxis().y, 0.); arc3d.set(center, axisMajor, axisMinor, arc.majorRadius(), arc.minorRadius(), arc.startAng(), arc.endAng()); return new OdGeNurbCurve3d(arc3d); } #ifdef _DEBUG static void checkMetafile(OdGiGeometryMetafile* pMf, int& contourCnt) { for(OdGiGeometryMetafile::Record* p = pMf->firstRecord(); p; p = p->tail()) { RecDrawingContour* pCont = dynamic_cast(p); if(pCont) { if(contourCnt == 0) { ODA_ASSERT(pCont->isStart()); contourCnt++; } else { ODA_ASSERT(!pCont->isStart()); contourCnt--; } } } } static void checkJointMetafile(OdGiGeometryMetafile* pMf1, OdGiGeometryMetafile* pMf2) { int contourCnt = 0; checkMetafile(pMf1, contourCnt); if(pMf2 && (pMf2 != pMf1)) { ODA_ASSERT(false); checkMetafile(pMf2, contourCnt); } ODA_ASSERT(!contourCnt); } #endif static OdGeExtents3d calcExtents(OdInt32 numVertices, const OdGePoint3d* vertexList) { OdGeExtents3d extents; for(OdInt32 f = 0; f < numVertices; ++f) extents.addPoint(vertexList[f]); return extents; } void PDF2dExportView::calculateViewportClip(OdGePoint2dArray& clipPoints) { clipPoints.clear(); double scale = (double)device()->exportParams().getGeomDPI() / 72.; OdIntArray counts; OdGePoint2dArray pnts; bool bClipLowerZ, bClipUpperZ; double dLowerZ, dUpperZ; m_pViewportClip->get(counts, pnts, bClipLowerZ, dLowerZ, bClipUpperZ, dUpperZ); if(pnts.size() == 4 && counts.size() == 1) { OdGeExtents2d clip; clip.addPoints(pnts); OdGePoint2d ll = clip.minPoint(); ll.x -= scale; ll.y -= scale; //expand the clipping area to 1 pixel (scaled) to avoid the color filling on clipping border OdGePoint2d ur = clip.maxPoint(); ur.x += scale; ur.y += scale; clipPoints.resize(4); clipPoints[0] = ll; clipPoints[1] = OdGePoint2d(ll.x, ur.y); clipPoints[2] = ur; clipPoints[3] = OdGePoint2d(ur.x, ll.y); } } bool PDF2dExportView::processSolidShell( const OdGeExtents3d& extents, OdUInt32 clipStat, OdInt32 numVertices, const OdGePoint3d* vertexList, OdInt32 faceListSize, const OdInt32* faceList, const OdGiEdgeData* pEdgeData, const OdGiFaceData* pFaceData, const OdGiVertexData* pVertexData) { bool bClip = !deviceGeom() ? true : false; if(GETBIT(clipStat, OdGiConveyorGeometry::kBoundaryProcClip))//Partially clipped { if(!deviceGeom()) return false; bool restartVpClip = false; if(!m_pViewportClip.isNull() && m_pViewportClip->enabled()) { m_pViewportClip->disable(); //disable VpClip to check if the text clipped by it or by clipping on the conveyor restartVpClip = true; } if(restartVpClip) clipStat = clipStatus(extents, m_modelEntryPoint.geometry()); if(GETBIT(clipStat, OdGiConveyorGeometry::kBoundaryProcClip))//Partially clipped on conveyor so it cannot be correctly exported as pdfPaths { if(restartVpClip) m_pViewportClip->enable(); return false; } else { bClip = true; } } OdDbBaseHatchPE* pH = OdGsDbRootLinkage::getDbBaseHatchPE(m_pSolidHatch); ODA_ASSERT(pH); #ifdef _DEBUG if(m_pMetafiler->metafile()) checkJointMetafile(m_pMetafiler->metafile(), nullptr); #endif drawHatchAsPaths(*pH, bClip, numVertices, vertexList, faceListSize, faceList, pEdgeData, pFaceData, pVertexData); return true; } void PDF2dExportView::shell(OdInt32 numVertices, const OdGePoint3d* vertexList, OdInt32 faceListSize, const OdInt32* faceList, const OdGiEdgeData* pEdgeData, const OdGiFaceData* pFaceData, const OdGiVertexData* pVertexData) { bool bHasTransparency = false; if(isPlotTransparency() && effectiveTraits().transparency().alphaPercent() < 1. && //Transparent polygons can be correctly exported to pdf only as bitmaps (as Autocad does) or as filled contour (a.k.a. PdfPaths) faceList[0] != faceListSize - 1) //in case of only one face, the transparent result will not be affected with inner lines { bHasTransparency = true; } if(m_bHatchToBitmapMode || bHasTransparency) { //ODA_ASSERT(isGsCacheEnabled() || !updateExtentsOnly()); const OdGeExtents3d extents = calcExtents(numVertices, vertexList); if(updateExtentsOnly()) { if(!isGsCacheEnabled()) { //When we calculate extents with GsCache we just need to add extents to extAccum, because of: //1. There is almost absolute probability, that the hatch will need to be regenerated on vectorization stage, because of changed deviation, so there is no need to spend resources on its vectorization //2. In HatchAsBitmap mode, the generated picture can affect the extents calculation, when it will be sent to conveyor, because of objectToDeviceMatrix cannot be calculated properly on extents calculation stage OdGiBaseVectorizerImpl::setExtents((OdGePoint3d*)&extents); m_awareFlags.setFlag(kEntityRegenDraw); } else super::shell(numVertices, vertexList, faceListSize, faceList, pEdgeData, pFaceData, pVertexData); //need to draw to calculate extents properly return; } const OdUInt32 clipStat = clipStatus(extents, m_modelEntryPoint.geometry()); if(GETBIT(clipStat, OdGiConveyorGeometry::kBoundaryProcClipFull)) return; //Fully clipped, vectorization can be skipped if(m_pSolidHatch.get()) { if(processSolidShell(extents, clipStat, numVertices, vertexList, faceListSize, faceList, pEdgeData, pFaceData, pVertexData)) return; else if(bHasTransparency && drawHatchAsBitmap(extents, clipStat, numVertices, vertexList, faceListSize, faceList, pEdgeData, pFaceData, pVertexData)) return; } else if(drawHatchAsBitmap(extents, clipStat, numVertices, vertexList, faceListSize, faceList, pEdgeData, pFaceData, pVertexData)) return; } super::shell(numVertices, vertexList, faceListSize, faceList, pEdgeData, pFaceData, pVertexData); } void PDF2dExportView::setFillRule(PDF2dExportGeometry::PolygonFillRules rule) { if(m_pMetafiler->metafile()) { m_pMetafiler->flush(); m_pMetafiler->add(new RecFillRule(rule)); } else deviceGeom()->setFillRule(rule); } void PDF2dExportView::fillContour() { if(!m_pMetafiler->metafile()) return deviceGeom()->dc_fill(); m_pMetafiler->flush(); m_pMetafiler->add(new RecFillContour()); } void PDF2dExportView::setDrawingContour(bool bStart) { if(deviceGeom()) { if(bStart) deviceGeom()->startDrawingContour(); else deviceGeom()->endDrawingContour(); return; } if(m_pMetafiler->metafile()) { m_pMetafiler->flush(); m_pMetafiler->add(new RecDrawingContour(bStart)); } } void PDF2dExportView::enableViewportClip(bool bEnable) { if(deviceGeom()) { if(bEnable) m_pViewportClip->enable(); else m_pViewportClip->disable(); return; } if(m_pMetafiler->metafile()) { m_pMetafiler->flush(); m_pMetafiler->add(new RecEnableClipper(bEnable)); } } static double getHatchDeviation(OdGiWorldDraw* pWd) { double dev(1e-3); if(pWd) { dev = pWd->deviation(kOdGiMaxDevForCurve, OdGePoint3d::kOrigin); if(OdZero(dev)) dev = 1e-3; else { //Temporarily enable kDrawHatchGroup flag to inform Gs that cache shouldn't be marked as ModelTfDependent during getModelToWorldTransform call. OdGiDrawFlagsHelper _dd(pWd->subEntityTraits(), OdGiSubEntityTraits::kDrawHatchGroup); dev /= pWd->geometry().getModelToWorldTransform().scale(); } } return dev; } void findIntersections(const OdGePoint2dArray& loopPoints, const OdGePoint2dArray& clipPoints, OdArray& outPoints) { gpc_polygon gpLoop, gpClip, gpResult; gpLoop.num_contours = 1; gpLoop.hole = 0; gpLoop.contour = ::gpc_alloc(1); gpLoop.contour->num_vertices = loopPoints.size(); gpLoop.contour->vertex = ::gpc_alloc(loopPoints.size());; for(OdUInt32 i = 0; i < loopPoints.size(); i++) { gpLoop.contour[0].vertex[i].x = loopPoints[i].x; gpLoop.contour[0].vertex[i].y = loopPoints[i].y; } gpClip.num_contours = 1; gpClip.hole = 0; gpClip.contour = ::gpc_alloc(1); gpClip.contour->num_vertices = clipPoints.size(); gpClip.contour->vertex = ::gpc_alloc(clipPoints.size());; for(OdUInt32 i = 0; i < clipPoints.size(); i++) { gpClip.contour[0].vertex[i].x = clipPoints[i].x; gpClip.contour[0].vertex[i].y = clipPoints[i].y; } gpResult.hole = NULL; gpResult.contour = NULL; gpResult.num_contours = 0; ::gpc_polygon_clip(GPC_INT, &gpLoop, &gpClip, &gpResult); for(int j = 0; j < gpResult.num_contours; j++) { OdGePoint2dArray out; for(int k = 0; k < gpResult.contour[j].num_vertices; k++) out.push_back(OdGePoint2d(gpResult.contour[j].vertex[k].x, gpResult.contour[j].vertex[k].y)); outPoints.push_back(out); } gpc_free_polygon(&gpLoop); gpc_free_polygon(&gpClip); gpc_free_polygon(&gpResult); } void PDF2dExportView::drawHatchAsPaths(OdDbBaseHatchPE& hatchPE, bool bClip, OdInt32 numVertices, const OdGePoint3d* vertexList, OdInt32 faceListSize, const OdInt32* faceList, const OdGiEdgeData* pEdgeData, const OdGiFaceData* pFaceData, const OdGiVertexData* pVertexData) { const int nLoops = hatchPE.numLoops(m_pSolidHatch); if(!nLoops || !effectivelyVisible()) return; const OdDbBaseHatchPE::HatchStyle hs = hatchPE.hatchStyle(m_pSolidHatch); if(hs != OdDbBaseHatchPE::kIgnore) { for(int l = 0; l < nLoops; ++l) { OdInt32 type = hatchPE.loopTypeAt(m_pSolidHatch, l); if((~type & OdDbBaseHatchPE::kTextIsland) && (type & OdDbBaseHatchPE::kTextbox)) //cannot be processed as contours return super::shell(numVertices, vertexList, faceListSize, faceList, pEdgeData, pFaceData, pVertexData); } } if(m_pMetafiler.get()) { setLineWeight(OdDb::kLnWt000); onTraitsModified(); m_pMetafiler->flush(true); } #ifdef _DEBUG OdGiGeometryMetafilePtr pMf = m_pMetafiler->metafile(); #endif if(m_bRestartClipper) enableViewportClip(false); setFillRule(PDF2dExportGeometry::kNoFill); try { { OdGePoint2dArray clipPoints; if(bClip && !m_pMetafiler->metafile()) calculateViewportClip(clipPoints); const double dev = getHatchDeviation(this); for(int l = 0; l < nLoops; ++l) { bool bEmptyContour = true; bool bContourStarted = false; OdGePoint3dArray loopPoints; OdInt32 type = hatchPE.loopTypeAt(m_pSolidHatch, l); if( (hs == OdDbBaseHatchPE::kIgnore && (~type & OdDbBaseHatchPE::kExternal)) || (hs == OdDbBaseHatchPE::kOuter && ((~type & OdDbBaseHatchPE::kOutermost) && (~type & OdDbBaseHatchPE::kExternal))) || type & OdDbBaseHatchPE::kDuplicate || (type & OdDbBaseHatchPE::kTextbox && type & OdDbBaseHatchPE::kTextIsland) || // TextBox + TextIsland loops are ignored by ACAD ((~type & OdDbBaseHatchPE::kPolyline) && (type & OdDbBaseHatchPE::kNotClosed)) ) { goto nextloop; //continue; } setDrawingContour(true); bContourStarted = true; if((type & OdDbBaseHatchPE::kPolyline) != 0) { OdGePoint2dArray vertices; OdGeDoubleArray bulges; hatchPE.getLoopAt(m_pSolidHatch, l, vertices, bulges); const bool bClosed = !(type & OdDbBaseHatchPE::kNotClosed); OdGePoint3dArray pnts3d; const unsigned int n = bClosed ? vertices.size() : vertices.size() - 1; for(unsigned i = 0; i < n; ++i) { const OdGePoint2d& pt = vertices[i]; const double bulge = bulges.isEmpty() ? 0. : bulges[i]; if(OdZero(bulge)) { pnts3d.push_back(OdGePoint3d(pt.x, pt.y, 0.)); continue; } if(!pnts3d.isEmpty()) { pnts3d.push_back(OdGePoint3d(pt.x, pt.y, 0.)); if(!bClip) polyline(pnts3d.logicalLength(), pnts3d.asArrayPtr()); else loopPoints.append(pnts3d); pnts3d.clear(); bEmptyContour = false; } const OdGePoint2d ptNext = vertices[(i + 1) % n]; if(pt.isEqualTo(ptNext)) { continue; } OdGeCircArc2d arc(pt, ptNext, bulge, false); OdSharedPtr nurb3d = ellipse2dtoNurbs3d(arc); if(!bClip) nurbs(*nurb3d); else { OdGeInterval intvl; nurb3d->getInterval(intvl); double param1 = intvl.lowerBound(); double param2 = intvl.upperBound(); nurb3d->appendSamplePoints(param1, param2, dev, loopPoints); } bEmptyContour = false; } if(!pnts3d.isEmpty()) { pnts3d.push_back(bClosed ? OdGePoint3d(vertices[0].x, vertices[0].y, 0.) : OdGePoint3d(vertices.last().x, vertices.last().y, 0.)); if(!bClip) polyline(pnts3d.logicalLength(), pnts3d.asArrayPtr()); else loopPoints.append(pnts3d); bEmptyContour = false; } } else { OdArray edgePtrs; hatchPE.getLoopAt(m_pSolidHatch, l, edgePtrs); OdGePoint2d pLast; for(unsigned f = 0; f < edgePtrs.size(); ++f) { OdGeCurve2d* cv = edgePtrs.getAt(f); // try to get nurbs OdSharedPtr nurb3d = NULL; switch(cv->type()) { case OdGe::kNurbCurve2d: nurb3d = convert2dto3d((OdGeNurbCurve2d*)cv); break; case OdGe::kEllipArc2d: nurb3d = ellipse2dtoNurbs3d(*((OdGeEllipArc2d*)cv)); break; case OdGe::kCircArc2d: nurb3d = ellipse2dtoNurbs3d(*((OdGeCircArc2d*)cv)); break; } if(nurb3d != NULL) { OdGeInterval intvl; nurb3d->getInterval(intvl); double param1 = intvl.lowerBound(); double param2 = intvl.upperBound(); OdGePoint3dArray pnts; nurb3d->appendSamplePoints(param1, param2, dev, pnts); if(!bClip) nurbs(*nurb3d); else loopPoints.append(pnts); //CORE-11119 PdfExport: Export solid hatch regression - hatch w/o one edge pLast = OdGePoint2d(pnts.last().x, pnts.last().y); ////////////////////////////////////////////////////////////////////////// bEmptyContour = false; } else { OdGeInterval intvl; cv->getInterval(intvl); double param1 = intvl.lowerBound(); double param2 = intvl.upperBound(); OdGePoint2dArray pnts; cv->appendSamplePoints(param1, param2, dev, pnts); /*const*/ unsigned int nPt = pnts.logicalLength(); if(nPt > 0) { OdGePoint3dArray pnts3d; //CORE-11119 PdfExport: Export solid hatch regression - hatch w/o one edge OdUInt32 k = 0, kk = 0; if(!pLast.isEqualTo(pnts[0], 1.e-6) && f) { pnts3d.setLogicalLength(++nPt); pnts3d[k].set(pLast.x, pLast.y, 0.); k++; } else { pnts3d.setLogicalLength(nPt); } ////////////////////////////////////////////////////////////////////////// for(; k < nPt; ++k) { pLast = pnts[kk]; pnts3d[k].set(pnts[kk].x, pnts[kk].y, 0.); ++kk; } if(!bClip) polyline(nPt, pnts3d.asArrayPtr()); else loopPoints.append(pnts3d); bEmptyContour = false; } } } } nextloop: if(!m_pMetafiler->metafile()) passHatchLoopToGeometry(loopPoints, clipPoints, false); else { HatchLoopGeometry geom(m_pMetafiler->input(), output()); super::polyline(loopPoints.logicalLength(), loopPoints.asArrayPtr()); OdGiGeometryMetafile::Record* pRec = geom.getResult(); if(pRec) m_pMetafiler->add(pRec); } if(l == nLoops - 1) setFillRule(PDF2dExportGeometry::kFillStrokeCloseRule); //Bug 22740. It was PDF2dExportGeometry::kFillEvenOddRule before // last path will be closed by fill command if(l == nLoops - 1 && bEmptyContour) fillContour(); if(bContourStarted) setDrawingContour(false); } // loops } setFillRule(PDF2dExportGeometry::defaultFillRule()); //restore previous rule if(m_bRestartClipper) enableViewportClip(true); #ifdef _DEBUG if(m_pMetafiler->metafile()) checkJointMetafile(pMf, m_pMetafiler->metafile()); #endif return; } catch(...) { setDrawingContour(false); setFillRule(PDF2dExportGeometry::defaultFillRule()); //restore previous rule if(m_bRestartClipper) enableViewportClip(true); #ifdef _DEBUG if(m_pMetafiler->metafile()) checkJointMetafile(pMf, m_pMetafiler->metafile()); #endif super::shell(numVertices, vertexList, faceListSize, faceList, pEdgeData, pFaceData, pVertexData); } } static inline void calcImageSize(long& imageWidth, long& imageHeight , const double& fieldWidth, const double& fieldHeight, const double& coef , const double& dpiScale) { if(fieldHeight < fieldWidth) { imageHeight = long(odmax((fieldHeight / coef + 0.5)*dpiScale, 1.0)); imageWidth = long(odmax(double(imageHeight) / fieldHeight * fieldWidth + 0.5, 1.0)); } else { imageWidth = long(odmax((fieldWidth / coef + 0.5)*dpiScale, 1.0)); imageHeight = long(odmax(double(imageWidth) / fieldWidth * fieldHeight + 0.5, 1.0)); } } static const long maxImageDimension = 65500; #if ((defined(_MSC_VER) && (WIN64)) || (defined(__GNUC_) && defined(__x86_64__)) || (defined(__APPLE__))) static const bool bX64Platform = true; static const long maxImageSize = 8000 * 8000; #else static const bool bX64Platform = false; static const long maxImageSize = 4000 * 4000; #endif static OdGiRasterImagePtr convertToBGRA(const OdGiRasterImagePtr& pSource) { OdSmartPtr pDesc = OdRxObjectImpl::createObject(); pDesc->setPixelWidth(pSource->pixelWidth()); pDesc->setPixelHeight(pSource->pixelHeight()); pDesc->setColorDepth(32); pDesc->pixelFormat().setBGRA(); pDesc->setScanLinesAlignment(4); return pSource->convert(false, 50, 50, 0, ODRGB(255, 255, 255), false, false, false, pDesc, true); } static void createTransparentImage(OdGiRasterImagePtr& pImg, const OdGiRasterImagePtr& pMask) { OdUInt32 srcScanLineSz = pImg->scanLineSize(); OdUInt32 srcScanLineSz2 = pMask->scanLineSize(); OdUInt8Array srcScLnBuf2; srcScLnBuf2.resize(srcScanLineSz2, 0); OdUInt8* pSrcScLnBuf2 = srcScLnBuf2.asArrayPtr(); OdUInt32 dstY, dstX, dstHeight = pImg->pixelHeight(), dstWidth = pImg->pixelWidth(); OdUInt8Array tmpScanline4Copy; const OdUInt8* pScanline4Copy = pImg->scanLines(); if(!pScanline4Copy) { tmpScanline4Copy.resize(srcScanLineSz * pImg->pixelHeight()); pImg->scanLines(tmpScanline4Copy.asArrayPtr(), 0, pImg->pixelHeight()); pScanline4Copy = tmpScanline4Copy.getPtr(); } for(dstY = 0; dstY < dstHeight; dstY++) { OdUInt8* pRGBA = (OdUInt8*)pScanline4Copy + dstY * srcScanLineSz; pMask->scanLines(pSrcScLnBuf2, dstY); OdUInt8* pRGB = pSrcScLnBuf2; dstX = 0; for(; dstX < dstWidth; dstX++) { if(pRGB[0] == 0 && pRGB[1] == 0 && pRGB[2] == 0 && pRGBA[0] == 255 && pRGBA[1] == 255 && pRGBA[2] == 255) { pRGBA[3] = 0; // transparent } else { pRGBA[3] = 255; } pRGB += 3; pRGBA += 4; } } } void getAsImages(bool bFromMf, ShellDrawable* pDrw, const OdGeExtents3d& extents, PDF2dExportView& vect, OdGsDevice* pRenderDevice, OdGsView& newView, ImageArray& images) { OdGeMatrix3d mx = (bFromMf ? vect.metafileToDeviceMatrix() : vect.objectToDeviceMatrix()).inverse(); const double coef = sqrt(odmax(OdGeVector2d(mx[0][0], mx[1][0]).lengthSqrd(), OdGeVector2d(mx[0][1], mx[1][1]).lengthSqrd())) * ((double)vect.device()->exportParams().getGeomDPI() / 72.); const OdUInt16 hatchDPI = vect.device()->getHatchDPI(); const double dpiScale = double(hatchDPI) / 72.0; const OdGeVector3d dir = OdGeVector3d::kZAxis; const OdGeVector3d upVec = OdGeVector3d::kYAxis; double fieldWidth = extents.maxPoint().x - extents.minPoint().x; double fieldHeight = extents.maxPoint().y - extents.minPoint().y; long imageWidth, imageHeight; const OdGePoint3d target = (extents.minPoint() + extents.maxPoint().asVector()) * 0.5; OdAbstractViewPEPtr(&newView)->setView(&newView, target, dir, upVec, fieldWidth, fieldHeight, false); calcImageSize(imageWidth, imageHeight, fieldWidth, fieldHeight, coef, dpiScale); if(vect.device()->getPalette()) pRenderDevice->setLogicalPalette(vect.device()->getPalette(), 256); pRenderDevice->setUserGiContext(vect.view().userGiContext()); BmpTilesGen _tg(pRenderDevice, OdGsDCRect(0, imageWidth, imageHeight, 0)); OdGeExtents3d paperClip; if(vect.deviceGeom()) { paperClip.set(OdGePoint3d(vect.deviceGeom()->m_paperClip.minPoint().x, vect.deviceGeom()->m_paperClip.minPoint().y, 0), OdGePoint3d(vect.deviceGeom()->m_paperClip.maxPoint().x, vect.deviceGeom()->m_paperClip.maxPoint().y, 0)); paperClip.transformBy(mx); paperClip.set(OdGePoint3d(paperClip.minPoint().x, paperClip.minPoint().y, 0), OdGePoint3d(paperClip.maxPoint().x, paperClip.maxPoint().y, 0)); } else paperClip = extents; const OdGeVector3d yVec = OdGeVector3d::kYAxis * fieldHeight / imageHeight; const OdGeVector3d xVec = OdGeVector3d::kXAxis * fieldWidth / imageWidth; OdUInt32 startY = imageHeight; OdUInt32 tileW = imageWidth / ((long)(imageWidth / maxImageDimension) + 1); OdUInt32 tileH = imageHeight / ((long)(imageHeight / maxImageDimension) + 1); if(tileH * tileW > maxImageSize && !bX64Platform && bFromMf /*collecting images in metafile on x32 platforms can lead to crash because of memory shortage*/) { images.clear(); return; } bool bHoriz = tileW > tileH; while(tileH * tileW > maxImageSize) { if(bHoriz) tileW /= 2; else tileH /= 2; bHoriz = !bHoriz; } OdGeExtents3d tileExtents(OdGePoint3d(extents.minPoint().x, extents.minPoint().y, 0), OdGePoint3d(extents.minPoint().x, extents.minPoint().y, 0)); OdInt32 nextY = startY; while(nextY > 0) { nextY -= tileH; if(nextY < 0) nextY = 0; tileExtents.set(tileExtents.minPoint(), tileExtents.maxPoint() + yVec * (startY - nextY)); OdInt32 startX = 0, nextX = 0; while(nextX < imageWidth) { nextX += tileW; if(nextX > imageWidth) nextX = imageWidth; tileExtents.set(tileExtents.minPoint(), tileExtents.maxPoint() + xVec * (nextX - startX)); if(paperClip.intersectWith(tileExtents) == OdGeExtents3d::kIntersectNot) { tileExtents.set(OdGePoint3d(tileExtents.maxPoint().x, tileExtents.minPoint().y, 0), tileExtents.maxPoint()); startX = nextX; continue; } OdGiRasterImagePtr pImgBlackBg, pImg; OdGsDCRect pictRect(startX, nextX, startY, nextY); pRenderDevice->setBackgroundColor(ODRGB(255, 255, 255)); pImgBlackBg = _tg.regenTile(pictRect); pImg = convertToBGRA(pImgBlackBg); pRenderDevice->setBackgroundColor(ODRGB(0, 0, 0)); pImgBlackBg = _tg.regenTile(pictRect); createTransparentImage(pImg, pImgBlackBg); ////////////////////////////////////////////////////////////////////////// // OdString name(L"f:\\rast_source_black_bg.bmp"); // OdString name1(L"f:\\rast_source_white_bg.bmp"); // OdRxRasterServicesPtr pRasSvcs = odrxDynamicLinker()->loadApp(RX_RASTER_SERVICES_APPNAME); // if(pRasSvcs.get()) // { // pRasSvcs->saveRasterImage(pImg, name1); // pRasSvcs->saveRasterImage(pImgBlackBg, name); // } ////////////////////////////////////////////////////////////////////////// ImageData* pImage = images.append(); pImage->m_origin = OdGePoint3d(tileExtents.minPoint().x, tileExtents.minPoint().y, 0); pImage->m_uVec = xVec; pImage->m_vVec = yVec; pImage->m_pImage = pImg; //set boundary to avoid clipping pImage->uvBoundary.resize(4); pImage->uvBoundary[0].set(-0.5, -0.5); pImage->uvBoundary[2].set(pImg->pixelWidth() + 0.5, pImg->pixelHeight() + 0.5); pImage->uvBoundary[1].set(pImage->uvBoundary[0].x, pImage->uvBoundary[2].y); pImage->uvBoundary[3].set(pImage->uvBoundary[2].x, pImage->uvBoundary[0].y); if(!bFromMf) { vect.drawImages(pImage, 1); images.clear(); } tileExtents.set(OdGePoint3d(tileExtents.maxPoint().x, tileExtents.minPoint().y, 0), tileExtents.maxPoint()); startX = nextX; } tileExtents.set(OdGePoint3d(extents.minPoint().x, tileExtents.maxPoint().y, 0), OdGePoint3d(extents.minPoint().x, tileExtents.maxPoint().y, 0)); startY = nextY; } } void PDF2dExportView::drawImages(const ImageData* aImg, OdUInt32 nImg) { if(deviceGeom()) { deviceGeom()->setHatchDrawingMode(true); for(OdUInt32 i = 0; i < nImg; ++i) { rasterImageDc(aImg[i].m_origin, aImg[i].m_uVec, aImg[i].m_vVec, aImg[i].m_pImage, aImg[i].uvBoundary.asArrayPtr(), aImg[i].uvBoundary.size(), true); } deviceGeom()->setHatchDrawingMode(false); } else { onTraitsModified(); m_pMetafiler->flush(true); m_pMetafiler->add(new RecHatchDrawingMode(true)); const OdUInt16 savedColorBitmapDPI = device()->getColorBimapDPI(); const OdUInt16 hatchDPI = device()->getHatchDPI(); if(hatchDPI != savedColorBitmapDPI) m_pMetafiler->add(new RecColorBitmapDPI(hatchDPI)); for(OdUInt32 i = 0; i < nImg; ++i) { rasterImageDc(aImg[i].m_origin, aImg[i].m_uVec, aImg[i].m_vVec, aImg[i].m_pImage, aImg[i].uvBoundary.asArrayPtr(), aImg[i].uvBoundary.size(), true); } if(hatchDPI != savedColorBitmapDPI) m_pMetafiler->add(new RecColorBitmapDPI(savedColorBitmapDPI)); m_pMetafiler->add(new RecHatchDrawingMode(false)); } } OdGsDevicePtr PDF2dExportView::getBitmapDevice() { if(!m_bitmapDevice.get()) m_bitmapDevice = getRenderingDevice(); return m_bitmapDevice; } // temporarily before refactoring the export to PRC void PDF2dExportView::adjustRenderMode() { if ((device()->exportParams().getPRCMode() == PDFExportParams::kDisabled)) super::adjustRenderMode(); else m_renderMode = kFlatShaded; // #UpgradePRC: Until refactored for export to PRC as is } void getShellAsImages(bool bFromMf, const OdGeExtents3d& extents, PDF2dExportView& vect, ImageArray& images, OdInt32 numVertices, const OdGePoint3d* vertexList, OdInt32 faceListSize, const OdInt32* faceList, const OdGiEdgeData* pEdgeData, const OdGiFaceData* pFaceData, const OdGiVertexData* pVertexData) { OdGsDevicePtr pRenderDevice = vect.getBitmapDevice(); if(!pRenderDevice.get()) return; vect.onTraitsModified(); OdSmartPtr myTmpDrawable = OdRxObjectImpl::createObject(vect.effectiveTraits(), vect, numVertices, vertexList, faceListSize, faceList, pEdgeData, pFaceData, pVertexData); OdGsViewPtr pNewView = pRenderDevice->createView(); pNewView->add(myTmpDrawable, 0); pRenderDevice->addView(pNewView); getAsImages(bFromMf, myTmpDrawable, extents, vect, pRenderDevice, *pNewView, images); for(unsigned i = 0; i < images.size(); ++i) images[i].m_pImage = images[i].m_pImage->clone(); pRenderDevice->eraseView(pNewView.get()); } void PDF2dExportView::saveHatchToMetafiler(OdInt32 numVertices, const OdGePoint3d* vertexList, OdInt32 faceListSize, const OdInt32* faceList, const OdGiEdgeData* pEdgeData, const OdGiFaceData* pFaceData, const OdGiVertexData* pVertexData) { onTraitsModified(); m_pMetafiler->flush(true); //this is important. ShellGeometry geom(m_pMetafiler->input(), output()); super::shell(numVertices, vertexList, faceListSize, faceList, pEdgeData, pFaceData, pVertexData); OdGiGeometryMetafile::Record* pRec = geom.getResult(); if(pRec) m_pMetafiler->add(pRec); } bool PDF2dExportView::drawHatchAsBitmap( const OdGeExtents3d& extents, OdUInt32 clipStat, OdInt32 numVertices, const OdGePoint3d* vertexList, OdInt32 faceListSize, const OdInt32* faceList, const OdGiEdgeData* pEdgeData, const OdGiFaceData* pFaceData, const OdGiVertexData* pVertexData) { OdGsDevicePtr pRenderDevice; OdGsViewPtr pNewView; try { pRenderDevice = getBitmapDevice(); if(!pRenderDevice.get()) return false; if((!GETBIT(clipStat, OdGiConveyorGeometry::kBoundaryProcClip)) && !deviceGeom()) { saveHatchToMetafiler(numVertices, vertexList, faceListSize, faceList, pEdgeData, pFaceData, pVertexData); OdGiBaseVectorizerImpl::setExtents((const OdGePoint3d*)&extents); return true; } OdSmartPtr myTmpDrawable = OdRxObjectImpl2::createObject(effectiveTraits(), *this, numVertices, vertexList, faceListSize, faceList, pEdgeData, pFaceData, pVertexData); pNewView = pRenderDevice->createView(); pNewView->add(myTmpDrawable, 0); pRenderDevice->addView(pNewView); ImageArray images; getAsImages(false, myTmpDrawable, extents, *this, pRenderDevice, *pNewView, images); pRenderDevice->eraseView(pNewView.get()); } catch(...) { if(!pRenderDevice.isNull() && !pNewView.isNull()) pRenderDevice->eraseView(pNewView.get()); return false; } return true; } bool PDF2dExportView::getHyperlink(const OdGiDrawable* pDrawable, HyperlinkData& dataToFill) const { if(!GETBIT(device()->exportParams().exportFlags(), PDFExportParams::kExportHyperlinks)) return false; if(!pDrawable || OdGsDbRootLinkage::isLayoutDrawable(pDrawable)) return false; OdDbHyperlinkCollectionPtr collection; try { OdDbEntityHyperlinkPEPtr phPE = OdDbEntityHyperlinkPE::cast(pDrawable); if(phPE.get()) collection = phPE->getHyperlinkCollectionEx(pDrawable, true); } catch(const OdError&) { return false; } if(collection.isNull() || !collection->count()) return false; OdString hLink = collection->item(0)->name(); OdString subLocation = collection->item(0)->subLocation(); if(hLink.isEmpty() && subLocation.isEmpty()) return false; if(!hLink.isEmpty()) { const bool looksLikeWebPage = hLink.getLength() > 4 && (hLink.left(4).iCompare(L"http") == 0 || hLink.left(4).iCompare(L"www.") == 0); if(looksLikeWebPage || subLocation.isEmpty()) { hLink.replace('\\', '/'); dataToFill.m_link = hLink; dataToFill.m_bInternal = false; return true; } } const PDFExportParams& params = device()->exportParams(); const OdStringArray layouts = params.layouts(); if(layouts.size() > 1 && !subLocation.isEmpty()) { int idx = subLocation.find(L","); if(idx != -1) subLocation.deleteChars(0, idx + 1); if(layouts.contains(subLocation)) { OdInt32 pageIndex(-1); if(device()->dbLayoutToPage()) pageIndex = device()->dbLayoutToPage()->findPage(hLink, subLocation, params.database()); if(pageIndex == -1) //still not found { const PdfExportParamsHolderPointer pParamsHolder = device()->exportParamsHolder(); for(OdUInt32 i = 0; i < layouts.size(); i++) { OdRxObject* pDb = pParamsHolder->getDatabase(i).get(); if(!layouts.at(i).compare(subLocation) && (params.databases().size() == 0 || pDb == params.database())) { pageIndex = i; break; } } } if(hLink.isEmpty() || pageIndex != -1) { // page number must be found for specified locations // otherwise it means the given location is not exported and the link should be omitted dataToFill.m_link = subLocation; dataToFill.m_bInternal = true; dataToFill.m_page = pageIndex; return true; } } } return false; } void PDF2dExportView::pushMetafileTransform(const OdGeMatrix3d& mtx, OdUInt32 uFlags) { if (GETBIT(uFlags, kSharedRefUpdate)) { OdGsBaseVectorizer::pushMetafileTransform(mtx, uFlags); return; } const OdUInt32 transformCount = m_iExportToBlock + m_iExportNonGrapchicalInfo; if(transformCount) { ODA_ASSERT(m_MFTransforms.size() == transformCount); m_MFTransforms.at(transformCount-1).postMultBy(mtx); } super::pushMetafileTransform(mtx, uFlags); metafileToDeviceMatrix(); } OdGeMatrix3d PDF2dExportView::metafileToDeviceMatrix() const { //NS, TODO: set m_bValidMfToDeviceMatrix flag in push/pop/setTransformForMetafileGeometry /*if(m_bValidMfToDeviceMatrix) { ODA_ASSERT((gsView()->worldToDeviceMatrix()*getTransformForMetafileGeometry()).isEqualTo(m_mfToDeviceMatrix)); } else*/ { m_mfToDeviceMatrix.setToProduct(gsView()->worldToDeviceMatrix(), getTransformForMetafileGeometry()); m_bValidMfToDeviceMatrix = true; } return m_mfToDeviceMatrix; } static void textExtentsTo2d(PDF2dExportView& vect, const OdGeExtents3d& ext, bool bFromMf, OdGeExtents2d& res) { ODA_ASSERT(vect.deviceGeom()); OdGeMatrix3d mx = bFromMf ? vect.metafileToDeviceMatrix() : vect.objectToDeviceMatrix(); OdGeExtents3d extents(ext); extents.transformBy(mx); ////////////////////////////////////////////////////////////////////////// OdGeMatrix3d m; //unscale extents because of annotation coordinates are not in the content stream m.setToScaling(72. / (double)vect.device()->exportParams().getGeomDPI()); extents.transformBy(m); ////////////////////////////////////////////////////////////////////////// res.set((const OdGePoint2d&)(extents.minPoint()), (const OdGePoint2d&)(extents.maxPoint())); } void PDF2dExportView::passHyperlinkToGeometry(const HyperlinkData& data, const OdGeExtents3d& ext, bool bFromMf) { ODA_ASSERT(deviceGeom()); OdGeExtents2d rect; textExtentsTo2d(*this, ext, bFromMf, rect); if(!bFromMf && isFullyClipped(ext, m_modelEntryPoint.geometry())) //Check if entity with hyperlink is clipped return; OdString layer; if(deviceGeom()->isLayersSupported()) { if(data.m_layer.isEmpty()) this->layerName(layer, subEntityTraits().layer()); else layer = data.m_layer; } deviceGeom()->dc_hyperlink(rect, data.m_link, layer, data.m_bInternal, data.m_page); } void PDF2dExportView::passTextAnnotationToGeometry(const OdString& sText, const OdString &layerName, const OdGeExtents3d& ext, bool bFromMf) { ODA_ASSERT(deviceGeom()); OdGeExtents2d rect; textExtentsTo2d(*this, ext, bFromMf, rect); OdString sTitle(OD_T("Text")); OdString layer; if(deviceGeom()->isLayersSupported()) { if(layerName.isEmpty()) this->layerName(layer, subEntityTraits().layer()); else layer = layerName; } deviceGeom()->dc_textAnnot(rect, sTitle, sText, layer); } void PDF2dExportView::passHatchLoopToGeometry(const OdGePoint3dArray& loopPoints, const OdGePoint2dArray& clipPoints, bool bFromMf) { if(loopPoints.size()) { ODA_ASSERT(deviceGeom()); OdGePoint3dArray loopPointsToGeom = loopPoints; OdGeMatrix3d mx = bFromMf ? metafileToDeviceMatrix() : objectToDeviceMatrix(); OdGePoint2dArray pnts; pnts.resize(loopPointsToGeom.size()); for(OdUInt32 i = 0; i < loopPointsToGeom.size(); i++) { loopPointsToGeom[i].transformBy(mx); pnts[i].set(loopPointsToGeom[i].x, loopPointsToGeom[i].y); } OdArray out; if(clipPoints.size()) findIntersections(pnts, clipPoints, out); else out.push_back(pnts); if(out.size()) //some loops can be placed completely out of paper so the contours will not be intersected { for(OdUInt32 i = 0; i < out.size(); i++) { deviceGeom()->dc_polyline(out[i].size(), out[i].asArrayPtr()); //restart contours polyline setDrawingContour(false); setDrawingContour(true); } } } } OdInt32 PDF2dExportView::calculateTextLen(const OdChar* msg, OdInt32 length, bool raw, const OdGiTextStyle* pStyle) { OdDbBaseDatabase* pDb = drawContext()->giContext().database(); OdBaseTextIteratorPtr itText = OdDbBaseDatabasePEPtr(pDb)->createTextIterator(pDb, msg, length, raw, pStyle); OdInt32 res = 0; for(bool bLast = false; !bLast; bLast = itText->currProperties().bLastChar) { OdUInt16 ch = itText->nextChar(); if(ch == 0) break; else res++; } return res; } //Draws text in one or two passes if necessary for the purposes of Ctrl+F searchable text void PDF2dExportView::doDrawText(const OdGePoint3d& position, const OdGeVector3d& normal, const OdGeVector3d& direction, const OdChar* msg, OdInt32 length, bool raw, const OdGiTextStyle* pTextStyle, bool bAsGeom) { super::text(position, normal, direction, msg, length, raw, pTextStyle); if(!deviceGeom()) return; if(exportSearchableTextOnly()) { ODA_ASSERT(deviceGeom()->exportHiddenText()); //already set in beginViewViewctorization() return; } if(!bAsGeom) return; if(!device()->exportSearchableTextAsHiddenText()) return; bool restartVpClip = false; if(!m_pViewportClip.isNull() && m_pViewportClip->enabled()) { m_pViewportClip->disable(); restartVpClip = true; } OdSaveStateFunc ssEHT(deviceGeom(), &PDF2dExportGeometry::exportHiddenText, &PDF2dExportGeometry::setExportHiddenText, true); super::text(position, normal, direction, msg, length, raw, pTextStyle); if(restartVpClip) m_pViewportClip->enable(); } bool PDF2dExportView::isSearchableText(const OdGiTextStyle* pStyle) const { return (pStyle->isTtfFont() && GETBIT(device()->exportParams().searchableTextType(), PDFExportParams::kTTF) || pStyle->isShxFont() && GETBIT(device()->exportParams().searchableTextType(), PDFExportParams::kSHX)); } static const void* lastRecord(const OdGiGeometryMetafile* pMf) { return pMf ? pMf->lastRecord() : NULL; } void PDF2dExportView::setAnnotationText(const OdChar* msg, OdInt32 length, const OdGiTextStyle* pStyle) { const bool bTextAsGeom = (pStyle->isTtfFont() && device()->exportParams().isTTFTextAsGeometry() || pStyle->isShxFont() && device()->exportParams().isSHXTextAsGeometry()); if(bTextAsGeom && !device()->exportSearchableTextAsHiddenText() && !exportSearchableTextOnly() && isSearchableText(pStyle)) m_annotationText += OD_T(" ") + (length < 0 ? OdString(msg) : OdString(msg, length)); //export as annotation } void PDF2dExportView::drawTextByFragments(const OdGePoint3d& position, const OdGeVector3d& normal, const OdGeVector3d& direction, const OdChar* msg, OdInt32 length, bool raw, const OdGiTextStyle* pStyle, const OdGeVector3d& uNorm, const OdGeVector3d& vNorm, bool bTextAsGeom, bool bToMf) { OdGePoint3d pMin, pMax, endPos; OdGePoint3d tmpPos = position, newPos = position; OdChar ch[2] = L" "; if(length == -1) //length can be == -1 for missing xrefs, pdf underlays in shaded mode etc length = (OdInt32)odStrLen(msg); OdUInt32 bbStatus = 0; OdString sFragment; for(OdInt32 i = 0; i < length; ++i) { ch[0] = msg[i]; OdUInt32 bbNewStatus = 0; device()->userGiContext()->textExtentsBox(*pStyle, ch, length, 0, pMin, pMax, &endPos); OdGeBoundBlock3d bbChk(tmpPos + uNorm * pMin.x + vNorm * pMin.y, uNorm * (endPos.x - pMin.x), vNorm * (pMax.y - pMin.y), OdGePoint3d::kOrigin.asVector()); try { m_modelEntryPoint.geometry().conveyorBoundaryInfoProc(bbChk, bbNewStatus); } catch(...) { return; } if(bbNewStatus != bbStatus && !sFragment.isEmpty()) { if(bToMf && !GETBIT(bbStatus, OdGiConveyorGeometry::kBoundaryProcClipFull)) super::text(newPos, normal, direction, sFragment.c_str(), sFragment.getLength(), raw, pStyle); else if(!GETBIT(bbStatus, OdGiConveyorGeometry::kBoundaryProcClipFull)) doDrawText(newPos, normal, direction, sFragment.c_str(), sFragment.getLength(), raw, pStyle, bTextAsGeom); sFragment = ch[0]; newPos = tmpPos; } else sFragment += ch[0]; bbStatus = bbNewStatus; tmpPos += endPos.asVector(); } if(!sFragment.isEmpty() && !GETBIT(bbStatus, OdGiConveyorGeometry::kBoundaryProcClipFull)) { if(bToMf) super::text(newPos, normal, direction, sFragment.c_str(), sFragment.getLength(), raw, pStyle); else doDrawText(newPos, normal, direction, sFragment.c_str(), sFragment.getLength(), raw, pStyle, bTextAsGeom); } } void PDF2dExportView::text(const OdGePoint3d& position, const OdGeVector3d& normal, const OdGeVector3d& direction, const OdChar* msg, OdInt32 length, bool raw, const OdGiTextStyle* pStyle) { if(!effectivelyVisible() || regenAbort()) return; bool bTextAsGeom = (pStyle->isTtfFont() && device()->exportParams().isTTFTextAsGeometry() || pStyle->isShxFont() && device()->exportParams().isSHXTextAsGeometry()); OdGeVector3d u, v; ::odgiCalculateTextBasis(u, v, normal, direction, pStyle->textSize(), pStyle->xScale(), pStyle->obliquingAngle(), pStyle->isBackward(), pStyle->isUpsideDown()); onTextProcessing(position, u, v); OdGePoint3d pMin, pMax, endPos; device()->userGiContext()->textExtentsBox(*pStyle, msg, length, 0, pMin, pMax, &endPos); if(!OdGeExtents3d(pMin, pMax).isValidExtents()) return;// NS ?? OdGeVector3d uNorm(u.normal()), vNorm(v.normal()); OdGeBoundBlock3d bbChk(position + uNorm * pMin.x + vNorm * pMin.y, uNorm * (endPos.x - pMin.x), vNorm * (pMax.y - pMin.y), OdGePoint3d::kOrigin.asVector()); OdUInt32 bbStatus = 0; try { m_modelEntryPoint.geometry().conveyorBoundaryInfoProc(bbChk, bbStatus); } catch(...) { return; } if(GETBIT(bbStatus, OdGiConveyorGeometry::kBoundaryProcClipFull)) // Text is fully clipped, skip vectorization return; if(gsWriter().isRecordingMetafile()) { onTraitsModified(); const void* pLastRec = lastRecord(m_pMetafiler->metafile()); if(bTextAsGeom && !exportSearchableTextOnly()) m_pMetafiler->setOptions(cMfDefaultOptions & ~OdGiMetafiler::kTextAsText); else m_pMetafiler->setOptions(cMfDefaultOptions); if(!GETBIT(bbStatus, OdGiConveyorGeometry::kBoundaryProcClip)) super::text(position, normal, direction, msg, length, raw, pStyle); else drawTextByFragments(position, normal, direction, msg, length, raw, pStyle, uNorm, vNorm, bTextAsGeom, true); m_pMetafiler->flush(); //if text is transformed into geometry there can be primitives just cached, but not added into MF, so we need to flush them if(pLastRec != lastRecord(m_pMetafiler->metafile())) { //passed through geometry conveyor and some geometry has been added to the metafile setAnnotationText(msg, length, pStyle); } //process searchable text as hidden text if(bTextAsGeom && device()->exportSearchableTextAsHiddenText() && !exportSearchableTextOnly() && isSearchableText(pStyle)) { m_pMetafiler->add(new RecHiddenText(true)); m_pMetafiler->setOptions(cMfDefaultOptions); if(!GETBIT(bbStatus, OdGiConveyorGeometry::kBoundaryProcClip)) super::text(position, normal, direction, msg, length, raw, pStyle); else drawTextByFragments(position, normal, direction, msg, length, raw, pStyle, uNorm, vNorm, bTextAsGeom, true); m_pMetafiler->add(new RecHiddenText(false)); } return; } if(length > 0) //length can be < 0 for missing xrefs { onTraitsModified(); setAnnotationText(msg, length, pStyle); if(GETBIT(bbStatus, OdGiConveyorGeometry::kBoundaryProcClip)) { // Text is partially clipped, so we do not clip it at all bool restartVpClip = false; if(!m_pViewportClip.isNull() && m_pViewportClip->enabled()) { m_pViewportClip->disable(); //disable VpClip to check if the text clipped by it or by clipping on the conveyor restartVpClip = true; } bbStatus = 0; m_modelEntryPoint.geometry().conveyorBoundaryInfoProc(bbChk, bbStatus); if(bTextAsGeom && !exportSearchableTextOnly() && restartVpClip) //restore disabled clipping to clip invisible geometry and save pdf size { m_pViewportClip->enable(); restartVpClip = false; } if(GETBIT(bbStatus, OdGiConveyorGeometry::kBoundaryProcClip)) { //if text is still clipped with disabled viewport clip, then there is the conveyor clip and the text will be transformed to geometry drawTextByFragments(position, normal, direction, msg, length, raw, pStyle, uNorm, vNorm, bTextAsGeom, false); if(restartVpClip) m_pViewportClip->enable(); return; } doDrawText(position, normal, direction, msg, length, raw, pStyle, bTextAsGeom); if(restartVpClip) m_pViewportClip->enable(); } else doDrawText(position, normal, direction, msg, length, raw, pStyle, bTextAsGeom); return; } onTraitsModified(); return doDrawText(position, normal, direction, msg, length, raw, pStyle, bTextAsGeom); } bool PDF2dExportView::processPRC(const OdGiDrawable* pDrawable) { //This method is called only when Gs cache is NOT used ODA_ASSERT((device()->exportParams().getPRCMode() != PDFExportParams::kDisabled)); ODA_ASSERT(deviceGeom()); // apply filter to find you whether entity should be exported or not bool canExportToPRC = false; OdUInt32 viewportidx = -1; const OdGiPathNode* pGiPath = currentGiPath(); if(!updateExtentsOnly() && device()->exportParams().getPRCContext()) canExportToPRC = device()->exportParams().getPRCContext()->shouldExportAsPRC(pDrawable, pGiPath, &m_viewInfo, viewportidx); if(!canExportToPRC) return canExportToPRC; // setup PDF2PRCExportParams prcParams; prcParams.m_arrDeviation.resize(5); prcParams.m_arrDeviation[kOdGiMaxDevForCircle] = getDeviation(kOdGiMaxDevForCircle, OdGePoint3d::kOrigin, true); prcParams.m_arrDeviation[kOdGiMaxDevForCurve] = getDeviation(kOdGiMaxDevForCurve, OdGePoint3d::kOrigin, true); prcParams.m_arrDeviation[kOdGiMaxDevForBoundary] = getDeviation(kOdGiMaxDevForBoundary, OdGePoint3d::kOrigin, true); prcParams.m_arrDeviation[kOdGiMaxDevForIsoline] = getDeviation(kOdGiMaxDevForIsoline, OdGePoint3d::kOrigin, true); prcParams.m_arrDeviation[kOdGiMaxDevForFacet] = getDeviation(kOdGiMaxDevForFacet, OdGePoint3d::kOrigin, true); // brep or mesh mode prcParams.m_exportAsBrep = device()->exportParams().getPRCMode() == PDFExportParams::kAsBrep; //get traits data prcParams.m_pTraitsData = &effectiveTraits(); prcParams.m_pByBlockTraitsData = byBlockTraits(); // compression PDF3D_ENUMS::PRCCompressionLevel compressLev; bool hasBrepCompression = device()->exportParams().hasPrcBrepCompression(compressLev); prcParams.setPRCCompression(compressLev, hasBrepCompression, device()->exportParams().hasPrcTessellationCompression()); prcParams.m_bodyTransformationMatr = modelToWorldTransform(); prcParams.m_ExportAmbientColorBehavior = device()->exportParams().getPrcExportAmbientColorBehavior(); OdGeExtents3d extents; OdGeVector3d cameraVector = viewDir(); OdGeVector3d normalToCamera = upVector(); OdGeVector3d third = normalToCamera.crossProduct(cameraVector); OdGeMatrix3d cameraView; // using OdGePoint3d::kOrigin instead of position() is correct cameraView.setCoordSystem(OdGePoint3d::kOrigin, third, normalToCamera, cameraVector); device()->exportParams().getPRCContext()->getExtents(pDrawable, m_viewInfo.viewportObjectId, &(drawContext())->giContext(), cameraView, modelToWorldTransform(), extents); if(!extents.isValidExtents()) { return canExportToPRC; } canExportToPRC = true; // user context prcParams.m_extraOptions->putAt("user data", deviceGeom()->exportParams().getPRCContext()->userData()); // export drawable to prc file and/or stream int viewIdx = 0; for(int i = 0; i < device()->numViews(); ++i) { if(device()->viewAt(i) == this) { viewIdx = i; break; } } std::map< OdUInt32, std::pair > ::iterator it = deviceGeom()->m_prc_context.find(viewportidx); if(it == deviceGeom()->m_prc_context.end()) { // no extents for this viewport yet => create new ones it = deviceGeom()->m_prc_context.insert( std::pair >(viewportidx, std::pair((OdRxObject *)NULL, ViewExtents())) ).first; //is this code correct? (m_viewIdx sets one time per every viewport) it->second.second.m_viewIdx = viewIdx; } it->second.second.m_extents.addExt(extents); prcParams.m_viewportIdx = viewportidx; prcParams.m_viewIdx = viewIdx; savePalleteFromDeviceToParams(prcParams, *device()); OdResult result = deviceGeom()->dc_prc(pDrawable, prcParams); ODA_ASSERT(result == ::eOk); return canExportToPRC; } void PDF2dExportView::processFrozenLayer(const OdString& name /*= L""*/) { if(deviceGeom()) { if(!name.isEmpty()) deviceGeom()->open_Frozen_Layer(name); else deviceGeom()->close_Frozen_Layer(); } } bool PDF2dExportView::doDrawImp(OdUInt32 drawableFlags, const OdGiDrawable* pDrawable, const OdString &layerName) { m_annotationText.empty(); HyperlinkData curHl; OdSaveState hlss(m_curHl, getHyperlink(pDrawable, curHl) ? &curHl : NULL); if(m_curHl) m_curHl->m_layer = layerName; bool bRes = super::doDraw(drawableFlags, pDrawable); if(deviceGeom()) { if(m_curHl || !m_annotationText.isEmpty()) { OdGeExtents3d extents; if(pDrawable->getGeomExtents(extents) != ::eOk) return bRes; if(m_curHl) passHyperlinkToGeometry(curHl, extents, false); if(!m_annotationText.isEmpty()) { passTextAnnotationToGeometry(m_annotationText, layerName, extents, false); m_annotationText.empty(); } } return bRes; } if(m_curHl) passHyperlinkToMetafiler(); if(!m_annotationText.isEmpty()) { passTextAnnotationToMetafiler(layerName); m_annotationText.empty(); } return bRes; } bool PDF2dExportView::isBlockRef(OdUInt32 drawableFlags, const OdGiDrawable* pDrawable) { if(!isGsCacheEnabled()) return OdGsDbRootLinkage::isBlockRefDrawable(pDrawable); OdGsBlockReferenceNode *pNode = NULL; if(GETBIT(drawableFlags, OdGiDrawable::kDrawableIsCompoundObject)) { OdGsNode *pCsh = NULL; // 1) Try to use cache attached to drawable (actual for top-level entities) if(gsWriter().gsModel()) pCsh = static_cast(gsWriter().gsModel()->drawableGsNode(pDrawable)); if(pCsh && (pCsh->nodeType() == kEntityNode)) { OdGsEntityNode *pEntNode = static_cast(pCsh); if(pEntNode->isReference()) pNode = static_cast(pEntNode); } else // 2) Try to use queryX (actual for non top-level entities) pNode = static_cast(pDrawable->queryX(OdGsBlockReferenceNode::desc())); } return pNode != NULL; } bool PDF2dExportView::doDraw(OdUInt32 drawableFlags, const OdGiDrawable* pDrawable) { if(updateExtentsOnly() && !isGsCacheEnabled()) return super::doDraw(drawableFlags, pDrawable); // simulation of frozen BlockRefs const bool isOffLayersEnabled = device()->isLayersSupported() && device()->isOffLayersEnabled(); if(isOffLayersEnabled /*&& device()->frozenLayerSimulationEnabled()*/) { // start rendering of BlockReference if(isBlockRef(drawableFlags, pDrawable)) { bool bByBlock = false; OdDbStub* pLayer = subEntityTraits().layer(); OdGiDrawablePtr pDrwLayer = userGiContext()->openDrawable(pLayer); if(!pDrwLayer.isNull()) { OdGiLayerTraitsTaker layerTraits; OdUInt32 flags = pDrwLayer->setAttributes(static_cast(&layerTraits)); bByBlock = GETBIT(flags, OdGiLayerTraits::kByBlock); } OdString layerName; if(!bByBlock) this->layerName(layerName, pLayer, 0); const bool isFrozen = !layerName.isEmpty() && device()->isFrozenLayer(layerName); if(isFrozen) { layerName += PDF_FROZEN_LAYER; processFrozenLayer(layerName); } bool bRet = doDrawImp(drawableFlags, pDrawable, layerName); if(isFrozen) processFrozenLayer(); return bRet; } } OdDbBaseHatchPE* pH = OdGsDbRootLinkage::isHatchDrawable(pDrawable) ? OdGsDbRootLinkage::getDbBaseHatchPE(pDrawable) : NULL; const PDFExportParams::PRCSupport pramsMode = device()->exportParams().getPRCMode(); const bool bExportPrc = device()->exportParams().getPRCMode() != PDFExportParams::kDisabled; if (bExportPrc) { if (m_drawableFilterFunction) { const OdUInt32 filterFlags = giContext().drawableFilterFunction(m_drawableFilterFunction, currentGiPath(), drawableFlags); if (GETBIT(filterFlags, OdGiContext::kSkipVectorization)) return doDrawImp(drawableFlags, pDrawable, OdString::kEmpty); } if (processPRC(pDrawable)) return true; return doDrawImp(drawableFlags, pDrawable, OdString::kEmpty); } else { if (!pH) { return doDrawImp(drawableFlags, pDrawable, OdString::kEmpty); } return drawHatch(drawableFlags, pDrawable, *pH); } } bool PDF2dExportView::drawHatch(OdUInt32 drawableFlags, const OdGiDrawable* pDrawable, OdDbBaseHatchPE& hatchPE) { OdSaveState ssHb(m_bHatchToBitmapMode); if(hatchPE.isGradient(pDrawable)) m_bHatchToBitmapMode = device()->exportParams().gradientHatchesExportType() == PDFExportParams::kBitmap; else if(hatchPE.isSolidFill(pDrawable) || hatchPE.hasBackgroundColor(pDrawable)) m_bHatchToBitmapMode = device()->exportParams().solidHatchesExportType() == PDFExportParams::kBitmap; if(hatchPE.isSolidFill(pDrawable)) return drawSolidHatch(drawableFlags, pDrawable, hatchPE); else return doDrawImp(drawableFlags, pDrawable, OdString::kEmpty); return false; } bool PDF2dExportView::drawSolidHatch(OdUInt32 drawableFlags, const OdGiDrawable* pDrawable, OdDbBaseHatchPE& hatchPE) { // export non solid hatch using bitmap||vectorizer OdSaveState ssSh(m_pSolidHatch); OdSaveState ssRc(m_bRestartClipper); if(hatchPE.isGradient(pDrawable)) { m_bHatchToBitmapMode = (device()->exportParams().gradientHatchesExportType() == PDFExportParams::kBitmap); } else { m_bHatchToBitmapMode = true; switch(device()->exportParams().solidHatchesExportType()) { case PDFExportParams::kPdfPaths: case PDFExportParams::kPolygons: { m_pSolidHatch = pDrawable; OdGeExtents3d extents; if((!gsWriter().isRecordingMetafile()) && (::eOk == pDrawable->getGeomExtents(extents))) { OdGeMatrix3d mx = objectToDeviceMatrix(); extents.transformBy(mx); if(m_pViewportClip->checkExtentsVisibility(extents) == OdGiOrthoClipperEx::kStatusClipped) m_bRestartClipper = true; } } break; case PDFExportParams::kDrawing: m_bHatchToBitmapMode = false; break; } } return doDrawImp(drawableFlags, pDrawable, OdString::kEmpty); } void PDF2dExportView::passTextAnnotationToMetafiler(const OdString &layer) { ODA_ASSERT(!deviceGeom()); if(m_pMetafiler->metafile()) { m_pMetafiler->flush(); m_pMetafiler->add(new RecTextAnnot(m_annotationText, layer)); } m_annotationText.empty(); } void PDF2dExportView::update() { Od2dExportView::update(); } //==================================================================== // ////////////////////////////////////////////////////////////////// double PDF2dExportView::calcScaledDeviation(const OdGiDeviationType type, const OdGePoint3d& pt) const { if(!OdEqual(dcScreenMax().x, dcScreenMin().x) && !OdEqual(dcScreenMax().y, dcScreenMin().y)) { OdGePoint2d pixelsPerUnit; OdGsBaseVectorizer::getNumPixelsInUnitSquare(pt, pixelsPerUnit, false); pixelsPerUnit.x = fabs(pixelsPerUnit.x); pixelsPerUnit.y = fabs(pixelsPerUnit.y); if(type == kOdGiMaxDevForCircle) { unsigned int viewres = userGiContext()->circleZoomPercent(m_viewInfo.viewportObjectId); if(viewres < 1 || viewres > 20000) viewres = 100; double cft = double(viewres) / 100; pixelsPerUnit.x *= cft; pixelsPerUnit.y *= cft; } double deviation = 0.5 / pow(odmax(pixelsPerUnit.x, pixelsPerUnit.y), 1. / 64.); //calculate deviation this way gives us the same result as Autocad export //because the increased deviation will be compensated with increased radius of arcs if(OdGsBaseVectorizer::isPerspective()) { OdGePoint3d eyePt(pt); eyePt.transformBy(viewingMatrix()); deviation *= 1.0 - eyePt.z / focalLength(); if(deviation < 0.0) deviation = -deviation; } return deviation; } return 1e100; } double PDF2dExportView::deviation(const OdGiDeviationType type, const OdGePoint3d& pt) const { //Former kVpMaxDevForBoundary and kVpMaxDevForIsoline are used for other purposes now switch(type) { case kOdGiMaxDevForCircle: m_awareFlags.setFlag(kVpMaxDevForCircle); break; case kOdGiMaxDevForCurve: case kOdGiMaxDevForBoundary: m_awareFlags.setFlag(kVpMaxDevForCurve); break; default: m_awareFlags.setFlag(kVpMaxDevForFacet); break; } double deviation = view().getDeviation(type, pt, true/*!GETBIT(m_flags, kModelCache)*/); //recalculate always to provide the same result either with GsCache or w/o it return !device()->exportParams().get720DPIMode() ? deviation : deviation * .1; } void PDF2dExportView::ownerDrawDc(const OdGePoint3d& _origin, const OdGeVector3d& _u, const OdGeVector3d& _v, const OdGiSelfGdiDrawable* pDrawable, bool bDcAligned, bool bAllowClipping) { if(mode() != OdGsView::k2DOptimized) return; OdOleItemHandlerPtr pOLE = OdOleItemHandler::cast(pDrawable); if(pOLE.isNull()) { Od2dExportView::ownerDrawDc(_origin, _u, _v, pDrawable, bDcAligned, bAllowClipping); return; } if(deviceGeom()) { drawOwnerDrawDc(_origin, _u, _v, pDrawable, bDcAligned, bAllowClipping); return; } { OdGeExtents3d extents; extents.comparingSet(_origin, _origin + _u); extents.addPoint(_origin + _v); extents.addPoint(_origin + _u + _v); OdGiBaseVectorizerImpl::setExtents((OdGePoint3d*)&extents); //set extents regardless updateExtentsOnly() mode, since geometry output ends here } if(gsWriter().isRecordingMetafile()) { OdGeMatrix3d x = eyeToOutputTransform() * getModelToEyeTransform(); //no geometry, recording metafile m_pMetafiler->flush(); m_pMetafiler->add(new RecOwnerDrawDc(_origin, _u, _v, pDrawable, bDcAligned, bAllowClipping)); } } void PDF2dExportView::drawOwnerDrawDc( const OdGePoint3d& _origin, const OdGeVector3d& _u, const OdGeVector3d& _v, const OdGiSelfGdiDrawable* pDrawable, bool bDcAligned, bool bAllowClipping) { ImageData img; if(getOwnerDrawDcAsImage(_origin, _u, _v, pDrawable, bDcAligned, bAllowClipping, img)) output().destGeometry().rasterImageProc(img.m_origin, img.m_uVec, img.m_vVec, img.m_pImage, 0, 0); } class RasterGeometry: public OdGiEmptyGeometry { ImageData& m_resImg; OdGiConveyorOutput& m_output; OdGiConveyorGeometry& m_geomToRestore; public: RasterGeometry(ImageData& resImg, OdGiConveyorOutput& o): m_resImg(resImg), m_output(o), m_geomToRestore(o.destGeometry()) { m_output.setDestGeometry(*this); } ~RasterGeometry() override { m_output.setDestGeometry(m_geomToRestore); } void rasterImageProc( const OdGePoint3d& origin, const OdGeVector3d& u, const OdGeVector3d& v, const OdGiRasterImage* pImage, const OdGePoint2d* uvBoundary, OdUInt32 numBoundPts, bool transparency = false, double brightness = 50.0, double contrast = 50.0, double fade = 0.0) ODRX_OVERRIDE { m_resImg.m_origin = origin; m_resImg.m_uVec = u; m_resImg.m_vVec = v; m_resImg.m_pImage = pImage; } }; bool PDF2dExportView::getOwnerDrawDcAsImage( const OdGePoint3d& _origin, const OdGeVector3d& _u, const OdGeVector3d& _v, const OdGiSelfGdiDrawable* pDrawable, bool bDcAligned, bool bAllowClipping, ImageData& resImg) { OdOleItemHandlerPtr pOLE = OdOleItemHandler::cast(pDrawable); ODA_ASSERT(pOLE.get()); OdOleItemHandler::PlotQuality plotQuality = pOLE->outputQuality(); bool bMonochrome = false; double iScale = 1.; if(plotQuality == OdOleItemHandler::kMonochrome) { iScale = 72. / (double)device()->exportParams().bwImagesDPI(); bMonochrome = true; } else iScale = 72. / (double)device()->exportParams().colorImagesDPI(); OdGeMatrix3d x = getModelToEyeTransform(); OdGePoint3d origin(x * _origin); OdGeVector3d u(x * _u), v(x * _v); OdGeExtents3d ext(origin, origin); ext.addPoint(origin + u); ext.addPoint(origin + v); ext.addPoint(origin + u + v); OdGePoint3d imgMin = ext.minPoint(); OdGePoint3d imgMax = ext.maxPoint(); OdGePoint3d origin2; OdGeVector3d u2; OdGeVector3d v2; origin2 = imgMin; v2 = u2 = imgMax - imgMin; u2.z = u2.y = 0.; v2.z = v2.x = 0.; x = view().eyeToScreenMatrix(); origin2.transformBy(x); u2.transformBy(x); v2.transformBy(x); OdGeVector3d u2Tmp = (u2 / ((double)device()->exportParams().getGeomDPI() / 72.)) / iScale; //downscale vectors because of geomDPI (see SUP-716) OdGeVector3d v2Tmp = (v2 / ((double)device()->exportParams().getGeomDPI() / 72.)) / iScale; //u2 /= iScale; //v2 /= iScale; OdUInt32 rasterWidth, rasterHeight; try { rasterWidth = OdRoundToLong(fabs(u2Tmp.x)); rasterHeight = OdRoundToLong(fabs(v2Tmp.y)); if(OdUInt64(rasterWidth) * rasterHeight > 48 * 1024 * 1024) // CreateDibBitmap limitation (huge rasters are scaled anyway) { double scale = sqrt((((double)(rasterWidth) / 1024.) * ((double)(rasterHeight) / 1024.)) / 48.);//that is equal to (rasterWidth * rasterHeight) / (48 * 1024 * 1024); if(OdLessOrEqual(scale, 1.)) scale = 1.1; while(OdUInt64(rasterWidth) * rasterHeight > 48 * 1024 * 1024) { rasterWidth = OdRoundToLong((double)rasterWidth / scale); rasterHeight = OdRoundToLong((double)rasterHeight / scale); } } } catch(const OdError&) { // #10225 : Can't render if arithmetic overflow thrown return false; } //u2 *= iScale; //v2 *= iScale; OdGsDCRect rect; // RECT rect.m_min.x = 0; // left rect.m_min.y = rasterHeight; // top rect.m_max.x = rasterWidth; // right rect.m_max.y = 0; // bottom OdSmartPtr pOdGiSelfGiDrawablePE = OdGiSelfGiDrawablePE::cast(pDrawable); if( pOdGiSelfGiDrawablePE.get() && pOdGiSelfGiDrawablePE->draw(const_cast(pDrawable), &output().destGeometry(), drawContext(), regenType(), origin2, u2, v2, rect, true)) return false; RasterGeometry rg(resImg, output()); OdGeMatrix3d m2w; if(isGsCacheEnabled()) { //do not need to transform when metafile is active. Transformations will be applied on metafile playing //we just need to recalculate u and v to handle if OLE has negative width or height OdGeExtents3d ext(_origin, _origin); ext.addPoint(_origin + _u); ext.addPoint(_origin + _v); ext.addPoint(_origin + _u + _v); OdGePoint3d imgMin = ext.minPoint(); OdGePoint3d imgMax = ext.maxPoint(); origin2 = imgMin; v2 = u2 = imgMax - imgMin; u2.z = u2.y = 0.; v2.z = v2.x = 0.; ////////////////////////////////////////////////////////////////////////// m2w = m_pModelToEyeProc->modelToWorldTransform(); } drawWithGdiBmp(pDrawable, this, m2w*origin2, m2w*u2, m2w*v2, rasterWidth, rasterHeight, rect, false/*bMonochrome*/); if(!resImg.m_pImage.isNull()) //drawWithGdiBmp defined only for Windows platforms { if(bMonochrome) { OdSmartPtr pMon = OdRxObjectImpl::createObject(); pMon->setOriginal(resImg.m_pImage); resImg.m_pImage = pMon; } return true; } return false; } void PDF2dExportView::nurbs(const OdGeNurbCurve3d& nurbs) { if(isGsCacheEnabled()) { const bool bSavedFlag = GETBIT(drawContextFlags(), kEmbranchmentSimplNurbs); try { setDrawContextFlags(kEmbranchmentSimplNurbs, false); super::nurbs(nurbs); setDrawContextFlags(kEmbranchmentSimplNurbs, bSavedFlag); } catch(...) { setDrawContextFlags(kEmbranchmentSimplNurbs, bSavedFlag); } } else super::nurbs(nurbs); } double PDF2dExportView::linetypeGenerationCriteria() const { m_awareFlags.setFlag(kVpViewport); return super::linetypeGenerationCriteria(); } }