/////////////////////////////////////////////////////////////////////////////// // 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. /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // // PdfExportParamsHolder.cpp : implementation of the PdfExportParamsHolder class // /////////////////////////////////////////////////////////////////////////////// #include "OdaCommon.h" //#ifdef _WIN32 #define STL_USING_IOSTREAM #include "OdaSTL.h" //#endif #include "PdfExportCommon.h" #include "Ge/GeExtents3d.h" #include "OdTimeStamp.h" #include "ColorMapping.h" #include "Pdf2dExportDevice.h" #include "Pdf2dExportView.h" #include "PdfExportParamsHolder.h" #include "PdfPageNodeDictionary.h" #include "PdfOCManager.h" #include "Gs/GsPageParams.h" #include "PdfAux.h" #include "QPDFHelper.h" #define STL_USING_MAP #define STL_USING_VECTOR #include "OdaSTL.h" #include "PdfExportService.h" #include "RxObject.h" #include "AbstractViewPE.h" #include "MemoryStream.h" namespace TD_PDF_2D_EXPORT { //*************************************************************************** // //*************************************************************************** PdfExportParamsHolder::PdfExportParamsHolder(PDFDocument& document, PDFFontOptimizer& font_optimizer, PDFType3Optimizer& type_3_optimizer) : m_Document(document) , m_pContentCommands(nullptr) , m_pCurPage(nullptr) , m_FontOptimizer(font_optimizer) , m_Type3Optimizer(type_3_optimizer) , m_BookmarkNames(nullptr) , m_BookmarkPoints(nullptr) { } PdfExportParamsHolder::~PdfExportParamsHolder() { } PDFResultEx PdfExportParamsHolder::getPageParamsFromLayout(OdRxObject* pLayoutPtr, OdGsPageParams &pageParams) const { OdDbBaseLayoutPEPtr pLayout(pLayoutPtr); if(pLayout->paperName(pLayoutPtr).isEmpty()) return exCannotRestorePaperFromLayout; // Get printer paper info double dPrinterWidth, dPrinterHeight; pLayout->getPaperSize(pLayoutPtr, dPrinterWidth, dPrinterHeight); //in mm const double dLeftMargin = pLayout->getLeftMargin(pLayoutPtr); // in mm const double dRightMargin = pLayout->getRightMargin(pLayoutPtr); // in mm const double dTopMargin = pLayout->getTopMargin(pLayoutPtr); // in mm const double dBottomMargin = pLayout->getBottomMargin(pLayoutPtr);// in mm if(pLayout->plotRotation(pLayoutPtr) == OdDbBaseLayoutPE::k90degrees || pLayout->plotRotation(pLayoutPtr) == OdDbBaseLayoutPE::k270degrees) pageParams.set( dPrinterHeight, dPrinterWidth, dBottomMargin, dTopMargin, dLeftMargin, dRightMargin ); else pageParams.set( dPrinterWidth, dPrinterHeight, dLeftMargin, dRightMargin, dBottomMargin, dTopMargin ); return exOk; } PDFResultEx PdfExportParamsHolder::init(const PDFExportParams &pParams) { // Prepare input parameters for using in CPdfExportImpl m_Params = pParams; PDFExportParams::PDFExportFlags exportFlags = m_Params.exportFlags(); const auto& pageParams = m_Params.pageParams(); if(m_Params.getPRCMode() != PDFExportParams::kDisabled && GETBIT(exportFlags, PDFExportParams::kUseHLR)) return exUseHLRConflictedWithEnablePRC; if(OdPDF::kPDFAuto == m_Params.version()) { m_Params.setVersion(OdPDF::kPDFv1_7); //if (m_Params.getPRCMode() != PDFExportParams::kDisabled) //formally, PRC stream requires pdf version 2.0, but since it has been working for years with 1.7 (and with 1.6 before that) version, let's keep it that way // m_Params.setVersion(OdPDF::kPDFv1_7); if(GETBIT(exportFlags, PDFExportParams::kMeasuring) && m_Params.measuringType() == PDFExportParams::kGEO) m_Params.setVersion(OdPDF::kPDFv2_0); if(pageParams.size() > 0) { for(auto it = pageParams.begin(); it < pageParams.end(); ++it) { if(it->getPaperHeight() > 5080 || it->getPaperWidth() > 5080) //PDF limits before 2.0 { m_Params.setVersion(OdPDF::kPDFv2_0); break; } } } if(m_Params.archived() == PDFExportParams::kPDFA_1b) //CHECK m_Params.setVersion(OdPDF::kPDFv1_4); } if(m_Params.getPRCMode() != PDFExportParams::kDisabled) { m_Params.setUseGsCache(false); //using GsCache with PRC is not implemented yet if(m_Params.archived() != PDFExportParams::kPDFA_None) return exInternalError;//Annotation types not defined in ISO 32000-1 shall not be permitted. Additionally, the 3D, Sound, Screen and Movie types shall not be permitted. PdfExportServiceInterfacePtr pExportIFace = getPdfExportService(); if(pExportIFace.isNull()) return exPdfExportServiceMissed; } if(m_Params.database() == 0 && !m_Params.databases().size()) return exNullDatabase; if(!m_Params.export2XObject() && m_Params.output().isNull()) return exNullOutputStream; if(m_Params.hatchDPI() < 1 || m_Params.colorImagesDPI() < 1 || m_Params.bwImagesDPI() < 1) return exInvalidImageDPI; //check layout names if exist if(!m_Params.layoutNames().isEmpty() && m_Params.layouts().size() != m_Params.layoutNames().size() && !(m_Params.layouts().isEmpty() && m_Params.layoutNames().size() == 1)) { OdStringArray empty; m_Params.setLayoutNames(empty); } if(GETBIT(exportFlags, PDFExportParams::kZoomToExtentsMode) == 0 && m_Params.layouts().size()) //in case of export the active layout from only one Db, the layout can be undefined, so, page params will be added later m_Params.pageParams().resize(m_Params.layouts().size()); //check selection set if(m_Params.getSelectionSetsArray().size() && m_Params.databases().size() && m_Params.getSelectionSetsArray().size() != m_Params.databases().size()) { //selection set array size should correspond the databases array size OdArray emptySet; m_Params.setSelectionSetsArray(emptySet); } // Layers functionality if(!GETBIT(exportFlags, PDFExportParams::kEnableLayers)) exportFlags = ((PDFExportParams::PDFExportFlags)(exportFlags & ~PDFExportParams::kIncludeOffLayers)); if(m_Params.archived() != PDFExportParams::kPDFA_None) { if(m_Params.archived() == PDFExportParams::kPDFA_1b) { //Check layers (Layers are not supported in PDF/A-1) exportFlags = ((PDFExportParams::PDFExportFlags)(exportFlags & ~PDFExportParams::kEnableLayers)); exportFlags = ((PDFExportParams::PDFExportFlags)(exportFlags & ~PDFExportParams::kIncludeOffLayers)); //Transparency (including merging colors) is not supported in PDF/A-1 exportFlags = ((PDFExportParams::PDFExportFlags)(exportFlags & ~PDFExportParams::kMergeLines)); //Measuring functionality is not supported in PDF/A-1 exportFlags = ((PDFExportParams::PDFExportFlags)(exportFlags & ~PDFExportParams::kMeasuring)); ////According to PDF 1.4 specs we must prevent strings (include stream parts) from being greater then 64KB m_Params.setDCTCompression(true); m_Params.setDCTQuality(40); exportFlags = ((PDFExportParams::PDFExportFlags)(exportFlags | PDFExportParams::kFlateCompression)); exportFlags = ((PDFExportParams::PDFExportFlags)(exportFlags & ~PDFExportParams::kASCIIHexEncoding)); ///Check paper size and resolution double max_coord = m_Params.pageParams().size() ? (odmax(m_Params.pageParams()[0].getPaperWidth(), m_Params.pageParams()[0].getPaperHeight()) / (72. / (double)m_Params.getGeomDPI())) : 0; if(max_coord > 32000.) //According to specs we must prevent real values (like coordinates) from being greater then 32767 so we have to reduce DPI with some reserve { double scale = max_coord / 32000.; OdUInt16 newDPI = static_cast(m_Params.getGeomDPI() / scale); if(newDPI > 200) newDPI = (newDPI - 100) - ((newDPI - 100) % 100); //another reserve if(newDPI < 72) newDPI = 72; m_Params.setGeomDPI(newDPI); if(m_Params.colorImagesDPI() > newDPI) m_Params.setColorImagesDPI(newDPI); if(m_Params.bwImagesDPI() > newDPI) m_Params.setBWImagesDPI(newDPI); } else if(!max_coord && m_Params.getGeomDPI() > 600) //Export the active layout from only one Db w/o Z2E mode. //In this case max page size is 4A0, //which could be exported with maximum 600 DPI resolution to meet PDF/A-1b requirement. { m_Params.setGeomDPI(600); if(m_Params.colorImagesDPI() > 600) m_Params.setColorImagesDPI(600); if(m_Params.bwImagesDPI() > 600) m_Params.setBWImagesDPI(600); } } if(!GETBIT(exportFlags, PDFExportParams::kTTFTextAsGeometry)) exportFlags = ((PDFExportParams::PDFExportFlags)(exportFlags | PDFExportParams::kEmbededTTF)); //only embedded fonts are supported for PDF/A } //check adaptive 720 dpi mode if(m_Params.get720DPIMode()) m_Params.setGeomDPI(72); m_Params.setExportFlags(exportFlags); PDFOCManager::clearLayersData(); return ::exOk; } void PdfExportParamsHolder::prepareLayers(OdDbBaseDatabase* pDb, OdGiContext* pCtx) { if(!pDb) return; m_OffLayers.clear(); m_FrozenLayers.clear(); OdDbBaseDatabasePEPtr pDbPE(pDb); if(pDbPE.isNull()) return; OdRxIteratorPtr it = pDbPE->layers(pDb, pCtx); const bool bExportOffLayers = GETBIT(m_Params.exportFlags(), PDFExportParams::kIncludeOffLayers); if(bExportOffLayers) { for(; !it->done(); it->next()) { OdRxObjectPtr pLayer = it->object(); OdDbBaseLayerPEPtr pLayerPE(pLayer); if(pLayerPE.isNull()) { ODA_ASSERT(!pLayerPE.isNull()); return; } const bool bLayerOff = pLayerPE->isOff(pLayer); const bool bLayerFrozen = pLayerPE->isFrozen(pLayer); const OdString strLayerName = pLayerPE->name(pLayer); if(bLayerFrozen) { m_FrozenLayers[strLayerName] = pLayer; m_OffLayers[strLayerName] = pLayer; } else if(bLayerOff) { m_OffLayers[strLayerName] = pLayer; } } } } bool PdfExportParamsHolder::isLayerOriginallyVisible(const OdString &layerName) const { if(m_OffLayers.find(layerName) != m_OffLayers.end()) return false; //This check is necessary only when layer is created in PDF document, so it was moved on that stage // if(layerName.right(PDF_FROZEN_LAYER_SIZE) == PDF_FROZEN_LAYER) // { // if(m_FrozenLayers.find(layerName.left(layerName.getLength() - PDF_FROZEN_LAYER_SIZE)) != m_FrozenLayers.end()) // return false; // } return true; } bool PdfExportParamsHolder::addFrozenLayer(const OdString& layerName) { bool bRes = false; if(m_FrozenLayers.find(layerName) == m_FrozenLayers.end()) { m_OffLayers[layerName] = nullptr; m_FrozenLayers[layerName] = nullptr; //we have no access to the layer here, but seems it's not needed so nullptr is good bRes = true; } return bRes; } }