/////////////////////////////////////////////////////////////////////////////// // 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. /////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // // PdfWriter.cpp: implementation of the GsPdfFactory, GsPdfDevice, GsPdfView classes. // ////////////////////////////////////////////////////////////////////// #include "PdfExportCommon.h" #include "OdBinaryData.h" #include "EllipseArc2Bezier.h" #include "Pdf2dExportDevice.h" #include "PdfResourceDictionary.h" #include "PdfPageNodeDictionary.h" #include "PdfImage.h" #include "PdfAux.h" #include "PdfExportService.h" #include "PdfOptionalContentGroupDictionary.h" #include "PdfCatalogDictionary.h" #include "PdfOptionalContentPropertiesDictionary.h" #include "PdfOptionalContentConfigurationDictionary.h" #include "PdfOCManager.h" #include "PdfXObjectForm.h" #include "Ge/GeEllipArc2d.h" #include "Ge/GeEllipArc3d.h" #include "Ge/GeNurbCurve2d.h" #include "Ge/GeKnotVector.h" #include "Ge/GeLineSeg2d.h" #include "PdfType0Font.h" #include "PdfCIDFont2.h" #include "PdfCID2GIDStream.h" #include "OdTrueTypeFontBase.h" #include "PdfShadingPattern.h" #include "PdfShadingT4Dictionary.h" #include "MemoryStream.h" #include "Pdf3dAnnotationDictionary.h" #include "Pdf3dStream.h" #include "Pdf3dActivationDictionary.h" #include "Pdf3dViewDictionary.h" #include "PdfProjectionDictionary.h" #include "Pdf3dRenderModeDictionary.h" #include "PdfOptionalContentPropertiesDictionary.h" #include "PdfSquareAnnotationDictionary.h" #include "PdfLinkAnnotationDictionary.h" #include "PdfURIActionDictionary.h" #include "PdfGoToActionDictionary.h" #include "PdfExtGState.h" #include "DynamicLinker.h" #include "Gi/GiRasterWrappers.h" #include "RxRasterServices.h" #include "OdPlatformStreamer.h" //using this instead of include "GeClipUtils.h" namespace OdGeClipUtils { // Checks is polygon has self-intersections. bool isPolygonSelfItersections(const OdGePoint2d* pPoints, OdUInt32 nbPoints, const OdGeTol& tol = OdGeContext::gTol); // Fixes polygon self-intersections and returns result as transformed 3d points array. OdUInt32 fixPolygonSelfIntersections(const OdGePoint2d* pPoints, OdUInt32 nbPoints, OdInt32Array& nCounts, OdGePoint3dArray& nPoints, const OdGeMatrix3d& mtx); } //==================================================================== // namespace TD_PDF_2D_EXPORT { void PDF2dExportGeometry::saveGraphState(const PDFDevicePoint &LastPoint) { ODA_ASSERT(!m_bGraphStateFixed); m_bGraphStateFixed = true; m_num = 0; saveLastPoint(LastPoint); } void PDF2dExportGeometry::saveLastPoint(const PDFDevicePoint &LastPoint) { ODA_ASSERT(m_bGraphStateFixed); ODA_ASSERT(!(m_LastPoint.x == LastPoint.x && m_LastPoint.y == LastPoint.y)); m_bOnePointPoly = false; PDFIContentCommands *pOut = cc(); if (m_num >= 2) { OdGePoint2d start_point; OdGePoint2d middle_point; if (m_num < 6) { start_point.x = m_points[m_num - 2].x; start_point.y = m_points[m_num - 2].y; middle_point.x = m_points[m_num - 1].x; middle_point.y = m_points[m_num - 1].y; } else { start_point.x = m_points[m_num - 3].x; start_point.y = m_points[m_num - 3].y; middle_point.x = m_points[m_num - 2].x; middle_point.y = m_points[m_num - 2].y; } OdGePoint2d last_point(LastPoint.x, LastPoint.y); OdGeLineSeg2d res_line(start_point, last_point); OdGeLineSeg2d start_line(start_point, middle_point); OdGeLineSeg2d mid_line(middle_point, last_point); double distance = 1; if (res_line.length() && res_line.length() > start_line.length() && res_line.length() > mid_line.length()) //if res_line is lesser than start_line or mid_line the polyline changes direction { double paramOf = res_line.paramOf(middle_point); OdGePoint2d bb = res_line.evalPoint(paramOf); distance = bb.distanceTo(middle_point); } const double coeff = !exportParams().get720DPIMode() ? 1. : .1; const double dev = .5 * 72. / (double)m_pParamsHolder->getParams().getGeomDPI(); /*0.5 pixel scaled to correspond geom dpi*/ if (OdZero(distance, dev * coeff)) if (m_num < 6) m_points[m_num - 1] = LastPoint; else m_points[m_num - 2] = LastPoint; else { if (m_num < 6) m_points[m_num++] = LastPoint; if (m_num == 6) { pOut->m(m_points[0].x, m_points[0].y, m_CoordProc); for (OdUInt32 f = 1; f < m_num - 2; ++f) pOut->l(m_points[f].x, m_points[f].y, m_CoordProc); m_num++; } else if (m_num == 7) { pOut->l(m_points[m_num-3].x, m_points[m_num - 3].y, m_CoordProc); m_points[m_num - 3] = m_points[m_num - 2]; m_points[m_num - 2] = LastPoint; } } } else m_points[m_num++] = LastPoint; m_LastPoint.x = LastPoint.x; m_LastPoint.y = LastPoint.y; } void PDF2dExportGeometry::GraphStateChanged() { if (m_bGraphStateFixed) { PDFIContentCommands *pOut = cc(); double X0 = m_points[0].x; double Y0 = m_points[0].y; if (m_num == 5 && X0 == m_points[4].x && Y0 == m_points[4].y) { double X1 = m_points[1].x , Y1 = m_points[1].y , X2 = m_points[2].x , Y2 = m_points[2].y , X3 = m_points[3].x , Y3 = m_points[3].y; bool bRe = (X0 == X1 && X2 == X3 && Y0 == Y3 && Y2 == Y1) || (Y0 == Y1 && Y2 == Y3 && X0 == X3 && X2 == X1); if (bRe) { double w = fabs(X0 - X2); double h = fabs(Y0 - Y2); if (h != 0 && w != 0) { pOut->re(odmin(X0, X2), odmin(Y0, Y2), w, h); pOut->S(); m_bOnePointPoly = true; m_bGraphStateFixed = false; m_LastPoint.x = m_LastPoint.y = -HUGE_VAL; m_num = 0; return; } } } if (m_bOnePointPoly) { PDFIContentCommands::drawPoint(*pOut, OdGePoint2d(m_LastPoint.x, m_LastPoint.y), m_CoordProc); pOut->S(); } else { ODA_ASSERT(m_num > 1); if (m_num < 6) { pOut->m(m_points[0].x, m_points[0].y, m_CoordProc); for (OdUInt32 f = 1; f < m_num - 1; ++f) pOut->l(m_points[f].x, m_points[f].y, m_CoordProc); } else { pOut->l(m_points[m_num-3].x, m_points[m_num - 3].y, m_CoordProc); } if (m_points[0].x == m_LastPoint.x && m_points[0].y == m_LastPoint.y) { pOut->s(); // h & S } else { if (m_num < 6) pOut->l(m_points[m_num - 1].x, m_points[m_num - 1].y, m_CoordProc); else pOut->l(m_points[m_num - 2].x, m_points[m_num - 2].y, m_CoordProc); pOut->S(); } } m_bOnePointPoly = true; m_bGraphStateFixed = false; m_LastPoint.x = m_LastPoint.y = -HUGE_VAL; m_num = 0; } } bool PDF2dExportGeometry::needNewPolyline() const { return !m_bGraphStateFixed; } bool PDF2dExportGeometry::isDublicatedSegment(const PDFDevicePoint &LastPoint) const { ODA_ASSERT(m_bGraphStateFixed); return m_LastPoint.x == LastPoint.x && m_LastPoint.y == LastPoint.y; } void PDF2dExportGeometry::ContinuePolyline(const PDFDevicePoint &LastPoint) { ODA_ASSERT(m_bGraphStateFixed); if (!isDublicatedSegment(LastPoint)) { saveLastPoint(LastPoint); } } void PDF2dExportGeometry::StartNewPolyline(const PDFDevicePoint &LastPoint) { GraphStateChanged(); processModifiedTraits(); saveGraphState(LastPoint); m_bOnePointPoly = true; } // call from beginViewVectorization() void PDF2dExportGeometry::StartOptimizator() { GraphStateChanged(); m_curRGB = 0xFFFFFFFF; m_curColorIndex = 0; m_bRGBActive = true; // don't change it to false m_lineWeight = -HUGE_VAL; m_savedLineWeight = -HUGE_VAL; m_curLayerName = ""; m_LayerOpen = false; m_stubLayerCreated = false; } // call from endViewVectorization() void PDF2dExportGeometry::StopOptimizator() { ClosePolyline(); if (m_LayerOpen) { CloseLayerBracket(); m_curLayerName = ""; } } void PDF2dExportGeometry::ClosePolyline() { GraphStateChanged(); } bool PDF2dExportGeometry::CheckLayerVisibility(const OdString& layer_name) const { OdString layerNameCut = layer_name.right(PDF_FROZEN_LAYER_SIZE); if(layerNameCut == PDF_FROZEN_LAYER) layerNameCut = layer_name.left(layer_name.getLength() - PDF_FROZEN_LAYER_SIZE); else layerNameCut.empty(); return m_pParamsHolder->isLayerOriginallyVisible(layer_name) && m_pParamsHolder->isLayerOriginallyVisible(layerNameCut); } void PDF2dExportGeometry::OpenLayerBracket(const OdString &layer_name) { ODA_ASSERT(isLayersSupported()); if (m_curLayerName.isEmpty()) // strange situation return; PDFIContentCommands *pOut = cc(); PDFDocument &PDFDoc = document(); PDFNamePtr pOCG = PDFOCManager::getOCGbyName(layer_name, PDFDoc, getResourceDictionary()); if(pOCG.isNull()) { const bool bVisible = CheckLayerVisibility(layer_name); pOCG = PDFOCManager::CreateOC4Layer(layer_name, PDFDoc, getResourceDictionary(), bVisible); } pOut->BDC(PDF_OC_NAME, pOCG); m_LayerOpen = true; } void PDF2dExportGeometry::CloseLayerBracket() { ODA_ASSERT(isLayersSupported()); PDFIContentCommands *pOut = cc(); pOut->EMC(); m_LayerOpen = false; } // frozen layers are always around BlockRefs and are not the same as general "layer" void PDF2dExportGeometry::open_Frozen_Layer(const OdString &layer_name) { ODA_ASSERT(isLayersSupported()); // close "general" layer before if (m_LayerOpen) { GraphStateChanged(); CloseLayerBracket(); m_curLayerName = ""; m_prevLayerName = ""; } PDFDocument &PDFDoc = document(); PDFNamePtr pOCG = PDFOCManager::getOCGbyName(layer_name, PDFDoc, getResourceDictionary()); if(pOCG.isNull()) { const bool bVisible = CheckLayerVisibility(layer_name); pOCG = PDFOCManager::CreateOC4Layer(layer_name, PDFDoc, getResourceDictionary(), bVisible); } PDFIContentCommands* pOut; if(!recordingEnabled() && currentPdfPage()) //recording to the stream can be disabled if the previous entity was completely transparent pOut = m_pParamsHolder->ContentCommands();//so we should handle this situation for frozen layers, because such layers go into pdf else //independent from entities traits pOut = cc(); //(the same behavior is implemented below in PDF2dExportGeometry::close_Frozen_Layer()) pOut->BDC(PDF_OC_NAME, pOCG); } void PDF2dExportGeometry::close_Frozen_Layer() { if (m_LayerOpen) { GraphStateChanged(); CloseLayerBracket(); m_curLayerName = ""; m_prevLayerName = ""; } PDFIContentCommands* pOut; if(!recordingEnabled() && currentPdfPage()) pOut = m_pParamsHolder->ContentCommands(); else pOut = cc(); pOut->EMC(); } //==================================================================== // void PDF2dExportGeometry::dc_polyline(OdUInt32 nPts, const OdGePoint2d* pPoints) { PDFIContentCommands *pOut = cc(); if (isSimpleGeomOptimizationEnabled() && m_stateDrawingContour == 0 && m_bRecordingEnabled) { const bool bRoundTo720DPI = m_CoordProc == kRoundTo720DPI; PDFDevicePoint tmp; tmp.x = !bRoundTo720DPI ? (int)(pPoints[0].x) : (OdRoundToLong(10. * pPoints[0].x)) / 10.; tmp.y = !bRoundTo720DPI ? (int)(pPoints[0].y) : (OdRoundToLong(10. * pPoints[0].y)) / 10.; if (!needNewPolyline() && !isDublicatedSegment(tmp)) { ClosePolyline(); } if (needNewPolyline()) { StartNewPolyline(tmp); } else { ContinuePolyline(tmp); } for(OdUInt32 f = 1; f < nPts; ++f) { PDFDevicePoint tmp; tmp.x = !bRoundTo720DPI ? (int)(pPoints[f].x) : (OdRoundToLong(10. * pPoints[f].x)) / 10.; tmp.y = !bRoundTo720DPI ? (int)(pPoints[f].y) : (OdRoundToLong(10. * pPoints[f].y)) / 10.; ContinuePolyline(tmp); } } else { processModifiedTraits(); if (nPts == 1 && m_stateDrawingContour == 0) { PDFIContentCommands::drawPoint(*pOut, pPoints[0], m_CoordProc); } else { if(m_stateDrawingContour == 2) { pOut->m((pPoints[0].x), (pPoints[0].y), m_CoordProc); m_stateDrawingContour = 1; } if(m_stateDrawingContour == 0 || m_stateDrawingContour == 3) { pOut->m((pPoints[0].x), (pPoints[0].y), m_CoordProc); } for(OdUInt32 f = 1; f < nPts; ++f) { pOut->l(pPoints[f].x, pPoints[f].y, m_CoordProc); } } if(m_stateDrawingContour == 0) { pOut->S(); } } } //==================================================================== // void PDF2dExportGeometry::dc_polygon(OdUInt32 nPoints, const OdGePoint2d* pPoints) { ClosePolyline(); if(!nPoints) return; const OdGePoint2d* pOptimizedPoints = 0; OdGePoint2d* pNewPoints = 0; OdUInt32 nNewPoints = 0; const bool bOptimize = isSimpleGeomOptimizationEnabled(); double oldLw = -1; const bool bRoundTo720DPI = (m_CoordProc == kRoundTo720DPI); const bool bPlinePart = !bRoundTo720DPI && m_bPlineDrawingMode; if(bOptimize) { pNewPoints = new OdGePoint2d[nPoints]; pNewPoints[0] = pPoints[0]; nNewPoints++; for(OdUInt32 j = 1; j < nPoints; j++) { if (((int)pPoints[j].x != (int)pNewPoints[nNewPoints - 1].x || (int)pPoints[j].y != (int)pNewPoints[nNewPoints - 1].y) && !bRoundTo720DPI || (pPoints[j].x != pNewPoints[nNewPoints - 1].x || pPoints[j].y != pNewPoints[nNewPoints - 1].y) && bRoundTo720DPI) { pNewPoints[nNewPoints] = OdGePoint2d(pPoints[j].x, pPoints[j].y); nNewPoints++; } } if(nNewPoints > 1) { if(((int)pNewPoints[0].x == (int)pNewPoints[nNewPoints - 1].x) && ((int)pPoints[0].y == (int)pNewPoints[nNewPoints - 1].y) && !bRoundTo720DPI || (pNewPoints[0].x == pNewPoints[nNewPoints - 1].x) && (pPoints[0].y == pNewPoints[nNewPoints - 1].y) && bRoundTo720DPI) --nNewPoints; } pOptimizedPoints = pNewPoints; } else { nNewPoints = nPoints; pOptimizedPoints = pPoints; } PDFIContentCommands *pOut = cc(); if(nNewPoints == 1) { oldLw = m_lineWeight; set_LineWeight(0); processModifiedTraits(); pOut->m(pOptimizedPoints[0].x, pOptimizedPoints[0].y, !bPlinePart ? m_CoordProc : kProcessAsDouble); pOut->l(pOptimizedPoints[0].x, pOptimizedPoints[0].y, !bPlinePart ? m_CoordProc : kProcessAsDouble); pOut->s(); } else if(nNewPoints == 2) { oldLw = m_lineWeight; set_LineWeight(0); processModifiedTraits(); pOut->m(pOptimizedPoints[0].x, pOptimizedPoints[0].y, !bPlinePart ? m_CoordProc : kProcessAsDouble); pOut->l(pOptimizedPoints[1].x, pOptimizedPoints[1].y, !bPlinePart ? m_CoordProc : kProcessAsDouble); pOut->s(); } else if (nNewPoints > 2) { if (GETBIT(m_effTraits.drawFlags(), OdGiSubEntityTraits::kDrawContourFill)) { // .DGN specific mode ODCOLORREF rgb = m_curSecondaryColorRGB; if (exportParams().colorPolicy() == PDFExportParams::kGrayscale) convertColorToGrayscale(rgb); else if (exportParams().colorPolicy() == PDFExportParams::kMono && (ODGETRED(rgb) != 255 || ODGETGREEN(rgb) != 255 || ODGETBLUE(rgb) != 255)) rgb = ODRGBA(0, 0, 0, ODGETALPHA(rgb)); set_Color(rgb); processModifiedTraits(); pOut->m(pOptimizedPoints[0].x, pOptimizedPoints[0].y, !bPlinePart ? m_CoordProc : kProcessAsDouble); for (OdUInt32 f = 1; f < nNewPoints; ++f) pOut->l(pOptimizedPoints[f].x, pOptimizedPoints[f].y, !bPlinePart ? m_CoordProc : kProcessAsDouble); pOut->b(); if (fillMode() && m_renderMode != OdGsView::kHiddenLine) { const OdCmEntityColor& color = m_effTraits.trueColor(); if(color.isByColor()) set_Color(ODRGB(color.red(), color.green(), color.blue())); else set_Color(m_refDevice.getColor(color.colorIndex())); processModifiedTraits(); pOut->m(pOptimizedPoints[0].x, pOptimizedPoints[0].y, !bPlinePart ? m_CoordProc : kProcessAsDouble); for(OdUInt32 f = 1; f < nNewPoints; ++f) pOut->l(pOptimizedPoints[f].x, pOptimizedPoints[f].y, !bPlinePart ? m_CoordProc : kProcessAsDouble); pOut->s(); } if (m_bRGBActive) { pOut->rg(m_curRGB); } else { pOut->cs(DWG_PALETTE_NAME); m_bDwgPaletteNeeded = true; pOut->sc((OdUInt8)m_curColorIndex); } } else { if (m_fillRule == kFillStrokeCloseRule && m_lineWeight) { oldLw = m_lineWeight; set_LineWeight(0); } processModifiedTraits(); pOut->m(pOptimizedPoints[0].x, pOptimizedPoints[0].y, !bPlinePart ? m_CoordProc : kProcessAsDouble); for (OdUInt32 f = 1; f < nNewPoints; ++f) pOut->l(pOptimizedPoints[f].x, pOptimizedPoints[f].y, !bPlinePart ? m_CoordProc : kProcessAsDouble); if (m_fillRule != kNoFill) { if (m_fillRule == kFillEvenOddRule) pOut->f_odd(); else if (m_fillRule == kFillStrokeCloseRule) pOut->b(); else pOut->f(); } } } if (oldLw > 0) set_LineWeight(oldLw); if (bOptimize) delete[] pNewPoints; } //==================================================================== // void PDF2dExportGeometry::dc_fill() { PDFIContentCommands *pOut = cc(); if (m_fillRule != kNoFill) { if (m_fillRule == kFillEvenOddRule) pOut->f_odd(); else if (m_fillRule == kFillStrokeCloseRule) pOut->b_odd(); else pOut->f(); } } //==================================================================== // void PDF2dExportGeometry::dc_nurb(const OdGeNurbCurve2d& nurb ) { //ClosePolyline(); int degree; bool rational, periodic; OdGeKnotVector aKt; OdGePoint2dArray aCP; OdGeDoubleArray aWt; nurb.getDefinitionData(degree, rational, periodic, aKt, aCP, aWt); ODA_ASSERT(degree == 3); ODA_ASSERT(!rational); ODA_ASSERT(aCP.size() == 4); PDFIContentCommands *pOut = cc(); if (isSimpleGeomOptimizationEnabled()) { const double coeff = !exportParams().get720DPIMode() ? 1. : .1; OdGeVector2d vec = aCP[3] - aCP[0]; if (vec.length() < coeff) { OdGePoint2d points2d[2]; points2d[0] = aCP[0]; points2d[1] = aCP[3]; dc_polyline(2, points2d); } else { OdGeExtents2d extents; vec.normalize(); OdGeMatrix2d matTranslate, matRotate; matTranslate.setToTranslation(-aCP[0].asVector()); OdGePoint2dArray aCP_Tmp = aCP; aCP_Tmp[0].transformBy(matTranslate); aCP_Tmp[1].transformBy(matTranslate); aCP_Tmp[2].transformBy(matTranslate); aCP_Tmp[3].transformBy(matTranslate); matRotate.setCoordSystem(OdGePoint2d::kOrigin, vec, -vec.perpVector()); aCP_Tmp[0].transformBy(matRotate); aCP_Tmp[1].transformBy(matRotate); aCP_Tmp[2].transformBy(matRotate); aCP_Tmp[3].transformBy(matRotate); if (fabs(aCP_Tmp[1].y) < coeff && fabs(aCP_Tmp[2].y) < coeff) //it looks like a line in pdf { OdGePoint2d points2d[2]; points2d[0] = aCP[0]; points2d[1] = aCP[3]; dc_polyline(2, points2d); } else { ClosePolyline(); processModifiedTraits(); pOut->m(aCP[0], m_CoordProc == TD_PDF::kRoundTo720DPI ? m_CoordProc : TD_PDF::kProcessAsDouble); pOut->c(aCP[1], aCP[2], aCP[3], m_CoordProc == TD_PDF::kRoundTo720DPI ? m_CoordProc : TD_PDF::kProcessAsDouble); pOut->S(); } } } else { ClosePolyline(); processModifiedTraits(); pOut->m(aCP[0], m_CoordProc == TD_PDF::kRoundTo720DPI ? m_CoordProc : TD_PDF::kProcessAsDouble); pOut->c(aCP[1], aCP[2], aCP[3], m_CoordProc == TD_PDF::kRoundTo720DPI ? m_CoordProc : TD_PDF::kProcessAsDouble); pOut->S(); } } void PDF2dExportGeometry::dc_circle(const OdGeEllipArc3d& ellipse) { dc_ellipse(ellipse); } void PDF2dExportGeometry::dc_ellipse(const OdGeEllipArc3d& ellipArc3d, bool bRoundCoord) { // got it? probably you are in extents calculation code. You shouldn't be here in extents mode. ODA_ASSERT(ellipArc3d.normal().isParallelTo(OdGeVector3d::kZAxis)); OdGeEllipArc2d ellipArc2d( ellipArc3d.center().convert2d(), ellipArc3d.majorAxis().convert2d(), ellipArc3d.minorAxis().convert2d(), ellipArc3d.majorRadius(), ellipArc3d.minorRadius(), ellipArc3d.startAng(), ellipArc3d.endAng() ); if (isSimpleGeomOptimizationEnabled()) //try to optimize curve in pdf to reduce file size { const double coeff = !exportParams().get720DPIMode() ? 1. : .1; //check if the curve looks like line (or like point again) if (!ellipArc2d.startPoint().isEqualTo(ellipArc2d.endPoint())) //the curve is a part of an ellipse { OdGeExtents2d extents; ellipArc2d.getGeomExtents(extents); double maxExt = odmax(fabs(extents.maxPoint().x - extents.minPoint().x), fabs(extents.maxPoint().y - extents.minPoint().y)); if(maxExt < coeff) //extents lesser than 1 pixel, so the curve looks like line anyway { OdGePoint2d points2d[2]; points2d[0] = extents.minPoint(); points2d[1] = extents.maxPoint(); //well the points are two corners of rectangle, but the width is lesser than 1 pixel, so we don't care dc_polyline(2, points2d); return; } } else //the curve is an ellipse or circle { const double checkR = .5 * coeff; double minR = ellipArc2d.minorRadius(); double maxR = ellipArc2d.majorRadius(); if (minR < checkR || maxR < checkR) { if (minR < checkR && maxR < checkR) //looks like point in pdf again { OdGePoint2d points2d[2]; points2d[0] = points2d[1] = ellipArc2d.center(); dc_polyline(2, points2d); return; } else //looks like line, let's find the coordinates { OdGePoint2d points2d[2]; points2d[0] = ellipArc2d.center() + maxR*ellipArc2d.majorAxis(); points2d[1] = ellipArc2d.center() - maxR*ellipArc2d.majorAxis(); dc_polyline(2, points2d); return; } } } } ClosePolyline(); processModifiedTraits(); PDFIContentCommands *pOut = cc(); PDFIContentCommands::drawEllipse(*pOut, ellipArc2d, false, deviation(kOdGiMaxDevForCurve, OdGePoint3d(ellipArc2d.center().x, ellipArc2d.center().y, 0.)), bRoundCoord ? m_CoordProc : kProcessAsDouble); pOut->S(); } //==================================================================== // void PDF2dExportGeometry::dc_layer(const OdString &layer_name, bool bClose) { GraphStateChanged(); if (bClose) CloseLayerBracket(); OpenLayerBracket(layer_name); } //==================================================================== // void PDF2dExportGeometry::dc_gouraud_polytriangle( const OdGePoint3d* pVertexList, OdInt32 faceListSize, const OdInt32* pFaceList, const OdCmEntityColor* pColors) { ClosePolyline(); processModifiedTraits(); PDFIContentCommands *pOut = cc(); PDFDocument &PDFDoc = document(); PDFShadingT4Ptr pType4Shading = PDFShadingT4::createObject( PDFDoc, true ); // fill Shading stream with triangls pType4Shading->preFill(); // calculate coordinates range double dMaxx = -HUGE_VAL; double dMinx = HUGE_VAL; double dMaxy = -HUGE_VAL; double dMiny = HUGE_VAL; OdInt32 f; for(f=0; faddTriangle( x1, y1, x2, y2, x3, y3, color1.red(), color1.green(), color1.blue(), color2.red(), color2.green(), color2.blue(), color3.red(), color3.green(), color3.blue() ); } f += num + 1; } pType4Shading->postFill(dMinx, dMaxx, dMiny, dMaxy); //PDFShadingPatternPtr pPattern = PDFShadingPattern::createObject( PDFDoc, true ); //pPattern->setPatternType(2); //pPattern->setShading(pType4Shading); PDFResourceDictionaryPtr pResDict = PDFPageNodeDictionaryPtr(PDFDoc.Root()->getPages())->getResources(); PDFResourceDictionaryPtr pAuxResDict = getResourceDictionary(); PDFShadingSubDictionaryPtr pShadingSub = pResDict->getShading(); PDFNamePtr pName = pShadingSub->AddUniqueProperty(0, pType4Shading); if(!pAuxResDict.isNull()) { pShadingSub = pAuxResDict->getShading(); pShadingSub->AddItem(pName->str(), pType4Shading); } pOut->sh(pName); } OdGeMatrix2d PDF2dExportGeometry::createImageClipTransform( const OdGiRasterImage* pImg ,const OdGePoint3d& origin ,const OdGeVector3d& u ,const OdGeVector3d& v ) { OdUInt32 height(pImg->pixelHeight()); OdGeVector2d u2( u.x, u.y ); OdGeVector2d v2( -v.x, -v.y ); // image in svg(& pdf) is upside-down OdGePoint2d o2( origin.x, origin.y ); OdGeMatrix2d m; m.setCoordSystem( o2 - v2 * height, u2, v2 ); return m; } OdGeMatrix2d PDF2dExportGeometry::createImageTransform( const OdGiRasterImage* pImg ,const OdGePoint3d& origin ,const OdGeVector3d& u ,const OdGeVector3d& v ) { OdUInt32 width(pImg->pixelWidth()), height(pImg->pixelHeight()); OdGeVector2d u2( u.x * width, u.y * width ); OdGeVector2d v2( -v.x * height, -v.y * height ); // image in svg(& pdf) is upside-down OdGePoint2d o2( origin.x, origin.y ); OdGeMatrix2d m; m.setCoordSystem( o2 - v2, u2, v2 ); return m; } void PDF2dExportGeometry::dc_mx() { PDFAUX::updateContentStreamWithCM(document(), m_currentPdfPage, exportParams()); // OdGeMatrix2d m; // m.setToScaling(coeff); // // PDFIContentCommands *pOut = cc(); // pOut->cm(m.entry[0][0], m.entry[1][0], m.entry[0][1], m.entry[1][1], m.entry[0][2], m.entry[1][2]); // // m_scaleCoeff = coeff; } OdGiRasterImagePtr PDF2dExportGeometry::cropImage(const OdGiRasterImage* pImg, const OdGeVector2d& u1, const OdGeVector2d& v1, OdGeExtents2d& cropArea, OdGePoint2d& origin) { OdSmartPtr pImgClipped; OdGeExtents2d rasterExtents; OdGePoint2d newOrigin(origin); OdGeVector2d u = u1, v = v1; OdGeMatrix2d m; if (OdZero(u1.x)) //picture rotated { if (u1.y > 0) //rotation == 90 m.setToRotation(3 * OdaPI2); //to complete full circle (360) else //rotation == 270 m.setToRotation(OdaPI2); //to complete full circle (360) cropArea.transformBy(m); u.transformBy(m); v.transformBy(m); origin.transformBy(m); newOrigin.transformBy(m); m.invert(); } rasterExtents.addPoint(OdGePoint2d(origin.x, origin.y)); rasterExtents.addPoint(OdGePoint2d(origin.x + u.x*pImg->pixelWidth(), origin.y)); rasterExtents.addPoint(OdGePoint2d(origin.x, origin.y + v.y*pImg->pixelHeight())); rasterExtents.addPoint(OdGePoint2d(origin.x + u.x*pImg->pixelWidth(), origin.y + v.y*pImg->pixelHeight())); if (rasterExtents.minPoint().x < cropArea.minPoint().x || rasterExtents.minPoint().y < cropArea.minPoint().y || rasterExtents.maxPoint().x > cropArea.maxPoint().x || rasterExtents.maxPoint().y > cropArea.maxPoint().y) {//crop needed double newWidth = fabs(u.x)*pImg->pixelWidth(); double newHeight = fabs(v.y)*pImg->pixelHeight(); double cropX = 0; double cropY = 0; if (rasterExtents.maxPoint().x > cropArea.maxPoint().x) newWidth -= rasterExtents.maxPoint().x - cropArea.maxPoint().x; if (rasterExtents.minPoint().x < cropArea.minPoint().x) newWidth -= cropArea.minPoint().x - rasterExtents.minPoint().x; if (rasterExtents.maxPoint().y > cropArea.maxPoint().y) newHeight -= rasterExtents.maxPoint().y - cropArea.maxPoint().y; if (rasterExtents.minPoint().y < cropArea.minPoint().y) newHeight -= cropArea.minPoint().y - rasterExtents.minPoint().y; if (newOrigin.x > cropArea.maxPoint().x) { newOrigin.x = cropArea.maxPoint().x; if (u.x < 0) cropX += rasterExtents.maxPoint().x - cropArea.maxPoint().x; } else if (newOrigin.x < cropArea.minPoint().x) { newOrigin.x = cropArea.minPoint().x; if (u.x > 0) cropX += cropArea.minPoint().x - rasterExtents.minPoint().x; } if (newOrigin.y > cropArea.maxPoint().y) { newOrigin.y = cropArea.maxPoint().y; if (v.y < 0) cropY += rasterExtents.maxPoint().y - cropArea.maxPoint().y; } else if (newOrigin.y < cropArea.minPoint().y) { newOrigin.y = cropArea.minPoint().y; if (v.y > 0) cropY += cropArea.minPoint().y - rasterExtents.minPoint().y; } if (newWidth <= 0 || newHeight <= 0) { // ODA_ASSERT(0); if (OdZero(u1.x)) origin.transformBy(m); return pImg; } OdUInt32 newPixelWidth = static_cast(newWidth / fabs(u.x)) + 1; OdUInt32 newPixelHeight = static_cast(newHeight / fabs(v.y)) + 1; cropX /= fabs(u.x); cropY /= fabs(v.y); if (!OdEqual(cropX, (OdUInt32)cropX)/*it is necessary to crop part of pixel that we can't do*/ && newOrigin.x != origin.x) { newOrigin.x -= u.x*(cropX - (OdUInt32)cropX); newPixelWidth++; } if (!OdEqual(cropY, (OdUInt32)cropY)/*it is necessary to crop part of pixel that we can't do*/ && newOrigin.y != origin.y) { newOrigin.y -= v.y*(cropY - (OdUInt32)cropY); newPixelHeight++; } pImgClipped = pImg->crop(OdUInt32(cropX), OdUInt32(cropY), newPixelWidth, newPixelHeight); origin = newOrigin; } else pImgClipped = pImg; if (OdZero(u1.x)) { origin.transformBy(m); cropArea.transformBy(m); } return pImgClipped; } OdGiRasterImagePtr PDF2dExportGeometry::rotateImage(double angRad, const OdGiRasterImage* pImg, OdGeVector2d& moveOrig) { OdGiRasterImagePtr pTmp = pImg; if (!pImg || angRad == 0) return pImg; ODA_ASSERT(pImg->colorDepth() >= 8); //cannot rotate 1bpp image with this algorithm OdUInt32 pixelWidth = pImg->pixelWidth(); OdUInt32 pixelHeight = pImg->pixelHeight(); if (pixelHeight == 1 && pixelWidth == 1) return pImg; OdGeMatrix2d rotMat, translateMat; rotMat.setToRotation(-angRad); OdUInt32 colorDepth = pImg->colorDepth(); //Calculate new picture size and relative origin point OdGePoint2d p1(0, 0); OdGePoint2d p2(0, pixelHeight); OdGePoint2d p3(pixelWidth, pixelHeight); OdGePoint2d p4(pixelWidth, 0); p1.transformBy(rotMat); p2.transformBy(rotMat); p3.transformBy(rotMat); p4.transformBy(rotMat); p2.x = OdRound(p2.x); p2.y = OdRound(p2.y); p3.x = OdRound(p3.x); p3.y = OdRound(p3.y); p4.x = OdRound(p4.x); p4.y = OdRound(p4.y); int minx = int(odmin(p1.x, odmin(p2.x, odmin(p3.x, p4.x)))); int miny = int(odmin(p1.y, odmin(p2.y, odmin(p3.y, p4.y)))); int maxx = int(odmax(p1.x, odmax(p2.x, odmax(p3.x, p4.x)))); int maxy = int(odmax(p1.y, odmax(p2.y, odmax(p3.y, p4.y)))); OdUInt32 w = maxx - minx; OdUInt32 h = maxy - miny; OdGePoint2d newOrig(minx, miny); //Calculate new scanline size OdUInt32 lineLen = colorDepth * w; OdUInt32 scnLnAlign = pImg->scanLinesAlignment(); OdUInt32 newScLnSize = lineLen / 8 + ((lineLen % 8) ? 1 : 0); if (newScLnSize % scnLnAlign) newScLnSize = (newScLnSize / scnLnAlign + 1) * scnLnAlign; // Create a bitmap to hold the result OdUInt8Array m_Bits; m_Bits.resize(newScLnSize * h, 255); // Computing the destination point for each source point will leave a few pixels that do not get covered // So we use a reverse transform - e.i. compute the source point for each destination point const OdUInt8 *pScanline = pImg->scanLines(); OdUInt8 bytesPP = static_cast(colorDepth / 8); moveOrig = p1 - newOrig; translateMat.setToTranslation(moveOrig); OdUInt32 x1, y1; #if 0 //direct transformation for (OdUInt32 y = 0; y < pixelHeight; y++) { for (OdUInt32 x = 0; x < pixelWidth; x ++) { OdGePoint2d p(x, y); p.transformBy(rotMat); p.transformBy(translateMat); x1 = p.x; y1 = p.y; if (x1 >= 0 && x1 < w && y1 >= 0 && y1 < h) { m_Bits[y1*newScLnSize + x1 * bytesPP] = pScanline[y*pImg->scanLineSize() + x * bytesPP]; m_Bits[y1*newScLnSize + x1 * bytesPP + 1] = pScanline[y*pImg->scanLineSize() + x * bytesPP + 1]; m_Bits[y1*newScLnSize + x1 * bytesPP + 2] = pScanline[y*pImg->scanLineSize() + x * bytesPP + 2]; } } } #else OdGeMatrix2d transformMat; transformMat.setToProduct(rotMat.inverse(), translateMat.inverse()); for (OdUInt32 y = 0; y < h; y++) { for (OdUInt32 x = 0; x < w; x++) { OdGePoint2d p(x, y); p.transformBy(transformMat); x1 = OdUInt32(p.x); y1 = OdUInt32(p.y); if (x1 >= 0 && x1 < pixelWidth && y1 >= 0 && y1 < pixelHeight) { for (OdUInt32 byteIndex = 0; byteIndex < bytesPP; byteIndex++) m_Bits[y*newScLnSize + x * bytesPP + byteIndex] = pScanline[y1 * pImg->scanLineSize() + x1 * bytesPP + byteIndex]; } } } #endif //Save new image OdUInt32 palSize = pImg->paletteDataSize(); OdStreamBufPtr pFileBuf = OdMemoryStream::createNew(); OdPlatformStreamer::wrInt16(*pFileBuf, 0x4d42); OdUInt32 scanLinesPos = 14 + 40 + palSize; // Below version is correct only on Windows OdUInt32 size = scanLinesPos + w * bytesPP * h; OdPlatformStreamer::wrInt32(*pFileBuf, size); OdPlatformStreamer::wrInt32(*pFileBuf, 0); // reserved OdPlatformStreamer::wrInt32(*pFileBuf, scanLinesPos); // offBits OdPlatformStreamer::wrInt32(*pFileBuf, 40); // not portable: sizeof(BITMAPINFOHEADER)); // save BITMAPINFOHEADER OdPlatformStreamer::wrInt32(*pFileBuf, w); OdPlatformStreamer::wrInt32(*pFileBuf, h); OdPlatformStreamer::wrInt16(*pFileBuf, 8); OdPlatformStreamer::wrInt16(*pFileBuf, static_cast(colorDepth)); OdPlatformStreamer::wrInt32(*pFileBuf, 0); OdPlatformStreamer::wrInt32(*pFileBuf, 0); OdPlatformStreamer::wrInt32(*pFileBuf, 0); OdPlatformStreamer::wrInt32(*pFileBuf, 0); OdPlatformStreamer::wrInt32(*pFileBuf, 0); OdPlatformStreamer::wrInt32(*pFileBuf, 0); if(palSize) //copy palette data { OdUInt8Array imgPalette; imgPalette.resize(palSize); pImg->paletteData(imgPalette.asArrayPtr()); pFileBuf->putBytes(imgPalette.asArrayPtr(), palSize); } pFileBuf->putBytes(m_Bits.asArrayPtr(), m_Bits.length()); pFileBuf->rewind(); OdRxRasterServicesPtr pRasSvcs = odrxDynamicLinker()->loadApp(RX_RASTER_SERVICES_APPNAME); if (!pRasSvcs.isNull()) { pTmp = pRasSvcs->loadRasterImage(pFileBuf); //pRasSvcs->saveRasterImage(pTmp, "F:\\03.bmp", OdRxRasterServices::kBMP); } return pTmp; } void PDF2dExportGeometry::processImageBoundaries(const OdGePoint2d* uvBoundary, OdUInt32 numBoundPts, const OdGeVector3d& normal) { PDFIContentCommands* pOut = cc(); if(!pOut || !uvBoundary || !numBoundPts) return; if(!OdGeClipUtils::isPolygonSelfItersections(uvBoundary, numBoundPts - 1)) //draw simple clip path { pOut->m(uvBoundary[0].x, uvBoundary[0].y, m_CoordProc); for(OdUInt32 f = 1; f < numBoundPts; ++f) pOut->l(uvBoundary[f].x, uvBoundary[f].y, m_CoordProc); } else { OdGePoint3dArray worldBoundary; worldBoundary.resize(numBoundPts); OdGeMatrix3d UV2World; for(OdUInt32 i = 0; i < numBoundPts; i++) { OdGePoint3d pt(uvBoundary[i].x + 0.5, uvBoundary[i].y + 0.5, 0.0); worldBoundary[numBoundPts - i - 1] = pt; } OdInt32Array loops; OdUInt32 nContours = OdGeClipUtils::fixPolygonSelfIntersections(uvBoundary, numBoundPts, loops, worldBoundary, UV2World); OdGeVector3dArray normals; normals.resize(nContours, normal); OdGiFaceData faceData; faceData.setNormals(normals.getPtr()); { int stateDrawingContour = m_stateDrawingContour; //should be 0 here, but anyway m_stateDrawingContour = 3; //start drawing clipping contour OdSaveState ss_m_renderMode(m_renderMode, OdGsView::k2DOptimized); shellProc((OdInt32)worldBoundary.size(), worldBoundary.getPtr(), (OdInt32)loops.size(), loops.getPtr(), 0, &faceData); m_stateDrawingContour = stateDrawingContour; } } pOut->W(); // set current clip path to uvBoundary pOut->n(); // apply it (draw without filling) } void PDF2dExportGeometry::dc_raster_image( const OdGePoint3d& origin, const OdGeVector3d& u, const OdGeVector3d& v, const OdGiRasterImage* pImg, // image object const OdGePoint2d* uvBoundary, // may not be null OdUInt32 numBoundPts, bool transparency, double brightness, double contrast, double fade, ODCOLORREF entityColor) { if (!pImg) return; OdUInt32 pixelWidth = pImg->pixelWidth(); OdUInt32 pixelHeight = pImg->pixelHeight(); if (pixelWidth == 0 || pixelHeight == 0) return; // #13599 if (u.isZeroLength() || v.isZeroLength()) return; const double uLen = u.length(); const double vLen = v.length(); if(Odisnan(uLen) || Odisnan(vLen)) //check for -nan(ind), see DGN-5075 return; ClosePolyline(); processModifiedTraits(); //Crop Images///////////////////////////////////////////////////////////// OdSmartPtr pImgClipped; OdGePoint2d newOrigin(origin.x, origin.y); OdGeVector3d new_u(u); OdGeVector3d new_v(v); if ((m_bRasterClipping || exportParams().imageCropping() && pixelWidth > 1 && pixelHeight > 1)) { PDF2dExportDevice* pDev = (PDF2dExportDevice*)&m_refDevice; ODA_ASSERT(pDev); OdGePoint2dArray clipPointsArray = pDev->m_clipPoints; if(clipPointsArray.size() == 4 && !m_paperClip.isValidExtents()) { int nrcCountsOut = 4; //find min & max points of clip rectangle OdUInt32 min_x = OdUInt32(clipPointsArray[0].x); OdUInt32 max_x = OdUInt32(clipPointsArray[0].x); OdUInt32 min_y = OdUInt32(clipPointsArray[0].y); OdUInt32 max_y = OdUInt32(clipPointsArray[0].y); for (int f = 1; f < nrcCountsOut - 1; ++f) { if (clipPointsArray[f].x < min_x) min_x = OdInt32(clipPointsArray[f].x); else if (clipPointsArray[f].x > max_x) max_x = OdInt32(clipPointsArray[f].x); if (clipPointsArray[f].y < min_y) min_y = OdInt32(clipPointsArray[f].y); else if (clipPointsArray[f].y > max_y) max_y = OdInt32(clipPointsArray[f].y); } m_paperClip.set(OdGePoint2d((double)min_x, (double)min_y), OdGePoint2d((double)max_x, (double)max_y)); } if (m_paperClip.isValidExtents()) { OdGeVector2d u2d(new_u.x, new_u.y); OdGeVector2d v2d(new_v.x, new_v.y); OdGeMatrix2d matRotate, matTranslate; OdGiRasterImagePtr pImageRotated; if (pImg->colorDepth() >= 8 && !new_u.isParallelTo(OdGeVector3d::kXAxis, OdGeTol(1e-6)) && !new_u.isParallelTo(OdGeVector3d::kYAxis, OdGeTol(1e-6)) && OdEqual(u2d.length(), v2d.length(), 1e-4)) //cannot rotate 1bpp images or crop scaled rotated images { double angRad = u2d.angleTo(OdGeVector2d::kXAxis); if (!OdZero(v2d.x) && (v2d.x < 0)) { angRad = -angRad; } OdGeVector2d moveOrig; pImageRotated = rotateImage(angRad, pImg, moveOrig); matRotate.setToRotation(angRad); u2d.transformBy(matRotate); v2d.transformBy(matRotate); matTranslate.setToTranslation(-moveOrig*v2d.length()); newOrigin.transformBy(matTranslate); pImgClipped = cropImage(pImageRotated, u2d, v2d, m_paperClip, newOrigin); if (pImgClipped->pixelHeight()*pImgClipped->pixelWidth() > pImg->pixelHeight()*pImg->pixelWidth()) {//because it is meaningless to put picture in pdf if its size increase after rotation and crop pImgClipped = pImg; newOrigin.x = origin.x; newOrigin.y = origin.y; } else { new_u = OdGeVector3d(u2d.x, u2d.y, 0); new_v = OdGeVector3d(v2d.x, v2d.y, 0); } // { // OdRxRasterServicesPtr pRasSvcs = odrxDynamicLinker()->loadApp(RX_RASTER_SERVICES_APPNAME); // pRasSvcs->saveRasterImage(pImg, "F:\\original.bmp", OdRxRasterServices::kBMP); // pRasSvcs->saveRasterImage(pImageRotated, "F:\\rotated.bmp", OdRxRasterServices::kBMP); // pRasSvcs->saveRasterImage(pImgClipped, "F:\\clipped.bmp", OdRxRasterServices::kBMP); // } } else if (new_u.isParallelTo(OdGeVector3d::kXAxis) || new_u.isParallelTo(OdGeVector3d::kYAxis)) pImgClipped = cropImage(pImg, u2d, v2d, m_paperClip, newOrigin); else pImgClipped = pImg; } else pImgClipped = pImg; } else pImgClipped = pImg; //OdRxRasterServicesPtr pRasSvcs = odrxDynamicLinker()->loadApp(RX_RASTER_SERVICES_APPNAME); //pRasSvcs->saveRasterImage(pImg, "F:\\01.bmp", OdRxRasterServices::kBMP); //pRasSvcs->saveRasterImage(pImgClipped, "F:\\02.bmp", OdRxRasterServices::kBMP); PDFIContentCommands *pOut = cc(); PDFDocument &PDFDoc = document(); PDFImagePtr pImage = TD_PDF_HELPER_FUNCS::addNewImage(PDFDoc, getResourceDictionary()); try { ODCOLORREF bgColor = m_refDevice.getPaletteBackground(); if (GETBIT(m_effTraits.drawFlags(), OdGiSubEntityTraits::kDrawContourFill)) { if (m_effTraits.secondaryTrueColor().isByColor()) bgColor = ODTOCOLORREF(m_effTraits.secondaryTrueColor()); else bgColor = m_refDevice.getColor(m_effTraits.secondaryTrueColor().colorIndex()); } if (exportParams().colorPolicy() == PDFExportParams::kGrayscale) { convertColorToGrayscale(entityColor); convertColorToGrayscale(bgColor); } else if (exportParams().colorPolicy() == PDFExportParams::kMono) { double r0 = double(ODGETRED(entityColor)); double g0 = double(ODGETGREEN(entityColor)); double b0 = double(ODGETBLUE(entityColor)); double r1 = double(ODGETRED(bgColor)); double g1 = double(ODGETGREEN(bgColor)); double b1 = double(ODGETBLUE(bgColor)); if (30 * r0 + 59 * g0 + 11 * b0 > 30 * r1 + 59 * g1 + 11 * b1) { bgColor = ODRGBA(0, 0, 0, ODGETALPHA(bgColor)); entityColor = ODRGBA(255, 255, 255, ODGETALPHA(entityColor)); } else { entityColor = ODRGBA(0, 0, 0, ODGETALPHA(entityColor)); bgColor = ODRGBA(255, 255, 255, ODGETALPHA(bgColor)); } } OdSmartPtr pConverted; if (exportParams().colorPolicy() == PDFExportParams::kGrayscale || exportParams().colorPolicy() == PDFExportParams::kMono) { if (exportParams().colorPolicy() == PDFExportParams::kGrayscale) pConverted = OdRxObjectImpl::createObject(); else if (exportParams().colorPolicy() == PDFExportParams::kMono) { pConverted = OdRxObjectImpl::createObject(); if (m_bHatchDrawingMode) { OdSmartPtr pMon = OdGiMonochromaticRasterTransformer::cast(pConverted); if (!pMon.isNull()) pMon->setThreshold(254); } } OdSmartPtr pWrapper = OdGiRasterImageWrapper::cast(pConverted); if (!pWrapper.isNull()) pWrapper->setOriginal(pImgClipped); else pConverted = pImgClipped; } else pConverted = pImgClipped; TD_PDF_HELPER_FUNCS::dcImageToPdfImage(pConverted, pImage, transparency, brightness, contrast, fade, entityColor, PDFDoc, bgColor, exportParams().dctQuality(), getResourceDictionary(), exportParams().monoImagesAsMask()); pOut->q(); // push graph state to stack if(numBoundPts) processImageBoundaries(uvBoundary, numBoundPts, u.crossProduct(v)); if(transparency && exportParams().monoImagesAsMask() && pConverted->numColors() == 2) { OdUInt8 r = ODGETRED(entityColor); OdUInt8 g = ODGETGREEN(entityColor); OdUInt8 b = ODGETBLUE(entityColor); pOut->RG(r, g, b); pOut->rg(r, g, b); } OdGeMatrix2d m = createImageTransform(pConverted, OdGePoint3d(newOrigin.x, newOrigin.y, 0.), new_u, new_v); pOut->cm(OdRoundToLong(m.entry[0][0]), OdRoundToLong(m.entry[1][0]), OdRoundToLong(m.entry[0][1]), OdRoundToLong(m.entry[1][1]), OdRoundToLong(m.entry[0][2]), OdRoundToLong(m.entry[1][2])); pOut->Do(pImage->dictionary()->getName()); pOut->Q(); // restore prev graph state from stack } catch(const OdError&) { // skip unsupported images } } void PDF2dExportGeometry::dc_raster_image(OdGiRasterImagePtr pRaster, const OdGeExtents2d& imgExts) { ///SUP-716 Accuracy exporting DXF to PDF////////////////////////////////// ///We have to put cm matrix into content stream if view is exported as bitmap because of scale coefficient (dpi) dc_mx(); //disable jpeg compression for shaded viewports bool bDctCompression = document().encodingDCTEnabled(); if(!exportParams().dctCompressionShadedViewports()) m_pParamsHolder->document().enableEncodingDCT(false); // used only for rendering of shaded viewports OdUInt32 pxWidth = pRaster->pixelWidth(), pxHeight = pRaster->pixelHeight(); OdGePoint2d origin(imgExts.minPoint()); dc_raster_image(OdGePoint3d(origin.x, origin.y, 0.0), OdGeVector3d::kXAxis * (imgExts.maxPoint().x-origin.x)/pxWidth, OdGeVector3d::kYAxis * (imgExts.maxPoint().y-origin.y)/pxHeight, pRaster, 0, 0, false, 50.0, 50.0, 0.0, 0); if(!exportParams().dctCompressionShadedViewports()) m_pParamsHolder->document().enableEncodingDCT(bDctCompression); } /* OdAnsiString PDF2dExportDevice::dc_shx_font(const OdGiTextStyle& pTextStyle, PDFFontPtr *pOutFont) { PDFDocument &PDFDoc = document(); OdAnsiString fontName = PDFAUX::addType3Font(PDFDoc, pTextStyle, pOutFont); ODA_ASSERT(!fontName.isEmpty()); return fontName; } */ OdAnsiString PDF2dExportGeometry::dc_truetype_font(const OdGiTextStyle& pTextStyle, PDFFontPtr& pOutFont) { PDFDocument &PDFDoc = document(); bool bEmbededFont = AllowEmbededFonts(); bool bOptimizedEmbeddedFont = AllowEmbededOptimizedFonts(); bool bAllowedEdit = true; PDFResourceDictionaryPtr pResDict = getResourceDictionary(); OdAnsiString fontName = TD_PDF_HELPER_FUNCS::addType0Font(PDFDoc, pTextStyle, bEmbededFont, bOptimizedEmbeddedFont, pOutFont, pResDict, bAllowedEdit); if (fontName.isEmpty()) { fontName = TD_PDF_HELPER_FUNCS::addTrueTypeFont(PDFDoc, pTextStyle, bEmbededFont, bOptimizedEmbeddedFont, pOutFont, pResDict, bAllowedEdit); } ODA_ASSERT(!fontName.isEmpty()); return fontName; } static void getTextAsDByte(OdBaseTextIteratorPtr iter, OdCharArray& retArray) { OdChar ch = iter->nextChar(); while (ch) { retArray.push_back(ch); ch = iter->nextChar(); } } bool findGlyphs(bool bSkipSpecSymbols, OdUInt32 endIndex, const OdUInt16Array& CID2GID, const OdCharArray& Glyphs, OdString& unicodeStr, OdArray* pUnicode) { OdUInt32 startIndex = bSkipSpecSymbols ? 32 : 1; ODA_ASSERT_ONCE(startIndex <= endIndex && endIndex <= CID2GID.size()); bool bFound = false; if(pUnicode->size() < Glyphs.size()) pUnicode->resize(Glyphs.size()); for(OdUInt32 k = 0; k < Glyphs.size(); k++) { OdUInt16 tmp = OdUInt16((((Glyphs[k]) & 0xff00) >> 8) + (((Glyphs[k]) & 0x00ff) << 8)); bFound = false; for(OdUInt32 l = startIndex; l < endIndex; l++) { if(tmp == CID2GID[l]) { bFound = true; OdUInt16 ch = OdUInt16(((l & 0xff00) >> 8) + ((l & 0x00ff) << 8)); unicodeStr += ch; (*pUnicode)[k] = static_cast(l); break; } } if(!bFound) { if(!bSkipSpecSymbols) { OdChar pCh = (*pUnicode)[k]; PDF_SWAP_16(pCh); unicodeStr += pCh; bFound = true; } else return false; } } return bFound; } void PDF2dExportGeometry::dc_drawunicodetext(const OdAnsiString &fontName, const OdGePoint2d& pos, const OdGeVector2d& u, const OdGeVector2d& v, OdArray* pUnicode, const OdGiTextStyle& pTextStyle, PDFFontPtr& pPDFFont, bool raw) { if (!pUnicode->size()) return; ClosePolyline(); processModifiedTraits(); OdString unicodeStr; bool bProcessed = false; bool bPseudoBold = false; bool bPseudoItalic = false; const bool bUnicodeCmap = !pPDFFont->isNonUnicodeTable(); OdFont* pFont = pTextStyle.getFont(); if(!pFont->isShxFont() && bUnicodeCmap) { OdTrueTypeFontBase* pTT = static_cast(pFont); if(pTT->isSupportReorder()) { OdDbBaseDatabase* pDb = drawContext()->giContext().database(); OdString wString(OdUS2W(pUnicode->asArrayPtr()), pUnicode->size()); // not null-terminated OdBaseTextIteratorPtr iter = OdDbBaseDatabasePEPtr(pDb)->createTextIterator(pDb, wString.c_str(), pUnicode->size(), raw, &pTextStyle); OdCharArray srcArray, dstArray; getTextAsDByte(iter, srcArray); OdGeVector2dArray offsets; OdIntArray visualFromLogical; pTT->getGlyphIndexes(srcArray, dstArray, &visualFromLogical, NULL, &offsets); bPseudoBold = pPDFFont->isPseudoBold(); bPseudoItalic = pPDFFont->isPseudoItalic(); PDFType0FontPtr pNewT0TTF = pPDFFont; PDFArrayPtr pAr = pNewT0TTF->getDescendantFonts(); PDFCIDFont2Ptr pCIDFont = pAr->first(); PDFCID2GIDStreamPtr pCID2GID = pCIDFont->getCIDToGIDMap(); OdUInt16Array data; pCID2GID->getData(data); if(srcArray.size() == dstArray.size()) { bProcessed = true; for(OdUInt32 k = 0; k < dstArray.size(); k++) //check if we can use pUnicode array as is { OdUInt16 tmp = OdUInt16((((dstArray[k]) & 0xff00) >> 8) + (((dstArray[k]) & 0x00ff) << 8)); if(srcArray[k] >= data.size() || tmp != data[srcArray[k]]) { bProcessed = false; break; } } } if(bProcessed) bProcessed = false; //process later as non-reordered text else { bool bFound = findGlyphs(true, data.size(), data, dstArray, unicodeStr, pUnicode); //skip spec symbols if(!bFound) { unicodeStr.empty(); bFound = findGlyphs(false, data.size(), data, dstArray, unicodeStr, pUnicode); } ODA_ASSERT_ONCE(bFound); bProcessed = true; } } } OdAnsiString nonUnicodeStr; if(!bProcessed) //In case of FtFontEngine, fonts do not support reorder { if(bUnicodeCmap) //unicode encoding { unicodeStr.empty(); const OdUInt32 bufSize = pUnicode->size(); OdChar* pCh = unicodeStr.getBufferSetLength(bufSize); for(OdUInt32 f = 0; f < bufSize; ++f) { pCh[f] = (*pUnicode)[f]; PDF_SWAP_16(pCh[f]); } } else { OdAnsiString formatStr; for(OdUInt32 f = 0; f < pUnicode->size(); ++f) nonUnicodeStr += formatStr.format("%04x", (*pUnicode)[f]); } } ODA_ASSERT_ONCE(pTextStyle.xScale() == 1.); // is it always = 1 ? OdGeMatrix2d m; m.setCoordSystem(pos, u, v); if(bPseudoItalic) { OdGeMatrix2d slant; slant(0, 1) = (1.0 / 3.0); m *= slant; } PDFDocument &PDFDoc = document(); PDFTextStringPtr pText = PDFTextString::createObject(PDFDoc); if(nonUnicodeStr.isEmpty()) { pText->set(unicodeStr); pText->enableFixParenthesis(); // fix '(' ')' '\' -> '\(' '\)' '\\' } else { pText->set(nonUnicodeStr); pText->enableFixParenthesis(false); pText->setRoundBrackets(false); } pText->setExportUnicodeMarker(false); PDFIContentCommands *pOut = cc(); const double iTrackingPercent = pTextStyle.trackingPercent(); const bool saveGraphicsState = (!exportHiddenText() && bPseudoBold) || !OdEqual(iTrackingPercent, 1.); const double dHeight = TD_PDF_HELPER_FUNCS::textHeight(pTextStyle); if(saveGraphicsState) pOut->q(); pOut->BT(); pOut->Tm(m.entry[0][0], m.entry[1][0], m.entry[0][1], m.entry[1][1], m.entry[0][2], m.entry[1][2]); if(!OdEqual(iTrackingPercent, 1.)) { if(iTrackingPercent < 1.25) pOut->Tc((iTrackingPercent - 1.) * pTextStyle.getFont()->getAverageWidth() * .001); else { ODA_ASSERT(exportHiddenText()); pOut->Tc(.25 * pTextStyle.getFont()->getAverageWidth() * .001); } } pOut->Tf(PDFName::createObject(PDFDoc, fontName.c_str()), dHeight); if(exportHiddenText()) pOut->Tr(3); // transparent rendering else if(bPseudoBold) { pOut->Tr(2); // draw outlines and fill ODA_ASSERT(saveGraphicsState); pOut->w(dHeight * ::fabs(v.y) * 0.025); // set thickness of outlines pOut->j(kMiterJoin); pOut->J(kButtCap); } else pOut->Tr(0); pOut->Tj(pText); if(!OdEqual(iTrackingPercent, 1.)) pOut->Tc(0); pOut->ET(); if(saveGraphicsState) pOut->Q(); } void PDF2dExportGeometry::dc_drawtext(const OdAnsiString &fontName, const OdGePoint2d& pos, const OdGeVector2d& u, const OdGeVector2d& v, const char& msg, OdInt32 length, const OdGiTextStyle& pTextStyle, const OdDoubleArray& charSpaces) { ClosePolyline(); processModifiedTraits(); ODA_VERIFY(pTextStyle.xScale() == 1.); // is it always = 1 ? PDFDocument &PDFDoc = document(); double dHeight = TD_PDF_HELPER_FUNCS::textHeight(pTextStyle); double fontScale = 1.; PDFType3FontPtr pFont = TD_PDF_HELPER_FUNCS::getType3FontByName(PDFDoc, fontName); if (!pFont.isNull()) { fontScale *= pFont->getScale(); } OdGeMatrix2d m; m.setCoordSystem(pos, u*fontScale, v*fontScale); PDFTextStringPtr pText = PDFTextString::createObject(PDFDoc); // dont fix here '(' ')' '\' -> '\(' '\)' '\\' OdAnsiString str(&msg, length); pText->set(str); PDFIContentCommands *pOut = cc(); pOut->BT(); if (exportHiddenText()) pOut->Tr(3); // transparent rendering else pOut->Tr(0); pOut->Tm(m.entry[0][0], m.entry[1][0], m.entry[0][1], m.entry[1][1], (int)m.entry[0][2], (int)m.entry[1][2]); pOut->Tf(PDFName::createObject(PDFDoc, fontName.c_str()), dHeight); if (!OdEqual(pTextStyle.trackingPercent(), 1.) && !charSpaces.empty() && length%4 == 0) { PDFArrayPtr pTextArray = PDFArray::createObject(document()); const char* cPos = &msg; int iPos = 0; while (iPos < length) { PDFTextStringPtr pSymbol = PDFTextString::createObject(PDFDoc); // dont fix here '(' ')' '\' -> '\(' '\)' '\\' OdAnsiString str(cPos, 4); pSymbol->set(str); pTextArray->push_back(pSymbol); pTextArray->push_int(OdInt32(charSpaces.at(iPos / 4) / fontScale)); iPos += 4; cPos += 4; } pOut->TJ(pTextArray); } else pOut->Tj(pText); pOut->ET(); } void PDF2dExportGeometry::dc_pushClip(int nrcContours, const int* nrcCounts, const OdGsDCPointArray &nrcPoints) { PDFIContentCommands *pOut = cc(); pOut->q(); // push graph state to stack int nCurPnt = 0; for(int f = 0; f < nrcContours; ++f) { pOut->m(nrcPoints[nCurPnt].x, nrcPoints[nCurPnt].y, m_CoordProc); ++nCurPnt; for(int j = 1; j < nrcCounts[f]; ++j, ++nCurPnt) { pOut->l(nrcPoints[nCurPnt].x, nrcPoints[nCurPnt].y, m_CoordProc); } pOut->h(); // close current subPath } pOut->W(); // enable clip mode pOut->n(); // apply it (draw without filling) } void PDF2dExportGeometry::dc_popClip() { PDFIContentCommands *pOut = cc(); pOut->Q(); // restore prev graph state from stack // restart color cache system m_bRGBActive = true; } void PDF2dExportGeometry::setCapJoin(PDFLineCap linecap, PDFLineJoin linejoin) { if (m_CapStyle != linecap) { GraphStateChanged(); m_CapStyle = linecap; PDFIContentCommands* pOut = cc(); pOut->J(m_CapStyle); } if (m_JoinStyle != linejoin) { GraphStateChanged(); m_JoinStyle = linejoin; PDFIContentCommands* pOut = cc(); pOut->j(m_JoinStyle); } } // exports pDrawable into context OdResult PDF2dExportGeometry::dc_prc(const OdGiDrawable* pDrawable, const PDF2PRCExportParams ¶ms) { PdfExportServiceInterfacePtr pExportIFace = getPdfExportService(); OdRxObjectPtr & pContext = m_prc_context[params.m_viewportIdx].first; if (pContext.isNull()) { pContext = pExportIFace->createPrcExportContext(exportParams().database()); } return pExportIFace->exportPrc(pContext, pDrawable, ¶ms); } // writes prc stream into pdf page OdResult PDF2dExportGeometry::dc_prc(OdStreamBuf *pStream, const OdGeExtents2d &extents2d, const OdGeVector3d ¢er, const OdGeVector3d &x, const OdGeVector3d &y, const OdGeVector3d &z, double distanceToTarget, double scale, OdGeMatrix3d *pmatr) { ODA_ASSERT(pStream); if (!pStream) { return ::eInvalidInput; } if (pStream->length() > 0) { PDFPageDictionary *pPdfPage = currentPdfPage(); ODA_ASSERT(pPdfPage); if (pPdfPage) { PDFArrayPtr annots = pPdfPage->getAnnots(); PDF3dAnnotationDictionaryPtr pDict = PDF3dAnnotationDictionary::createObject(document(), true); PDFRectanglePtr rect = PDFRectangle::createObject(document()); rect->set( OdInt32(extents2d.minPoint().x), OdInt32(extents2d.minPoint().y), OdInt32(extents2d.maxPoint().x), OdInt32(extents2d.maxPoint().y) ); pDict->setRect(rect); pDict->setP(pPdfPage); pDict->setF(PDFInteger::createObject(document(), OdInt32(68))); pDict->setContents(PDFTextString::createObject(document(), "3D Model")); PDF3dActivationDictionaryPtr p3DA = PDF3dActivationDictionary::createObject(document()); p3DA->setTransparent(PDFBoolean::createObject(document(), true, false)); p3DA->setA(PDFName::createObject(document(), "PO")); p3DA->setD(PDFName::createObject(document(), "XD")); pDict->set3DA(p3DA); PDF3dStreamPtr p3dStream = PDF3dStream::createObject(document(), true); PDF3dViewDictionaryPtr pView = PDF3dViewDictionary::createObject(document()); pView->setXN(PDFTextString::createObject(document(), OdAnsiString("Default View"))); pView->setMS(PDFName::createObject(document(), "M")); pView->setCO(PDFNumber::createObject(document(), distanceToTarget)); PDFArrayPtr pMatrix = PDFArray::createObject(document()); OdGeMatrix3d mx; mx.setCoordSystem(OdGePoint3d::kOrigin, x, y, z); if (pmatr) { mx = (*pmatr) * mx; } OdGeMatrix3d cameraTranslation; cameraTranslation.setToTranslation(center); mx = cameraTranslation * mx; ODA_ASSERT(mx[3][0] == 0.); ODA_ASSERT(mx[3][1] == 0.); ODA_ASSERT(mx[3][2] == 0.); ODA_ASSERT(mx[3][3] == 1.); pMatrix->push_number(mx[0][0]); pMatrix->push_number(mx[1][0]); pMatrix->push_number(mx[2][0]); pMatrix->push_number(mx[0][1]); pMatrix->push_number(mx[1][1]); pMatrix->push_number(mx[2][1]); pMatrix->push_number(mx[0][2]); pMatrix->push_number(mx[1][2]); pMatrix->push_number(mx[2][2]); pMatrix->push_number(mx[0][3]); pMatrix->push_number(mx[1][3]); pMatrix->push_number(mx[2][3]); pView->setC2W(pMatrix); PDFProjectionDictionaryPtr pProj = PDFProjectionDictionary::createObject(document()); pProj->setSubtype(PDFName::createObject(document(), "O")); pProj->setOB(PDFName::createObject(document(), "Max")); pProj->setOS(PDFNumber::createObject(document(), scale)); pView->setP(pProj); // set render mode PDF3D_ENUMS::PRCRenderingMode rendMode = exportParams().getPrcRenderingMode(); if (rendMode != PDF3D_ENUMS::kDefaultMode) { PDF3dRenderModeDictionaryPtr pRM = PDF3dRenderModeDictionary::createObject(document()); pRM->setSubtype((TD_PDF::RenderingMode)rendMode); pView->setRM(pRM); } pDict->set3DV(pView); PDFArrayPtr pAr = PDFArray::createObject(document()); pAr->push_back(pView); p3dStream->getDictionary()->setVA(pAr); pDict->set3DD(p3dStream); annots->push_back(pDict); // TODO : prevent duplication { OdUInt8Array tmp; tmp.resize(static_cast(pStream->length()), 0); pStream->rewind(); pStream->getBytes(tmp.asArrayPtr(), static_cast(pStream->length())); p3dStream->putData((char *)tmp.asArrayPtr(), static_cast(pStream->length())); } } } return ::eOk; } void encodeFileURL(OdString& path) { static OdString fileProtocol = OD_T("file:///"); path.trimLeft(); #ifdef ODA_WINDOWS OdString drive; #if !defined(_WIN32_WCE) ::_wsplitpath(path, drive.getBuffer(odmin(_MAX_DRIVE, path.getLength())), nullptr, nullptr, nullptr); #else ::_wsplitpath_s(path, drive.getBuffer(odmin(_MAX_DRIVE, path.getLength())), odmin(_MAX_DRIVE, path.getLength()), nullptr, 0, nullptr, 0, nullptr, 0); #endif drive.releaseBuffer(); if(!drive.isEmpty()) // absolute path { if(path.left(7).iCompare(fileProtocol) != 0) path = fileProtocol + path; } path.replace('\\', '/'); #else if(path[0] == '/') // absolute path { if(path.left(7).iCompare(fileProtocol) != 0) path = fileProtocol + path; } #endif } static OdString encodeURL(const OdString& url) { OdString tmpStr, out; OdInt32 urlLen = url.getLength(); for (int i = 0; i < urlLen; ++i) { OdChar xCharUnicode = url.getAt(i); OdAnsiCharArray tmpBuf; OdCharMapper::unicodeToUtf8(&xCharUnicode, 1, tmpBuf); tmpStr = tmpBuf.asArrayPtr(); OdUInt32 tmpStrLen = tmpStr.getLength(); if (tmpStrLen == 1) //ASCII-symbol out += xCharUnicode; else if (tmpStrLen <= 4) //non ASCII-symbol { char charBuf[100]; memset(charBuf, 0, 100); if (tmpStr.getLength() == 2) odSprintfA(charBuf, 100, "%%%X%%%X", (OdUInt8)tmpBuf.getAt(0), (OdUInt8)tmpBuf.getAt(1)); else if (tmpStr.getLength() == 3) odSprintfA(charBuf, 100, "%%%X%%%X%%%X", (OdUInt8)tmpBuf.getAt(0), (OdUInt8)tmpBuf.getAt(1), (OdUInt8)tmpBuf.getAt(2)); else if (tmpStr.getLength() == 4) odSprintfA(charBuf, 100, "%%%X%%%X%%%X%%%X", (OdUInt8)tmpBuf.getAt(0), (OdUInt8)tmpBuf.getAt(1), (OdUInt8)tmpBuf.getAt(2), (OdUInt8)tmpBuf.getAt(3)); out += charBuf; } else { ODA_ASSERT(tmpStrLen <= 4); return OdString(); } } encodeFileURL(out); return out; } void PDF2dExportGeometry::dc_transparency(double alphaPercent, OdUInt8 alpha, BlendMode mode) { if (mode == kDarken && m_bDarkenGSSet) return; else if (mode == kUndefined) { //reset state m_bDarkenGSSet = false; m_currentTransparencyState = ""; return; } PDFResourceDictionaryPtr pResDict(PDFPageNodeDictionaryPtr((this->document()).Root()->getPages())->getResources()); PDFResourceDictionaryPtr pResDictPage = getResourceDictionary(); OdAnsiString lwstr; odDToStr(lwstr.getBufferSetLength(512), alpha, 'f', 0); lwstr.releaseBuffer(); OdAnsiString name; if (mode == kDarken /*|| mode == kRestoreDarken*/) name = OdAnsiString("GM"); //set darken blend mode else if (mode == kTransparency) name = OdAnsiString("GT" + lwstr); else if (mode == kRestoreDarken) name = OdAnsiString("GMX"); if (!name.isEmpty()) { PDFExtGStateSubDictionaryPtr pExtGsSub = pResDict->getExtGState(); if (!pExtGsSub->HasItem(name)) { PdfExtGStatePtr pExtGState = PdfExtGState::createObject(document(), true); pExtGState->setBM(PDFName::createObject(document(), (mode == kDarken || mode == kRestoreDarken) ? "Darken" : "Normal")); pExtGState->setType(PDFName::createObject(document(), "ExtGState")); PDFNumberPtr pCa; if (mode != kDarken) { pCa = PDFNumber::createObject(document(), alphaPercent); pExtGState->setCA(pCa); pExtGState->setca(pCa); } else m_bDarkenGSSet = true; pExtGsSub->AddItem(name, pExtGState); if (!pResDictPage.isNull()) pResDictPage->setExtGState(pExtGsSub); } } if (m_currentTransparencyState.isEmpty()) { m_currentTransparencyState = name; if (name != OdAnsiString("GT255")) { GraphStateChanged(); PDFIContentCommands *pOut = cc(); pOut->gs(PDFName::createObject(document(), name.c_str())); } } else if (m_currentTransparencyState != name) { if (m_currentTransparencyState == "GM" && name == "GMX") return; m_currentTransparencyState = name; GraphStateChanged(); PDFIContentCommands *pOut = cc(); pOut->gs(PDFName::createObject(document(), name.c_str())); } } bool PDF2dExportGeometry::applyLayerToAnnotation(const OdString& layer, PDFAnnotationDictionaryPtr pADict) { if (layer.isEmpty() || pADict.isNull()) return false; if (!m_stubLayerCreated) { PDFPageDictionary *pPdfPage = currentPdfPage(); if (!pPdfPage) return false; PDFArrayPtr annots = pPdfPage->getAnnots(); OdString uniqueName = odrxSystemServices()->createGuid(); if (uniqueName.isEmpty()) return false; PDFSquareAnnotationDictionaryPtr pDict = PDFSquareAnnotationDictionary::createObject(document(), true); if (!pADict->HasItem("Rect")) return false; pDict->setRect(pADict->getRect()); PDFArrayPtr pBorder = PDFArray::createObject(document()); pBorder->push_int(0); pBorder->push_int(0); pBorder->push_int(0); pDict->setBorder(pBorder); if (m_pParamsHolder->getParams().archived() != PDFExportParams::kPDFA_None) { PDFIntegerPtr pInt = PDFInteger::createObject(document(), 4, false); pDict->setF(pInt); PDFSubDictionaryPtr pAp = PDFSubDictionary::createObject(document()); PDFXObjectFormPtr pN = PDFXObjectForm::createObject(document(), true); PDFRectanglePtr rect = PDFRectangle::createObject(document()); rect->set(0, 0, 0, 0); pN->dictionary()->setBBox(rect); pAp->AddItem("N", pN); pDict->setAP(pAp); } const bool bVisible = CheckLayerVisibility(uniqueName); PDFNamePtr pOCG = PDFOCManager::CreateOC4Layer(uniqueName, document(), getResourceDictionary(), bVisible, true); PDFPageNodeDictionaryPtr node = PDFOCManager::getOCGNodeByName(uniqueName, document()); if (node.isNull()) return false; pDict->setOC(node); annots->push_back(pDict); m_stubLayerCreated = true; } PDFPageNodeDictionaryPtr node = PDFOCManager::getOCGNodeByName(layer, document()); if (node.isNull()) { const bool bVisible = CheckLayerVisibility(layer); PDFNamePtr pOCG = PDFOCManager::CreateOC4Layer(layer, document(), getResourceDictionary(), bVisible); node = PDFOCManager::getOCGNodeByName(layer, document()); } if (node.isNull()) return false; pADict->setOC(node); return true; } void PDF2dExportGeometry::dc_hyperlink(const OdGeExtents2d &extents2d, const OdString &link, const OdString &layer, bool bInternalLink, OdInt32 pageIndex) { if (!extents2d.maxPoint().isEqualTo(extents2d.minPoint())) { PDFPageDictionary *pPdfPage = currentPdfPage(); ODA_ASSERT(pPdfPage); if (pPdfPage) { OdString url; if (!bInternalLink) { url = encodeURL(link); if (url.isEmpty()) return; } PDFArrayPtr annots = pPdfPage->getAnnots(); PDFLinkAnnotationDictionaryPtr pDict = PDFLinkAnnotationDictionary::createObject(document(), true); PDFRectanglePtr rect = PDFRectangle::createObject(document()); rect->set(extents2d); pDict->setRect(rect); PDFArrayPtr pBorder = PDFArray::createObject(document()); pBorder->push_int(0); pBorder->push_int(0); pBorder->push_int(0); pDict->setBorder(pBorder); pDict->setH(PDFName::createObject(document(), "I")); if (m_pParamsHolder->getParams().archived() != PDFExportParams::kPDFA_None) { PDFIntegerPtr pInt = PDFInteger::createObject(document(), 4, false); pDict->setF(pInt); } if (!bInternalLink) { PDFURIActionDictionaryPtr pAction = PDFActionDictionary::createObject(document()); pAction->setS(PDFName::createObject(document(), "URI")); PDFTextStringPtr sURI = PDFTextString::createObject(document(), OdAnsiString(url)); sURI->enableFixParenthesis(true); pAction->setURI(sURI); pDict->setA(pAction); } else { const PDFExportParams& params = m_pParamsHolder->getParams(); OdStringArray layouts = params.layouts(); OdInt32 index = -1; if(pageIndex >= 0) index = pageIndex; else { for(OdUInt32 i = 0; i < layouts.size(); i++) { OdRxObject* pDb = m_pParamsHolder->getDatabase(i).get(); if(!layouts.at(i).compare(link) && (params.databases().size() == 0 || pDb == params.database())) { index = i; break; } } } if(index == -1) //destination not found in the list of exported layouts for current db return; PDFGoToActionDictionaryPtr pAction = PDFActionDictionary::createObject(document()); pAction->setS(PDFName::createObject(document(), "GoTo")); PDFArrayPtr pD = PDFArray::createObject(document()); PDFNamePtr pFit = PDFName::createObject(document(), "Fit"); PDFPageNodeDictionaryPtr pPageTree(document().Root()->getPages()); PDFArrayPtr pPages(pPageTree->Find(("Kids"))); pD->append(pPages->at(index)); pD->append(pFit); pAction->setD(pD); pDict->setA(pAction); } if (!layer.isEmpty()) { if (!applyLayerToAnnotation(layer, pDict)) return; } annots->push_back(pDict); } } } void PDF2dExportGeometry::dc_textAnnot(const OdGeExtents2d &extents2d, const OdString &title, const OdString &content, const OdString &layer) { if (!extents2d.maxPoint().isEqualTo(extents2d.minPoint())) { PDFPageDictionary *pPdfPage = currentPdfPage(); ODA_ASSERT(pPdfPage); if (pPdfPage) { PDFArrayPtr annots = pPdfPage->getAnnots(); PDFSquareAnnotationDictionaryPtr pDict = PDFSquareAnnotationDictionary::createObject(document(), true); PDFRectanglePtr rect = PDFRectangle::createObject(document()); rect->set(extents2d); pDict->setRect(rect); PDFArrayPtr pBorder = PDFArray::createObject(document()); pBorder->push_int(0); pBorder->push_int(0); pBorder->push_int(0); pDict->setBorder(pBorder); PDFTextStringPtr annotContent = PDFTextString::createObject(document(), false); TD_PDF_HELPER_FUNCS::getUnicodeTextString(content, annotContent); annotContent->enableFixParenthesis(true); pDict->setContents(annotContent); pDict->setT(PDFTextString::createObject(document(), OdAnsiString(title))); if (m_pParamsHolder->getParams().archived() != PDFExportParams::kPDFA_None) { PDFIntegerPtr pInt = PDFInteger::createObject(document(), 4, false); pDict->setF(pInt); PDFSubDictionaryPtr pAp = PDFSubDictionary::createObject(document()); PDFXObjectFormPtr pN = PDFXObjectForm::createObject(document(), true); PDFRectanglePtr rect = PDFRectangle::createObject(document()); rect->set(0, 0, (OdUInt32)extents2d.maxPoint().x - (OdUInt32)extents2d.minPoint().x, (OdUInt32)extents2d.maxPoint().y - (OdUInt32)extents2d.minPoint().y); pN->dictionary()->setBBox(rect); pAp->AddItem("N", pN); pDict->setAP(pAp); } if (!layer.isEmpty()) { if (!applyLayerToAnnotation(layer, pDict)) return; } annots->push_back(pDict); } } } void PDF2dExportGeometry::flushTraits() { PDFIContentCommands* pOut = cc(); pOut->setTraits(m_lineWeight, m_curRGB, m_curRGB, m_curColorIndex, m_curColorIndex, m_CapStyle, m_JoinStyle); } void PDF2dExportGeometry::restoreTraits() { ODCOLORREF RGB_S, RGB_NS; OdUInt16 CI_S, CI_NS; PDFIContentCommands* pOut = cc(); pOut->getTraits(m_lineWeight, RGB_S, RGB_NS, CI_S, CI_NS, m_CapStyle, m_JoinStyle); //For this moment the color is always the same for stroking and non-stroking operations ODA_ASSERT_ONCE(RGB_S == RGB_NS); ODA_ASSERT_ONCE(CI_S == CI_NS); m_curRGB = RGB_S; m_curColorIndex = CI_S; } //Process modified traits void PDF2dExportGeometry::processModifiedTraits() { if (m_prevLayerName.compare(m_curLayerName) != 0) { dc_layer(m_curLayerName, m_LayerOpen); m_prevLayerName = m_curLayerName; } } void PDF2dExportGeometry::suspendStream() { ClosePolyline(); if (isLayersSupported() && !m_curLayerName.isEmpty()) CloseLayerBracket(); m_prevLayerName = L""; m_curLayerName = L""; } }