summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorALONSO Laurent <laurent.alonso@inria.fr>2021-09-14 13:22:35 +0200
committerALONSO Laurent <laurent.alonso@inria.fr>2021-09-14 13:22:35 +0200
commit7742191bb0971dc165170f86edd3970f1a4c7052 (patch)
treee0ae0db6fedd8eacdfc16ca057e9d56ce63ad8fc
parent87ae19225bd5e04d34d5a42a3aee47b19662bb7f (diff)
add basic code to implement simple image's cropping...
Change-Id: Id039d2f1220c143efdb2cb93f2c751d5ed43b0aa
-rw-r--r--src/lib/IWORKCollector.cpp42
-rw-r--r--src/lib/libetonyek_utils.cpp249
-rw-r--r--src/lib/libetonyek_utils.h1
3 files changed, 286 insertions, 6 deletions
diff --git a/src/lib/IWORKCollector.cpp b/src/lib/IWORKCollector.cpp
index b5f7f8f..bd17cf9 100644
--- a/src/lib/IWORKCollector.cpp
+++ b/src/lib/IWORKCollector.cpp
@@ -1127,11 +1127,45 @@ void IWORKCollector::drawMedia(const IWORKMediaPtr_t &media)
glm::dvec3 dim = trafo * glm::dvec3(media->m_geometry->m_size.m_width, media->m_geometry->m_size.m_height, 0);
if (media->m_cropGeometry)
{
- /* cropping seems to pose problem to LibreOffice because
- sometimes it does not use the real picture size to clip
- the picture (or I make some mistakes).
+ /* cropping is difficult:
+ - LibreOffice uses the data's picture dimension to clip the picture (or I make some mistakes),
+ - iWorks defines the clipping using the final picture position on a page
+ (as it can store an image with different resolutions).
+ Thus, in order to do the conversion, we need to also
+ retrieve the data's picture dimension.
+
+ So for now, we always reset the origin and resize the
+ picture to its final size and define the cropping only when
+ we can retrieve the image size and there is no
+ symetry, ...*/
+ double w, h;
+ if (detectImageDimension(input, w, h) && dim[0]>0 && dim[1]>0 &&
+ media->m_geometry->m_size.m_width>0 && media->m_geometry->m_size.m_height>0 && w>0 && h>0)
+ {
+ double decalBegin[]= {media->m_cropGeometry->m_position.m_x-media->m_geometry->m_position.m_x,
+ media->m_cropGeometry->m_position.m_y-media->m_geometry->m_position.m_y
+ };
+ if (decalBegin[0]<0) decalBegin[0]=0;
+ if (decalBegin[1]<1) decalBegin[1]=0;
+ double decalEnd[]= {media->m_geometry->m_position.m_x+media->m_geometry->m_size.m_width
+ -media->m_cropGeometry->m_position.m_x-media->m_cropGeometry->m_size.m_width,
+ media->m_geometry->m_position.m_y+media->m_geometry->m_size.m_height
+ -media->m_cropGeometry->m_position.m_y-media->m_cropGeometry->m_size.m_height,
+ };
+ if (decalEnd[0]<0) decalEnd[0]=0;
+ if (decalEnd[1]<1) decalEnd[1]=0;
+ if (decalBegin[0]>0 || decalBegin[1]>0 || decalEnd[0]>0 || decalEnd[1]>0)
+ {
+ const double factor[]= { double(w)/media->m_geometry->m_size.m_width,
+ double(h)/media->m_geometry->m_size.m_height
+ };
+ std::stringstream s; // TRBL
+ s << "rect(" << pt2in(factor[1]*decalBegin[1]) << "in, " << pt2in(factor[0]*decalEnd[0]) << "in, "
+ << pt2in(factor[1]*decalEnd[1]) << "in, " << pt2in(factor[0]*decalBegin[0]) << "in)";
+ props.insert("fo:clip", s.str().c_str());
+ }
+ }
- So for now, we only reset the origin and resize the picture to its final size */
pos = glm::dvec3(media->m_cropGeometry->m_position.m_x, media->m_cropGeometry->m_position.m_y, 1);
dim = glm::dvec3(media->m_cropGeometry->m_size.m_width, media->m_cropGeometry->m_size.m_height, 0);
if (m_accumulateTransform)
diff --git a/src/lib/libetonyek_utils.cpp b/src/lib/libetonyek_utils.cpp
index cfc78c2..1743aa3 100644
--- a/src/lib/libetonyek_utils.cpp
+++ b/src/lib/libetonyek_utils.cpp
@@ -318,25 +318,34 @@ void writeBorder(const IWORKStroke &stroke, const char *const name, librevenge::
namespace
{
+const unsigned char SIGNATURE_BMP[] = { 'B', 'M' };
+const unsigned char SIGNATURE_GIF87[] = { 'G', 'I', 'F', '8', '7', 'a' };
+const unsigned char SIGNATURE_GIF89[] = { 'G', 'I', 'F', '8', '9', 'a' };
+const unsigned char SIGNATURE_JPEG[] = { 0xff, 0xd8 };
+const unsigned char SIGNATURE_JPEG2000[] = { 0, 0, 0, 0xc, 'j', 'P', ' ', ' ' };
const unsigned char SIGNATURE_PDF[] = { '%', 'P', 'D', 'F' };
const unsigned char SIGNATURE_PNG[] = { 0x89, 'P', 'N', 'G', 0x0d, 0x0a, 0x1a, 0x0a };
-const unsigned char SIGNATURE_JPEG[] = { 0xff, 0xd8 };
const unsigned char SIGNATURE_QUICKTIME[] = { 'm', 'o', 'o', 'v' };
const unsigned char SIGNATURE_TIFF_1[] = { 0x49, 0x49, 0x2a, 0x00 };
const unsigned char SIGNATURE_TIFF_2[] = { 0x4d, 0x4d, 0x00, 0x2a };
}
std::string detectMimetype(const RVNGInputStreamPtr_t &stream)
+try
{
stream->seek(0, librevenge::RVNG_SEEK_SET);
unsigned long numBytesRead = 0;
const unsigned char *const sig = stream->read(8, numBytesRead);
- if (8 != numBytesRead)
+ if (!sig || 8 != numBytesRead)
// looks like the binary is broken anyway: just bail out
return std::string();
+ if (0 == std::memcmp(sig, SIGNATURE_GIF87, ETONYEK_NUM_ELEMENTS(SIGNATURE_GIF87)) ||
+ 0 == std::memcmp(sig, SIGNATURE_GIF89, ETONYEK_NUM_ELEMENTS(SIGNATURE_GIF89)))
+ return std::string("image/gif");
+
if (0 == std::memcmp(sig, SIGNATURE_PNG, ETONYEK_NUM_ELEMENTS(SIGNATURE_PNG)))
return std::string("image/png");
@@ -353,6 +362,12 @@ std::string detectMimetype(const RVNGInputStreamPtr_t &stream)
if (0 == std::memcmp(sig, SIGNATURE_JPEG, ETONYEK_NUM_ELEMENTS(SIGNATURE_JPEG)))
return std::string("image/jpeg");
+ if (0 == std::memcmp(sig, SIGNATURE_JPEG2000, ETONYEK_NUM_ELEMENTS(SIGNATURE_JPEG2000)))
+ return std::string("image/jpx");
+
+ if (0 == std::memcmp(sig, SIGNATURE_BMP, ETONYEK_NUM_ELEMENTS(SIGNATURE_BMP)))
+ return std::string("image/bmp");
+
// FIXME: add code to detect apple pict file, ie. MathType can generate some
static bool first=true;
if (first)
@@ -362,6 +377,236 @@ std::string detectMimetype(const RVNGInputStreamPtr_t &stream)
}
return std::string();
}
+catch (...)
+{
+ return std::string();
+}
+
+
+bool detectImageDimension(const RVNGInputStreamPtr_t &stream, double &width, double &height)
+try
+{
+ width=height=0;
+ stream->seek(0, librevenge::RVNG_SEEK_SET);
+
+ unsigned long numBytesRead = 0;
+ const unsigned char *const sig = stream->read(8, numBytesRead);
+ if (!sig && numBytesRead!=8)
+ return false;
+ if (0 == std::memcmp(sig, SIGNATURE_GIF87, ETONYEK_NUM_ELEMENTS(SIGNATURE_GIF87)) ||
+ 0 == std::memcmp(sig, SIGNATURE_GIF89, ETONYEK_NUM_ELEMENTS(SIGNATURE_GIF89)))
+ {
+ stream->seek(6, librevenge::RVNG_SEEK_SET);
+ width=double(readU16(stream, false));
+ height=double(readU16(stream, false));
+ }
+ else if (0 == std::memcmp(sig, SIGNATURE_PNG, ETONYEK_NUM_ELEMENTS(SIGNATURE_PNG)))
+ {
+ stream->seek(8, librevenge::RVNG_SEEK_SET);
+ unsigned len=readU32(stream, true);
+ unsigned type=readU32(stream, true);
+ if (type!=0x49484452 || len<8) // IHDR
+ return false;
+ width=double(readU32(stream, true));
+ height=double(readU32(stream, true));
+ if (stream->seek(len-8+4, librevenge::RVNG_SEEK_CUR)!=0)
+ return false;
+ while (!stream->isEnd())
+ {
+ len=readU32(stream, true);
+ type=readU32(stream, true);
+ if (type==0x49454e44) // IEND
+ break;
+ if (type!=0x70485973) // pHYs
+ {
+ if (stream->seek(len+4, librevenge::RVNG_SEEK_CUR)!=0)
+ return false;
+ continue;
+ }
+ if (len>=9)
+ {
+ unsigned xPixels=readU32(stream, true);
+ unsigned yPixels=readU32(stream, true);
+ unsigned unit=readU8(stream);
+ if (unit==1) // unit is meter 1 inch=0.0254 meters.
+ {
+ const double factor[]= {std::floor(0.0254*xPixels+0.5)/72, std::floor(0.0254*yPixels+0.5)/72};
+ if (factor[0]<=0 || factor[1]<=0)
+ return false;
+ width/=factor[0];
+ height/=factor[1];
+ }
+ else
+ return false;
+ }
+ break;
+ }
+ }
+ else if (0 == std::memcmp(sig, SIGNATURE_JPEG, ETONYEK_NUM_ELEMENTS(SIGNATURE_JPEG)))
+ {
+ stream->seek(0, librevenge::RVNG_SEEK_SET);
+ unsigned size=2, type=0;
+ double factor[]= {1,1};
+ while (true)
+ {
+ unsigned nextPos=unsigned(stream->tell())+size;
+ if (type==0xc0 || type==0xc2)
+ {
+ stream->seek(1, librevenge::RVNG_SEEK_CUR);
+ height=double(readU16(stream, true))/factor[1];
+ width=double(readU16(stream, true))/factor[0];
+ break;
+ }
+ else if (type==0xd9)
+ break;
+ else if (type==0xe0)
+ {
+ if (readU32(stream, true)==0x4a464946) // JFIG
+ {
+ stream->seek(3, librevenge::RVNG_SEEK_CUR);
+ unsigned unit=readU8(stream);
+ unsigned xPixels=readU16(stream, true);
+ unsigned yPixels=readU16(stream, true);
+ if (unit==1) // by inch
+ {
+ factor[0]=double(xPixels)/72;
+ factor[1]=double(yPixels)/72;
+ }
+ else if (unit==2) // by cm
+ {
+ factor[0]=std::floor(2.54*xPixels+0.5)/72;
+ factor[1]=std::floor(2.54*yPixels+0.5)/72;
+ }
+ if (factor[0]<=0 || factor[1]<=0)
+ return false;
+ }
+ }
+ if (stream->seek(nextPos, librevenge::RVNG_SEEK_SET)!=0)
+ return false;
+ if (stream->isEnd())
+ break;
+ type=readU8(stream, true);
+ while (type==0xff)
+ type=readU8(stream, true);
+ if (type>=0xd0 && type<=0xd8)
+ size=0;
+ else
+ {
+ size=readU16(stream, true);
+ if (size<2)
+ return false;
+ size-=2;
+ }
+ }
+ }
+#if 0
+ else if (0 == std::memcmp(sig, SIGNATURE_JPEG2000, ETONYEK_NUM_ELEMENTS(SIGNATURE_JPEG2000)))
+ {
+ // fixme: this format can store images with different resolutions
+ stream->seek(48, librevenge::RVNG_SEEK_SET);
+ height=double(readU32(stream, true));
+ width=double(readU32(stream, true));
+ }
+#endif
+#if 0
+ else if ((0 == std::memcmp(sig, SIGNATURE_TIFF_1, ETONYEK_NUM_ELEMENTS(SIGNATURE_TIFF_1))) ||
+ (0 == std::memcmp(sig, SIGNATURE_TIFF_2, ETONYEK_NUM_ELEMENTS(SIGNATURE_TIFF_2))))
+ {
+ // fixme: the code seems ok, but LibreOffice seems to compute the image's dimensions differently :-~
+ bool bigEndian=std::memcmp(sig, SIGNATURE_TIFF_2, ETONYEK_NUM_ELEMENTS(SIGNATURE_TIFF_2))==0;
+ stream->seek(4, librevenge::RVNG_SEEK_SET);
+ if (stream->seek(readU32(stream, bigEndian), librevenge::RVNG_SEEK_SET)!=0 || stream->isEnd())
+ return false;
+ unsigned N=readU16(stream, bigEndian);
+ unsigned unit=2;
+ double factor[]= {0, 0};
+ for (unsigned n=0; n<N; ++n)
+ {
+ unsigned tag=readU16(stream, bigEndian);
+ unsigned type=readU16(stream, bigEndian);
+ if (stream->seek(4, librevenge::RVNG_SEEK_CUR)!=0)
+ return false;
+ unsigned nextPos=unsigned(stream->tell())+4;
+ if (tag==256 || tag==257)
+ {
+ double &wh=tag==256 ? width : height;
+ if (type==3)
+ wh=double(readU16(stream, bigEndian));
+ else if (type==4)
+ wh=double(readU32(stream, bigEndian));
+ else
+ return false;
+ }
+ if (tag==296 && type==3)
+ unit=readU16(stream, bigEndian);
+ if ((tag==282 || tag==283) && type==5)
+ {
+ if (stream->seek(readU32(stream, bigEndian), librevenge::RVNG_SEEK_SET)!=0)
+ return false;
+ unsigned numer=readU32(stream, bigEndian);
+ unsigned denom=readU32(stream, bigEndian);
+ if (!denom)
+ return false;
+ factor[tag==282 ? 0 : 1]=double(numer)/double(denom);
+ }
+ if (stream->seek(nextPos, librevenge::RVNG_SEEK_SET)!=0)
+ return false;
+ }
+ if (unit==2) // inch
+ {
+ factor[0]/=72;
+ factor[1]/=72;
+ }
+ else if (unit==3) // cm
+ {
+ factor[0]=std::floor(2.54*factor[0]+0.5)/72;
+ factor[1]=std::floor(2.54*factor[1]+0.5)/72;
+ }
+ if (factor[0]>0 && factor[1]>0)
+ {
+ width /= factor[0];
+ height /= factor[1];
+ }
+ }
+#endif
+ else if (0 == std::memcmp(sig, SIGNATURE_BMP, ETONYEK_NUM_ELEMENTS(SIGNATURE_BMP)))
+ {
+ stream->seek(14, librevenge::RVNG_SEEK_SET);
+ unsigned headerSz=readU32(stream, false);
+ if (headerSz==12)
+ {
+ width=double(readU16(stream, false));
+ height=double(readU16(stream, false));
+ }
+ else
+ {
+ width=double(readU32(stream, false));
+ height=double(std::abs<int32_t>(int32_t(readU32(stream, false))));
+ if (headerSz>=46)
+ {
+ stream->seek(38, librevenge::RVNG_SEEK_SET);
+ // resolution unit meters
+ double factor[2];
+ factor[0]=std::floor(0.0254*readU32(stream, false)+0.5)/72;
+ factor[1]=std::floor(0.0254*readU32(stream, false)+0.5)/72;
+ if (factor[0]<=0 || factor[1]<=0)
+ return false;
+ width/=factor[0];
+ height/=factor[1];
+ }
+ }
+ }
+ else
+ return false;
+ if (width<10 || width>3000 || height<10 || height>3000)
+ return false;
+ return true;
+}
+catch (...)
+{
+ return false;
+}
+
}
diff --git a/src/lib/libetonyek_utils.h b/src/lib/libetonyek_utils.h
index d25f2cc..ad17caa 100644
--- a/src/lib/libetonyek_utils.h
+++ b/src/lib/libetonyek_utils.h
@@ -145,6 +145,7 @@ librevenge::RVNGString makeColor(const IWORKGradient &gradient);
void writeBorder(const IWORKStroke &stroke, const char *name, librevenge::RVNGPropertyList &props);
std::string detectMimetype(const RVNGInputStreamPtr_t &stream);
+bool detectImageDimension(const RVNGInputStreamPtr_t &stream, double &width, double &height);
class EndOfStreamException
{