/* AbiWord -- Embedded graphics for layout * Copyright (C) 1999 Matt Kimball * Copyright (C) 2009 Hubert Figuiere * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include #include #include "ut_assert.h" #include "ut_png.h" #include "ut_jpeg.h" #include "ut_string.h" #include "ut_debugmsg.h" #include "ut_bytebuf.h" #include "fl_ContainerLayout.h" #include "px_CR_Object.h" #include "pd_Document.h" #include "pp_AttrProp.h" #include "fg_GraphicRaster.h" FG_Graphic* FG_GraphicRaster::createFromChangeRecord(const fl_ContainerLayout* pFL, const PX_ChangeRecord_Object* pcro) { FG_GraphicRaster* pFG = new FG_GraphicRaster(); bool bFoundDataItem = false; const PD_Document* pDoc = pFL->getDocument(); PT_BlockOffset blockOffset = pcro->getBlockOffset(); /* Get the attribute list for this offset, lookup the dataid for the image, and get the dataItem. The bytes in the dataItem should be a PNG image. */ pFL->getSpanAP(blockOffset, false, pFG->m_pSpanAP); if (pFG->m_pSpanAP) { bool bFoundDataID = pFG->m_pSpanAP->getAttribute("dataid", pFG->m_pszDataID); if (bFoundDataID && pFG->m_pszDataID) { std::string mime_type; bFoundDataItem = pDoc->getDataItemDataByName(pFG->m_pszDataID, &pFG->m_pbb, &mime_type, NULL); if(bFoundDataItem) { if(mime_type == "image/jpeg") { pFG->m_format = JPEG_FORMAT; } } } } if (!bFoundDataItem) DELETEP(pFG); return pFG; } FG_Graphic* FG_GraphicRaster::createFromStrux(const fl_ContainerLayout* pFL) { FG_GraphicRaster* pFG = new FG_GraphicRaster(); bool bFoundDataItem = false; const PD_Document* pDoc = pFL->getDocument(); /* Get the attribute list for this offset, lookup the dataid for the image, and get the dataItem. The bytes in the dataItem should be a PNG image. */ pFL->getAP(pFG->m_pSpanAP); if (pFG->m_pSpanAP) { bool bFoundDataID = pFG->m_pSpanAP->getAttribute(PT_STRUX_IMAGE_DATAID, pFG->m_pszDataID); if (bFoundDataID && pFG->m_pszDataID) { std::string mime_type; bFoundDataItem = pDoc->getDataItemDataByName(pFG->m_pszDataID, &pFG->m_pbb, &mime_type, NULL); if(bFoundDataItem) { if(mime_type == "image/jpeg") { pFG->m_format = JPEG_FORMAT; } } } } if (!bFoundDataItem) DELETEP(pFG); return pFG; } FG_GraphicRaster::FG_GraphicRaster() : m_format(PNG_FORMAT) , m_pbb(NULL) , m_bOwnBuffer(false) , m_iWidth(0) , m_iHeight(0) , m_iMaxW(0) , m_iMaxH(0) , m_pSpanAP(NULL) , m_pszDataID(NULL) { xxx_UT_DEBUGMSG(("GraphRaster created %x \n",this)); } FG_GraphicRaster::~FG_GraphicRaster() { if (m_bOwnBuffer) DELETEP(m_pbb); else m_pbb = NULL; xxx_UT_DEBUGMSG(("GraphRaster Deleted %x \n",this)); } FG_Graphic * FG_GraphicRaster::clone(void) const { FG_GraphicRaster * pClone = new FG_GraphicRaster(); pClone->m_format = m_format; pClone->m_pbb = m_pbb; pClone->m_bOwnBuffer = false; pClone->m_pSpanAP = m_pSpanAP; pClone->m_pszDataID = m_pszDataID; pClone->m_iWidth = m_iWidth; pClone->m_iHeight = m_iHeight; pClone->m_iMaxW = m_iMaxW; pClone->m_iMaxH = m_iMaxH; return pClone; } FGType FG_GraphicRaster::getType(void) const { return FGT_Raster; } const UT_ByteBuf* FG_GraphicRaster::getBuffer() const { return m_pbb; } const char * FG_GraphicRaster::getDataId(void) const { return m_pszDataID; } double FG_GraphicRaster::getWidth(void) const { UT_ASSERT(m_pbb); return m_iWidth; } double FG_GraphicRaster::getHeight(void) const { UT_ASSERT(m_pbb); return m_iHeight; } /*! * Return the width property of the span that contains this image. */ const char * FG_GraphicRaster::getWidthProp(void) { const gchar * szWidth = NULL; m_pSpanAP->getProperty("width", szWidth); if(szWidth == NULL) { szWidth = "0in"; } return szWidth; } /*! * Return the Height property of the span that contains this image. */ const char * FG_GraphicRaster::getHeightProp(void) { const gchar * szHeight = NULL; m_pSpanAP->getProperty("height", szHeight); if(szHeight == NULL) { szHeight = "0in"; } return szHeight; } /*! * If either the graphics class or graphics resolution changes, regenerate * the image. */ GR_Image * FG_GraphicRaster::regenerateImage(GR_Graphics * pG) { return generateImage(pG,m_pSpanAP,m_iMaxW,m_iMaxH); } // // We will generate an image at the proper resolution for display in the // graphics object we are given. // GR_Image* FG_GraphicRaster::generateImage(GR_Graphics* pG, const PP_AttrProp * pSpanAP, UT_sint32 maxW, UT_sint32 maxH) { UT_ASSERT(m_pSpanAP); UT_ASSERT(m_pszDataID); /* We need to know the display size of the new image. */ const gchar *pszWidth; const gchar *pszHeight; if(pSpanAP != NULL) { m_pSpanAP = pSpanAP; } bool bFoundWidthProperty = m_pSpanAP->getProperty("width", pszWidth); bool bFoundHeightProperty = m_pSpanAP->getProperty("height", pszHeight); UT_sint32 iDisplayWidth = 0; UT_sint32 iDisplayHeight = 0; // try frame-width, frame-height if(!bFoundWidthProperty || !bFoundHeightProperty) { bFoundWidthProperty = m_pSpanAP->getProperty("frame-width", pszWidth); bFoundHeightProperty = m_pSpanAP->getProperty("frame-height", pszHeight); } if (bFoundWidthProperty && bFoundHeightProperty && pszWidth && pszHeight && pszWidth[0] && pszHeight[0]) { iDisplayWidth = UT_convertToLogicalUnits(static_cast(pszWidth)); iDisplayHeight = UT_convertToLogicalUnits(static_cast(pszHeight)); } if((iDisplayWidth==0) || (iDisplayHeight == 0)) { UT_sint32 iImageWidth; UT_sint32 iImageHeight; switch(m_format) { case PNG_FORMAT: UT_PNG_getDimensions(m_pbb, iImageWidth, iImageHeight); break; case JPEG_FORMAT: UT_JPEG_getDimensions(m_pbb, iImageWidth, iImageHeight); break; default: UT_ASSERT(UT_SHOULD_NOT_HAPPEN); break; } iDisplayWidth = pG->tlu(iImageWidth); iDisplayHeight = pG->tlu(iImageHeight); } UT_ASSERT(iDisplayWidth > 0); UT_ASSERT(iDisplayHeight > 0); if (maxW != 0 && iDisplayWidth > maxW) { iDisplayHeight = iDisplayHeight * maxW / iDisplayWidth; iDisplayWidth = maxW; } if (maxH != 0 && iDisplayHeight > maxH) { iDisplayWidth = iDisplayWidth * maxH / iDisplayHeight; iDisplayHeight = maxH; } m_iMaxW = maxW; m_iMaxH = maxH; GR_Image *pImage = pG->createNewImage(m_pszDataID, m_pbb, getMimeType(), iDisplayWidth, iDisplayHeight, GR_Image::GRT_Raster); return pImage; } static const std::string s_none; static const std::string s_png_type = "image/png"; static const std::string s_jpeg_type = "image/jpeg"; const std::string & FG_GraphicRaster::getMimeType() const { switch(m_format) { case PNG_FORMAT: return s_png_type; case JPEG_FORMAT: return s_jpeg_type; } return s_none; } // // We need to be able to add a representation of ourselves to an // existing document. This added representation can be used to // reconstruct an equivalent FG_GraphicRaster object after this one // is discarded. // UT_Error FG_GraphicRaster::insertIntoDocument(PD_Document* pDoc, UT_uint32 res, UT_uint32 iPos, const char* szName) { UT_return_val_if_fail(pDoc, UT_ERROR); UT_ASSERT(szName); /* Create the data item */ pDoc->createDataItem(szName, false, m_pbb, getMimeType(), NULL); /* Insert the object into the document. */ std::string szProps; szProps += "width:"; szProps += UT_convertInchesToDimensionString(DIM_IN, static_cast(m_iWidth)/res, "3.2"); szProps += "; height:"; szProps += UT_convertInchesToDimensionString(DIM_IN, static_cast(m_iHeight)/res, "3.2"); const gchar* attributes[] = { "dataid", szName, PT_PROPS_ATTRIBUTE_NAME, szProps.c_str(), NULL, NULL }; pDoc->insertObject(iPos, PTO_Image, attributes, NULL); // TODO: better error checking in this function return UT_OK; } const char * FG_GraphicRaster::createDataItem(PD_Document *pDoc, const char * szName) { UT_return_val_if_fail(pDoc,NULL); UT_ASSERT(szName); pDoc->createDataItem(szName, false, m_pbb, getMimeType(), NULL); return szName; } /*! * Insert an image at the strux given. This will become the * background image for the container defined by the strux. */ UT_Error FG_GraphicRaster::insertAtStrux(PD_Document* pDoc, UT_uint32 res, UT_uint32 iPos, PTStruxType iStruxType, const char* szName) { UT_return_val_if_fail(pDoc, UT_ERROR); UT_ASSERT(szName); /* Create the data item */ pDoc->createDataItem(szName, false, m_pbb, getMimeType(), NULL); /* Insert the object into the document. */ std::string szProps; szProps += "width:"; szProps += UT_convertInchesToDimensionString(DIM_IN, static_cast(m_iWidth)/res, "3.2"); szProps += "; height:"; szProps += UT_convertInchesToDimensionString(DIM_IN, static_cast(m_iHeight)/res, "3.2"); const gchar* attributes[] = { PT_STRUX_IMAGE_DATAID, szName, PT_PROPS_ATTRIBUTE_NAME, szProps.c_str(), NULL, NULL }; pDoc->changeStruxFmt(PTC_AddFmt,iPos,iPos,attributes,NULL,iStruxType); // TODO: better error checking in this function return UT_OK; } bool FG_GraphicRaster::setRaster_PNG(const UT_ByteBuf* pBB) { if (m_bOwnBuffer) DELETEP(m_pbb); m_pbb = pBB; m_format = PNG_FORMAT; m_bOwnBuffer = true; // We want to calculate the dimensions of the image here. return UT_PNG_getDimensions(pBB, m_iWidth, m_iHeight); } bool FG_GraphicRaster::setRaster_JPEG(const UT_ByteBuf* pBB) { if (m_bOwnBuffer) DELETEP(m_pbb); m_pbb = pBB; m_format = JPEG_FORMAT; m_bOwnBuffer = true; // We want to calculate the dimensions of the image here. return UT_JPEG_getDimensions(pBB, m_iWidth, m_iHeight); }