/////////////////////////////////////////////////////////////////////////////// // 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. /////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////// // // PdfAux.cpp // ////////////////////////////////////////////////////////////////////// #include "PdfExportCommon.h" #include "PdfAux.h" #include "OdPlatform.h" #include "Pdf2dExportDevice.h" #include "PdfDocument.h" #include "PdfCatalogDictionary.h" #include "PdfPageNodeDictionary.h" #include "PdfPageDictionary.h" #include "PdfOutputIntentsDictionary.h" #include "PdfContentStream.h" #include "PdfRGBStream.h" #include "PdfExportParams.h" #include "PdfDocumentInformation.h" #include "PdfExportVersion.h" #include "PdfToolkitVersion.h" #include "PdfTempFileStream.h" #include "PdfMetadataStream.h" #include "PdfICCBasedStream.h" #include "PdfViewportDictionary.h" #include "PdfMeasureDictionary.h" #include "PdfNumberFormatDictionary.h" #include "PdfWatermarkAnnotationDictionary.h" #include "PdfFixedPrintDictionary.h" #include "PdfExtGState.h" #include "PdfOutlineDictionary.h" #include "PdfGoToActionDictionary.h" #include "PdfCoordinateSystemDictionary.h" #include "MemoryStream.h" #include "OdCharMapper.h" #include "Gi/GiRasterWrappers.h" #include "RxObjectImpl.h" #include "DynamicLinker.h" namespace TD_PDF_2D_EXPORT { void PDFAUX::createBasePDF4DWG(PDFDocument &PDFDoc, const PDFExportParams &pParams, OdUInt16 pagesCount) { PDFPageNodeDictionaryPtr pRootPageNode = PDFPageNodeDictionary::createObject(PDFDoc, true); for(OdUInt32 f = 0; f < pagesCount; ++f) { PDFPageDictionaryPtr pCurPage(PDFPageDictionary::createObject(PDFDoc, true)); PDFContentStreamPtr pCStream = PDFContentStream::createObject(PDFDoc, true); PDFArrayPtr pContentArray = PDFArray::createObject(PDFDoc); pContentArray->append(pCStream); pCurPage->setContents(pContentArray); if(pParams.archived() >= PDFExportParams::kPDFA_2b) { pCStream->CS(PDFName::createObject(PDFDoc, ("DeviceRGB"))); pCStream->cs(PDFName::createObject(PDFDoc, ("DeviceRGB"))); } pRootPageNode->AddKids(pCurPage); } PDFCatalogDictionaryPtr pCatalog = PDFCatalogDictionary::createObject(PDFDoc, true); PDFDoc.setRoot(pCatalog); pCatalog->setPages(pRootPageNode); pCatalog->setPageLayout(PDFName::createObject(PDFDoc, "SinglePage")); //fit page to screen } void calculateStartPoint(const Watermark& wm, OdGePoint2d& startPoint, const OdGsDCRect& bbox, OdGeMatrix2d& rotation, double ang, double width, double height, double descender) { TD_PDF_2D_EXPORT::Watermark::WatermarkPosition position = wm.position; OdGePoint2d endPoint, checkBBoxPoint; OdGeExtents2d extents; OdGeVector2d moveVec; //set offset OdGePoint2d offset = wm.scaleToPage ? OdGePoint2d() : wm.offset; offset.x = int(offset.x * MM2INCH72(1.)); offset.y = int(offset.y * MM2INCH72(1.)); switch(position) { case Watermark::kLeftToRight: case Watermark::kLowerLeftToUpperRight: case Watermark::kUpperLeftToLowerRight: { OdGePoint2d rotationPoint = OdGePoint2d(double(bbox.m_max.x + bbox.m_min.x) / 2., double(bbox.m_max.y + bbox.m_min.y) / 2.); // center startPoint = OdGePoint2d((double)(bbox.m_max.x + bbox.m_min.x) / 2. - width / 2., (double)(bbox.m_max.y + bbox.m_min.y) / 2. - height / 2.); if(position == Watermark::kLowerLeftToUpperRight) rotation.setToRotation(ang, rotationPoint); else if(position == Watermark::kUpperLeftToLowerRight) rotation.setToRotation(-ang, rotationPoint); else rotation.setToRotation(ang, rotationPoint); startPoint.transformBy(rotation); startPoint.x += offset.x; startPoint.y += offset.y; return; } break; case Watermark::kLowerLeft: { startPoint.x = bbox.m_min.x; //lower left angle startPoint.y = bbox.m_min.y + descender; endPoint.x = startPoint.x + width; //upper right angle endPoint.y = startPoint.y + height; checkBBoxPoint = startPoint; //point to move extent to (lower left angle of BBox) } break; case Watermark::kLowerRight: { startPoint.x = bbox.m_max.x - width; startPoint.y = bbox.m_min.y + descender; endPoint.x = bbox.m_max.x; //upper right angle endPoint.y = startPoint.y + height; checkBBoxPoint.set(endPoint.x, startPoint.y); //point to move extent to (lower right angle of BBox) } break; case Watermark::kLowerMiddle: { startPoint.x = (bbox.m_max.x + bbox.m_min.x - width)/2.; startPoint.y = bbox.m_min.y + descender; endPoint.x = startPoint.x + width; //upper right angle endPoint.y = startPoint.y + height; checkBBoxPoint.set((bbox.m_max.x + bbox.m_min.x)/2., startPoint.y); //point to move extent to } break; case Watermark::kUpperLeft: { startPoint.x = bbox.m_min.x; startPoint.y = bbox.m_max.y - height - descender; endPoint.x = startPoint.x + width; //upper right angle endPoint.y = bbox.m_max.y; checkBBoxPoint.set(startPoint.x, endPoint.y); //point to move extent to (upper left angle of BBox) } break; case Watermark::kUpperRight: { startPoint.x = bbox.m_max.x - width; startPoint.y = bbox.m_max.y - height - descender; endPoint.x = bbox.m_max.x; endPoint.y = bbox.m_max.y; checkBBoxPoint = endPoint; } break; case Watermark::kUpperMiddle: { startPoint.x = (bbox.m_max.x + bbox.m_min.x - width)/2.; startPoint.y = bbox.m_max.y - height - descender; endPoint.x = startPoint.x + width; endPoint.y = bbox.m_max.y; checkBBoxPoint.set((bbox.m_max.x + bbox.m_min.x) / 2., endPoint.y); //point to move extent to } break; case Watermark::kLeftMiddle: { startPoint.x = bbox.m_min.x; startPoint.y = (bbox.m_max.y + bbox.m_min.y - height - descender) / 2.; endPoint.x = startPoint.x + width; //upper right angle endPoint.y = startPoint.y + height + descender; checkBBoxPoint.set(startPoint.x, (bbox.m_max.y + bbox.m_min.y) / 2.); } break; case Watermark::kRightMiddle: { startPoint.x = bbox.m_max.x - width; startPoint.y = (bbox.m_max.y + bbox.m_min.y - height - descender) / 2.; endPoint.x = bbox.m_max.x; //upper right angle endPoint.y = startPoint.y + height + descender; checkBBoxPoint.set(endPoint.x, (bbox.m_max.y + bbox.m_min.y) / 2.); } break; default: ODA_ASSERT(false); break; } rotation.setToRotation(ang, startPoint); extents.set(startPoint, endPoint); extents.transformBy(rotation); switch(position) { case Watermark::kUpperLeft: moveVec = checkBBoxPoint - OdGePoint2d(extents.minPoint().x - offset.x, extents.maxPoint().y + offset.y); break; case Watermark::kUpperRight: moveVec = checkBBoxPoint - OdGePoint2d(extents.maxPoint().x + offset.x, extents.maxPoint().y + offset.y);//extents.maxPoint(); break; case Watermark::kLowerRight: moveVec = checkBBoxPoint - OdGePoint2d(extents.maxPoint().x + offset.x, extents.minPoint().y - offset.y); break; case Watermark::kLowerLeft: moveVec = checkBBoxPoint - OdGePoint2d(extents.minPoint().x - offset.x, extents.minPoint().y - offset.y);// extents.minPoint(); break; case Watermark::kUpperMiddle: moveVec = checkBBoxPoint - OdGePoint2d((extents.maxPoint().x + extents.minPoint().x) / 2. - offset.x, extents.maxPoint().y + offset.y); break; case Watermark::kLowerMiddle: moveVec = checkBBoxPoint - OdGePoint2d((extents.maxPoint().x + extents.minPoint().x) / 2. - offset.x, extents.minPoint().y - offset.y); break; case Watermark::kLeftMiddle: moveVec = checkBBoxPoint - OdGePoint2d(extents.minPoint().x - offset.x, (extents.maxPoint().y + extents.minPoint().y) / 2. + offset.y); break; case Watermark::kRightMiddle: moveVec = checkBBoxPoint - OdGePoint2d(extents.maxPoint().x + offset.x, (extents.maxPoint().y + extents.minPoint().y) / 2. + offset.y); break; default: ODA_ASSERT(false); break; } startPoint += moveVec; } void prepareWMarkText(const Watermark& wm, const OdGsDCRect& bbox, const PDFExportParams& params, PDFDocument& PDFDoc, PDFFontPtr& pOutFont, PDFResourceDictionaryPtr& pResources, OdGeMatrix2d& rotation, OdUInt16& fontSize, OdAnsiString& fontName, OdDbBaseDatabase* pDb) { OdGiTextStyle style; const PDFType1Font::StandardType1FontsEnum font_type = (PDFType1Font::StandardType1FontsEnum)wm.font; if(wm.font == -1) { style.setFont(wm.fontName, /*bold*/false, /*italic*/false, 1/*default charset*/, 0); style.loadStyleRec(pDb); bool bAllowedEdit = true; const bool bEmbedOpt = GETBIT(params.exportFlags(), PDFExportParams::kEmbededOptimizedTTF) || (params.archived() != PDFExportParams::kPDFA_None); //looks like embedded font for watermarks can be optimized always const bool bEmbed = GETBIT(params.exportFlags(), PDFExportParams::kEmbededTTF) || (params.archived() != PDFExportParams::kPDFA_None); fontName = TD_PDF_HELPER_FUNCS::addType0Font(PDFDoc, style, bEmbed, bEmbedOpt, pOutFont, pResources, bAllowedEdit); if(fontName.isEmpty()) fontName = TD_PDF_HELPER_FUNCS::addTrueTypeFont(PDFDoc, style, bEmbed, bEmbedOpt, pOutFont, pResources, bAllowedEdit); } else fontName = TD_PDF_HELPER_FUNCS::addType1Font(PDFDoc, font_type, pOutFont, pResources); rotation.setToIdentity(); ODRECT textBox; double textBaseWidth; if(wm.font != -1) { textBaseWidth = PDFType1Font::getTextBaseWidth(font_type, wm.text); textBox = PDFType1Font::getTextBBox(font_type); } else { textBox = TD_PDF_HELPER_FUNCS::textBBox(&style); textBaseWidth = TD_PDF_HELPER_FUNCS::textWidth(wm.text, &style); } fontSize = wm.fontSize; double textWidth = textBaseWidth * fontSize; double textHeight = (double)(textBox.bottom - Od_abs(textBox.top) + 1) / 1000. * fontSize; //fit text to page if (wm.scaleToPage) { double ang = wm.rotation; if(wm.position == Watermark::kLowerLeftToUpperRight || wm.position == Watermark::kUpperLeftToLowerRight) { ang = atan(double(bbox.m_max.y - bbox.m_min.y) / double(bbox.m_max.x - bbox.m_min.x)); if(OdEqual(wm.rotation, OdaPI, 1e-6)) //only upside-down rotation can be applied in this case ang += OdaPI; } OdGePoint2d minPage(bbox.m_min.x, bbox.m_min.y), maxPage(bbox.m_max.x, bbox.m_max.y); OdGeExtents2d pageExt(minPage, maxPage); OdGePoint2d minText(pageExt.center().x - textWidth / 2, pageExt.center().y - textHeight / 2), maxText(pageExt.center().x + textWidth / 2, pageExt.center().y + textHeight / 2); OdGeExtents2d textExt(minText, maxText); rotation.setToRotation(ang, textExt.center()); textExt.transformBy(rotation); if (pageExt.contains(textExt)) { while(pageExt.contains(textExt)) { fontSize++; textWidth = textBaseWidth * fontSize; textHeight = (double)(textBox.bottom - Od_abs(textBox.top) + 1) / 1000. * fontSize; minText.set(pageExt.center().x - textWidth / 2, pageExt.center().y - textHeight / 2); maxText.set(pageExt.center().x + textWidth / 2, pageExt.center().y + textHeight / 2); textExt.set(minText, maxText); textExt.transformBy(rotation); } fontSize--; textWidth = textBaseWidth * fontSize; textHeight = (double)(textBox.bottom - Od_abs(textBox.top) + 1) / 1000. * fontSize; } else { while (!pageExt.contains(textExt) && fontSize > 1) { fontSize--; textWidth = textBaseWidth * fontSize; textHeight = (double)(textBox.bottom - Od_abs(textBox.top) + 1) / 1000. * fontSize; minText.set(pageExt.center().x - textWidth / 2, pageExt.center().y - textHeight / 2); maxText.set(pageExt.center().x + textWidth / 2, pageExt.center().y + textHeight / 2); textExt.set(minText, maxText); textExt.transformBy(rotation); } } } rotation.setToIdentity(); double descender = fabs((double)textBox.top / 1000. * (double)fontSize); //calculate rotation angle double ang = wm.rotation; if(wm.position == Watermark::kLowerLeftToUpperRight || wm.position == Watermark::kUpperLeftToLowerRight) { ang = atan(double(bbox.m_max.y - bbox.m_min.y) / double(bbox.m_max.x - bbox.m_min.x)); if(OdEqual(wm.rotation, OdaPI, 1e-6)) //only upside-down rotation can be applied in this case ang += OdaPI; } OdGePoint2d startPoint; calculateStartPoint(wm, startPoint, bbox, rotation, ang, textWidth, textHeight, descender); rotation.setTranslation(startPoint.asVector()); } void prepareWMarkPicture(const Watermark& wm, const OdGsDCRect& bbox, PDFDocument& PDFDoc, PDFNamePtr& pImageFormName, PDFResourceDictionaryPtr& pResources, OdGeMatrix2d& rotation) { OdRxRasterServicesPtr pRasSvcs = ::odrxDynamicLinker()->loadApp(RX_RASTER_SERVICES_APPNAME, false); OdGiRasterImagePtr pRasterImage = pRasSvcs->loadRasterImage(wm.imagePath); if(pRasterImage.isNull()) return; rotation.setToIdentity(); const double pixelWidth = pRasterImage->pixelWidth(); const double pixelHeight = pRasterImage->pixelHeight(); if(pixelWidth <= 0 || pixelHeight <= 0) return; double pictW = (double)wm.imageWidth * MM2INCH72(1.); double pictH = (double)wm.imageHeight * MM2INCH72(1.); const double ang = wm.rotation; //fit picture to page if(wm.scaleToPage) { OdGePoint2d minPage(bbox.m_min.x, bbox.m_min.y), maxPage(bbox.m_max.x, bbox.m_max.y); OdGeExtents2d pageExt(minPage, maxPage); OdGePoint2d minImg(pageExt.center().x - pictW / 2, pageExt.center().y - pictH / 2), maxImg(pageExt.center().x + pictW / 2, pageExt.center().y + pictH / 2); OdGeExtents2d imgExt(minImg, maxImg); rotation.setToRotation(ang, imgExt.center()); imgExt.transformBy(rotation); if(pageExt.contains(imgExt)) { while(pageExt.contains(imgExt)) { pictW += .01 * pictW; pictH += .01 * pictH; minImg.set(pageExt.center().x - pictW / 2, pageExt.center().y - pictH / 2); maxImg.set(pageExt.center().x + pictW / 2, pageExt.center().y + pictH / 2); imgExt.set(minImg, maxImg); imgExt.transformBy(rotation); } pictW -= .01 * pictW; pictH -= .01 * pictH; } else { while(!pageExt.contains(imgExt) && pictH > 1. && pictW > 1.) { pictW -= .01 * pictW; pictH -= .01 * pictH; minImg.set(pageExt.center().x - pictW / 2, pageExt.center().y - pictH / 2); maxImg.set(pageExt.center().x + pictW / 2, pageExt.center().y + pictH / 2); imgExt.set(minImg, maxImg); imgExt.transformBy(rotation); } if(pictH < 1.) pictH = 1.; if(pictW < 1.) pictW = 1.; } } double imageCoef = odmin(pictW / pixelWidth, pictH / pixelHeight); OdGePoint2d startPoint; TD_PDF_2D_EXPORT::Watermark::WatermarkPosition position = wm.position; if(position == Watermark::kLowerLeftToUpperRight || position == Watermark::kUpperLeftToLowerRight) position = Watermark::kLeftToRight; calculateStartPoint(wm, startPoint, bbox, rotation, ang, pictW, pictH, 0); TD_PDF_HELPER_FUNCS::addNewImageAsXobject(PDFDoc, pRasterImage, pImageFormName, pResources); OdGeMatrix2d scaling; scaling.setToScaling(imageCoef); rotation.setToProduct(rotation, scaling); rotation.setTranslation(startPoint.asVector()); } void placeTextWatermark(const Watermark& wm, PDFDocument& PDFDoc, PDFFontOptimizer& optimizer, PDFFontPtr& pOutFont, PDFXObjectFormPtr& pN, const OdGeMatrix2d& matTransform, const OdAnsiString& fontName, OdUInt16 fontSize) { pN->BT(); pN->Tf(PDFName::createObject(PDFDoc, fontName, false), fontSize); pN->Tm(matTransform.entry[0][0], matTransform.entry[1][0], matTransform.entry[0][1], matTransform.entry[1][1], (int)matTransform.entry[0][2], (int)matTransform.entry[1][2]); PDFTextStringPtr pText = PDFTextString::createObject(PDFDoc); if(wm.font != -1) { OdAnsiString str(wm.text); pText->set(str); } else { OdString unicodeStr; OdArray pUnicodeTmp; for(int f = 0; f < wm.text.getLength(); ++f) { OdChar ch = wm.text.getAt(f); unicodeStr += PDF_SWAP_16(ch); pUnicodeTmp.push_back(wm.text.getAt(f)); } pText->set(unicodeStr); pText->setExportUnicodeMarker(false); pText->enableFixParenthesis(); // fix '(' ')' '\' -> '\(' '\)' '\\' optimizer.addUnicodeText(pOutFont, pUnicodeTmp); } pN->Tj(pText); pN->ET(); pN->Q(); } void placePictureWatermark(PDFNamePtr& pImageFormName, PDFXObjectFormPtr& pN, const OdGeMatrix2d& matTransform) { pN->q(); pN->cm(matTransform.entry[0][0], matTransform.entry[1][0], matTransform.entry[0][1], matTransform.entry[1][1], matTransform.entry[0][2], matTransform.entry[1][2]); pN->Do(pImageFormName); pN->Q(); } void PDFAUX::CreateWatermark(const Watermark& wm, const OdGsDCRect& bbox, PDFDocument& PDFDoc, PDFPageDictionary* pPage, OdDbBaseDatabase* pDb, const PDFExportParams& params, PDFFontOptimizer& optimizer) { if(!pPage || (wm.imagePath.isEmpty() && wm.text.isEmpty())) return; bool bTextWMark = true; if(!wm.imagePath.isEmpty()) { OdRxSystemServices* pSs = odrxSystemServices(); if(pSs->accessFile(wm.imagePath, Oda::kFileRead) && wm.imageHeight > 0 && wm.imageWidth > 0) bTextWMark = false; else return; } else if(wm.text.isEmpty()) return; const OdUInt16 opacity = wm.opacity; const int xOffset = int(wm.offset.x * MM2INCH72(1.)); const int yOffset = int(wm.offset.y * MM2INCH72(1.)); OdGsDCRect boxWithMargins = bbox; if(wm.scaleToPage) //offset changes in CORE - 19134 { if(wm.position == Watermark::kUpperLeft || wm.position == Watermark::kLowerLeft || wm.position == Watermark::kLeftMiddle || wm.position == Watermark::kLowerMiddle || wm.position == Watermark::kUpperMiddle) boxWithMargins.m_min.x += xOffset; if(wm.position == Watermark::kUpperRight || wm.position == Watermark::kLowerRight || wm.position == Watermark::kRightMiddle || wm.position == Watermark::kLowerMiddle || wm.position == Watermark::kUpperMiddle) boxWithMargins.m_max.x -= xOffset; if(wm.position == Watermark::kLowerLeft || wm.position == Watermark::kLowerRight || wm.position == Watermark::kLowerMiddle || wm.position == Watermark::kLeftMiddle || wm.position == Watermark::kRightMiddle) boxWithMargins.m_min.y += yOffset; if(wm.position == Watermark::kUpperRight || wm.position == Watermark::kUpperLeft || wm.position == Watermark::kUpperMiddle || wm.position == Watermark::kLeftMiddle || wm.position == Watermark::kRightMiddle) boxWithMargins.m_max.y -= yOffset; if (wm.position == Watermark::kLeftToRight || wm.position == Watermark::kLowerLeftToUpperRight || wm.position == Watermark::kUpperLeftToLowerRight) boxWithMargins = OdGsDCRect(bbox.m_min.x + xOffset, bbox.m_max.x - xOffset, bbox.m_min.y + yOffset, bbox.m_max.y - yOffset); } if (boxWithMargins.m_min.x >= boxWithMargins.m_max.x || boxWithMargins.m_min.y >= boxWithMargins.m_max.y) //the same as OdGsDCRect::is_null but w/o ASSERT return; PDFArrayPtr annots = pPage->getAnnots(); PDFWatermarkAnnotationDictionaryPtr pDict = PDFWatermarkAnnotationDictionary::createObject(PDFDoc, true); PDFRectanglePtr rect = PDFRectangle::createObject(PDFDoc); rect->set(bbox.m_min.x, bbox.m_min.y, bbox.m_max.x, bbox.m_max.y); pDict->setRect(rect); pDict->setP(pPage); PDFFixedPrintDictionaryPtr fpDict = PDFFixedPrintDictionary::createObject(PDFDoc); fpDict->setH(PDFNumber::createObject(PDFDoc, 0.)); fpDict->setV(PDFNumber::createObject(PDFDoc, 0.)); PDFArrayPtr pMatrix = PDFArray::createObject(PDFDoc); pMatrix->push_number(1); pMatrix->push_number(0); pMatrix->push_number(0); pMatrix->push_number(1); pMatrix->push_number(0); pMatrix->push_number(0); fpDict->setMatrix(pMatrix); pDict->setFixedPrint(fpDict); PDFIntegerPtr pInt = PDFInteger::createObject(PDFDoc, 196, false); //print (4) & read only (64) & locked (128) pDict->setF(pInt); PDFSubDictionaryPtr pAp = PDFSubDictionary::createObject(PDFDoc); PDFXObjectFormPtr pN = PDFXObjectForm::createObject(PDFDoc, true); pN->dictionary()->setBBox(rect); pAp->AddItem("N", pN); pDict->setAP(pAp); PDFFontPtr pOutFont; PDFResourceDictionaryPtr pResources = pN->dictionary()->getResources(); OdAnsiString fontName; OdUInt16 fontSize = wm.fontSize > 1 ? wm.fontSize : 1; OdGeMatrix2d matTransform; PDFNamePtr pImageName; if (bTextWMark) { prepareWMarkText(wm, boxWithMargins, params, PDFDoc, pOutFont, pResources, matTransform, fontSize, fontName, pDb); if(fontName.isEmpty()) return; } else { prepareWMarkPicture(wm, boxWithMargins, PDFDoc, pImageName, pResources, matTransform); if(pImageName->str().isEmpty()) return; } pN->q(); pN->n(); pN->rg(wm.color); pN->RG(wm.color); if (opacity < 100) { OdAnsiString lwstr; odDToStr(lwstr.getBufferSetLength(512), opacity, 'f', 0); lwstr.releaseBuffer(); PDFResourceDictionaryPtr pResDict(PDFPageNodeDictionaryPtr(PDFDoc.Root()->getPages())->getResources()); PDFExtGStateSubDictionaryPtr pExtGsSub = pResDict->getExtGState(); OdAnsiString opacityGroup = OdAnsiString("WatermarkGS"+ lwstr); PdfExtGStatePtr pExtGState; if (!pExtGsSub->HasItem(opacityGroup)) { pExtGState = PdfExtGState::createObject(PDFDoc, true); //pExtGState->setBM(PDFName::createObject(PDFDoc, "Normal")); pExtGState->setType(PDFName::createObject(PDFDoc, "ExtGState")); PDFNumberPtr pCa = PDFNumber::createObject(PDFDoc, (double)opacity / 100.); pExtGState->setCA(pCa); pExtGState->setca(pCa); pExtGsSub->AddItem(opacityGroup, pExtGState); } else pExtGState = pExtGsSub->Find(opacityGroup); PDFExtGStateSubDictionaryPtr pExtGsSubAnnot = pResources->getExtGState(); if (!pExtGsSubAnnot->HasItem(opacityGroup)) pExtGsSubAnnot->AddItem(opacityGroup, pExtGState); pN->gs(PDFName::createObject(PDFDoc, opacityGroup.c_str())); } if(bTextWMark) placeTextWatermark(wm, PDFDoc, optimizer, pOutFont, pN, matTransform, fontName, fontSize); else placePictureWatermark(pImageName, pN, matTransform); annots->push_back(pDict); } void PDFAUX::createMeasuringViewPort(const OdGsDCRect& bbox, PDFDocument &PDFDoc, PDFPageDictionary* pPage, double iMeasureScale) { PDFArrayPtr pVPArray = pPage->getVP(); PDFViewportDictionaryPtr pVP(PDFViewportDictionary::createObject(PDFDoc)); PDFRectanglePtr rect = PDFRectangle::createObject(PDFDoc); rect->set(bbox.m_min.x, bbox.m_min.y, bbox.m_max.x, bbox.m_max.y); pVP->setBBox(rect); PDFMeasureDictionaryPtr pMeasure = PDFMeasureDictionary::createObject(PDFDoc, true); pMeasure->setSubtype(PDFName::createObject(PDFDoc, "RL")); PDFTextStringPtr pEmptyStr = PDFTextString::createObject(PDFDoc, L"", false); pMeasure->setR(pEmptyStr); PDFArrayPtr pADArray = PDFArray::createObject(PDFDoc); PDFArrayPtr pXArray = PDFArray::createObject(PDFDoc); PDFNumberFormatDictionaryPtr pNumF1 = PDFNumberFormatDictionary::createObject(PDFDoc); PDFNumberFormatDictionaryPtr pNumF2 = PDFNumberFormatDictionary::createObject(PDFDoc); pNumF1->setU(pEmptyStr); pNumF1->setC(PDFNumber::createObject(PDFDoc, 1, false)); pNumF2->setU(pEmptyStr); pNumF2->setC(PDFNumber::createObject(PDFDoc, iMeasureScale, false)); pADArray->push_back(pNumF1); pXArray->push_back(pNumF2); pMeasure->setA(pADArray); pMeasure->setD(pADArray); pMeasure->setX(pXArray); pVP->setMeasure(pMeasure); pVPArray->append(pVP); pPage->setVP(pVPArray); } void PDFAUX::createMeasuringViewPortGEO(PDFDocument& PDFDoc, PDFPageDictionary* pPage, const OdString& coordSysWKT, int coordSysType, int epsgCode, const OdGePoint2dArray& geoExtents, const OdGePoint2dArray& drwExtents) { if(!pPage || coordSysWKT.isEmpty() || geoExtents.isEmpty() || drwExtents.isEmpty() || geoExtents.size() != drwExtents.size()) return; PDFArrayPtr pVPArray = pPage->getVP(); PDFViewportDictionaryPtr pVP(PDFViewportDictionary::createObject(PDFDoc, true)); PDFRectanglePtr rect = PDFRectangle::createObject(PDFDoc); OdGeExtents2d ext; ext.addPoints(drwExtents); rect->set(OdInt32(ext.minPoint().x), OdInt32(ext.minPoint().y), OdInt32(ext.maxPoint().x), OdInt32(ext.maxPoint().y)); pVP->setBBox(rect); PDFMeasureDictionaryPtr pMeasure = PDFMeasureDictionary::createObject(PDFDoc, true); pMeasure->setSubtype(PDFName::createObject(PDFDoc, "GEO")); PDFCoordinateSystemDictionaryPtr pCoordSys = PDFCoordinateSystemDictionary::createObject(PDFDoc); pCoordSys->SetType(kGEO); //((PDFCoordinateSystemType)coordSysType); - doesn't work with projected coordinate system type, maybe it is necessary to investigate it again. Anyway, projected system includes geo system if(epsgCode) pCoordSys->SetEPSG(epsgCode); OdAnsiString sWKT(coordSysWKT); pCoordSys->SetWKT(sWKT); pMeasure->setGCS(pCoordSys); //Set GPTS array - geo coordinates points PDFArrayPtr pGPTS = PDFArray::createObject(PDFDoc); for (OdGePoint2dArray::const_iterator iter = geoExtents.begin(); iter != geoExtents.end(); iter++) { pGPTS->push_number((*iter).y); //latitude (first coordinate in geospatial pdf) pGPTS->push_number((*iter).x); //longitude (second coordinate) } pMeasure->setGPTS(pGPTS); //Set LPTS array - viewport points OdGeMatrix2d mat; OdGePoint2d ul(ext.minPoint().x, ext.maxPoint().y); OdGePoint2d lr(ext.maxPoint().x, ext.minPoint().y); OdGeVector2d yAxis = ul - ext.minPoint(); OdGeVector2d xAxis = lr - ext.minPoint(); mat.setCoordSystem(ext.minPoint(), xAxis, yAxis); mat.invert(); PDFArrayPtr pLPTS = PDFArray::createObject(PDFDoc); for(OdGePoint2dArray::const_iterator iter = drwExtents.begin(); iter != drwExtents.end(); iter++) { OdGePoint2d buf = (*iter); buf.transformBy(mat); pLPTS->push_number(buf.x); pLPTS->push_number(buf.y); } pMeasure->setLPTS(pLPTS); //set boundaries pMeasure->setBounds(pLPTS); pVP->setMeasure(pMeasure); pVPArray->append(pVP); pPage->setVP(pVPArray); } void PDFAUX::updateContentStreamWithCM(PDFDocument &PDFDoc, PDFPageDictionary* pPage, const PDFExportParams &pParams) { if (!pPage || pParams.export2XObject()) //recording disabled return; double dScale = 72. / (double)(pParams.getGeomDPI()); PDFArrayPtr pContent = pPage->getContents(); if (pContent->size() == 1 && //now we can have only 1 CS for page dScale != 1.) //there is no point to put scale coeff 1 { PDFContentStreamPtr pCStream = PDFContentStream::createObject(PDFDoc, true); OdGeMatrix2d m; m.setToScaling(dScale); pCStream->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]); pContent->insert(pContent->begin(), pCStream); } } void PDFAUX::createDocumentInformation(PDFDocument &PDFDoc, const PDFExportParams &pParams) { PDFDocumentInformationPtr pDI = PDFDocumentInformation::createObject(PDFDoc, true); PDFMetadataStreamPtr pMD; if (PDFDoc.isPdfA()) pMD = PDFMetadataStream::createObject(PDFDoc, true); if (!pParams.title().isEmpty()) { PDFTextStringPtr pTitle = PDFTextString::createObject(PDFDoc, false); TD_PDF_HELPER_FUNCS::getUnicodeTextString(pParams.title(), pTitle); pTitle->enableFixParenthesis(); // fix '(' ')' '\' -> '\(' '\)' '\\' pDI->setTitle(pTitle); if (!pMD.isNull()) pMD->setTitle(pParams.title()); } if (!pParams.author().isEmpty()) { PDFTextStringPtr pAuthor = PDFTextString::createObject(PDFDoc, false); TD_PDF_HELPER_FUNCS::getUnicodeTextString(pParams.author(), pAuthor); pAuthor->enableFixParenthesis(); // fix '(' ')' '\' -> '\(' '\)' '\\' pDI->setAuthor(pAuthor); if (!pMD.isNull()) pMD->setAuthor(pParams.author()); } if (!pParams.subject().isEmpty()) { PDFTextStringPtr pSubject = PDFTextString::createObject(PDFDoc, false); TD_PDF_HELPER_FUNCS::getUnicodeTextString(pParams.subject(), pSubject); pSubject->enableFixParenthesis(); // fix '(' ')' '\' -> '\(' '\)' '\\' pDI->setSubject(pSubject); if (!pMD.isNull()) pMD->setSubject(pParams.subject()); } if (!pParams.keywords().isEmpty()) { PDFTextStringPtr pKeywords = PDFTextString::createObject(PDFDoc, false); TD_PDF_HELPER_FUNCS::getUnicodeTextString(pParams.keywords(), pKeywords); pKeywords->enableFixParenthesis(); // fix '(' ')' '\' -> '\(' '\)' '\\' pDI->setKeywords(pKeywords); if (!pMD.isNull()) pMD->setKeywords(pParams.keywords()); } if (!pParams.creator().isEmpty()) { PDFTextStringPtr pCreator = PDFTextString::createObject(PDFDoc, false); TD_PDF_HELPER_FUNCS::getUnicodeTextString(pParams.creator(), pCreator); pCreator->enableFixParenthesis(); // fix '(' ')' '\' -> '\(' '\)' '\\' pDI->setCreator(pCreator); if (!pMD.isNull()) pMD->setCreator(pParams.creator()); } if (!pParams.producer().isEmpty()) { PDFTextStringPtr pProducer = PDFTextString::createObject(PDFDoc, false); TD_PDF_HELPER_FUNCS::getUnicodeTextString(pParams.producer(), pProducer); pProducer->enableFixParenthesis(); // fix '(' ')' '\' -> '\(' '\)' '\\' pDI->setProducer(pProducer); if (!pMD.isNull()) pMD->setProducer(pParams.producer()); } else { OdAnsiString str; str.format(("ODA PDF Export v%d.%d.%d.%d (v%d.%d.%d.%d)"), PDF_EXPORT_MAJOR_VERSION, PDF_EXPORT_MINOR_VERSION, PDF_EXPORT_MAJOR_BUILD_VERSION, PDF_EXPORT_MINOR_BUILD_VERSION, PDF_TOOLKIT_MAJOR_VERSION, PDF_TOOLKIT_MINOR_VERSION, PDF_TOOLKIT_MAJOR_BUILD_VERSION, PDF_TOOLKIT_MINOR_BUILD_VERSION); pDI->setProducer(PDFTextString::createObject(PDFDoc, str, false)); if (!pMD.isNull()) pMD->setProducer(str); } PDFDatePtr pCreationDate = PDFDate::createObject(PDFDoc); OdTimeStamp curDate(OdTimeStamp::kInitUniversalTime); pCreationDate->set(curDate, 0, 0); pDI->setCreationDate(pCreationDate); if (!pMD.isNull()) { pMD->setCreationDate(curDate); pMD->setPdfALevelConf((OdUInt16)pParams.archived(), 2);//conformance B is hardcoded PDFCatalogDictionaryPtr pCatalog = PDFDoc.Root(); pCatalog->setMetadata(pMD); } PDFDoc.setDocumentInformation(pDI); } void PDFAUX::CreateOutputIntent(PDFDocument &PDFDoc) { PDFCatalogDictionaryPtr pCatalog = PDFDoc.Root(); PDFArrayPtr pIntents = pCatalog->Find("OutputIntents"); if (pIntents.isNull()) { PDFOutputIntentsDictionaryPtr pIntent = PDFOutputIntentsDictionary::createObject(PDFDoc); PDFICCBasedStreamPtr pDestOutProfile = PDFICCBasedStream::createObject(PDFDoc, true); pIntent->setDestOutputProfile(pDestOutProfile); pIntents = PDFArray::createObject(PDFDoc); pIntents->append(pIntent); pCatalog->setOutputIntents(pIntents); } } PDFIndexedRGBColorSpacePtr PDFAUX::CreateIndexedRGBColorSpace(const ODCOLORREF *pRGB, OdUInt32 num, PDFDocument &PDFDoc) { PDFIndexedRGBColorSpacePtr pIndexed = PDFIndexedRGBColorSpace::createObject(PDFDoc, true); PDFRGBStreamPtr pRGBStream( pIndexed->getLookup() ); for(OdUInt32 f=0; faddRGB(ODGETRED(*pRGB), ODGETGREEN(*pRGB), ODGETBLUE(*pRGB)); } return pIndexed; } PDFIndexedRGBColorSpacePtr PDFAUX::CreateIndexedRGBColorSpace(const OdUInt8 *pRGB, OdUInt32 num, PDFDocument &PDFDoc) { PDFIndexedRGBColorSpacePtr pIndexed = PDFIndexedRGBColorSpace::createObject(PDFDoc, true); PDFRGBStreamPtr(pIndexed->getLookup())->addRGB(pRGB, num); return pIndexed; } PDFResourceDictionaryPtr PDFAUX::createResDictionary4DWG(PDFDocument &PDFDoc) { PDFResourceDictionaryPtr pResDic = PDFResourceDictionary::createObject(PDFDoc, true); return pResDic; } void PDFAUX::createIndexedDWGPalette(const ODCOLORREF *pRGB, OdUInt32 num, PDFDocument &PDFDoc) { PDFPageNodeDictionaryPtr pPageTree(PDFDoc.Root()->getPages()); PDFResourceDictionaryPtr pResDict = pPageTree->getResources(); PDFSubDictionaryPtr pSubColorDict = pResDict->getColorSpace(); PDFIndexedRGBColorSpacePtr pIndexed = CreateIndexedRGBColorSpace(pRGB, num, PDFDoc); pSubColorDict->AddItem(("DWGPalette"), pIndexed); } class OdGiRasterImageMaskWrapper : public OdGiRasterImageWrapper { public: OdGiRasterImage::PixelFormatInfo pixelFormat() const override { OdGiRasterImage::PixelFormatInfo pf = OdGiRasterImageWrapper::pixelFormat(); return pf; } OdUInt32 scanLinesAlignment() const override { return 1; } }; }